From 3d1659651c8ec8a20fae5beb84476ff8035809db Mon Sep 17 00:00:00 2001 From: "main()" Date: Thu, 27 Apr 2023 16:02:09 +0200 Subject: [PATCH 1/3] Support configuring a CA certificates bundle The trust_cert_ca() config option configures one specific trusted CA certificate. However, there are two downsides: - it requires a file path, so an in-memory certificate would have to be written to a temporary file - it supports loading exactly one certificate, so if you need to load an entire bundle (e.g. the AWS RDS bundle) you're out of luck The trust_cert_ca_bundle() method implemented here solves both of these issues by taking a bundle of PEM-encoded CA certificates in a Vec and adding all of them to the TLS context. For cases where a CA bundle needs to be loaded from disk, users can of course simply read the file on their end and pass the contents to trust_cert_ca_bundle. --- src/client/config.rs | 33 ++++++++++++++++----- src/client/tls_stream.rs | 27 +++++++++++++++++ src/client/tls_stream/native_tls_stream.rs | 7 +++++ src/client/tls_stream/opentls_tls_stream.rs | 7 +++++ src/client/tls_stream/rustls_tls_stream.rs | 10 +++++++ 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/client/config.rs b/src/client/config.rs index 581676f4..3bfb792b 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -36,6 +36,7 @@ pub struct Config { #[derive(Clone, Debug)] pub(crate) enum TrustConfig { CaCertificateLocation(PathBuf), + CaCertificateBundle(Vec), TrustAll, Default, } @@ -127,12 +128,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_cert_ca_bundle` was called before. /// - /// - Defaults to `default`, meaning server certificate is validated against system-truststore. + /// - Defaults to validating the server certificate is validated against system's certificate storage. 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 !matches!(&self.trust, TrustConfig::Default) { + panic!("'trust_cert'/'trust_cert_ca'/'trust_cert_ca_bundle' are mutual exclusive! Only use one.") } self.trust = TrustConfig::TrustAll; } @@ -143,14 +144,30 @@ impl Config { /// trust-chain. /// /// # Panics - /// Will panic in case `trust_cert` was called before. + /// Will panic in case `trust_cert` or `trust_cert_ca_bundle` 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 !matches!(&self.trust, TrustConfig::Default) { + panic!("'trust_cert'/'trust_cert_ca'/'trust_cert_ca_bundle' 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 given bundle of PEM-encoded CA certificates. + /// Useful when using self-signed certificates on the server without having to disable the + /// trust-chain. + /// + /// # Panics + /// Will panic in case `trust_cert` or `trust_cert_ca` was called before. + /// + /// - Defaults to validating the server certificate is validated against system's certificate storage. + pub fn trust_cert_ca_bundle(&mut self, bundle: Vec) { + if !matches!(&self.trust, TrustConfig::Default) { + panic!("'trust_cert'/'trust_cert_ca'/'trust_cert_ca_bundle' are mutual exclusive! Only use one.") } else { - self.trust = TrustConfig::CaCertificateLocation(PathBuf::from(path.to_string())) + self.trust = TrustConfig::CaCertificateBundle(bundle); } } diff --git a/src/client/tls_stream.rs b/src/client/tls_stream.rs index 9eba1060..2466a487 100644 --- a/src/client/tls_stream.rs +++ b/src/client/tls_stream.rs @@ -42,3 +42,30 @@ pub(crate) async fn create_tls_stream( ) -> crate::Result> { opentls_tls_stream::create_tls_stream(config, stream).await } + +#[cfg(any(feature = "native-tls", feature = "vendored-openssl"))] +mod iter_certs { + const BEGIN: &[u8] = b"-----BEGIN"; + pub struct IterCertBundle<'a> { + bundle: &'a [u8], + current_begin: Option, + } + impl<'a> IterCertBundle<'a> { + pub fn new(bundle: &'a [u8]) -> Self { + Self { + bundle, + current_begin: bundle.windows(BEGIN.len()).position(|x| x == BEGIN), + } + } + } + impl<'a> Iterator for IterCertBundle<'a> { + type Item = &'a [u8]; + fn next(&mut self) -> Option<&'a [u8]> { + self.current_begin.map(|begin| { + let next_begin = self.bundle.windows(BEGIN.len()).skip(begin + 1).position(|x| x == BEGIN).map(|x| x + begin + 1); + self.current_begin = next_begin; + &self.bundle[begin..next_begin.unwrap_or(self.bundle.len() - 1)] + }) + } + } +} diff --git a/src/client/tls_stream/native_tls_stream.rs b/src/client/tls_stream/native_tls_stream.rs index cf5591d8..750c7aa5 100644 --- a/src/client/tls_stream/native_tls_stream.rs +++ b/src/client/tls_stream/native_tls_stream.rs @@ -8,6 +8,8 @@ use futures_util::io::{AsyncRead, AsyncWrite}; use std::fs; use tracing::{event, Level}; +use super::iter_certs::IterCertBundle; + pub(crate) async fn create_tls_stream( config: &Config, stream: S, @@ -41,6 +43,11 @@ pub(crate) async fn create_tls_stream( }); } } + TrustConfig::CaCertificateBundle(bundle) => { + for cert in IterCertBundle::new(bundle) { + builder = builder.add_root_certificate(Certificate::from_pem(cert)?); + } + } TrustConfig::TrustAll => { event!( Level::WARN, diff --git a/src/client/tls_stream/opentls_tls_stream.rs b/src/client/tls_stream/opentls_tls_stream.rs index 1f028669..cbe59f51 100644 --- a/src/client/tls_stream/opentls_tls_stream.rs +++ b/src/client/tls_stream/opentls_tls_stream.rs @@ -8,6 +8,8 @@ use opentls::Certificate; use std::fs; use tracing::{event, Level}; +use super::iter_certs::IterCertBundle; + pub(crate) async fn create_tls_stream( config: &Config, stream: S, @@ -41,6 +43,11 @@ pub(crate) async fn create_tls_stream( }); } } + TrustConfig::CaCertificateBundle(bundle) => { + for cert in IterCertBundle::new(bundle) { + builder = builder.add_root_certificate(Certificate::from_pem(cert)?); + } + } TrustConfig::TrustAll => { event!( Level::WARN, diff --git a/src/client/tls_stream/rustls_tls_stream.rs b/src/client/tls_stream/rustls_tls_stream.rs index 0ccd0af9..b6a62434 100644 --- a/src/client/tls_stream/rustls_tls_stream.rs +++ b/src/client/tls_stream/rustls_tls_stream.rs @@ -115,6 +115,16 @@ impl TlsStream { }); } } + TrustConfig::CaCertificateBundle(bundle) => { + let mut cert_store = RootCertStore::empty(); + let certs = rustls_pemfile::certs(&mut bundle.as_slice())?; + for cert in certs { + cert_store.add(&Certificate(cert))?; + } + builder + .with_root_certificates(cert_store) + .with_no_client_auth() + } TrustConfig::TrustAll => { event!( Level::WARN, From 23f30053f04d81222d61d44f3591abb28f1373dc Mon Sep 17 00:00:00 2001 From: Flavian Desverne Date: Fri, 19 Jul 2024 12:25:17 +0200 Subject: [PATCH 2/3] Bump v0.12.3 (#349) --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d2ac38..4455343b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changes +## Version 0.12.3 +- feat: improve column type accuracy (#347) +- fix: encoding of zero-length values for large varlen columns (#315) +- update tokio_rustls (#306) +- Allow iterating over the cells in a row. (#303) +- Send ReadOnlyIntent when ApplicationIntent=ReadOnly specified (#297) +- Replace encoding with encoding_rs (#285) +- Disable chrono's oldtime feature (#284) + ## Version 0.12.2 - Update connection-string crate to 0.2 (#286) diff --git a/Cargo.toml b/Cargo.toml index 081e6491..9d8ccf95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT/Apache-2.0" name = "tiberius" readme = "README.md" repository = "https://github.com/prisma/tiberius" -version = "0.12.2" +version = "0.12.3" [workspace] members = ["runtimes-macro"] From 611202fdbfd4fa03ba2d9c82e6b971824956b337 Mon Sep 17 00:00:00 2001 From: "main()" Date: Fri, 21 Feb 2025 15:59:52 +0100 Subject: [PATCH 3/3] Update to tokio-rustls 0.26.1 --- Cargo.toml | 4 +- src/client/tls_stream/rustls_tls_stream.rs | 72 +++++++++++++++------- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9d8ccf95..d13b5ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,9 +118,9 @@ version = "1.12.0" optional = true [dependencies.tokio-rustls] -version = "0.24.0" +version = "0.26.1" optional = true -features = ["dangerous_configuration"] +default-features = false [dependencies.rustls-pemfile] version = "1" diff --git a/src/client/tls_stream/rustls_tls_stream.rs b/src/client/tls_stream/rustls_tls_stream.rs index 6dd29b5a..97c79ec7 100644 --- a/src/client/tls_stream/rustls_tls_stream.rs +++ b/src/client/tls_stream/rustls_tls_stream.rs @@ -4,21 +4,21 @@ use crate::{ Error, }; use futures_util::io::{AsyncRead, AsyncWrite}; +use tokio_rustls::rustls::client::WantsClientCert; +use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName}; use std::{ fs, io, pin::Pin, sync::Arc, task::{Context, Poll}, - time::SystemTime, }; use tokio_rustls::{ rustls::{ - client::{ + client::danger::{ HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, - WantsTransparencyPolicyOrClientCert, }, - Certificate, ClientConfig, ConfigBuilder, DigitallySignedStruct, Error as RustlsError, - RootCertStore, ServerName, WantsVerifier, + ClientConfig, ConfigBuilder, DigitallySignedStruct, Error as RustlsError, + RootCertStore, WantsVerifier, }, TlsConnector, }; @@ -35,17 +35,17 @@ pub(crate) struct TlsStream( Compat>>, ); +#[derive(Debug)] struct NoCertVerifier; impl ServerCertVerifier for NoCertVerifier { fn verify_server_cert( &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], - _server_name: &ServerName, - _scts: &mut dyn Iterator, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &tokio_rustls::rustls::pki_types::ServerName<'_>, _ocsp_response: &[u8], - _now: SystemTime, + _now: tokio_rustls::rustls::pki_types::UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } @@ -53,15 +53,43 @@ impl ServerCertVerifier for NoCertVerifier { fn verify_tls12_signature( &self, _message: &[u8], - _cert: &Certificate, + _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 { + use tokio_rustls::rustls::SignatureScheme::*; + vec![ + RSA_PKCS1_SHA1, + ECDSA_SHA1_Legacy, + RSA_PKCS1_SHA256, + ECDSA_NISTP256_SHA256, + RSA_PKCS1_SHA384, + ECDSA_NISTP384_SHA384, + RSA_PKCS1_SHA512, + ECDSA_NISTP521_SHA512, + RSA_PSS_SHA256, + RSA_PSS_SHA384, + RSA_PSS_SHA512, + ED25519, + ED448, + ] + } } -fn get_server_name(config: &Config) -> crate::Result { - match (ServerName::try_from(config.get_host()), &config.trust) { +fn get_server_name(config: &Config) -> crate::Result> { + match (ServerName::try_from(config.get_host().to_owned()), &config.trust) { (Ok(sn), _) => Ok(sn), (Err(_), TrustConfig::TrustAll) => { Ok(ServerName::try_from("placeholder.domain.com").unwrap()) @@ -74,7 +102,7 @@ impl TlsStream { pub(super) async fn new(config: &Config, stream: S) -> crate::Result { event!(Level::INFO, "Performing a TLS handshake"); - let builder = ClientConfig::builder().with_safe_defaults(); + let builder = ClientConfig::builder(); let client_config = match &config.trust { TrustConfig::CaCertificateLocation(path) => { @@ -92,10 +120,10 @@ impl TlsStream { }); } - Certificate(pem_cert.into_iter().next().unwrap()) + CertificateDer::from(pem_cert.into_iter().next().unwrap()) } Some(ext) if ext.to_ascii_lowercase() == "der" => { - Certificate(buf) + CertificateDer::from(buf) } Some(_) | None => return Err(crate::Error::Io { kind: IoErrorKind::InvalidInput, @@ -103,7 +131,7 @@ impl TlsStream { }), }; let mut cert_store = RootCertStore::empty(); - cert_store.add(&cert)?; + cert_store.add(cert)?; builder .with_root_certificates(cert_store) .with_no_client_auth() @@ -118,7 +146,7 @@ impl TlsStream { let mut cert_store = RootCertStore::empty(); let certs = rustls_pemfile::certs(&mut bundle.as_slice())?; for cert in certs { - cert_store.add(&Certificate(cert))?; + cert_store.add(CertificateDer::from(cert))?; } builder .with_root_certificates(cert_store) @@ -191,19 +219,19 @@ impl AsyncWrite for TlsStream { } trait ConfigBuilderExt { - fn with_native_roots(self) -> ConfigBuilder; + fn with_native_roots(self) -> ConfigBuilder; } impl ConfigBuilderExt for ConfigBuilder { - fn with_native_roots(self) -> ConfigBuilder { + fn with_native_roots(self) -> ConfigBuilder { let mut roots = RootCertStore::empty(); let mut valid_count = 0; let mut invalid_count = 0; for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { - let cert = Certificate(cert.0); - match roots.add(&cert) { + let c = CertificateDer::from_slice(&cert.0); + match roots.add(c) { Ok(_) => valid_count += 1, Err(err) => { tracing::event!(Level::TRACE, "invalid cert der {:?}", cert.0);