Have the old builder use the new one underneath

This commit is contained in:
Paolo Barbolini
2025-05-02 05:17:53 +02:00
parent 2f4e36ac61
commit 5cbe9ba283
3 changed files with 73 additions and 241 deletions

View File

@@ -37,6 +37,7 @@ use self::net::NetworkStream;
pub(super) use self::tls::deprecated::InnerTlsParameters;
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub use self::tls::deprecated::TlsVersion;
#[allow(deprecated)]
pub use self::{
connection::SmtpConnection,
tls::deprecated::{Certificate, Identity, Tls, TlsParameters, TlsParametersBuilder},

View File

@@ -1,16 +1,4 @@
use std::fmt::{self, Debug};
#[cfg(feature = "rustls")]
use std::sync::Arc;
#[cfg(feature = "boring-tls")]
use boring::{
ssl::{SslConnector, SslVersion},
x509::store::X509StoreBuilder,
};
#[cfg(feature = "native-tls")]
use native_tls::{Protocol, TlsConnector};
#[cfg(feature = "rustls")]
use rustls::{ClientConfig, RootCertStore};
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
use crate::transport::smtp::{error, Error};
@@ -154,49 +142,6 @@ pub enum CertificateStore {
None,
}
/// Source for the base set of root certificate to trust when using `native-tls`.
#[allow(missing_copy_implementations)]
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub enum NativeTlsCertificateStore {
#[default]
System,
None,
}
/// Source for the base set of root certificate to trust when using `rustls-tls`.
#[allow(missing_copy_implementations)]
#[derive(Debug, Clone)]
#[non_exhaustive]
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub enum RustlsCertificateStore {
#[cfg(all(feature = "rustls", feature = "rustls-native-certs"))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "rustls", feature = "rustls-native-certs")))
)]
NativeCerts,
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "webpki-roots"))))]
WebpkiRoots,
None,
}
/// Source for the base set of root certificate to trust when using `boring-tls`.
#[allow(missing_copy_implementations)]
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub enum BoringTlsCertificateStore {
#[default]
System,
None,
}
/// Parameters to use for secure clients
#[derive(Clone)]
pub struct TlsParameters {
@@ -338,9 +283,9 @@ impl TlsParametersBuilder {
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub fn build_native(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let certificate_store = match self.cert_store {
CertificateStore::Default => NativeTlsCertificateStore::System,
CertificateStore::None => NativeTlsCertificateStore::None,
let cert_store = match self.cert_store {
CertificateStore::Default => super::native_tls::CertificateStore::System,
CertificateStore::None => super::native_tls::CertificateStore::None,
#[allow(unreachable_patterns)]
other => {
return Err(error::tls(format!(
@@ -348,37 +293,10 @@ impl TlsParametersBuilder {
)))
}
};
self.build_native_with_certificate_store(certificate_store)
}
/// Creates a new `TlsParameters` using native-tls with the provided configuration
///
/// Warning: this uses the certificate store passed via `certificate_store`
/// instead of the one configured in [`TlsParametersBuilder::certificate_store`].
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub fn build_native_with_certificate_store(
self,
certificate_store: NativeTlsCertificateStore,
) -> Result<TlsParameters, Error> {
let mut tls_builder = TlsConnector::builder();
match certificate_store {
NativeTlsCertificateStore::System => {}
NativeTlsCertificateStore::None => {
tls_builder.disable_built_in_roots(true);
}
}
for cert in self.root_certs {
tls_builder.add_root_certificate(cert.native_tls.0);
}
tls_builder.danger_accept_invalid_hostnames(self.accept_invalid_hostnames);
tls_builder.danger_accept_invalid_certs(self.accept_invalid_certs);
let min_tls_version = match self.min_tls_version {
TlsVersion::Tlsv10 => Protocol::Tlsv10,
TlsVersion::Tlsv11 => Protocol::Tlsv11,
TlsVersion::Tlsv12 => Protocol::Tlsv12,
TlsVersion::Tlsv10 => super::native_tls::MinTlsVersion::Tlsv10,
TlsVersion::Tlsv11 => super::native_tls::MinTlsVersion::Tlsv11,
TlsVersion::Tlsv12 => super::native_tls::MinTlsVersion::Tlsv12,
TlsVersion::Tlsv13 => {
return Err(error::tls(
"min tls version Tlsv13 not supported in native tls",
@@ -386,16 +304,19 @@ impl TlsParametersBuilder {
}
};
tls_builder.min_protocol_version(Some(min_tls_version));
let mut builder = super::TlsParametersBuilder::<super::NativeTls>::new(self.domain)
.certificate_store(cert_store)
.dangerous_accept_invalid_certs(self.accept_invalid_certs)
.dangerous_accept_invalid_hostnames(self.accept_invalid_hostnames)
.min_tls_version(min_tls_version);
for cert in self.root_certs {
builder = builder.add_root_certificate(cert.native_tls);
}
if let Some(identity) = self.identity {
tls_builder.identity(identity.native_tls.0);
builder = builder.identify_with(identity.native_tls);
}
let connector = tls_builder.build().map_err(error::tls)?;
Ok(TlsParameters {
connector: InnerTlsParameters::NativeTls { connector },
domain: self.domain,
})
builder.build_legacy()
}
/// Creates a new `TlsParameters` using boring-tls with the provided configuration
@@ -406,76 +327,36 @@ impl TlsParametersBuilder {
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn build_boring(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let certificate_store = match self.cert_store {
CertificateStore::Default => BoringTlsCertificateStore::System,
CertificateStore::None => BoringTlsCertificateStore::None,
let cert_store = match self.cert_store {
CertificateStore::Default => super::boring_tls::CertificateStore::System,
CertificateStore::None => super::boring_tls::CertificateStore::None,
#[allow(unreachable_patterns)]
other => {
return Err(error::tls(format!(
"{other:?} is not supported in boring tls"
"{other:?} is not supported in native tls"
)))
}
};
self.build_boring_with_certificate_store(certificate_store)
}
/// Creates a new `TlsParameters` using boring-tls with the provided configuration
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn build_boring_with_certificate_store(
self,
certificate_store: BoringTlsCertificateStore,
) -> Result<TlsParameters, Error> {
use boring::ssl::{SslMethod, SslVerifyMode};
let mut tls_builder = SslConnector::builder(SslMethod::tls_client()).map_err(error::tls)?;
if self.accept_invalid_certs {
tls_builder.set_verify(SslVerifyMode::NONE);
} else {
match certificate_store {
BoringTlsCertificateStore::System => {}
BoringTlsCertificateStore::None => {
// Replace the default store with an empty store.
tls_builder
.set_cert_store(X509StoreBuilder::new().map_err(error::tls)?.build());
}
}
let cert_store = tls_builder.cert_store_mut();
for cert in self.root_certs {
cert_store.add_cert(cert.boring_tls.0).map_err(error::tls)?;
}
}
if let Some(identity) = self.identity {
tls_builder
.set_certificate(identity.boring_tls.chain.as_ref())
.map_err(error::tls)?;
tls_builder
.set_private_key(identity.boring_tls.key.as_ref())
.map_err(error::tls)?;
}
let min_tls_version = match self.min_tls_version {
TlsVersion::Tlsv10 => SslVersion::TLS1,
TlsVersion::Tlsv11 => SslVersion::TLS1_1,
TlsVersion::Tlsv12 => SslVersion::TLS1_2,
TlsVersion::Tlsv13 => SslVersion::TLS1_3,
TlsVersion::Tlsv10 => super::boring_tls::MinTlsVersion::Tlsv10,
TlsVersion::Tlsv11 => super::boring_tls::MinTlsVersion::Tlsv11,
TlsVersion::Tlsv12 => super::boring_tls::MinTlsVersion::Tlsv12,
TlsVersion::Tlsv13 => super::boring_tls::MinTlsVersion::Tlsv13,
};
tls_builder
.set_min_proto_version(Some(min_tls_version))
.map_err(error::tls)?;
let connector = tls_builder.build();
Ok(TlsParameters {
connector: InnerTlsParameters::BoringTls {
connector,
accept_invalid_hostnames: self.accept_invalid_hostnames,
},
domain: self.domain,
})
let mut builder = super::TlsParametersBuilder::<super::BoringTls>::new(self.domain)
.certificate_store(cert_store)
.dangerous_accept_invalid_certs(self.accept_invalid_certs)
.dangerous_accept_invalid_hostnames(self.accept_invalid_hostnames)
.min_tls_version(min_tls_version);
for cert in self.root_certs {
builder = builder.add_root_certificate(cert.boring_tls);
}
if let Some(identity) = self.identity {
builder = builder.identify_with(identity.boring_tls);
}
builder.build_legacy()
}
/// Creates a new `TlsParameters` using rustls with the provided configuration
@@ -483,98 +364,41 @@ impl TlsParametersBuilder {
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn build_rustls(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let certificate_store = match self.cert_store {
let cert_store = match self.cert_store {
#[cfg(feature = "rustls-native-certs")]
CertificateStore::Default => RustlsCertificateStore::NativeCerts,
CertificateStore::Default => super::rustls::CertificateStore::NativeCerts,
#[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))]
CertificateStore::Default => RustlsCertificateStore::WebpkiRoots,
CertificateStore::Default => super::rustls::CertificateStore::WebpkiRoots,
#[cfg(feature = "webpki-roots")]
CertificateStore::WebpkiRoots => RustlsCertificateStore::WebpkiRoots,
CertificateStore::None => RustlsCertificateStore::None,
CertificateStore::WebpkiRoots => super::rustls::CertificateStore::WebpkiRoots,
CertificateStore::None => super::rustls::CertificateStore::None,
};
self.build_rustls_with_certificate_store(certificate_store)
}
/// Creates a new `TlsParameters` using rustls with the provided configuration
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn build_rustls_with_certificate_store(
self,
certificate_store: RustlsCertificateStore,
) -> Result<TlsParameters, Error> {
let just_version3 = &[&rustls::version::TLS13];
let supported_versions = match self.min_tls_version {
let min_tls_version = match self.min_tls_version {
TlsVersion::Tlsv10 => {
return Err(error::tls("min tls version Tlsv10 not supported in rustls"))
}
TlsVersion::Tlsv11 => {
return Err(error::tls("min tls version Tlsv11 not supported in rustls"))
}
TlsVersion::Tlsv12 => rustls::ALL_VERSIONS,
TlsVersion::Tlsv13 => just_version3,
TlsVersion::Tlsv12 => super::rustls::MinTlsVersion::Tlsv12,
TlsVersion::Tlsv13 => super::rustls::MinTlsVersion::Tlsv13,
};
let crypto_provider = crate::rustls_crypto::crypto_provider();
let tls = ClientConfig::builder_with_provider(Arc::clone(&crypto_provider))
.with_protocol_versions(supported_versions)
.map_err(error::tls)?;
// Build TLS config
let mut root_cert_store = RootCertStore::empty();
match certificate_store {
#[cfg(feature = "rustls-native-certs")]
RustlsCertificateStore::NativeCerts => {
let rustls_native_certs::CertificateResult { certs, errors, .. } =
rustls_native_certs::load_native_certs();
let errors_len = errors.len();
let (added, ignored) = store.add_parsable_certificates(certs);
#[cfg(feature = "tracing")]
tracing::debug!(
"loaded platform certs with {errors_len} failing to load, {added} valid and {ignored} ignored (invalid) certs"
);
#[cfg(not(feature = "tracing"))]
let _ = (errors_len, added, ignored);
}
#[cfg(feature = "webpki-roots")]
RustlsCertificateStore::WebpkiRoots => {
root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
}
RustlsCertificateStore::None => {}
}
let mut builder = super::TlsParametersBuilder::<super::Rustls>::new(self.domain)
.certificate_store(cert_store)
.dangerous_accept_invalid_certs(self.accept_invalid_certs)
.dangerous_accept_invalid_hostnames(self.accept_invalid_hostnames)
.min_tls_version(min_tls_version);
for cert in self.root_certs {
for rustls_cert in cert.rustls {
root_cert_store.add(rustls_cert.0).map_err(error::tls)?;
for cert in cert.rustls {
builder = builder.add_root_certificate(cert);
}
}
if let Some(identity) = self.identity {
builder = builder.identify_with(identity.rustls_tls);
}
let tls = if self.accept_invalid_certs || self.accept_invalid_hostnames {
let verifier = super::rustls::InvalidCertsVerifier {
ignore_invalid_hostnames: self.accept_invalid_hostnames,
ignore_invalid_certs: self.accept_invalid_certs,
roots: root_cert_store,
crypto_provider,
};
tls.dangerous()
.with_custom_certificate_verifier(Arc::new(verifier))
} else {
tls.with_root_certificates(root_cert_store)
};
let tls = if let Some(identity) = self.identity {
tls.with_client_auth_cert(identity.rustls_tls.chain, identity.rustls_tls.key)
.map_err(error::tls)?
} else {
tls.with_no_client_auth()
};
Ok(TlsParameters {
connector: InnerTlsParameters::Rustls {
config: Arc::new(tls),
},
domain: self.domain,
})
builder.build_legacy()
}
}
@@ -582,12 +406,14 @@ impl TlsParametersBuilder {
#[allow(clippy::enum_variant_names)]
pub(crate) enum InnerTlsParameters {
#[cfg(feature = "native-tls")]
NativeTls { connector: TlsConnector },
NativeTls { connector: native_tls::TlsConnector },
#[cfg(feature = "rustls")]
Rustls { config: Arc<ClientConfig> },
Rustls {
config: std::sync::Arc<rustls::ClientConfig>,
},
#[cfg(feature = "boring-tls")]
BoringTls {
connector: SslConnector,
connector: boring::ssl::SslConnector,
accept_invalid_hostnames: bool,
},
}
@@ -600,12 +426,14 @@ impl TlsParameters {
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
#[allow(deprecated)]
pub fn new(domain: String) -> Result<Self, Error> {
// FIXME: use something different here
TlsParametersBuilder::new(domain).build()
}
/// Creates a new `TlsParameters` builder
#[deprecated]
#[allow(deprecated)]
pub fn builder(domain: String) -> TlsParametersBuilder {
TlsParametersBuilder::new(domain)
}
@@ -613,6 +441,8 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using native-tls
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_native(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_native()
}
@@ -620,6 +450,8 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using rustls
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_rustls(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_rustls()
}
@@ -627,6 +459,8 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using boring
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_boring(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_boring()
}

View File

@@ -4,14 +4,11 @@ use std::{
};
use rustls::{
client::{
danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
ClientConfig,
},
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider},
pki_types::{self, ServerName, UnixTime},
server::ParsedCertificate,
DigitallySignedStruct, RootCertStore, SignatureScheme,
ClientConfig, DigitallySignedStruct, RootCertStore, SignatureScheme,
};
use crate::transport::smtp::error::{self, Error};