From fda05bddd2bd7239ebbfe46472292bce6b929bbf Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 22 Feb 2022 14:01:25 -0800 Subject: [PATCH] Update 'rustls' to 0.20. Also updates 'tokio-rustls' to a compatible version. Additionally depends on 'rustls-pemfile' which includes functionality that was previously part of 'rustls' itself. --- core/http/Cargo.toml | 7 ++-- core/http/src/listener.rs | 2 +- core/http/src/tls/listener.rs | 63 ++++++++++++++++------------------- core/http/src/tls/util.rs | 16 ++++----- core/lib/src/config/tls.rs | 22 ++++++------ core/lib/src/ext.rs | 2 +- core/lib/src/server.rs | 2 +- 7 files changed, 55 insertions(+), 59 deletions(-) diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 3e4a8386..cff33d47 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -16,7 +16,7 @@ edition = "2018" [features] default = [] -tls = ["rustls", "tokio-rustls"] +tls = ["rustls", "tokio-rustls", "rustls-pemfile"] mtls = ["tls", "x509-parser"] private-cookies = ["cookie/private", "cookie/key-expansion"] serde = ["uncased/with-serde-alloc", "serde_"] @@ -28,8 +28,9 @@ percent-encoding = "2" http = "0.2" time = { version = "0.3", features = ["formatting", "macros"] } indexmap = { version = "1.5.2", features = ["std"] } -rustls = { version = "0.19", optional = true } -tokio-rustls = { version = "0.22.0", optional = true } +rustls = { version = "0.20", optional = true } +tokio-rustls = { version = "0.23.0", optional = true } +rustls-pemfile = { version = "0.3", optional = true } tokio = { version = "1.6.1", features = ["net", "sync", "time"] } log = "0.4" ref-cast = "1.0" diff --git a/core/http/src/listener.rs b/core/http/src/listener.rs index 2c608119..172d065f 100644 --- a/core/http/src/listener.rs +++ b/core/http/src/listener.rs @@ -54,7 +54,7 @@ pub trait Connection: AsyncRead + AsyncWrite { /// /// Defaults to an empty vector to indicate that no certificates were /// presented. - fn peer_certificates(&self) -> Option> { None } + fn peer_certificates(&self) -> Option<&[RawCertificate]> { None } } pin_project_lite::pin_project! { diff --git a/core/http/src/tls/listener.rs b/core/http/src/tls/listener.rs index 90d14f22..ac65b665 100644 --- a/core/http/src/tls/listener.rs +++ b/core/http/src/tls/listener.rs @@ -5,7 +5,6 @@ use std::task::{Context, Poll}; use std::net::SocketAddr; use std::future::Future; -use rustls::{ServerConfig, SupportedCipherSuite}; use tokio_rustls::{TlsAcceptor, Accept, server::TlsStream}; use tokio::net::{TcpListener, TcpStream}; @@ -27,7 +26,7 @@ enum State { pub struct Config { pub cert_chain: R, pub private_key: R, - pub ciphersuites: Vec<&'static SupportedCipherSuite>, + pub ciphersuites: Vec, pub prefer_server_order: bool, pub ca_certs: Option, pub mandatory_mtls: bool, @@ -37,40 +36,38 @@ impl TlsListener { pub async fn bind(addr: SocketAddr, mut c: Config) -> io::Result where R: io::BufRead { - let cert_chain = load_certs(&mut c.cert_chain).map_err(|e| { - let msg = format!("malformed TLS certificate chain: {}", e); - io::Error::new(e.kind(), msg) - })?; + use rustls::server::{AllowAnyAuthenticatedClient, AllowAnyAnonymousOrAuthenticatedClient}; + use rustls::server::{NoClientAuth, ServerSessionMemoryCache, ServerConfig}; - let key = load_private_key(&mut c.private_key).map_err(|e| { - let msg = format!("malformed TLS private key: {}", e); - io::Error::new(e.kind(), msg) - })?; + let cert_chain = load_certs(&mut c.cert_chain) + .map_err(|e| io::Error::new(e.kind(), format!("bad TLS cert chain: {}", e)))?; + + let key = load_private_key(&mut c.private_key) + .map_err(|e| io::Error::new(e.kind(), format!("bad TLS private key: {}", e)))?; let client_auth = match c.ca_certs { - Some(ref mut ca_certs) => { - let roots = load_ca_certs(ca_certs).map_err(|e| { - let msg = format!("malformed CA certificate(s): {}", e); - io::Error::new(e.kind(), msg) - })?; - - if c.mandatory_mtls { - rustls::AllowAnyAuthenticatedClient::new(roots) - } else { - rustls::AllowAnyAnonymousOrAuthenticatedClient::new(roots) - } - } - None => rustls::NoClientAuth::new(), + Some(ref mut ca_certs) => match load_ca_certs(ca_certs) { + Ok(ca_roots) if c.mandatory_mtls => AllowAnyAuthenticatedClient::new(ca_roots), + Ok(ca_roots) => AllowAnyAnonymousOrAuthenticatedClient::new(ca_roots), + Err(e) => return Err(io::Error::new(e.kind(), format!("bad CA cert(s): {}", e))), + }, + None => NoClientAuth::new(), }; - let mut tls_config = ServerConfig::new(client_auth); - let cache = rustls::ServerSessionMemoryCache::new(1024); - tls_config.set_persistence(cache); - tls_config.ticketer = rustls::Ticketer::new(); - tls_config.ciphersuites = c.ciphersuites; + let mut tls_config = ServerConfig::builder() + .with_cipher_suites(&c.ciphersuites) + .with_safe_default_kx_groups() + .with_safe_default_protocol_versions() + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS config: {}", e)))? + .with_client_cert_verifier(client_auth) + .with_single_cert(cert_chain, key) + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS config: {}", e)))?; + tls_config.ignore_client_order = c.prefer_server_order; - tls_config.set_single_cert(cert_chain, key).expect("invalid key"); - tls_config.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); + tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + tls_config.session_storage = ServerSessionMemoryCache::new(1024); + tls_config.ticketer = rustls::Ticketer::new() + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS ticketer: {}", e)))?; let listener = TcpListener::bind(addr).await?; let acceptor = TlsAcceptor::from(Arc::new(tls_config)); @@ -120,9 +117,7 @@ impl Connection for TlsStream { self.get_ref().0.peer_address() } - fn peer_certificates(&self) -> Option> { - use rustls::Session; - - self.get_ref().1.get_peer_certificates() + fn peer_certificates(&self) -> Option<&[RawCertificate]> { + self.get_ref().1.peer_certificates() } } diff --git a/core/http/src/tls/util.rs b/core/http/src/tls/util.rs index fa598f29..8edde83c 100644 --- a/core/http/src/tls/util.rs +++ b/core/http/src/tls/util.rs @@ -1,6 +1,6 @@ use std::io::{self, Cursor, Read}; -use rustls::{internal::pemfile, Certificate, PrivateKey, RootCertStore}; +use rustls::{Certificate, PrivateKey, RootCertStore}; fn err(message: impl Into>) -> io::Error { io::Error::new(io::ErrorKind::Other, message.into()) @@ -8,7 +8,8 @@ fn err(message: impl Into>) -> io::Error { /// Loads certificates from `reader`. pub fn load_certs(reader: &mut dyn io::BufRead) -> io::Result> { - pemfile::certs(reader).map_err(|_| err("invalid certificate")) + let certs = rustls_pemfile::certs(reader).map_err(|_| err("invalid certificate"))?; + Ok(certs.into_iter().map(Certificate).collect()) } /// Load and decode the private key from `reader`. @@ -19,8 +20,8 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result reader.read_line(&mut first_line)?; let private_keys_fn = match first_line.trim_end() { - "-----BEGIN RSA PRIVATE KEY-----" => pemfile::rsa_private_keys, - "-----BEGIN PRIVATE KEY-----" => pemfile::pkcs8_private_keys, + "-----BEGIN RSA PRIVATE KEY-----" => rustls_pemfile::rsa_private_keys, + "-----BEGIN PRIVATE KEY-----" => rustls_pemfile::pkcs8_private_keys, _ => return Err(err("invalid key header")) }; @@ -28,7 +29,7 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result .map_err(|_| err("invalid key file")) .and_then(|mut keys| match keys.len() { 0 => Err(err("no valid keys found; is the file malformed?")), - 1 => Ok(keys.remove(0)), + 1 => Ok(PrivateKey(keys.remove(0))), n => Err(err(format!("expected 1 key, found {}", n))), })?; @@ -41,9 +42,8 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result /// Load and decode CA certificates from `reader`. pub fn load_ca_certs(reader: &mut dyn io::BufRead) -> io::Result { let mut roots = rustls::RootCertStore::empty(); - let (_, e) = roots.add_pem_file(reader).map_err(|_| err("PEM format error"))?; - if e != 0 { - return Err(err("validity checks failed")); + for cert in load_certs(reader)? { + roots.add(&cert).map_err(|e| err(format!("CA cert error: {}", e)))?; } Ok(roots) diff --git a/core/lib/src/config/tls.rs b/core/lib/src/config/tls.rs index 3c833a0a..cc32d8fc 100644 --- a/core/lib/src/config/tls.rs +++ b/core/lib/src/config/tls.rs @@ -630,7 +630,7 @@ mod with_tls_feature { use crate::http::tls::Config; use crate::http::tls::rustls::SupportedCipherSuite as RustlsCipher; - use crate::http::tls::rustls::ciphersuite as rustls; + use crate::http::tls::rustls::cipher_suite; use yansi::Paint; @@ -675,26 +675,26 @@ mod with_tls_feature { }) } - fn rustls_ciphers(&self) -> impl Iterator + '_ { + fn rustls_ciphers(&self) -> impl Iterator + '_ { self.ciphers().map(|ciphersuite| match ciphersuite { CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => - &rustls::TLS13_CHACHA20_POLY1305_SHA256, + cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, CipherSuite::TLS_AES_256_GCM_SHA384 => - &rustls::TLS13_AES_256_GCM_SHA384, + cipher_suite::TLS13_AES_256_GCM_SHA384, CipherSuite::TLS_AES_128_GCM_SHA256 => - &rustls::TLS13_AES_128_GCM_SHA256, + cipher_suite::TLS13_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => - &rustls::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => - &rustls::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => - &rustls::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => - &rustls::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => - &rustls::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => - &rustls::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, }) } } diff --git a/core/lib/src/ext.rs b/core/lib/src/ext.rs index 0433c698..5da4facf 100644 --- a/core/lib/src/ext.rs +++ b/core/lib/src/ext.rs @@ -298,7 +298,7 @@ impl Connection for CancellableIo { self.io.peer_address() } - fn peer_certificates(&self) -> Option> { + fn peer_certificates(&self) -> Option<&[RawCertificate]> { self.io.peer_certificates() } } diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index ba2b78e9..6185554b 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -444,7 +444,7 @@ impl Rocket { let rocket = rocket.clone(); let connection = ConnectionMeta { remote: conn.peer_address(), - client_certificates: conn.peer_certificates().map(Arc::new), + client_certificates: conn.peer_certificates().map(|certs| Arc::new(certs.to_vec())), }; async move {