Skip to content

Commit

Permalink
feat: Implement accept_invalid_hostnames for rustls (#2249)
Browse files Browse the repository at this point in the history
Signed-off-by: Thales Fragoso <[email protected]>
  • Loading branch information
thalesfragoso committed Jul 15, 2024
1 parent 9e577f5 commit c660535
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased

- Implement `danger_accept_invalid_hostnames` for `rustls`.

## v0.12.5

- Add `blocking::ClientBuilder::dns_resolver()` method to change DNS resolver in blocking client.
Expand Down
52 changes: 33 additions & 19 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ struct Config {
// NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
accepts: Accepts,
headers: HeaderMap,
#[cfg(feature = "native-tls")]
#[cfg(feature = "__tls")]
hostname_verification: bool,
#[cfg(feature = "__tls")]
certs_verification: bool,
Expand Down Expand Up @@ -188,7 +188,7 @@ impl ClientBuilder {
error: None,
accepts: Accepts::default(),
headers,
#[cfg(feature = "native-tls")]
#[cfg(feature = "__tls")]
hostname_verification: true,
#[cfg(feature = "__tls")]
certs_verification: true,
Expand Down Expand Up @@ -388,10 +388,7 @@ impl ClientBuilder {
}
}

#[cfg(feature = "native-tls")]
{
tls.danger_accept_invalid_hostnames(!config.hostname_verification);
}
tls.danger_accept_invalid_hostnames(!config.hostname_verification);

tls.danger_accept_invalid_certs(!config.certs_verification);

Expand Down Expand Up @@ -500,7 +497,7 @@ impl ClientBuilder {
}
#[cfg(feature = "__rustls")]
TlsBackend::Rustls => {
use crate::tls::NoVerifier;
use crate::tls::{IgnoreHostname, NoVerifier};

// Set root certificates.
let mut root_cert_store = rustls::RootCertStore::empty();
Expand Down Expand Up @@ -578,10 +575,25 @@ impl ClientBuilder {
});

// Build TLS config
let signature_algorithms = provider.signature_verification_algorithms;
let config_builder = rustls::ClientConfig::builder_with_provider(provider)
.with_protocol_versions(&versions)
.map_err(|_| crate::error::builder("invalid TLS versions"))?
.with_root_certificates(root_cert_store);
.map_err(|_| crate::error::builder("invalid TLS versions"))?;

let config_builder = if !config.certs_verification {
config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoVerifier))
} else if !config.hostname_verification {
config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
root_cert_store,
signature_algorithms,
)))
} else {
config_builder.with_root_certificates(root_cert_store)
};

// Finalize TLS config
let mut tls = if let Some(id) = config.identity {
Expand All @@ -590,12 +602,6 @@ impl ClientBuilder {
config_builder.with_no_client_auth()
};

// Certificate verifier
if !config.certs_verification {
tls.dangerous()
.set_certificate_verifier(Arc::new(NoVerifier));
}

tls.enable_sni = config.tls_sni;

// ALPN protocol
Expand Down Expand Up @@ -1476,9 +1482,17 @@ impl ClientBuilder {
///
/// # Optional
///
/// This requires the optional `native-tls` feature to be enabled.
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
/// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
/// feature to be enabled.
#[cfg(feature = "__tls")]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "default-tls",
feature = "native-tls",
feature = "rustls-tls"
)))
)]
pub fn danger_accept_invalid_hostnames(
mut self,
accept_invalid_hostname: bool,
Expand Down Expand Up @@ -2243,7 +2257,7 @@ impl Config {
f.field("tcp_nodelay", &true);
}

#[cfg(feature = "native-tls")]
#[cfg(feature = "__tls")]
{
if !self.hostname_verification {
f.field("danger_accept_invalid_hostnames", &true);
Expand Down
14 changes: 11 additions & 3 deletions src/blocking/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,17 @@ impl ClientBuilder {
///
/// # Optional
///
/// This requires the optional `native-tls` feature to be enabled.
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
/// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
/// feature to be enabled.
#[cfg(feature = "__tls")]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "default-tls",
feature = "native-tls",
feature = "rustls-tls"
)))
)]
pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
self.with_inner(|inner| inner.danger_accept_invalid_hostnames(accept_invalid_hostname))
}
Expand Down
69 changes: 68 additions & 1 deletion src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
#[cfg(feature = "__rustls")]
use rustls::{
client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified,
client::danger::ServerCertVerifier, DigitallySignedStruct, Error as TLSError, SignatureScheme,
client::danger::ServerCertVerifier, crypto::WebPkiSupportedAlgorithms,
server::ParsedCertificate, DigitallySignedStruct, Error as TLSError, RootCertStore,
SignatureScheme,
};
#[cfg(feature = "__rustls")]
use rustls_pki_types::{ServerName, UnixTime};
Expand Down Expand Up @@ -571,6 +573,71 @@ impl ServerCertVerifier for NoVerifier {
}
}

#[cfg(feature = "__rustls")]
#[derive(Debug)]
pub(crate) struct IgnoreHostname {
roots: RootCertStore,
signature_algorithms: WebPkiSupportedAlgorithms,
}

#[cfg(feature = "__rustls")]
impl IgnoreHostname {
pub(crate) fn new(
roots: RootCertStore,
signature_algorithms: WebPkiSupportedAlgorithms,
) -> Self {
Self {
roots,
signature_algorithms,
}
}
}

#[cfg(feature = "__rustls")]
impl ServerCertVerifier for IgnoreHostname {
fn verify_server_cert(
&self,
end_entity: &rustls_pki_types::CertificateDer<'_>,
intermediates: &[rustls_pki_types::CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, TLSError> {
let cert = ParsedCertificate::try_from(end_entity)?;

rustls::client::verify_server_cert_signed_by_trust_anchor(
&cert,
&self.roots,
intermediates,
now,
self.signature_algorithms.all,
)?;
Ok(ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls_pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
rustls::crypto::verify_tls12_signature(message, cert, dss, &self.signature_algorithms)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls_pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
rustls::crypto::verify_tls13_signature(message, cert, dss, &self.signature_algorithms)
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.signature_algorithms.supported_schemes()
}
}

/// Hyper extension carrying extra TLS layer information.
/// Made available to clients on responses when `tls_info` is set.
#[derive(Clone)]
Expand Down
2 changes: 1 addition & 1 deletion tests/badssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async fn test_badssl_no_built_in_roots() {
assert!(result.is_err());
}

#[cfg(feature = "native-tls")]
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
#[tokio::test]
async fn test_badssl_wrong_host() {
let text = reqwest::Client::builder()
Expand Down

0 comments on commit c660535

Please sign in to comment.