From a9c54af668c0babefeb3e5e65535d08486c405b3 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 18 Feb 2026 20:58:21 +0800 Subject: [PATCH 1/2] feat: add rustls verifier for postgres sslmode --- src/tokio/client.rs | 3 + src/tokio/client/tls.rs | 165 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 src/tokio/client/tls.rs diff --git a/src/tokio/client.rs b/src/tokio/client.rs index cccdfa0..2d89d88 100644 --- a/src/tokio/client.rs +++ b/src/tokio/client.rs @@ -30,6 +30,9 @@ use crate::messages::{ SslNegotiationMetaMessage, }; +#[cfg(any(feature = "_aws-lc-rs", feature = "_ring"))] +pub mod tls; + #[non_exhaustive] #[derive(Debug, Default)] pub struct PgWireMessageClientCodec { diff --git a/src/tokio/client/tls.rs b/src/tokio/client/tls.rs new file mode 100644 index 0000000..d838b39 --- /dev/null +++ b/src/tokio/client/tls.rs @@ -0,0 +1,165 @@ +use std::sync::Arc; + +use tokio_rustls::rustls::RootCertStore; +use tokio_rustls::rustls::client; +use tokio_rustls::rustls::client::danger::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, +}; +use tokio_rustls::rustls::crypto::{self, CryptoProvider, WebPkiSupportedAlgorithms}; +use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use tokio_rustls::rustls::server::ParsedCertificate; +use tokio_rustls::rustls::{CertificateError, Error}; +use tokio_rustls::rustls::{DigitallySignedStruct, SignatureScheme}; + +/// A server certificate verifier that validates the certificate chain but skips hostname verification. +/// +/// This is useful for PostgreSQL's `verify-ca` SSL mode, which verifies that the server +/// certificate is issued by a trusted CA, but does not verify that the certificate's +/// hostname matches the server. +/// +/// # Example +/// +/// ```no_run +/// use rustls::{RootCertStore, ClientConfig}; +/// use rustls::pki_types::CertificateDer; +/// use postgres_rustls_verifiers::SkipHostnameVerifier; +/// use std::sync::Arc; +/// +/// # fn build_roots() -> Arc { Arc::new(RootCertStore::empty()) } +/// let roots = build_roots(); +/// +/// // Create a crypto provider (requires rustls ring feature) +/// let provider = rustls::crypto::CryptoProvider::get_default().unwrap().clone(); +/// +/// let verifier = SkipHostnameVerifier::new_with_provider(roots, provider); +/// +/// let mut config = ClientConfig::builder() +/// .dangerous() +/// .with_custom_certificate_verifier(Arc::new(verifier)); +/// ``` +#[derive(Debug, Clone)] +pub struct SkipHostnameVerifier { + roots: Arc, + supported: WebPkiSupportedAlgorithms, +} + +impl SkipHostnameVerifier { + /// Create a new `SkipHostnameVerifier` with the provided root certificates and crypto provider. + /// + /// # Arguments + /// + /// * `roots` - The set of root CA certificates to trust + /// * `provider` - The crypto provider to use for signature verification algorithms + pub fn new_with_provider( + roots: impl Into>, + provider: Arc, + ) -> Self { + Self { + roots: roots.into(), + supported: provider.signature_verification_algorithms, + } + } +} + +impl ServerCertVerifier for SkipHostnameVerifier { + 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) + .map_err(|_| Error::InvalidCertificate(CertificateError::BadEncoding))?; + + client::verify_server_cert_signed_by_trust_anchor( + &cert, + &self.roots, + intermediates, + now, + self.supported.all, + )?; + + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + crypto::verify_tls12_signature(message, cert, dss, &self.supported) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + crypto::verify_tls13_signature(message, cert, dss, &self.supported) + } + + fn supported_verify_schemes(&self) -> Vec { + self.supported.supported_schemes() + } +} + +/// A server certificate verifier that accepts any certificate without validation. +/// +/// This is useful for PostgreSQL's `prefer` and `required` SSL modes, which do not +/// perform any certificate validation. +/// +/// **Security Warning**: This verifier accepts ANY certificate without validation, +/// making connections vulnerable to man-in-the-middle attacks. Use with extreme caution. +/// +/// # Example +/// +/// ```no_run +/// use rustls::ClientConfig; +/// use postgres_rustls_verifiers::NoopVerifier; +/// use std::sync::Arc; +/// +/// let mut config = ClientConfig::builder() +/// .dangerous() +/// .with_custom_certificate_verifier(Arc::new(NoopVerifier)); +/// ``` +#[derive(Debug, Clone, Copy)] +pub struct NoopVerifier; + +impl ServerCertVerifier for NoopVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![] + } +} From cc34437f068122c26f32bc7ae303c0d81b58e196 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 18 Feb 2026 21:07:15 +0800 Subject: [PATCH 2/2] test: fix doc tests --- src/tokio/client/tls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tokio/client/tls.rs b/src/tokio/client/tls.rs index d838b39..19d8826 100644 --- a/src/tokio/client/tls.rs +++ b/src/tokio/client/tls.rs @@ -20,16 +20,16 @@ use tokio_rustls::rustls::{DigitallySignedStruct, SignatureScheme}; /// # Example /// /// ```no_run -/// use rustls::{RootCertStore, ClientConfig}; -/// use rustls::pki_types::CertificateDer; -/// use postgres_rustls_verifiers::SkipHostnameVerifier; +/// use tokio_rustls::rustls::{RootCertStore, ClientConfig, crypto}; +/// use tokio_rustls::rustls::pki_types::CertificateDer; +/// use pgwire::tokio::client::tls::SkipHostnameVerifier; /// use std::sync::Arc; /// /// # fn build_roots() -> Arc { Arc::new(RootCertStore::empty()) } /// let roots = build_roots(); /// /// // Create a crypto provider (requires rustls ring feature) -/// let provider = rustls::crypto::CryptoProvider::get_default().unwrap().clone(); +/// let provider = crypto::CryptoProvider::get_default().unwrap().clone(); /// /// let verifier = SkipHostnameVerifier::new_with_provider(roots, provider); /// @@ -118,8 +118,8 @@ impl ServerCertVerifier for SkipHostnameVerifier { /// # Example /// /// ```no_run -/// use rustls::ClientConfig; -/// use postgres_rustls_verifiers::NoopVerifier; +/// use tokio_rustls::rustls::ClientConfig; +/// use pgwire::tokio::client::tls::NoopVerifier; /// use std::sync::Arc; /// /// let mut config = ClientConfig::builder()