From fb208f6f8607847773c13d8998471bc1b3d73d15 Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Wed, 10 Jan 2024 17:02:32 +0800 Subject: [PATCH] Port link-tls to tokio and bump workspace rustls to 0.22.2 --- Cargo.toml | 5 +- io/zenoh-link-commons/Cargo.toml | 3 +- io/zenoh-link-commons/src/lib.rs | 1 + io/zenoh-link-commons/src/tls.rs | 87 +++++ io/zenoh-links/zenoh-link-tls/Cargo.toml | 13 +- io/zenoh-links/zenoh-link-tls/src/lib.rs | 8 +- io/zenoh-links/zenoh-link-tls/src/unicast.rs | 342 ++++++++----------- io/zenoh-links/zenoh-link-tls/src/verify.rs | 42 --- 8 files changed, 240 insertions(+), 261 deletions(-) create mode 100644 io/zenoh-link-commons/src/tls.rs delete mode 100644 io/zenoh-links/zenoh-link-tls/src/verify.rs diff --git a/Cargo.toml b/Cargo.toml index a9c9739960..d4029e8a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,6 @@ anyhow = { version = "1.0.69", default-features = false } # Default features are async-executor = "1.5.0" async-global-executor = "2.3.1" async-io = "=1.13.0" -async-rustls = "0.4.0" async-std = { version = "=1.12.0", default-features = false } # Default features are disabled due to some crates' requirements async-trait = "0.1.60" base64 = "0.21.4" @@ -126,10 +125,11 @@ regex = "1.7.1" ringbuffer-spsc = "0.1.9" rsa = "0.9" rustc_version = "0.4.0" -rustls = { version = "0.21.5", features = ["dangerous_configuration"] } +rustls = "0.22.2" rustls-native-certs = "0.7.0" rustls-pemfile = "2.0.0" rustls-webpki = "0.102.0" +rustls-pki-types = "1.1.0" schemars = "0.8.12" secrecy = { version = "0.8.0", features = ["serde", "alloc"] } serde = { version = "1.0.154", default-features = false, features = [ @@ -148,6 +148,7 @@ token-cell = { version = "1.4.2", default-features = false } tokio = { version = "1.32.0", default-features = false } # Default features are disabled due to some crates' requirements tokio-util = "0.7.10" tokio-tungstenite = "0.20" +tokio-rustls = "0.25.0" console-subscriber = "0.2" typenum = "1.16.0" uhlc = { version = "0.6.0", default-features = false } # Default features are disabled due to usage in no_std crates diff --git a/io/zenoh-link-commons/Cargo.toml b/io/zenoh-link-commons/Cargo.toml index 36e39eceed..f14721c9f6 100644 --- a/io/zenoh-link-commons/Cargo.toml +++ b/io/zenoh-link-commons/Cargo.toml @@ -28,8 +28,9 @@ description = "Internal crate for zenoh." compression = [] [dependencies] -async-std = { workspace = true } async-trait = { workspace = true } +rustls = { workspace = true } +rustls-webpki = { workspace = true } flume = { workspace = true } lz4_flex = { workspace = true } serde = { workspace = true, features = ["default"] } diff --git a/io/zenoh-link-commons/src/lib.rs b/io/zenoh-link-commons/src/lib.rs index 6fbf4c12bd..a3d3722fc5 100644 --- a/io/zenoh-link-commons/src/lib.rs +++ b/io/zenoh-link-commons/src/lib.rs @@ -21,6 +21,7 @@ extern crate alloc; mod multicast; +pub mod tls; mod unicast; use alloc::{borrow::ToOwned, boxed::Box, string::String}; diff --git a/io/zenoh-link-commons/src/tls.rs b/io/zenoh-link-commons/src/tls.rs new file mode 100644 index 0000000000..562b02c81e --- /dev/null +++ b/io/zenoh-link-commons/src/tls.rs @@ -0,0 +1,87 @@ +use alloc::vec::Vec; +use rustls::{ + client::{ + danger::{ServerCertVerified, ServerCertVerifier}, + verify_server_cert_signed_by_trust_anchor, + }, + crypto::{verify_tls12_signature, verify_tls13_signature}, + pki_types::{CertificateDer, ServerName, UnixTime}, + server::ParsedCertificate, + RootCertStore, +}; +use webpki::ALL_VERIFICATION_ALGS; + +impl ServerCertVerifier for WebPkiVerifierAnyServerName { + /// Will verify the certificate is valid in the following ways: + /// - Signed by a trusted `RootCertStore` CA + /// - Not Expired + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + let cert = ParsedCertificate::try_from(end_entity)?; + verify_server_cert_signed_by_trust_anchor( + &cert, + &self.roots, + intermediates, + now, + ALL_VERIFICATION_ALGS, + )?; + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } +} + +/// `ServerCertVerifier` that verifies that the server is signed by a trusted root, but allows any serverName +/// see the trait impl for more information. +#[derive(Debug)] +pub struct WebPkiVerifierAnyServerName { + roots: RootCertStore, +} + +#[allow(unreachable_pub)] +impl WebPkiVerifierAnyServerName { + /// Constructs a new `WebPkiVerifierAnyServerName`. + /// + /// `roots` is the set of trust anchors to trust for issuing server certs. + pub fn new(roots: RootCertStore) -> Self { + Self { roots } + } +} diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index 5d047b1160..975fa49467 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -25,21 +25,24 @@ description = "Internal crate for zenoh." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-rustls = { workspace = true } -rustls = { workspace = true } -async-std = { workspace = true } async-trait = { workspace = true } +base64 = { workspace = true } futures = { workspace = true } log = { workspace = true } +rustls = { workspace = true } rustls-pemfile = { workspace = true } +rustls-pki-types = { workspace = true } rustls-webpki = { workspace = true } +secrecy = {workspace = true } +tokio = { workspace = true, features = ["io-util", "net", "fs", "sync"] } +tokio-rustls = { workspace = true } +tokio-util = { workspace = true, features = ["rt"] } webpki-roots = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } zenoh-link-commons = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } +zenoh-runtime = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -base64 = { workspace = true } -secrecy = {workspace = true } \ No newline at end of file diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 7424ddbf94..95d59104b4 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -17,8 +17,6 @@ //! This crate is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) -use async_rustls::rustls::ServerName; -use async_std::net::ToSocketAddrs; use async_trait::async_trait; use config::{ TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, @@ -26,6 +24,7 @@ use config::{ TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, }; +use rustls_pki_types::ServerName; use secrecy::ExposeSecret; use std::{convert::TryFrom, net::SocketAddr}; use zenoh_config::Config; @@ -38,7 +37,6 @@ use zenoh_protocol::core::{ use zenoh_result::{bail, zerror, ZResult}; mod unicast; -mod verify; pub use unicast::*; // Default MTU (TLS PDU) in bytes. @@ -212,7 +210,7 @@ pub mod config { } pub async fn get_tls_addr(address: &Address<'_>) -> ZResult { - match address.as_str().to_socket_addrs().await?.next() { + match tokio::net::lookup_host(address.as_str()).await?.next() { Some(addr) => Ok(addr), None => bail!("Couldn't resolve TLS locator address: {}", address), } @@ -226,7 +224,7 @@ pub fn get_tls_host<'a>(address: &'a Address<'a>) -> ZResult<&'a str> { .ok_or_else(|| zerror!("Invalid TLS address").into()) } -pub fn get_tls_server_name(address: &Address<'_>) -> ZResult { +pub fn get_tls_server_name<'a>(address: &'a Address<'a>) -> ZResult> { Ok(ServerName::try_from(get_tls_host(address)?).map_err(|e| zerror!(e))?) } diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index 7761195e4b..457c9eb684 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -13,47 +13,39 @@ // use crate::{ base64_decode, config::*, get_tls_addr, get_tls_host, get_tls_server_name, - verify::WebPkiVerifierAnyServerName, TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, - TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, + TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, }; -use async_rustls::{ - rustls::{ - server::AllowAnyAuthenticatedClient, version::TLS13, Certificate, ClientConfig, - OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig, - }, - TlsAcceptor, TlsConnector, TlsStream, -}; -use async_std::fs; -use async_std::net::{SocketAddr, TcpListener, TcpStream}; -use async_std::prelude::FutureExt; -use async_std::sync::Mutex as AsyncMutex; -use async_std::task; -use async_std::task::JoinHandle; use async_trait::async_trait; -use futures::io::AsyncReadExt; -use futures::io::AsyncWriteExt; +use rustls::{ + pki_types::{CertificateDer, PrivateKeyDer, TrustAnchor}, + server::WebPkiClientVerifier, + version::TLS13, + ClientConfig, RootCertStore, ServerConfig, +}; use std::collections::HashMap; use std::convert::TryInto; use std::fmt; use std::fs::File; use std::io::{BufReader, Cursor}; -use std::net::{IpAddr, Shutdown}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::net::IpAddr; +use std::net::SocketAddr; use std::sync::{Arc, RwLock}; use std::time::Duration; use std::{cell::UnsafeCell, io}; -use webpki::{ - anchor_from_trusted_cert, - types::{CertificateDer, TrustAnchor}, -}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::Mutex as AsyncMutex; +use tokio_rustls::{TlsAcceptor, TlsConnector, TlsStream}; +use tokio_util::{sync::CancellationToken, task::TaskTracker}; +use webpki::anchor_from_trusted_cert; use zenoh_core::{zasynclock, zread, zwrite}; +use zenoh_link_commons::tls::WebPkiVerifierAnyServerName; use zenoh_link_commons::{ LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, NewLinkChannelSender, }; use zenoh_protocol::core::endpoint::Config; use zenoh_protocol::core::{EndPoint, Locator}; use zenoh_result::{bail, zerror, ZError, ZResult}; -use zenoh_sync::Signal; pub struct LinkUnicastTls { // The underlying socket as returned from the async-rustls library @@ -97,12 +89,9 @@ impl LinkUnicastTls { } // Set the TLS linger option - if let Err(err) = zenoh_util::net::set_linger( - tcp_stream, - Some(Duration::from_secs( - (*TLS_LINGER_TIMEOUT).try_into().unwrap(), - )), - ) { + if let Err(err) = tcp_stream.set_linger(Some(Duration::from_secs( + (*TLS_LINGER_TIMEOUT).try_into().unwrap(), + ))) { log::warn!( "Unable to set LINGER option on TLS link {} => {}: {}", src_addr, @@ -142,8 +131,8 @@ impl LinkUnicastTrait for LinkUnicastTls { let res = tls_stream.flush().await; log::trace!("TLS link flush {}: {:?}", self, res); // Close the underlying TCP stream - let (tcp_stream, _) = tls_stream.get_ref(); - let res = tcp_stream.shutdown(Shutdown::Both); + let (tcp_stream, _) = tls_stream.get_mut(); + let res = tcp_stream.shutdown().await; log::trace!("TLS link shutdown {}: {:?}", self, res); res.map_err(|e| zerror!(e).into()) } @@ -174,10 +163,11 @@ impl LinkUnicastTrait for LinkUnicastTls { async fn read_exact(&self, buffer: &mut [u8]) -> ZResult<()> { let _guard = zasynclock!(self.read_mtx); - self.get_sock_mut().read_exact(buffer).await.map_err(|e| { + let _ = self.get_sock_mut().read_exact(buffer).await.map_err(|e| { log::trace!("Read error on TLS link {}: {}", self, e); - zerror!(e).into() - }) + zerror!(e) + })?; + Ok(()) } #[inline(always)] @@ -209,8 +199,9 @@ impl LinkUnicastTrait for LinkUnicastTls { impl Drop for LinkUnicastTls { fn drop(&mut self) { // Close the underlying TCP stream - let (tcp_stream, _) = self.get_sock_mut().get_ref(); - let _ = tcp_stream.shutdown(Shutdown::Both); + let (tcp_stream, _) = self.get_sock_mut().get_mut(); + let _ = + zenoh_runtime::ZRuntime::TX.block_in_place(async move { tcp_stream.shutdown().await }); } } @@ -235,25 +226,28 @@ impl fmt::Debug for LinkUnicastTls { /*************************************/ struct ListenerUnicastTls { endpoint: EndPoint, - active: Arc, - signal: Signal, - handle: JoinHandle>, + token: CancellationToken, + tracker: TaskTracker, } impl ListenerUnicastTls { fn new( endpoint: EndPoint, - active: Arc, - signal: Signal, - handle: JoinHandle>, + token: CancellationToken, + tracker: TaskTracker, ) -> ListenerUnicastTls { ListenerUnicastTls { endpoint, - active, - signal, - handle, + token, + tracker, } } + + async fn stop(&self) { + self.token.cancel(); + self.tracker.close(); + self.tracker.wait().await; + } } pub struct LinkManagerUnicastTls { @@ -353,21 +347,22 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastTls { // Initialize the TlsAcceptor let acceptor = TlsAcceptor::from(Arc::new(tls_server_config.server_config)); - let active = Arc::new(AtomicBool::new(true)); - let signal = Signal::new(); + let token = CancellationToken::new(); + let c_token = token.clone(); // Spawn the accept loop for the listener - let c_active = active.clone(); - let c_signal = signal.clone(); let c_manager = self.manager.clone(); let c_listeners = self.listeners.clone(); let c_addr = local_addr; - let handle = task::spawn(async move { + + let tracker = TaskTracker::new(); + let task = async move { // Wait for the accept loop to terminate - let res = accept_task(socket, acceptor, c_active, c_signal, c_manager).await; + let res = accept_task(socket, acceptor, c_token, c_manager).await; zwrite!(c_listeners).remove(&c_addr); res - }); + }; + tracker.spawn_on(task, &zenoh_runtime::ZRuntime::TX); // Update the endpoint locator address let locator = Locator::new( @@ -376,7 +371,7 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastTls { endpoint.metadata(), )?; - let listener = ListenerUnicastTls::new(endpoint, active, signal, handle); + let listener = ListenerUnicastTls::new(endpoint, token, tracker); // Update the list of active listeners on the manager zwrite!(self.listeners).insert(local_addr, listener); @@ -399,9 +394,8 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastTls { })?; // Send the stop signal - listener.active.store(false, Ordering::Release); - listener.signal.trigger(); - listener.handle.await + listener.stop().await; + Ok(()) } fn get_listeners(&self) -> Vec { @@ -445,23 +439,12 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastTls { async fn accept_task( socket: TcpListener, acceptor: TlsAcceptor, - active: Arc, - signal: Signal, + token: CancellationToken, manager: NewLinkChannelSender, ) -> ZResult<()> { - enum Action { - Accept((TcpStream, SocketAddr)), - Stop, - } - - async fn accept(socket: &TcpListener) -> ZResult { + async fn accept(socket: &TcpListener) -> ZResult<(TcpStream, SocketAddr)> { let res = socket.accept().await.map_err(|e| zerror!(e))?; - Ok(Action::Accept(res)) - } - - async fn stop(signal: Signal) -> ZResult { - signal.wait().await; - Ok(Action::Stop) + Ok(res) } let src_addr = socket.local_addr().map_err(|e| { @@ -471,42 +454,44 @@ async fn accept_task( })?; log::trace!("Ready to accept TLS connections on: {:?}", src_addr); - while active.load(Ordering::Acquire) { - // Wait for incoming connections - let (tcp_stream, dst_addr) = match accept(&socket).race(stop(signal.clone())).await { - Ok(action) => match action { - Action::Accept((tcp_stream, dst_addr)) => (tcp_stream, dst_addr), - Action::Stop => break, - }, - Err(e) => { - log::warn!("{}. Hint: increase the system open file limit.", e); - // Throttle the accept loop upon an error - // NOTE: This might be due to various factors. However, the most common case is that - // the process has reached the maximum number of open files in the system. On - // Linux systems this limit can be changed by using the "ulimit" command line - // tool. In case of systemd-based systems, this can be changed by using the - // "sysctl" command line tool. - task::sleep(Duration::from_micros(*TLS_ACCEPT_THROTTLE_TIME)).await; - continue; - } - }; - // Accept the TLS connection - let tls_stream = match acceptor.accept(tcp_stream).await { - Ok(stream) => TlsStream::Server(stream), - Err(e) => { - let e = format!("Can not accept TLS connection: {e}"); - log::warn!("{}", e); - continue; + loop { + tokio::select! { + _ = token.cancelled() => break, + + res = accept(&socket) => { + match res { + Ok((tcp_stream, dst_addr)) => { + // Accept the TLS connection + let tls_stream = match acceptor.accept(tcp_stream).await { + Ok(stream) => TlsStream::Server(stream), + Err(e) => { + let e = format!("Can not accept TLS connection: {e}"); + log::warn!("{}", e); + continue; + } + }; + + log::debug!("Accepted TLS connection on {:?}: {:?}", src_addr, dst_addr); + // Create the new link object + let link = Arc::new(LinkUnicastTls::new(tls_stream, src_addr, dst_addr)); + + // Communicate the new link to the initial transport manager + if let Err(e) = manager.send_async(LinkUnicast(link)).await { + log::error!("{}-{}: {}", file!(), line!(), e) + } + } + Err(e) => { + log::warn!("{}. Hint: increase the system open file limit.", e); + // Throttle the accept loop upon an error + // NOTE: This might be due to various factors. However, the most common case is that + // the process has reached the maximum number of open files in the system. On + // Linux systems this limit can be changed by using the "ulimit" command line + // tool. In case of systemd-based systems, this can be changed by using the + // "sysctl" command line tool. + tokio::time::sleep(Duration::from_micros(*TLS_ACCEPT_THROTTLE_TIME)).await; + } + } } - }; - - log::debug!("Accepted TLS connection on {:?}: {:?}", src_addr, dst_addr); - // Create the new link object - let link = Arc::new(LinkUnicastTls::new(tls_stream, src_addr, dst_addr)); - - // Communicate the new link to the initial transport manager - if let Err(e) = manager.send_async(LinkUnicast(link)).await { - log::error!("{}-{}: {}", file!(), line!(), e) } } @@ -528,42 +513,29 @@ impl TlsServerConfig { let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; - let certs: Vec = + let certs: Vec = rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing server certificate: {err}.")) - .map(|der| Certificate(der.to_vec())) - }) - .collect::, ZError>>()?; - - let mut keys: Vec = + .collect::>() + .map_err(|err| zerror!("Error processing server certificate: {err}."))?; + + let mut keys: Vec = rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing server key: {err}.")) - .map(|key| PrivateKey(key.secret_pkcs1_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; if keys.is_empty() { keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing server key: {err}.")) - .map(|key| PrivateKey(key.secret_pkcs8_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; } if keys.is_empty() { keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing server key: {err}.")) - .map(|key| PrivateKey(key.secret_sec1_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; } if keys.is_empty() { @@ -579,17 +551,13 @@ impl TlsServerConfig { }, Ok, )?; - ServerConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&[&TLS13]) // Force TLS 1.3 - .map_err(|e| zerror!(e))? - .with_client_cert_verifier(Arc::new(AllowAnyAuthenticatedClient::new(root_cert_store))) + let client_auth = WebPkiClientVerifier::builder(root_cert_store.into()).build()?; + ServerConfig::builder_with_protocol_versions(&[&TLS13]) + .with_client_cert_verifier(client_auth) .with_single_cert(certs, keys.remove(0)) .map_err(|e| zerror!(e))? } else { ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_single_cert(certs, keys.remove(0)) .map_err(|e| zerror!(e))? @@ -646,13 +614,13 @@ impl TlsClientConfig { // Allows mixed user-generated CA and webPKI CA log::debug!("Loading default Web PKI certificates."); - let mut root_cert_store: RootCertStore = RootCertStore { - roots: load_default_webpki_certs().roots, + let mut root_cert_store = RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), }; if let Some(custom_root_cert) = load_trust_anchors(config)? { log::debug!("Loading user-generated certificates."); - root_cert_store.add_trust_anchors(custom_root_cert.roots.into_iter()); + root_cert_store.extend(custom_root_cert.roots); } let cc = if tls_client_server_auth { @@ -660,54 +628,37 @@ impl TlsClientConfig { let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; - let certs: Vec = + let certs: Vec = rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing client certificate: {err}.")) - .map(|der| Certificate(der.to_vec())) - }) - .collect::, ZError>>()?; - - let mut keys: Vec = + .collect::>() + .map_err(|err| zerror!("Error processing client certificate: {err}."))?; + + let mut keys: Vec = rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing client key: {err}.")) - .map(|key| PrivateKey(key.secret_pkcs1_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; if keys.is_empty() { keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing client key: {err}.")) - .map(|key| PrivateKey(key.secret_pkcs8_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; } if keys.is_empty() { keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|result| { - result - .map_err(|err| zerror!("Error processing client key: {err}.")) - .map(|key| PrivateKey(key.secret_sec1_der().to_vec())) - }) - .collect::, ZError>>()?; + .map(|x| x.map(|key| PrivateKeyDer::from(key))) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; } if keys.is_empty() { bail!("No private key found for TLS client."); } - let builder = ClientConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&[&TLS13]) - .map_err(|e| zerror!("Config parameters should be valid: {}", e))?; + let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); if tls_server_name_verification { builder @@ -715,6 +666,7 @@ impl TlsClientConfig { .with_client_auth_cert(certs, keys.remove(0)) } else { builder + .dangerous() .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( root_cert_store, ))) @@ -722,13 +674,14 @@ impl TlsClientConfig { } .map_err(|e| zerror!("Bad certificate/key: {}", e))? } else { - let builder = ClientConfig::builder().with_safe_defaults(); + let builder = ClientConfig::builder(); if tls_server_name_verification { builder .with_root_certificates(root_cert_store) .with_no_client_auth() } else { builder + .dangerous() .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( root_cert_store, ))) @@ -770,7 +723,7 @@ async fn load_tls_key( } else if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { return base64_decode(b64_key); } else if let Some(value) = config.get(tls_private_key_file_config_key) { - return Ok(fs::read(value) + return Ok(tokio::fs::read(value) .await .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) .and_then(|result| { @@ -795,7 +748,7 @@ async fn load_tls_certificate( } else if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { return base64_decode(b64_certificate); } else if let Some(value) = config.get(tls_certificate_file_config_key) { - return Ok(fs::read(value) + return Ok(tokio::fs::read(value) .await .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); } @@ -807,7 +760,7 @@ fn load_trust_anchors(config: &Config<'_>) -> ZResult> { if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { let mut pem = BufReader::new(value.as_bytes()); let trust_anchors = process_pem(&mut pem)?; - root_cert_store.add_trust_anchors(trust_anchors.into_iter()); + root_cert_store.extend(trust_anchors); return Ok(Some(root_cert_store)); } @@ -815,20 +768,20 @@ fn load_trust_anchors(config: &Config<'_>) -> ZResult> { let certificate_pem = base64_decode(b64_certificate)?; let mut pem = BufReader::new(certificate_pem.as_slice()); let trust_anchors = process_pem(&mut pem)?; - root_cert_store.add_trust_anchors(trust_anchors.into_iter()); + root_cert_store.extend(trust_anchors); return Ok(Some(root_cert_store)); } if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { let mut pem = BufReader::new(File::open(filename)?); let trust_anchors = process_pem(&mut pem)?; - root_cert_store.add_trust_anchors(trust_anchors.into_iter()); + root_cert_store.extend(trust_anchors); return Ok(Some(root_cert_store)); } Ok(None) } -fn process_pem(pem: &mut dyn io::BufRead) -> ZResult> { +fn process_pem(pem: &mut dyn io::BufRead) -> ZResult>> { let certs: Vec = rustls_pemfile::certs(pem) .map(|result| result.map_err(|err| zerror!("Error processing PEM certificates: {err}."))) .collect::, ZError>>()?; @@ -842,28 +795,5 @@ fn process_pem(pem: &mut dyn io::BufRead) -> ZResult> { }) .collect::, ZError>>()?; - let owned_trust_anchors: Vec = trust_anchors - .into_iter() - .map(|ta| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject.to_vec(), - ta.subject_public_key_info.to_vec(), - ta.name_constraints.map(|x| x.to_vec()), - ) - }) - .collect(); - - Ok(owned_trust_anchors) -} - -fn load_default_webpki_certs() -> RootCertStore { - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject.to_vec(), - ta.subject_public_key_info.to_vec(), - ta.name_constraints.clone().map(|x| x.to_vec()), - ) - })); - root_cert_store + Ok(trust_anchors) } diff --git a/io/zenoh-links/zenoh-link-tls/src/verify.rs b/io/zenoh-links/zenoh-link-tls/src/verify.rs deleted file mode 100644 index 6278e85109..0000000000 --- a/io/zenoh-links/zenoh-link-tls/src/verify.rs +++ /dev/null @@ -1,42 +0,0 @@ -use async_rustls::rustls::{ - client::{ServerCertVerified, ServerCertVerifier}, - Certificate, RootCertStore, ServerName, -}; -use rustls::client::verify_server_cert_signed_by_trust_anchor; -use rustls::server::ParsedCertificate; -use std::time::SystemTime; - -impl ServerCertVerifier for WebPkiVerifierAnyServerName { - /// Will verify the certificate is valid in the following ways: - /// - Signed by a trusted `RootCertStore` CA - /// - Not Expired - fn verify_server_cert( - &self, - end_entity: &Certificate, - intermediates: &[Certificate], - _server_name: &ServerName, - _scts: &mut dyn Iterator, - _ocsp_response: &[u8], - now: SystemTime, - ) -> Result { - let cert = ParsedCertificate::try_from(end_entity)?; - verify_server_cert_signed_by_trust_anchor(&cert, &self.roots, intermediates, now)?; - Ok(ServerCertVerified::assertion()) - } -} - -/// `ServerCertVerifier` that verifies that the server is signed by a trusted root, but allows any serverName -/// see the trait impl for more information. -pub struct WebPkiVerifierAnyServerName { - roots: RootCertStore, -} - -#[allow(unreachable_pub)] -impl WebPkiVerifierAnyServerName { - /// Constructs a new `WebPkiVerifierAnyServerName`. - /// - /// `roots` is the set of trust anchors to trust for issuing server certs. - pub fn new(roots: RootCertStore) -> Self { - Self { roots } - } -}