diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 004b50b9c..9b8e36318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,7 @@ jobs: - windows / stable-i686-gnu - "feat.: default-tls disabled" - "feat.: rustls-tls" + - "feat.: native-tls" - "feat.: default-tls and rustls-tls" - "feat.: cookies" - "feat.: blocking" @@ -100,6 +101,8 @@ jobs: features: "--no-default-features" - name: "feat.: rustls-tls" features: "--no-default-features --features rustls-tls" + - name: "feat.: native-tls" + features: "--features native-tls" - name: "feat.: default-tls and rustls-tls" features: "--features rustls-tls" - name: "feat.: cookies" diff --git a/Cargo.toml b/Cargo.toml index bef47582b..6678d720f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,13 @@ default = ["default-tls"] tls = [] -default-tls = ["hyper-tls", "native-tls", "tls", "tokio-tls"] -default-tls-vendored = ["default-tls", "native-tls/vendored"] +# Note: this doesn't enable the 'native-tls' feature, which adds specific +# functionality for it. +default-tls = ["hyper-tls", "native-tls-crate", "tls", "tokio-tls"] + +# Enables native-tls specific functionality not available by default. +native-tls = ["default-tls"] +native-tls-vendored = ["native-tls", "native-tls-crate/vendored"] rustls-tls = ["hyper-rustls", "tokio-rustls", "webpki-roots", "rustls", "tls"] @@ -74,7 +79,7 @@ serde_urlencoded = "0.6.1" ## default-tls hyper-tls = { version = "0.4", optional = true } -native-tls = { version = "0.2", optional = true } +native-tls-crate = { version = "0.2", optional = true, package = "native-tls" } tokio-tls = { version = "0.3.0", optional = true } # rustls-tls diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index c305d231b..fce15d26b 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -13,8 +13,8 @@ use http::header::{ use http::Uri; use http::uri::Scheme; use hyper::client::ResponseFuture; -#[cfg(feature = "default-tls")] -use native_tls::TlsConnector; +#[cfg(feature = "native-tls-crate")] +use native_tls_crate::TlsConnector; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; @@ -60,7 +60,7 @@ struct Config { // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder` gzip: bool, headers: HeaderMap, - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] hostname_verification: bool, #[cfg(feature = "tls")] certs_verification: bool, @@ -106,7 +106,7 @@ impl ClientBuilder { config: Config { gzip: cfg!(feature = "gzip"), headers, - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] hostname_verification: true, #[cfg(feature = "tls")] certs_verification: true, @@ -160,15 +160,24 @@ impl ClientBuilder { #[cfg(feature = "default-tls")] TlsBackend::Default => { let mut tls = TlsConnector::builder(); - tls.danger_accept_invalid_hostnames(!config.hostname_verification); + + #[cfg(feature = "native-tls")] + { + tls.danger_accept_invalid_hostnames(!config.hostname_verification); + } + tls.danger_accept_invalid_certs(!config.certs_verification); for cert in config.root_certs { cert.add_to_native_tls(&mut tls); } - if let Some(id) = config.identity { - id.add_to_native_tls(&mut tls)?; + + #[cfg(feature = "native-tls")] + { + if let Some(id) = config.identity { + id.add_to_native_tls(&mut tls)?; + } } Connector::new_default_tls( @@ -511,8 +520,8 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` or `rustls-tls` feature to be - /// enabled. + /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls` + /// feature to be enabled. #[cfg(feature = "tls")] pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder { self.config.root_certs.push(cert); @@ -523,7 +532,7 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` or `rustls-tls` feature to be + /// This requires the optional `native-tls` or `rustls-tls` feature to be /// enabled. #[cfg(feature = "tls")] pub fn identity(mut self, identity: Identity) -> ClientBuilder { @@ -544,8 +553,8 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` feature to be enabled. - #[cfg(feature = "default-tls")] + /// This requires the optional `native-tls` feature to be enabled. + #[cfg(feature = "native-tls")] pub fn danger_accept_invalid_hostnames( mut self, accept_invalid_hostname: bool, @@ -568,8 +577,8 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` or `rustls-tls` feature to be - /// enabled. + /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls` + /// feature to be enabled. #[cfg(feature = "tls")] pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder { self.config.certs_verification = !accept_invalid_certs; @@ -583,9 +592,9 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` feature to be enabled. - #[cfg(feature = "default-tls")] - pub fn use_default_tls(mut self) -> ClientBuilder { + /// This requires the optional `native-tls` feature to be enabled. + #[cfg(feature = "native-tls")] + pub fn use_native_tls(mut self) -> ClientBuilder { self.config.tls = TlsBackend::Default; self } @@ -888,7 +897,7 @@ impl Config { f.field("tcp_nodelay", &true); } - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] { if !self.hostname_verification { f.field("danger_accept_invalid_hostnames", &true); @@ -902,7 +911,7 @@ impl Config { } } - #[cfg(all(feature = "default-tls", feature = "rustls-tls"))] + #[cfg(all(feature = "native-tls-crate", feature = "rustls-tls"))] { f.field("tls_backend", &self.tls); } diff --git a/src/blocking/client.rs b/src/blocking/client.rs index 1d80c8b3e..13490cc95 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -331,44 +331,14 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` or `rustls-tls` feature to be - /// enabled. + /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls` + /// feature to be enabled. #[cfg(feature = "tls")] pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder { self.with_inner(move |inner| inner.add_root_certificate(cert)) } /// Sets the identity to be used for client certificate authentication. - /// - /// # Example - /// - /// ``` - /// # use std::fs::File; - /// # use std::io::Read; - /// # fn build_client() -> Result<(), Box> { - /// // read a local PKCS12 bundle - /// let mut buf = Vec::new(); - /// - /// #[cfg(feature = "default-tls")] - /// File::open("my-ident.pfx")?.read_to_end(&mut buf)?; - /// #[cfg(feature = "rustls-tls")] - /// File::open("my-ident.pem")?.read_to_end(&mut buf)?; - /// - /// #[cfg(feature = "default-tls")] - /// // create an Identity from the PKCS#12 archive - /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?; - /// #[cfg(feature = "rustls-tls")] - /// // create an Identity from the PEM file - /// let pkcs12 = reqwest::Identity::from_pem(&buf)?; - /// - /// // get a client builder - /// let client = reqwest::blocking::Client::builder() - /// .identity(pkcs12) - /// .build()?; - /// # drop(client); - /// # Ok(()) - /// # } - /// ``` #[cfg(feature = "tls")] pub fn identity(self, identity: Identity) -> ClientBuilder { self.with_inner(move |inner| inner.identity(identity)) @@ -384,7 +354,11 @@ impl ClientBuilder { /// hostname verification is not used, any valid certificate for any /// site will be trusted for use from any other. This introduces a /// significant vulnerability to man-in-the-middle attacks. - #[cfg(feature = "default-tls")] + /// + /// # Optional + /// + /// This requires the optional `native-tls` feature to be enabled. + #[cfg(feature = "native-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)) } @@ -412,10 +386,10 @@ impl ClientBuilder { /// /// # Optional /// - /// This requires the optional `default-tls` feature to be enabled. - #[cfg(feature = "default-tls")] - pub fn use_default_tls(self) -> ClientBuilder { - self.with_inner(move |inner| inner.use_default_tls()) + /// This requires the optional `native-tls` feature to be enabled. + #[cfg(feature = "native-tls")] + pub fn use_native_tls(self) -> ClientBuilder { + self.with_inner(move |inner| inner.use_native_tls()) } /// Force using the Rustls TLS backend. diff --git a/src/connect.rs b/src/connect.rs index d8e2fe1ac..287422654 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -3,8 +3,8 @@ use http::uri::{Scheme, Authority}; use http::Uri; use hyper::client::connect::{Connected, Connection}; use tokio::io::{AsyncRead, AsyncWrite}; -#[cfg(feature = "default-tls")] -use native_tls::{TlsConnector, TlsConnectorBuilder}; +#[cfg(feature = "native-tls-crate")] +use native_tls_crate::{TlsConnector, TlsConnectorBuilder}; #[cfg(feature = "tls")] use http::header::HeaderValue; use bytes::{Buf, BufMut}; diff --git a/src/tls.rs b/src/tls.rs index 53d405772..fa57acc61 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -4,11 +4,11 @@ use std::fmt; #[cfg(feature = "rustls-tls")] use tokio_rustls::webpki::DNSNameRef; -/// Represent a server X509 certificate. +/// Represents a server X509 certificate. #[derive(Clone)] pub struct Certificate { - #[cfg(feature = "default-tls")] - native: native_tls::Certificate, + #[cfg(feature = "native-tls-crate")] + native: native_tls_crate::Certificate, #[cfg(feature = "rustls-tls")] original: Cert, } @@ -20,14 +20,18 @@ enum Cert { Pem(Vec), } -/// Represent a private key and X509 cert as a client certificate. +/// Represents a private key and X509 cert as a client certificate. pub struct Identity { + #[cfg_attr( + not(any(feature = "native-tls", feature = "rustls-tls")), + allow(unused) + )] inner: ClientCert, } enum ClientCert { - #[cfg(feature = "default-tls")] - Pkcs12(native_tls::Identity), + #[cfg(feature = "native-tls")] + Pkcs12(native_tls_crate::Identity), #[cfg(feature = "rustls-tls")] Pem { key: rustls::PrivateKey, @@ -54,8 +58,8 @@ impl Certificate { /// ``` pub fn from_der(der: &[u8]) -> crate::Result { Ok(Certificate { - #[cfg(feature = "default-tls")] - native: native_tls::Certificate::from_der(der).map_err(crate::error::builder)?, + #[cfg(feature = "native-tls-crate")] + native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?, #[cfg(feature = "rustls-tls")] original: Cert::Der(der.to_owned()), }) @@ -79,15 +83,15 @@ impl Certificate { /// ``` pub fn from_pem(pem: &[u8]) -> crate::Result { Ok(Certificate { - #[cfg(feature = "default-tls")] - native: native_tls::Certificate::from_pem(pem).map_err(crate::error::builder)?, + #[cfg(feature = "native-tls-crate")] + native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?, #[cfg(feature = "rustls-tls")] original: Cert::Pem(pem.to_owned()), }) } - #[cfg(feature = "default-tls")] - pub(crate) fn add_to_native_tls(self, tls: &mut native_tls::TlsConnectorBuilder) { + #[cfg(feature = "native-tls-crate")] + pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) { tls.add_root_certificate(self.native); } @@ -147,11 +151,15 @@ impl Identity { /// # Ok(()) /// # } /// ``` - #[cfg(feature = "default-tls")] + /// + /// # Optional + /// + /// This requires the `native-tls` Cargo feature enabled. + #[cfg(feature = "native-tls")] pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result { Ok(Identity { inner: ClientCert::Pkcs12( - native_tls::Identity::from_pkcs12(der, password).map_err(crate::error::builder)?, + native_tls_crate::Identity::from_pkcs12(der, password).map_err(crate::error::builder)?, ), }) } @@ -175,6 +183,10 @@ impl Identity { /// # Ok(()) /// # } /// ``` + /// + /// # Optional + /// + /// This requires the `rustls-tls` Cargo feature enabled. #[cfg(feature = "rustls-tls")] pub fn from_pem(buf: &[u8]) -> crate::Result { use rustls::internal::pemfile; @@ -214,10 +226,10 @@ impl Identity { }) } - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] pub(crate) fn add_to_native_tls( self, - tls: &mut native_tls::TlsConnectorBuilder, + tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()> { match self.inner { ClientCert::Pkcs12(id) => { @@ -236,7 +248,7 @@ impl Identity { tls.set_single_client_cert(certs, key); Ok(()) } - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")), } } @@ -308,7 +320,7 @@ mod tests { Certificate::from_pem(b"not pem").unwrap_err(); } - #[cfg(feature = "default-tls")] + #[cfg(feature = "native-tls")] #[test] fn identity_from_pkcs12_der_invalid() { Identity::from_pkcs12_der(b"not der", "nope").unwrap_err(); diff --git a/tests/badssl.rs b/tests/badssl.rs index d3c285067..d40e94665 100644 --- a/tests/badssl.rs +++ b/tests/badssl.rs @@ -47,7 +47,7 @@ async fn test_badssl_self_signed() { assert!(text.contains("self-signed.badssl.com")); } -#[cfg(feature = "default-tls")] +#[cfg(feature = "native-tls")] #[tokio::test] async fn test_badssl_wrong_host() { let text = reqwest::Client::builder()