diff --git a/Cargo.toml b/Cargo.toml index 4f96e962..a3759129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,10 @@ optional = true version = "0.6" optional = true +[dependencies.webpki-roots] +version = "0.25" +optional = true + [dependencies.opentls] version = "0.2.1" optional = true @@ -200,5 +204,6 @@ sql-browser-smol = ["async-io", "async-net", "futures-lite"] integrated-auth-gssapi = ["libgssapi"] bigdecimal = ["bigdecimal_"] rustls = ["tokio-rustls", "tokio-util", "rustls-pemfile", "rustls-native-certs"] +rustls-webpki-roots = ["webpki-roots", "rustls"] native-tls = ["async-native-tls"] vendored-openssl = ["opentls"] diff --git a/src/client/config.rs b/src/client/config.rs index b23ad449..c763bedb 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -34,11 +34,13 @@ pub struct Config { pub(crate) readonly: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub(crate) enum TrustConfig { CaCertificateLocation(PathBuf), TrustAll, Default, + #[cfg(feature = "rustls-webpki-roots")] + WebPkiRoots } impl Default for Config { @@ -129,12 +131,12 @@ impl Config { /// storage (or use `trust_cert_ca` instead), using this setting is potentially dangerous. /// /// # Panics - /// Will panic in case `trust_cert_ca` was called before. + /// Will panic in case `trust_cert_ca` or `trust_webpki_roots` was called before. /// /// - Defaults to `default`, meaning server certificate is validated against system-truststore. pub fn trust_cert(&mut self) { - if let TrustConfig::CaCertificateLocation(_) = &self.trust { - panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") + if TrustConfig::Default != self.trust { + panic!("'trust_webpki_roots'/'trust_cert'/'trust_cert_ca' are mutual exclusive! Only use one.") } self.trust = TrustConfig::TrustAll; } @@ -145,17 +147,30 @@ impl Config { /// trust-chain. /// /// # Panics - /// Will panic in case `trust_cert` was called before. + /// Will panic in case `trust_cert` or `trust_webpki_roots` was called before. /// /// - Defaults to validating the server certificate is validated against system's certificate storage. pub fn trust_cert_ca(&mut self, path: impl ToString) { - if let TrustConfig::TrustAll = &self.trust { - panic!("'trust_cert' and 'trust_cert_ca' are mutual exclusive! Only use one.") + if TrustConfig::Default != self.trust { + panic!("'trust_webpki_roots'/'trust_cert'/'trust_cert_ca' are mutual exclusive! Only use one.") } else { self.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string())) } } + /// If set, the server certificate will be validated against the webpki-roots. + /// + /// # Panics + /// Will panic in case `trust_cert` or `trust_cert_ca` was called before. + /// + #[cfg(feature = "rustls-webpki-roots")] + pub fn trust_webpki_roots(&mut self) { + if self.trust != TrustConfig::Default { + panic!("'trust_webpki_roots'/'trust_cert'/'trust_cert_ca' are mutual exclusive! Only use one.") + } + self.trust = TrustConfig::WebPkiRoots; + } + /// Sets the authentication method. /// /// - Defaults to `None`. diff --git a/src/client/tls_stream/rustls_tls_stream.rs b/src/client/tls_stream/rustls_tls_stream.rs index e417583a..d21c2a0a 100644 --- a/src/client/tls_stream/rustls_tls_stream.rs +++ b/src/client/tls_stream/rustls_tls_stream.rs @@ -11,17 +11,14 @@ use std::{ task::{Context, Poll}, time::SystemTime, }; -use tokio_rustls::{ - rustls::{ - client::{ - HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, - WantsTransparencyPolicyOrClientCert, - }, - Certificate, ClientConfig, ConfigBuilder, DigitallySignedStruct, Error as RustlsError, - RootCertStore, ServerName, WantsVerifier, +use tokio_rustls::{rustls::{ + client::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, + WantsTransparencyPolicyOrClientCert, }, - TlsConnector, -}; + Certificate, ClientConfig, ConfigBuilder, DigitallySignedStruct, Error as RustlsError, + RootCertStore, ServerName, WantsVerifier, +}, rustls, TlsConnector}; use tokio_util::compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::{event, Level}; @@ -132,6 +129,11 @@ impl TlsStream { event!(Level::INFO, "Using default trust configuration."); builder.with_native_roots().with_no_client_auth() } + #[cfg(feature = "rustls-webpki-roots")] + TrustConfig::WebPkiRoots => { + event!(Level::INFO, "Using webpki trust configuration."); + builder.with_webpki_roots().with_no_client_auth() + } }; let connector = TlsConnector::from(Arc::new(client_config)); @@ -182,6 +184,9 @@ impl AsyncWrite for TlsStream { trait ConfigBuilderExt { fn with_native_roots(self) -> ConfigBuilder; + + #[cfg(feature = "rustls-webpki-roots")] + fn with_webpki_roots(self) -> ConfigBuilder; } impl ConfigBuilderExt for ConfigBuilder { @@ -212,4 +217,21 @@ impl ConfigBuilderExt for ConfigBuilder { self.with_root_certificates(roots) } + + #[cfg(feature = "rustls-webpki-roots")] + fn with_webpki_roots(self) -> ConfigBuilder { + let mut roots = rustls::RootCertStore::empty(); + roots.add_trust_anchors( + webpki_roots::TLS_SERVER_ROOTS + .iter() + .map(|ta| { + rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + }), + ); + self.with_root_certificates(roots) + } }