//! TLS connection handling functionality when using the `openssl` crate for //! handling TLS. //! //! This module is based on how native-tls handles setting up OpenSSL //! connections. The original native-tls code was provided under the following //! license: //! //! > Copyright (c) 2016 The rust-native-tls Developers //! > //! > Permission is hereby granted, free of charge, to any person obtaining a //! > copy of this software and associated documentation files (the "Software"), //! > to deal in the Software without restriction, including without limitation //! > the rights to use, copy, modify, merge, publish, distribute, sublicense, //! > and/or sell copies of the Software, and to permit persons to whom the //! > Software is furnished to do so, subject to the following conditions: //! > //! > The above copyright notice and this permission notice shall be included in //! > all copies or substantial portions of the Software. //! > //! > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //! > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //! > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //! > THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //! > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //! > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //! > DEALINGS IN THE SOFTWARE. use openssl::error::ErrorStack; use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVersion}; use openssl::x509::X509; use std::fs; use std::io::{self, Write}; use std::net::TcpStream; use crate::Error; use super::{Connection, HttpStream}; pub type SecuredStream = SslStream; impl From for Error { fn from(err: ErrorStack) -> Self { Error::IoError(io::Error::new(io::ErrorKind::Other, err)) } } pub fn create_secured_stream(conn: &Connection) -> Result { // openssl setup #[cfg(feature = "log")] log::trace!("Setting up TLS parameters for {}.", conn.request.url.host); let connector = { let mut connector_builder = SslConnector::builder(SslMethod::tls())?; connector_builder.set_min_proto_version(Some(SslVersion::TLS1))?; #[cfg(feature = "openssl-probe")] { let probe = openssl_probe::probe(); connector_builder .load_verify_locations(probe.cert_file.as_deref(), probe.cert_dir.as_deref())?; } if cfg!(target_os = "android") { if let Ok(dir) = fs::read_dir("/system/etc/security/cacerts") { let certs = dir .filter_map(|r| r.ok()) .filter_map(|e| fs::read(e.path()).ok()) .filter_map(|b| X509::from_pem(&b).ok()); for cert in certs { if let Err(_err) = connector_builder.cert_store_mut().add_cert(cert) { #[cfg(feature = "log")] log::debug!("load_android_root_certs error: {:?}", _err); } } } } connector_builder.build().configure()? }; // Connect #[cfg(feature = "log")] log::trace!("Establishing TCP connection to {}.", conn.request.url.host); let tcp = conn.connect()?; // Send request #[cfg(feature = "log")] log::trace!("Establishing TLS session to {}.", conn.request.url.host); let mut tls = match connector .use_server_name_indication(true) .verify_hostname(true) .connect(&conn.request.url.host, tcp) { Ok(tls) => tls, Err(err) => return Err(Error::IoError(io::Error::new(io::ErrorKind::Other, err))), }; #[cfg(feature = "log")] log::trace!("Writing HTTPS request to {}.", conn.request.url.host); let _ = tls.get_ref().set_write_timeout(conn.timeout()?); tls.write_all(&conn.request.as_bytes())?; Ok(HttpStream::create_secured(tls, conn.timeout_at)) }