Add TlsParametersBuilder with dangerous options

This commit is contained in:
Paolo Barbolini
2020-09-17 21:56:46 +02:00
parent c5fef28ac9
commit 30a8797acf
4 changed files with 145 additions and 31 deletions

View File

@@ -41,7 +41,7 @@ hostname = { version = "0.3", optional = true } # feature
## tls
native-tls = { version = "0.2", optional = true } # feature
rustls = { version = "0.18", optional = true }
rustls = { version = "0.18", features = ["dangerous_configuration"], optional = true }
webpki = { version = "0.21", optional = true }
webpki-roots = { version = "0.20", optional = true }

View File

@@ -45,7 +45,7 @@ where
pub fn relay(relay: &str) -> Result<AsyncSmtpTransportBuilder, Error> {
use super::{TlsParameters, SUBMISSIONS_PORT};
let tls_parameters = TlsParameters::new_tokio02(relay.into())?;
let tls_parameters = TlsParameters::builder(relay.into()).build_tokio02()?;
Ok(Self::builder_dangerous(relay)
.port(SUBMISSIONS_PORT)

View File

@@ -34,7 +34,7 @@ pub(super) use self::tls::InnerTlsParameters;
pub use self::{
connection::SmtpConnection,
mock::MockStream,
tls::{Tls, TlsParameters},
tls::{Tls, TlsParameters, TlsParametersBuilder},
};
#[cfg(feature = "tokio02")]

View File

@@ -1,10 +1,17 @@
#[cfg(feature = "rustls-tls")]
use std::sync::Arc;
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
use crate::transport::smtp::error::Error;
#[cfg(feature = "native-tls")]
use native_tls::{Protocol, TlsConnector};
#[cfg(feature = "rustls-tls")]
use rustls::ClientConfig;
use rustls::{
Certificate, ClientConfig, RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError,
};
#[cfg(feature = "rustls-tls")]
use webpki::DNSNameRef;
/// Accepted protocols by default.
/// This removes TLS 1.0 and 1.1 compared to tls-native defaults.
@@ -38,6 +45,119 @@ pub struct TlsParameters {
pub(super) domain: String,
}
/// Builder for `TlsParameters`
#[derive(Debug, Clone)]
pub struct TlsParametersBuilder {
domain: String,
accept_invalid_hostnames: bool,
accept_invalid_certs: bool,
}
impl TlsParametersBuilder {
/// Creates a new builder for `TlsParameters`
pub fn new(domain: String) -> Self {
Self {
domain,
accept_invalid_hostnames: false,
accept_invalid_certs: false,
}
}
/// Controls whether certificates with an invalid hostname are accepted
///
/// Defaults to `false`.
///
/// # Warning
///
/// You should think very carefully before using this method.
/// If hostname verification is disabled *any* valid certificate,
/// including those from other sites, are trusted.
///
/// This method introduces significant vulnerabilities to man-in-the-middle attacks.
///
/// Hostname verification can only be disabled with the `native-tls` TLS backend.
#[cfg(feature = "native-tls")]
pub fn dangerous_accept_invalid_hostnames(
&mut self,
accept_invalid_hostnames: bool,
) -> &mut Self {
self.accept_invalid_hostnames = accept_invalid_hostnames;
self
}
/// Controls whether invalid certificates are accepted
///
/// Defaults to `false`.
///
/// # Warning
///
/// You should think very carefully before using this method.
/// If certificate verification is disabled, *any* certificate
/// is trusted for use, including:
///
/// * Self signed certificates
/// * Certificates from different hostnames
/// * Expired certificates
///
/// This method should only be used as a last resort, as it introduces
/// significant vulnerabilities to man-in-the-middle attacks.
pub fn dangerous_accept_invalid_certs(&mut self, accept_invalid_certs: bool) -> &mut Self {
self.accept_invalid_certs = accept_invalid_certs;
self
}
/// Creates a new `TlsParameters` using native-tls or rustls
/// depending on which one is available
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
pub fn build(self) -> Result<TlsParameters, Error> {
#[cfg(feature = "native-tls")]
return self.build_native();
#[cfg(not(feature = "native-tls"))]
return self.build_rustls();
}
#[cfg(any(feature = "tokio02-native-tls", feature = "tokio02-rustls-tls"))]
pub(crate) fn build_tokio02(self) -> Result<TlsParameters, Error> {
#[cfg(feature = "tokio02-native-tls")]
return self.build_native();
#[cfg(not(feature = "tokio02-native-tls"))]
return self.build_rustls();
}
/// Creates a new `TlsParameters` using native-tls with the provided configuration
#[cfg(feature = "native-tls")]
pub fn build_native(self) -> Result<TlsParameters, Error> {
let mut tls_builder = TlsConnector::builder();
tls_builder.danger_accept_invalid_hostnames(self.accept_invalid_hostnames);
tls_builder.danger_accept_invalid_certs(self.accept_invalid_certs);
tls_builder.min_protocol_version(Some(DEFAULT_TLS_MIN_PROTOCOL));
let connector = tls_builder.build()?;
Ok(TlsParameters {
connector: InnerTlsParameters::NativeTls(connector),
domain: self.domain,
})
}
/// Creates a new `TlsParameters` using rustls with the provided configuration
#[cfg(feature = "rustls-tls")]
pub fn build_rustls(self) -> Result<TlsParameters, Error> {
use webpki_roots::TLS_SERVER_ROOTS;
let mut tls = ClientConfig::new();
if self.accept_invalid_certs {
tls.dangerous()
.set_certificate_verifier(Arc::new(InvalidCertsVerifier {}));
}
tls.root_store.add_server_trust_anchors(&TLS_SERVER_ROOTS);
Ok(TlsParameters {
connector: InnerTlsParameters::RustlsTls(tls),
domain: self.domain,
})
}
}
#[derive(Clone)]
pub enum InnerTlsParameters {
#[cfg(feature = "native-tls")]
@@ -51,48 +171,42 @@ impl TlsParameters {
/// depending on which one is available
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
pub fn new(domain: String) -> Result<Self, Error> {
#[cfg(feature = "native-tls")]
return Self::new_native(domain);
#[cfg(not(feature = "native-tls"))]
return Self::new_rustls(domain);
TlsParametersBuilder::new(domain).build()
}
#[cfg(any(feature = "tokio02-native-tls", feature = "tokio02-rustls-tls"))]
pub(crate) fn new_tokio02(domain: String) -> Result<Self, Error> {
#[cfg(feature = "tokio02-native-tls")]
return Self::new_native(domain);
#[cfg(not(feature = "tokio02-native-tls"))]
return Self::new_rustls(domain);
pub fn builder(domain: String) -> TlsParametersBuilder {
TlsParametersBuilder::new(domain)
}
/// Creates a new `TlsParameters` using native-tls
#[cfg(feature = "native-tls")]
pub fn new_native(domain: String) -> Result<Self, Error> {
let mut tls_builder = TlsConnector::builder();
tls_builder.min_protocol_version(Some(DEFAULT_TLS_MIN_PROTOCOL));
let connector = tls_builder.build()?;
Ok(Self {
connector: InnerTlsParameters::NativeTls(connector),
domain,
})
TlsParametersBuilder::new(domain).build_native()
}
/// Creates a new `TlsParameters` using rustls
#[cfg(feature = "rustls-tls")]
pub fn new_rustls(domain: String) -> Result<Self, Error> {
use webpki_roots::TLS_SERVER_ROOTS;
let mut tls = ClientConfig::new();
tls.root_store.add_server_trust_anchors(&TLS_SERVER_ROOTS);
Ok(Self {
connector: InnerTlsParameters::RustlsTls(tls),
domain,
})
TlsParametersBuilder::new(domain).build_rustls()
}
pub fn domain(&self) -> &str {
&self.domain
}
}
#[cfg(feature = "rustls-tls")]
struct InvalidCertsVerifier;
#[cfg(feature = "rustls-tls")]
impl ServerCertVerifier for InvalidCertsVerifier {
fn verify_server_cert(
&self,
_roots: &RootCertStore,
_presented_certs: &[Certificate],
_dns_name: DNSNameRef<'_>,
_ocsp_response: &[u8],
) -> Result<ServerCertVerified, TLSError> {
Ok(ServerCertVerified::assertion())
}
}