diff --git a/Cargo.lock b/Cargo.lock index a3bbbe5..ff2e610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,7 +702,7 @@ dependencies = [ "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.7", "subtle", "zeroize", ] @@ -734,9 +734,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -887,6 +887,7 @@ dependencies = [ "hickory-client", "mime", "num_enum", + "rustls-webpki 0.102.7", "sha2", "tokio", "tokio-rustls 0.26.0", diff --git a/Cargo.toml b/Cargo.toml index 2fefd57..e7b3546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring dashmap = { version = "6.0.1", optional = true } hickory-client = { version = "0.24.1", optional = true } async-trait = "0.1.81" +rustls-webpki = "0.102.7" [dev-dependencies] tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] } diff --git a/src/certs/verifier.rs b/src/certs/verifier.rs index 23c3eff..d1d180b 100644 --- a/src/certs/verifier.rs +++ b/src/certs/verifier.rs @@ -1,5 +1,6 @@ -//! Custom verifier for Rustls accepting any TLS cert -//! (usually called "insecure mode") +//! Custom verifier for Rustls allowing self-signed certs +//! but performing other required checks; +//! mostly for internal use use std::sync::Arc; @@ -9,6 +10,8 @@ use tokio_rustls::rustls::{ crypto::CryptoProvider, }; +use webpki::EndEntityCert; + /// Custom verifier for Rustls accepting any TLS certificate #[derive(Debug)] pub struct InternalCertVerifier(Arc); @@ -39,15 +42,42 @@ impl From> for InternalCertVerifier { } impl ServerCertVerifier for InternalCertVerifier { - #[inline] fn verify_server_cert( &self, - _end_entity: &rustls::pki_types::CertificateDer<'_>, + end_entity: &rustls::pki_types::CertificateDer<'_>, _intermediates: &[rustls::pki_types::CertificateDer<'_>], - _server_name: &rustls::pki_types::ServerName<'_>, + server_name: &rustls::pki_types::ServerName<'_>, _ocsp_response: &[u8], - _now: rustls::pki_types::UnixTime, + now: rustls::pki_types::UnixTime, ) -> Result { + let cert = EndEntityCert::try_from(end_entity).map_err(pki_error)?; + + match cert.verify_for_usage( + self.0.signature_verification_algorithms.all, + &[], // no trusted anchors (i.e. CAs) + &[], // i think there's no point in passing intermediates without CAs + now, // `now` as the time for expiration check + webpki::KeyUsage::server_auth(), + None, // no CRLs + None, // no verify_path callback + ) { + Ok(_) => Ok(()), // unreachable + Err(webpki::Error::UnknownIssuer) => { + // trust anchors verification is done after + // any other issuer-independent checks (including expiration), + // so this error can be safely ignored as + // we are working with self-signed certificates. + // for reference -- fn webpki::verify_cert::build_chain_inner + // (NOTE: should be re-checked on every rustls update) + Ok(()) + } + Err(e) => Err(e), + } + .map_err(pki_error)?; + + cert.verify_is_valid_for_subject_name(server_name) + .map_err(pki_error)?; + Ok(ServerCertVerified::assertion()) } @@ -86,3 +116,24 @@ impl ServerCertVerifier for InternalCertVerifier { self.0.signature_verification_algorithms.supported_schemes() } } + +pub fn pki_error(e: webpki::Error) -> rustls::CertificateError { + // partially copied from private fn rustls::webpki::pki_error(e) + use rustls::{CertificateError, OtherError}; + use webpki::Error::*; + match e { + BadDer | BadDerTime | TrailingData(_) => CertificateError::BadEncoding, + CertNotValidYet => CertificateError::NotValidYet, + CertExpired | InvalidCertValidity => CertificateError::Expired, + UnknownIssuer => CertificateError::UnknownIssuer, + CertNotValidForName => CertificateError::NotValidForName, + CertRevoked => CertificateError::Revoked, + UnknownRevocationStatus => CertificateError::UnknownRevocationStatus, + + InvalidSignatureForPublicKey + | UnsupportedSignatureAlgorithm + | UnsupportedSignatureAlgorithmForPublicKey => CertificateError::BadSignature, + + _ => CertificateError::Other(OtherError(Arc::new(e))), + } +}