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
						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,8 +57,6 @@ 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() { |  | ||||||
|             Ok(tls_connector_builder) => { |  | ||||||
|         let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2); |         let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2); | ||||||
|         headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT)); |         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")); |         headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime")); | ||||||
| @@ -67,21 +66,16 @@ impl ClientBuilder { | |||||||
|                 gzip: true, |                 gzip: true, | ||||||
|                 headers: headers, |                 headers: headers, | ||||||
|                 hostname_verification: true, |                 hostname_verification: true, | ||||||
|  |                 certs_verification: true, | ||||||
|                 proxies: Vec::new(), |                 proxies: Vec::new(), | ||||||
|                 redirect_policy: RedirectPolicy::default(), |                 redirect_policy: RedirectPolicy::default(), | ||||||
|                 referer: true, |                 referer: true, | ||||||
|                 timeout: None, |                 timeout: None, | ||||||
|                         tls: tls_connector_builder, |                 tls: TlsConnector::builder(), | ||||||
|                 dns_threads: 4, |                 dns_threads: 4, | ||||||
|             }), |             }), | ||||||
|             err: None, |             err: None, | ||||||
|         } |         } | ||||||
|             }, |  | ||||||
|             Err(e) => ClientBuilder { |  | ||||||
|                 config: None, |  | ||||||
|                 err: Some(::error::from(e)), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns a `Client` that uses this `ClientBuilder` configuration. |     /// Returns a `Client` that uses this `ClientBuilder` configuration. | ||||||
| @@ -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