Skip to content

Commit

Permalink
Separate default-tls and native-tls features
Browse files Browse the repository at this point in the history
To allow for the default-tls to change to a different backend by
default, this adds a new `native-tls` optional feature. Any TLS feature
that was only available using native-tls now requires the `native-tls`
feature to be enabled.
  • Loading branch information
seanmonstar committed Dec 20, 2019
1 parent 18fd9a6 commit 902b536
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 80 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
11 changes: 8 additions & 3 deletions Cargo.toml
Expand Up @@ -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"]

Expand Down Expand Up @@ -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
Expand Down
47 changes: 28 additions & 19 deletions src/async_impl/client.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down
48 changes: 11 additions & 37 deletions src/blocking/client.rs
Expand Up @@ -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<std::error::Error>> {
/// // 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))
Expand All @@ -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))
}
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/connect.rs
Expand Up @@ -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};
Expand Down
48 changes: 30 additions & 18 deletions src/tls.rs
Expand Up @@ -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,
}
Expand All @@ -20,14 +20,18 @@ enum Cert {
Pem(Vec<u8>),
}

/// 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,
Expand All @@ -54,8 +58,8 @@ impl Certificate {
/// ```
pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
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()),
})
Expand All @@ -79,15 +83,15 @@ impl Certificate {
/// ```
pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
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);
}

Expand Down Expand Up @@ -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<Identity> {
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)?,
),
})
}
Expand All @@ -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<Identity> {
use rustls::internal::pemfile;
Expand Down Expand Up @@ -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) => {
Expand All @@ -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")),
}
}
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion tests/badssl.rs
Expand Up @@ -47,7 +47,7 @@ async fn test_badssl_self_signed() {
assert!(text.contains("<title>self-signed.badssl.com</title>"));
}

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

0 comments on commit 902b536

Please sign in to comment.