wip: tls server
This commit is contained in:
parent
3069f12da0
commit
5ab9d09378
150
Cargo.lock
generated
150
Cargo.lock
generated
@ -28,6 +28,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -61,6 +67,12 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@ -114,7 +126,10 @@ dependencies = [
|
||||
"ipnetwork",
|
||||
"libc",
|
||||
"log",
|
||||
"mio",
|
||||
"nix",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
@ -389,6 +404,18 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.1"
|
||||
@ -420,6 +447,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
@ -544,6 +577,21 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
@ -553,6 +601,27 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
@ -574,6 +643,16 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.6"
|
||||
@ -621,6 +700,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@ -718,6 +803,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
@ -729,6 +820,12 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
@ -793,6 +890,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -823,3 +930,46 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
@ -25,6 +25,10 @@ tempfile = "3.3"
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
rustls = "0.20"
|
||||
rustls-pemfile = "1.0.0"
|
||||
mio = { version = "0.8", features = ["net", "os-poll", "os-ext"] }
|
||||
|
||||
[[bench]]
|
||||
name = "clone3"
|
||||
harness = false
|
||||
|
10
README.md
10
README.md
@ -32,6 +32,16 @@ To run this example:
|
||||
cargo build --example pipes
|
||||
target/debug/clone-shim -s examples/pipes/spec.json target/debug/examples/pipes
|
||||
|
||||
### examples/tls
|
||||
|
||||
The tls example runs a fully privilege separated TLS server under the shim. A TCP listener is handed to a "tcp_listener" process. This spawns separated "tls_handler" processes, which are the only processes with access to the certificate files. Finally, a "request_handler" process is handed shared memory with the "tls_handler" to handle the TLS request.
|
||||
|
||||
To run this example:
|
||||
|
||||
cargo build
|
||||
cargo build --example tls
|
||||
target/debug/clone-shim -s examples/tls/spec.json target/debug/examples/tls
|
||||
|
||||
## Debugging the shim
|
||||
|
||||
The shim can be debugged as with most processes, but it is exceptionally forky. Breaking before a clone in `rust-gdb` then running `set follow-fork-mode child` is often necessary. The best approach is to go in with a plan of attack.
|
||||
|
10
examples/tls/http.rs
Normal file
10
examples/tls/http.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
pub(super) fn handler(_request: File, mut response: File) -> i32 {
|
||||
println!("entered http handler");
|
||||
|
||||
response.write_all(b"hello world!").unwrap();
|
||||
|
||||
0
|
||||
}
|
177
examples/tls/main.rs
Normal file
177
examples/tls/main.rs
Normal file
@ -0,0 +1,177 @@
|
||||
mod http;
|
||||
mod tls;
|
||||
|
||||
use std::fs::File;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::os::unix::io::IntoRawFd;
|
||||
|
||||
fn main() {
|
||||
let mut args = std::env::args();
|
||||
|
||||
let _bin = args.next();
|
||||
let entrypoint = args.next();
|
||||
|
||||
match entrypoint {
|
||||
Some(s) => match s.as_str() {
|
||||
"connection_listener" => connection_listener_entrypoint(),
|
||||
"tls_handler" => tls_handler_entrypoint(),
|
||||
"request_handler" => request_handler_entrypoint(),
|
||||
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
None => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn connection_listener_entrypoint() {
|
||||
// imports
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
// argument parsing
|
||||
let mut args = std::env::args();
|
||||
|
||||
let _bin = args.next();
|
||||
let _entrypoint = args.next();
|
||||
|
||||
let tcp_listener = args.next();
|
||||
let tcp_listener: RawFd = tcp_listener
|
||||
.expect("tcp listener required")
|
||||
.parse()
|
||||
.expect("tcp listener should be a file descriptor");
|
||||
let tcp_listener = unsafe { TcpListener::from_raw_fd(tcp_listener) };
|
||||
|
||||
let tls_handler_trigger = args.next();
|
||||
let tls_handler_trigger: RawFd = tls_handler_trigger
|
||||
.expect("request handler required")
|
||||
.parse()
|
||||
.expect("tcp listener should be a file descriptor");
|
||||
let tls_handler_trigger = unsafe { File::from_raw_fd(tls_handler_trigger) };
|
||||
|
||||
// real function body
|
||||
fn connection_listener(tls_handler_trigger: File, tcp_listener: TcpListener) -> i32 {
|
||||
println!("connection_listener entered");
|
||||
|
||||
// handle incoming connections
|
||||
for stream in tcp_listener.incoming() {
|
||||
let stream = match stream {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
println!("connection listener: error: {}", e);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
tls_handler(&tls_handler_trigger, stream);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
std::process::exit(connection_listener(tls_handler_trigger, tcp_listener))
|
||||
}
|
||||
|
||||
fn tls_handler(trigger_socket: &File, stream: TcpStream) {
|
||||
// imports
|
||||
use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
// send file descriptor(s)
|
||||
let sockfd = trigger_socket.as_raw_fd();
|
||||
let fds = [stream.into_raw_fd()];
|
||||
|
||||
sendmsg::<()>(
|
||||
sockfd,
|
||||
&[],
|
||||
&[ControlMessage::ScmRights(&fds)],
|
||||
MsgFlags::empty(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn tls_handler_entrypoint() {
|
||||
// imports
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
// argument parsing
|
||||
let mut args = std::env::args();
|
||||
|
||||
let _bin = args.next();
|
||||
let _entrypoint = args.next();
|
||||
|
||||
let request_handler_trigger = args.next();
|
||||
let request_handler_trigger: RawFd = request_handler_trigger
|
||||
.expect("request handler required")
|
||||
.parse()
|
||||
.expect("request handler should be a file descriptor");
|
||||
let request_handler_trigger = unsafe { File::from_raw_fd(request_handler_trigger) };
|
||||
|
||||
let cert = args.next();
|
||||
let cert: RawFd = cert
|
||||
.expect("cert required")
|
||||
.parse()
|
||||
.expect("cert should be a file descriptor");
|
||||
let cert = unsafe { File::from_raw_fd(cert) };
|
||||
|
||||
let key = args.next();
|
||||
let key: RawFd = key
|
||||
.expect("key required")
|
||||
.parse()
|
||||
.expect("key should be a file descriptor");
|
||||
let key = unsafe { File::from_raw_fd(key) };
|
||||
|
||||
let stream = args.next();
|
||||
let stream: RawFd = stream
|
||||
.expect("tcp stream required")
|
||||
.parse()
|
||||
.expect("tcp stream should be a file descriptor");
|
||||
let stream = unsafe { TcpStream::from_raw_fd(stream) };
|
||||
|
||||
std::process::exit(tls::handler(request_handler_trigger, cert, key, stream))
|
||||
}
|
||||
|
||||
fn request_handler(trigger_socket: &File, requestfd: impl IntoRawFd, responsefd: impl IntoRawFd) {
|
||||
// imports
|
||||
use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
// send file descriptor(s)
|
||||
let sockfd = trigger_socket.as_raw_fd();
|
||||
let fds = [requestfd.into_raw_fd(), responsefd.into_raw_fd()];
|
||||
|
||||
sendmsg::<()>(
|
||||
sockfd,
|
||||
&[],
|
||||
&[ControlMessage::ScmRights(&fds)],
|
||||
MsgFlags::empty(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn request_handler_entrypoint() {
|
||||
// imports
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
// argument parsing
|
||||
let mut args = std::env::args();
|
||||
|
||||
let _bin = args.next();
|
||||
let _entrypoint = args.next();
|
||||
|
||||
let request = args.next();
|
||||
let request: RawFd = request
|
||||
.expect("request stream required")
|
||||
.parse()
|
||||
.expect("request stream should be a file descriptor");
|
||||
let request = unsafe { File::from_raw_fd(request) };
|
||||
|
||||
let response = args.next();
|
||||
let response: RawFd = response
|
||||
.expect("response stream required")
|
||||
.parse()
|
||||
.expect("response stream should be a file descriptor");
|
||||
let response = unsafe { File::from_raw_fd(response) };
|
||||
|
||||
std::process::exit(http::handler(request, response))
|
||||
}
|
117
examples/tls/spec.json
Normal file
117
examples/tls/spec.json
Normal file
@ -0,0 +1,117 @@
|
||||
{
|
||||
"entrypoints": {
|
||||
"connection_listener": {
|
||||
"args": [
|
||||
"BinaryName",
|
||||
"Entrypoint",
|
||||
{
|
||||
"TcpListener": {
|
||||
"addr": "0.0.0.0:8443"
|
||||
}
|
||||
},
|
||||
{
|
||||
"FileSocket": {
|
||||
"Tx": "tls"
|
||||
}
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libgcc_s.so.1",
|
||||
"environment_path": "/lib/libgcc_s.so.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libc.so.6",
|
||||
"environment_path": "/lib/libc.so.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib64/ld-linux-x86-64.so.2",
|
||||
"environment_path": "/lib64/ld-linux-x86-64.so.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tls_handler": {
|
||||
"trigger": {
|
||||
"FileSocket": "tls"
|
||||
},
|
||||
"args": [
|
||||
"BinaryName",
|
||||
"Entrypoint",
|
||||
{
|
||||
"FileSocket": {
|
||||
"Tx": "request"
|
||||
}
|
||||
},
|
||||
{
|
||||
"File": "/etc/ssl/certs/example.com.pem"
|
||||
},
|
||||
{
|
||||
"File": "/etc/ssl/private/example.com.key"
|
||||
},
|
||||
"Trigger"
|
||||
],
|
||||
"environment": [
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libgcc_s.so.1",
|
||||
"environment_path": "/lib/libgcc_s.so.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libc.so.6",
|
||||
"environment_path": "/lib/libc.so.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib64/ld-linux-x86-64.so.2",
|
||||
"environment_path": "/lib64/ld-linux-x86-64.so.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"request_handler": {
|
||||
"trigger": {
|
||||
"FileSocket": "request"
|
||||
},
|
||||
"args": [
|
||||
"BinaryName",
|
||||
"Entrypoint",
|
||||
"Trigger"
|
||||
],
|
||||
"environment": [
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/var/www/html",
|
||||
"environment_path": "/var/www/html"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libgcc_s.so.1",
|
||||
"environment_path": "/lib/libgcc_s.so.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib/x86_64-linux-gnu/libc.so.6",
|
||||
"environment_path": "/lib/libc.so.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Filesystem": {
|
||||
"host_path": "/lib64/ld-linux-x86-64.so.2",
|
||||
"environment_path": "/lib64/ld-linux-x86-64.so.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
306
examples/tls/tls.rs
Normal file
306
examples/tls/tls.rs
Normal file
@ -0,0 +1,306 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
//
|
||||
// This example is adapted from rustls/rustls-mio/examples/tlsserver.rs
|
||||
// Both pieces of work are under the MIT license. Thanks to the original
|
||||
// author.
|
||||
//
|
||||
// Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
// Copyright (c) 2022 Jake Hillion <jake@hillion.co.uk>
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
use super::request_handler;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use std::net;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mio::net::{TcpListener, TcpStream};
|
||||
use mio::unix::pipe;
|
||||
use rustls;
|
||||
|
||||
pub(super) fn handler(
|
||||
request_handler_trigger: File,
|
||||
mut cert: File,
|
||||
mut key: File,
|
||||
stream: net::TcpStream,
|
||||
) -> i32 {
|
||||
println!("tls handler entered");
|
||||
|
||||
let config = make_config(&mut cert, &mut key);
|
||||
|
||||
let mut stream = TcpStream::from_std(stream);
|
||||
|
||||
let mut poll = mio::Poll::new().unwrap();
|
||||
poll.registry()
|
||||
.register(&mut stream, TCP_STREAM, mio::Interest::READABLE)
|
||||
.unwrap();
|
||||
|
||||
let (tls_data_in_send, mut tls_data_in_recv) = pipe::new().unwrap();
|
||||
poll.registry()
|
||||
.register(&mut tls_data_in_recv, TLS_DATA_IN, mio::Interest::READABLE)
|
||||
.unwrap();
|
||||
|
||||
let (mut tls_data_out_send, tls_data_out_recv) = pipe::new().unwrap();
|
||||
|
||||
request_handler(
|
||||
&request_handler_trigger,
|
||||
tls_data_out_recv, // request
|
||||
tls_data_in_send, // response
|
||||
);
|
||||
|
||||
let mut events = mio::Events::with_capacity(256);
|
||||
let mut read_buf = [0_u8; 1024];
|
||||
loop {
|
||||
poll.poll(&mut events, None).unwrap();
|
||||
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
TCP_STREAM => {}
|
||||
TLS_DATA_IN => {
|
||||
let read_bytes = tls_data_in_recv.read(&mut read_buf).unwrap();
|
||||
// TODO: encrypt me
|
||||
}
|
||||
_ => unreachable!("only two tokens are registered"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
const TCP_STREAM: mio::Token = mio::Token(0);
|
||||
const TLS_DATA_IN: mio::Token = mio::Token(1);
|
||||
|
||||
/// This binds together a TCP listening socket, some outstanding
|
||||
/// connections, and a TLS server configuration.
|
||||
struct TlsServer {
|
||||
server: TcpListener,
|
||||
connections: HashMap<mio::Token, OpenConnection>,
|
||||
next_id: usize,
|
||||
tls_config: Arc<rustls::ServerConfig>,
|
||||
}
|
||||
|
||||
/// This is a connection which has been accepted by the server,
|
||||
/// and is currently being served.
|
||||
///
|
||||
/// It has a TCP-level stream, a TLS-level connection state, and some
|
||||
/// other state/metadata.
|
||||
struct OpenConnection {
|
||||
socket: TcpStream,
|
||||
token: mio::Token,
|
||||
closing: bool,
|
||||
closed: bool,
|
||||
tls_conn: rustls::ServerConnection,
|
||||
sent_http_response: bool,
|
||||
}
|
||||
|
||||
/// This used to be conveniently exposed by mio: map EWOULDBLOCK
|
||||
/// errors to something less-errory.
|
||||
fn try_read(r: io::Result<usize>) -> io::Result<Option<usize>> {
|
||||
match r {
|
||||
Ok(len) => Ok(Some(len)),
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenConnection {
|
||||
fn new(
|
||||
socket: TcpStream,
|
||||
token: mio::Token,
|
||||
tls_conn: rustls::ServerConnection,
|
||||
) -> OpenConnection {
|
||||
OpenConnection {
|
||||
socket,
|
||||
token,
|
||||
closing: false,
|
||||
closed: false,
|
||||
tls_conn,
|
||||
sent_http_response: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// We're a connection, and we have something to do.
|
||||
fn ready(&mut self, registry: &mio::Registry, ev: &mio::event::Event) {
|
||||
// If we're readable: read some TLS. Then
|
||||
// see if that yielded new plaintext. Then
|
||||
// see if the backend is readable too.
|
||||
if ev.is_readable() {
|
||||
self.do_tls_read();
|
||||
self.try_plain_read();
|
||||
}
|
||||
|
||||
if ev.is_writable() {
|
||||
self.do_tls_write_and_handle_error();
|
||||
}
|
||||
|
||||
if self.closing {
|
||||
let _ = self.socket.shutdown(net::Shutdown::Both);
|
||||
self.closed = true;
|
||||
self.deregister(registry);
|
||||
} else {
|
||||
self.reregister(registry);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_tls_read(&mut self) {
|
||||
// Read some TLS data.
|
||||
match self.tls_conn.read_tls(&mut self.socket) {
|
||||
Err(err) => {
|
||||
if let io::ErrorKind::WouldBlock = err.kind() {
|
||||
return;
|
||||
}
|
||||
|
||||
error!("read error {:?}", err);
|
||||
self.closing = true;
|
||||
return;
|
||||
}
|
||||
Ok(0) => {
|
||||
debug!("eof");
|
||||
self.closing = true;
|
||||
return;
|
||||
}
|
||||
Ok(_) => {}
|
||||
};
|
||||
|
||||
// Process newly-received TLS messages.
|
||||
if let Err(err) = self.tls_conn.process_new_packets() {
|
||||
error!("cannot process packet: {:?}", err);
|
||||
|
||||
// last gasp write to send any alerts
|
||||
self.do_tls_write_and_handle_error();
|
||||
|
||||
self.closing = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn try_plain_read(&mut self) {
|
||||
// Read and process all available plaintext.
|
||||
if let Ok(io_state) = self.tls_conn.process_new_packets() {
|
||||
if io_state.plaintext_bytes_to_read() > 0 {
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(io_state.plaintext_bytes_to_read(), 0u8);
|
||||
|
||||
self.tls_conn.reader().read_exact(&mut buf).unwrap();
|
||||
|
||||
debug!("plaintext read {:?}", buf.len());
|
||||
self.incoming_plaintext(&buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process some amount of received plaintext.
|
||||
fn incoming_plaintext(&mut self, buf: &[u8]) {
|
||||
self.tls_conn.writer().write_all(buf).unwrap();
|
||||
}
|
||||
|
||||
fn send_http_response_once(&mut self) {
|
||||
let response =
|
||||
b"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nHello world from rustls tlsserver\r\n";
|
||||
if !self.sent_http_response {
|
||||
self.tls_conn.writer().write_all(response).unwrap();
|
||||
self.sent_http_response = true;
|
||||
self.tls_conn.send_close_notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn tls_write(&mut self) -> io::Result<usize> {
|
||||
self.tls_conn.write_tls(&mut self.socket)
|
||||
}
|
||||
|
||||
fn do_tls_write_and_handle_error(&mut self) {
|
||||
let rc = self.tls_write();
|
||||
if rc.is_err() {
|
||||
error!("write failed {:?}", rc);
|
||||
self.closing = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&mut self, registry: &mio::Registry) {
|
||||
let event_set = self.event_set();
|
||||
registry
|
||||
.register(&mut self.socket, self.token, event_set)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn reregister(&mut self, registry: &mio::Registry) {
|
||||
let event_set = self.event_set();
|
||||
registry
|
||||
.reregister(&mut self.socket, self.token, event_set)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn deregister(&mut self, registry: &mio::Registry) {
|
||||
registry.deregister(&mut self.socket).unwrap();
|
||||
}
|
||||
|
||||
/// What IO events we're currently waiting for,
|
||||
/// based on wants_read/wants_write.
|
||||
fn event_set(&self) -> mio::Interest {
|
||||
let rd = self.tls_conn.wants_read();
|
||||
let wr = self.tls_conn.wants_write();
|
||||
|
||||
if rd && wr {
|
||||
mio::Interest::READABLE | mio::Interest::WRITABLE
|
||||
} else if wr {
|
||||
mio::Interest::WRITABLE
|
||||
} else {
|
||||
mio::Interest::READABLE
|
||||
}
|
||||
}
|
||||
|
||||
fn is_closed(&self) -> bool {
|
||||
self.closed
|
||||
}
|
||||
}
|
||||
|
||||
fn load_certs(certfile: &mut File) -> Vec<rustls::Certificate> {
|
||||
let mut reader = BufReader::new(certfile);
|
||||
rustls_pemfile::certs(&mut reader)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| rustls::Certificate(v.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn load_private_key(keyfile: &mut File) -> rustls::PrivateKey {
|
||||
let mut reader = BufReader::new(keyfile);
|
||||
|
||||
loop {
|
||||
match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") {
|
||||
Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key),
|
||||
Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key),
|
||||
Some(rustls_pemfile::Item::ECKey(key)) => return rustls::PrivateKey(key),
|
||||
None => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
panic!("no keys found in supplied file (encrypted keys not supported)");
|
||||
}
|
||||
|
||||
fn make_config(cert: &mut File, key: &mut File) -> Arc<rustls::ServerConfig> {
|
||||
let certs = load_certs(cert);
|
||||
let privkey = load_private_key(key);
|
||||
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_safe_default_protocol_versions()
|
||||
.expect("inconsistent cipher-suites/versions specified")
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, privkey)
|
||||
.expect("bad certificates/private key");
|
||||
|
||||
Arc::new(config)
|
||||
}
|
Loading…
Reference in New Issue
Block a user