upgrade to native-tls 0.2 + invalid certs (#325)
- Bumps `native-tls` dependency to 0.2 and adapt code accordingly - Import code used from `tokio-tls` into `connect_async` and adapt dependencies accordinlgy - Add an option for using `danger_accept_invalid_certs` inside the `Config` struct
This commit is contained in:
committed by
Sean McArthur
parent
a25f62f4cb
commit
11f8588989
@@ -18,18 +18,17 @@ encoding_rs = "0.7"
|
||||
futures = "0.1.21"
|
||||
http = "0.1.5"
|
||||
hyper = "0.12.2"
|
||||
hyper-tls = "0.2.1"
|
||||
hyper-tls = "0.3"
|
||||
libflate = "0.1.11"
|
||||
log = "0.4"
|
||||
mime = "0.3.7"
|
||||
mime_guess = "2.0.0-alpha.4"
|
||||
native-tls = "0.1.5"
|
||||
native-tls = "0.2"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.5"
|
||||
tokio = "0.1.7"
|
||||
tokio-io = "0.1"
|
||||
tokio-tls = "0.1"
|
||||
url = "1.2"
|
||||
uuid = { version = "0.6", features = ["v4"] }
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ struct Config {
|
||||
gzip: bool,
|
||||
headers: HeaderMap,
|
||||
hostname_verification: bool,
|
||||
certs_verification: bool,
|
||||
proxies: Vec<Proxy>,
|
||||
redirect_policy: RedirectPolicy,
|
||||
referer: bool,
|
||||
@@ -56,31 +57,24 @@ struct Config {
|
||||
impl ClientBuilder {
|
||||
/// Constructs a new `ClientBuilder`
|
||||
pub fn new() -> ClientBuilder {
|
||||
match TlsConnector::builder() {
|
||||
Ok(tls_connector_builder) => {
|
||||
let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
|
||||
headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
|
||||
headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"));
|
||||
let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
|
||||
headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
|
||||
headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"));
|
||||
|
||||
ClientBuilder {
|
||||
config: Some(Config {
|
||||
gzip: true,
|
||||
headers: headers,
|
||||
hostname_verification: true,
|
||||
proxies: Vec::new(),
|
||||
redirect_policy: RedirectPolicy::default(),
|
||||
referer: true,
|
||||
timeout: None,
|
||||
tls: tls_connector_builder,
|
||||
dns_threads: 4,
|
||||
}),
|
||||
err: None,
|
||||
}
|
||||
},
|
||||
Err(e) => ClientBuilder {
|
||||
config: None,
|
||||
err: Some(::error::from(e)),
|
||||
}
|
||||
ClientBuilder {
|
||||
config: Some(Config {
|
||||
gzip: true,
|
||||
headers: headers,
|
||||
hostname_verification: true,
|
||||
certs_verification: true,
|
||||
proxies: Vec::new(),
|
||||
redirect_policy: RedirectPolicy::default(),
|
||||
referer: true,
|
||||
timeout: None,
|
||||
tls: TlsConnector::builder(),
|
||||
dns_threads: 4,
|
||||
}),
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,18 +92,18 @@ impl ClientBuilder {
|
||||
if let Some(err) = self.err.take() {
|
||||
return Err(err);
|
||||
}
|
||||
let config = self.config
|
||||
let mut config = self.config
|
||||
.take()
|
||||
.expect("ClientBuilder cannot be reused after building a Client");
|
||||
|
||||
config.tls.danger_accept_invalid_hostnames(!config.hostname_verification);
|
||||
config.tls.danger_accept_invalid_certs(!config.certs_verification);
|
||||
|
||||
let tls = try_!(config.tls.build());
|
||||
|
||||
let proxies = Arc::new(config.proxies);
|
||||
|
||||
let mut connector = Connector::new(config.dns_threads, tls, proxies.clone());
|
||||
if !config.hostname_verification {
|
||||
connector.danger_disable_hostname_verification();
|
||||
}
|
||||
|
||||
let hyper_client = ::hyper::Client::builder()
|
||||
.build(connector);
|
||||
@@ -133,9 +127,7 @@ impl ClientBuilder {
|
||||
pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut ClientBuilder {
|
||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||
let cert = ::tls::cert(cert);
|
||||
if let Err(e) = config.tls.add_root_certificate(cert) {
|
||||
self.err = Some(::error::from(e));
|
||||
}
|
||||
config.tls.add_root_certificate(cert);
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -144,9 +136,7 @@ impl ClientBuilder {
|
||||
pub fn identity(&mut self, identity: Identity) -> &mut ClientBuilder {
|
||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||
let pkcs12 = ::tls::pkcs12(identity);
|
||||
if let Err(e) = config.tls.identity(pkcs12) {
|
||||
self.err = Some(::error::from(e));
|
||||
}
|
||||
config.tls.identity(pkcs12);
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -177,6 +167,31 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable certs verification.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// You should think very carefully before you use this method. If
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub fn danger_disable_certs_verification(&mut self) -> &mut ClientBuilder {
|
||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||
config.certs_verification = false;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable certs verification.
|
||||
#[inline]
|
||||
pub fn enable_certs_verification(&mut self) -> &mut ClientBuilder {
|
||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||
config.certs_verification = true;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default headers for every request.
|
||||
#[inline]
|
||||
pub fn default_headers(&mut self, headers: HeaderMap) -> &mut ClientBuilder {
|
||||
|
||||
@@ -167,6 +167,29 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable certs verification.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// You should think very carefully before you use this method. If
|
||||
/// 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.
|
||||
#[inline]
|
||||
pub fn danger_disable_certs_verification(&mut self) -> &mut ClientBuilder {
|
||||
self.inner.danger_disable_certs_verification();
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable certs verification.
|
||||
///
|
||||
/// Default is enabled.
|
||||
#[inline]
|
||||
pub fn enable_certs_verification(&mut self) -> &mut ClientBuilder {
|
||||
self.inner.enable_certs_verification();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default headers for every request.
|
||||
///
|
||||
/// # Example
|
||||
|
||||
@@ -6,7 +6,7 @@ use hyper::client::connect::{Connect, Connected, Destination};
|
||||
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
|
||||
use native_tls::TlsConnector;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tls::{TlsConnectorExt, TlsStream};
|
||||
use connect_async::{TlsConnectorExt, TlsStream};
|
||||
|
||||
use std::io::{self, Cursor, Read, Write};
|
||||
use std::sync::Arc;
|
||||
@@ -33,10 +33,6 @@ impl Connector {
|
||||
tls: tls,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn danger_disable_hostname_verification(&mut self) {
|
||||
self.https.danger_disable_hostname_verification(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect for Connector {
|
||||
|
||||
131
src/connect_async.rs
Normal file
131
src/connect_async.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use futures::{Poll, Future, Async};
|
||||
use native_tls;
|
||||
use native_tls::{HandshakeError, Error, TlsConnector};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
||||
/// protocol.
|
||||
///
|
||||
/// A `TlsStream<S>` represents a handshake that has been completed successfully
|
||||
/// and both the server and the client are ready for receiving and sending
|
||||
/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written
|
||||
/// to a `TlsStream` are encrypted when passing through to `S`.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsStream<S> {
|
||||
inner: native_tls::TlsStream<S>,
|
||||
}
|
||||
|
||||
/// Future returned from `TlsConnectorExt::connect_async` which will resolve
|
||||
/// once the connection handshake has finished.
|
||||
pub struct ConnectAsync<S> {
|
||||
inner: MidHandshake<S>,
|
||||
}
|
||||
|
||||
struct MidHandshake<S> {
|
||||
inner: Option<Result<native_tls::TlsStream<S>, HandshakeError<S>>>,
|
||||
}
|
||||
|
||||
/// Extension trait for the `TlsConnector` type in the `native_tls` crate.
|
||||
pub trait TlsConnectorExt: sealed::Sealed {
|
||||
/// Connects the provided stream with this connector, assuming the provided
|
||||
/// domain.
|
||||
///
|
||||
/// This function will internally call `TlsConnector::connect` to connect
|
||||
/// the stream and returns a future representing the resolution of the
|
||||
/// connection operation. The returned future will resolve to either
|
||||
/// `TlsStream<S>` or `Error` depending if it's successful or not.
|
||||
///
|
||||
/// This is typically used for clients who have already established, for
|
||||
/// example, a TCP connection to a remote server. That stream is then
|
||||
/// provided here to perform the client half of a connection to a
|
||||
/// TLS-powered server.
|
||||
///
|
||||
/// # Compatibility notes
|
||||
///
|
||||
/// Note that this method currently requires `S: Read + Write` but it's
|
||||
/// highly recommended to ensure that the object implements the `AsyncRead`
|
||||
/// and `AsyncWrite` traits as well, otherwise this function will not work
|
||||
/// properly.
|
||||
fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S>
|
||||
where S: Read + Write; // TODO: change to AsyncRead + AsyncWrite
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
impl<S: Read + Write> Read for TlsStream<S> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Read + Write> Write for TlsStream<S> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<S: AsyncRead + AsyncWrite> AsyncRead for TlsStream<S> {
|
||||
}
|
||||
|
||||
impl<S: AsyncRead + AsyncWrite> AsyncWrite for TlsStream<S> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
try_nb!(self.inner.shutdown());
|
||||
self.inner.get_mut().shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
impl TlsConnectorExt for TlsConnector {
|
||||
fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S>
|
||||
where S: Read + Write,
|
||||
{
|
||||
ConnectAsync {
|
||||
inner: MidHandshake {
|
||||
inner: Some(self.connect(domain, stream)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Sealed for TlsConnector {}
|
||||
|
||||
// TODO: change this to AsyncRead/AsyncWrite on next major version
|
||||
impl<S: Read + Write> Future for ConnectAsync<S> {
|
||||
type Item = TlsStream<S>;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<TlsStream<S>, Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change this to AsyncRead/AsyncWrite on next major version
|
||||
impl<S: Read + Write> Future for MidHandshake<S> {
|
||||
type Item = TlsStream<S>;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<TlsStream<S>, Error> {
|
||||
match self.inner.take().expect("cannot poll MidHandshake twice") {
|
||||
Ok(stream) => Ok(TlsStream { inner: stream }.into()),
|
||||
Err(HandshakeError::Failure(e)) => Err(e),
|
||||
Err(HandshakeError::WouldBlock(s)) => {
|
||||
match s.handshake() {
|
||||
Ok(stream) => Ok(TlsStream { inner: stream }.into()),
|
||||
Err(HandshakeError::Failure(e)) => Err(e),
|
||||
Err(HandshakeError::WouldBlock(s)) => {
|
||||
self.inner = Some(Err(HandshakeError::WouldBlock(s)));
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +145,8 @@ extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate serde_urlencoded;
|
||||
extern crate tokio;
|
||||
#[macro_use]
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_tls;
|
||||
extern crate url;
|
||||
extern crate uuid;
|
||||
|
||||
@@ -173,6 +173,7 @@ mod error;
|
||||
|
||||
mod async_impl;
|
||||
mod connect;
|
||||
mod connect_async;
|
||||
mod body;
|
||||
mod client;
|
||||
mod into_url;
|
||||
|
||||
@@ -66,7 +66,7 @@ impl fmt::Debug for Certificate {
|
||||
|
||||
|
||||
/// Represent a private key and X509 cert as a client certificate.
|
||||
pub struct Identity(native_tls::Pkcs12);
|
||||
pub struct Identity(native_tls::Identity);
|
||||
|
||||
impl Identity {
|
||||
/// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
|
||||
@@ -101,7 +101,7 @@ impl Identity {
|
||||
///
|
||||
/// If the provided buffer is not valid DER, an error will be returned.
|
||||
pub fn from_pkcs12_der(der: &[u8], password: &str) -> ::Result<Identity> {
|
||||
let inner = try_!(native_tls::Pkcs12::from_der(der, password));
|
||||
let inner = try_!(native_tls::Identity::from_pkcs12(der, password));
|
||||
Ok(Identity(inner))
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,6 @@ pub fn cert(cert: Certificate) -> native_tls::Certificate {
|
||||
cert.0
|
||||
}
|
||||
|
||||
pub fn pkcs12(identity: Identity) -> native_tls::Pkcs12 {
|
||||
pub fn pkcs12(identity: Identity) -> native_tls::Identity {
|
||||
identity.0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user