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"
|
futures = "0.1.21"
|
||||||
http = "0.1.5"
|
http = "0.1.5"
|
||||||
hyper = "0.12.2"
|
hyper = "0.12.2"
|
||||||
hyper-tls = "0.2.1"
|
hyper-tls = "0.3"
|
||||||
libflate = "0.1.11"
|
libflate = "0.1.11"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3.7"
|
mime = "0.3.7"
|
||||||
mime_guess = "2.0.0-alpha.4"
|
mime_guess = "2.0.0-alpha.4"
|
||||||
native-tls = "0.1.5"
|
native-tls = "0.2"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.5"
|
serde_urlencoded = "0.5"
|
||||||
tokio = "0.1.7"
|
tokio = "0.1.7"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-tls = "0.1"
|
|
||||||
url = "1.2"
|
url = "1.2"
|
||||||
uuid = { version = "0.6", features = ["v4"] }
|
uuid = { version = "0.6", features = ["v4"] }
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ struct Config {
|
|||||||
gzip: bool,
|
gzip: bool,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
hostname_verification: bool,
|
hostname_verification: bool,
|
||||||
|
certs_verification: bool,
|
||||||
proxies: Vec<Proxy>,
|
proxies: Vec<Proxy>,
|
||||||
redirect_policy: RedirectPolicy,
|
redirect_policy: RedirectPolicy,
|
||||||
referer: bool,
|
referer: bool,
|
||||||
@@ -56,31 +57,24 @@ struct Config {
|
|||||||
impl ClientBuilder {
|
impl ClientBuilder {
|
||||||
/// Constructs a new `ClientBuilder`
|
/// Constructs a new `ClientBuilder`
|
||||||
pub fn new() -> ClientBuilder {
|
pub fn new() -> ClientBuilder {
|
||||||
match TlsConnector::builder() {
|
let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
|
||||||
Ok(tls_connector_builder) => {
|
headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
|
||||||
let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
|
headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"));
|
||||||
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 {
|
ClientBuilder {
|
||||||
config: Some(Config {
|
config: Some(Config {
|
||||||
gzip: true,
|
gzip: true,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
hostname_verification: true,
|
hostname_verification: true,
|
||||||
proxies: Vec::new(),
|
certs_verification: true,
|
||||||
redirect_policy: RedirectPolicy::default(),
|
proxies: Vec::new(),
|
||||||
referer: true,
|
redirect_policy: RedirectPolicy::default(),
|
||||||
timeout: None,
|
referer: true,
|
||||||
tls: tls_connector_builder,
|
timeout: None,
|
||||||
dns_threads: 4,
|
tls: TlsConnector::builder(),
|
||||||
}),
|
dns_threads: 4,
|
||||||
err: None,
|
}),
|
||||||
}
|
err: None,
|
||||||
},
|
|
||||||
Err(e) => ClientBuilder {
|
|
||||||
config: None,
|
|
||||||
err: Some(::error::from(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,18 +92,18 @@ impl ClientBuilder {
|
|||||||
if let Some(err) = self.err.take() {
|
if let Some(err) = self.err.take() {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
let config = self.config
|
let mut config = self.config
|
||||||
.take()
|
.take()
|
||||||
.expect("ClientBuilder cannot be reused after building a Client");
|
.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 tls = try_!(config.tls.build());
|
||||||
|
|
||||||
let proxies = Arc::new(config.proxies);
|
let proxies = Arc::new(config.proxies);
|
||||||
|
|
||||||
let mut connector = Connector::new(config.dns_threads, tls, proxies.clone());
|
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()
|
let hyper_client = ::hyper::Client::builder()
|
||||||
.build(connector);
|
.build(connector);
|
||||||
@@ -133,9 +127,7 @@ impl ClientBuilder {
|
|||||||
pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut ClientBuilder {
|
pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut ClientBuilder {
|
||||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||||
let cert = ::tls::cert(cert);
|
let cert = ::tls::cert(cert);
|
||||||
if let Err(e) = config.tls.add_root_certificate(cert) {
|
config.tls.add_root_certificate(cert);
|
||||||
self.err = Some(::error::from(e));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -144,9 +136,7 @@ impl ClientBuilder {
|
|||||||
pub fn identity(&mut self, identity: Identity) -> &mut ClientBuilder {
|
pub fn identity(&mut self, identity: Identity) -> &mut ClientBuilder {
|
||||||
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
if let Some(config) = config_mut(&mut self.config, &self.err) {
|
||||||
let pkcs12 = ::tls::pkcs12(identity);
|
let pkcs12 = ::tls::pkcs12(identity);
|
||||||
if let Err(e) = config.tls.identity(pkcs12) {
|
config.tls.identity(pkcs12);
|
||||||
self.err = Some(::error::from(e));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -177,6 +167,31 @@ impl ClientBuilder {
|
|||||||
self
|
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.
|
/// Sets the default headers for every request.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_headers(&mut self, headers: HeaderMap) -> &mut ClientBuilder {
|
pub fn default_headers(&mut self, headers: HeaderMap) -> &mut ClientBuilder {
|
||||||
|
|||||||
@@ -167,6 +167,29 @@ impl ClientBuilder {
|
|||||||
self
|
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.
|
/// Sets the default headers for every request.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use hyper::client::connect::{Connect, Connected, Destination};
|
|||||||
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
|
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
|
||||||
use native_tls::TlsConnector;
|
use native_tls::TlsConnector;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_tls::{TlsConnectorExt, TlsStream};
|
use connect_async::{TlsConnectorExt, TlsStream};
|
||||||
|
|
||||||
use std::io::{self, Cursor, Read, Write};
|
use std::io::{self, Cursor, Read, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -33,10 +33,6 @@ impl Connector {
|
|||||||
tls: tls,
|
tls: tls,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn danger_disable_hostname_verification(&mut self) {
|
|
||||||
self.https.danger_disable_hostname_verification(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connect for Connector {
|
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_json;
|
||||||
extern crate serde_urlencoded;
|
extern crate serde_urlencoded;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
#[macro_use]
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate tokio_tls;
|
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
@@ -173,6 +173,7 @@ mod error;
|
|||||||
|
|
||||||
mod async_impl;
|
mod async_impl;
|
||||||
mod connect;
|
mod connect;
|
||||||
|
mod connect_async;
|
||||||
mod body;
|
mod body;
|
||||||
mod client;
|
mod client;
|
||||||
mod into_url;
|
mod into_url;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl fmt::Debug for Certificate {
|
|||||||
|
|
||||||
|
|
||||||
/// Represent a private key and X509 cert as a client 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 {
|
impl Identity {
|
||||||
/// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
|
/// 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.
|
/// If the provided buffer is not valid DER, an error will be returned.
|
||||||
pub fn from_pkcs12_der(der: &[u8], password: &str) -> ::Result<Identity> {
|
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))
|
Ok(Identity(inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,6 +119,6 @@ pub fn cert(cert: Certificate) -> native_tls::Certificate {
|
|||||||
cert.0
|
cert.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pkcs12(identity: Identity) -> native_tls::Pkcs12 {
|
pub fn pkcs12(identity: Identity) -> native_tls::Identity {
|
||||||
identity.0
|
identity.0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user