From 71888d40e71be0c217612319181a6a8ccd9dc35f Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 23 May 2022 19:42:07 +0100 Subject: [PATCH] added tls processing --- Cargo.lock | 95 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ examples/tls/tls.rs | 106 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 192 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 091c3f0..86a3f61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + [[package]] name = "atty" version = "0.2.14" @@ -28,6 +34,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 +73,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" @@ -106,6 +124,7 @@ dependencies = [ name = "clone-shim" version = "0.1.0" dependencies = [ + "anyhow", "clap 3.1.15", "close_fds", "criterion", @@ -116,6 +135,8 @@ dependencies = [ "libc", "log", "nix", + "rustls", + "rustls-pemfile", "serde", "serde_json", "tempfile", @@ -427,6 +448,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + [[package]] name = "oorandom" version = "11.1.3" @@ -551,6 +578,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" @@ -560,6 +602,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" @@ -581,6 +644,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" @@ -628,6 +701,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" @@ -725,6 +804,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" @@ -800,6 +885,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" diff --git a/Cargo.toml b/Cargo.toml index 97634c3..7d53d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,12 @@ tempfile = "3.3" [dev-dependencies] criterion = "0.3" +anyhow = "1" # examples/tls httparse = "1" +rustls = "0.20" +rustls-pemfile = "1" [[bench]] name = "clone3" diff --git a/examples/tls/tls.rs b/examples/tls/tls.rs index 62dfd2c..39cae5c 100644 --- a/examples/tls/tls.rs +++ b/examples/tls/tls.rs @@ -1,21 +1,29 @@ use std::fs::File; -use std::io::{self, ErrorKind, Read, Write}; +use std::io::{self, BufReader, ErrorKind, Read, Write}; use std::net::TcpStream; use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixStream; +use std::sync::Arc; use nix::poll::{poll, PollFd, PollFlags}; +use rustls::ServerConnection; + +use anyhow::Context; + const BUFFER_SIZE: usize = 4096; pub(crate) fn handler( http_trigger_socket: File, - _cert: File, - _key: File, + cert: File, + key: File, mut stream: TcpStream, ) -> i32 { let (mut socket, far_socket) = UnixStream::pair().unwrap(); + let config = make_config(cert, key); + let mut tls_conn = rustls::ServerConnection::new(config).unwrap(); + super::http_handler(&http_trigger_socket, far_socket); stream.set_nonblocking(true).unwrap(); @@ -32,13 +40,13 @@ pub(crate) fn handler( if let Some(events) = to_poll[0].revents() { if events.contains(PollFlags::POLLIN) { - handle_encrypted_data(&mut stream, &mut socket).unwrap(); + handle_encrypted_data(&mut tls_conn, &mut stream, &mut socket).unwrap(); } } if let Some(events) = to_poll[1].revents() { if events.contains(PollFlags::POLLIN) { - handle_new_data(&mut socket, &mut stream).unwrap(); + handle_new_data(&mut tls_conn, &mut socket, &mut stream).unwrap(); } if events.contains(PollFlags::POLLHUP) { @@ -51,29 +59,61 @@ pub(crate) fn handler( exitcode::OK } -fn handle_encrypted_data(stream: &mut impl Read, socket: &mut impl Write) -> io::Result<()> { - let mut buf = [0_u8; BUFFER_SIZE]; +fn handle_encrypted_data( + tls_conn: &mut ServerConnection, + stream: &mut (impl Read + Write), + socket: &mut impl Write, +) -> anyhow::Result<()> { + println!("handling newly received encrypted data"); loop { - let read = non_blocking_read(stream, &mut buf)?; + let read = match tls_conn.read_tls(stream) { + Err(e) => { + if e.kind() == ErrorKind::WouldBlock { + 0 + } else { + return Err(e).context("io error reading from stream"); + } + } + Ok(n) => n, + }; + if read == 0 { return Ok(()); } - socket.write_all(&buf[0..read]).unwrap(); + let process_result = tls_conn.process_new_packets(); + let write_tls_result = tls_conn.write_tls(stream); + + let io_state = process_result.context("tls processing failure")?; + write_tls_result.context("tls write failure")?; + + if io_state.plaintext_bytes_to_read() > 0 { + let mut reader = tls_conn + .reader() + .take(io_state.plaintext_bytes_to_read() as u64); + + std::io::copy(&mut reader, socket)?; + } } } -fn handle_new_data(socket: &mut impl Read, stream: &mut impl Write) -> io::Result<()> { - let mut buf = [0_u8; BUFFER_SIZE]; +fn handle_new_data( + tls_conn: &mut ServerConnection, + socket: &mut impl Read, + stream: &mut impl Write, +) -> anyhow::Result<()> { + println!("handling new data to encrypt"); + let mut buf = [0_u8; BUFFER_SIZE]; loop { let read = non_blocking_read(socket, &mut buf)?; if read == 0 { return Ok(()); } - stream.write_all(&buf[0..read]).unwrap(); + tls_conn.writer().write_all(&buf[0..read])?; + tls_conn.write_tls(stream)?; } } @@ -89,3 +129,45 @@ fn non_blocking_read(reader: &mut impl io::Read, buf: &mut [u8]) -> io::Result Ok(n), } } + +fn make_config(cert: File, key: File) -> Arc { + 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) +} + +fn load_certs(certfile: File) -> Vec { + 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: 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 (encrypted keys not supported)"); +}