From 78a0d7501b39a00fd9d82aed773a161052fa1ee9 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 21 Sep 2024 19:52:25 +0000 Subject: [PATCH] feat: use Rustls instead of native TLS for HTTPS requests HTTPS requests are used to fetch remote images in HTML emails, to fetch autoconfig XML, to POST requests for `DCACCOUNT:` QR codes to make OAuth 2 API requests and to connect to HTTPS proxies. Rustls is more aggressive than OpenSSL in deprecating cryptographic algorithms so we cannot use it for IMAP and SMTP to avoid breaking compatibility, but for HTTPS requests listed above this should not result in problems. As HTTPS requests use only strict TLS checks, there is no `strict_tls` argument in `wrap_rustls` function. Rustls is already used by iroh, so this change does not introduce new dependencies. --- Cargo.lock | 20 ++++++++++++-------- Cargo.toml | 5 ++++- src/net/http.rs | 7 +++---- src/net/proxy.rs | 6 +++--- src/net/session.rs | 8 ++++++-- src/net/tls.rs | 3 ++- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f20bc448..b9bddf75b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,6 +1325,8 @@ dependencies = [ "regex", "rusqlite", "rust-hsluv", + "rustls", + "rustls-pki-types", "sanitize-filename", "serde", "serde_json", @@ -1341,12 +1343,14 @@ dependencies = [ "thiserror", "tokio", "tokio-io-timeout", + "tokio-rustls", "tokio-stream", "tokio-tar", "tokio-util", "toml", "url", "uuid", + "webpki-roots", ] [[package]] @@ -4992,9 +4996,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "log", "once_cell", @@ -5030,9 +5034,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-platform-verifier" @@ -5063,9 +5067,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -6561,9 +6565,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] diff --git a/Cargo.toml b/Cargo.toml index f063d1084..f41d2252b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,8 @@ rand = { workspace = true } regex = { workspace = true } rusqlite = { workspace = true, features = ["sqlcipher"] } rust-hsluv = "0.1" +rustls-pki-types = "1.8.0" +rustls = { version = "0.23.13", default-features = false } sanitize-filename = { workspace = true } serde_json = { workspace = true } serde_urlencoded = "0.7.1" @@ -97,6 +99,7 @@ tagger = "4.3.4" textwrap = "0.16.1" thiserror = { workspace = true } tokio-io-timeout = "1.2.0" +tokio-rustls = { version = "0.26.0", default-features = false } tokio-stream = { version = "0.1.15", features = ["fs"] } tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar tokio-util = { workspace = true } @@ -104,6 +107,7 @@ tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] } toml = "0.8" url = "2" uuid = { version = "1", features = ["serde", "v4"] } +webpki-roots = "0.26.6" [dev-dependencies] anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests. @@ -197,7 +201,6 @@ yerpc = "0.6.2" default = ["vendored"] internals = [] vendored = [ - "async-native-tls/vendored", "rusqlite/bundled-sqlcipher-vendored-openssl" ] diff --git a/src/net/http.rs b/src/net/http.rs index 6b7bdcbb2..94e85f68c 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -10,7 +10,7 @@ use serde::Serialize; use crate::context::Context; use crate::net::proxy::ProxyConfig; use crate::net::session::SessionStream; -use crate::net::tls::wrap_tls; +use crate::net::tls::wrap_rustls; /// HTTP(S) GET response. #[derive(Debug)] @@ -67,17 +67,16 @@ where "https" => { let port = parsed_url.port_u16().unwrap_or(443); let load_cache = true; - let strict_tls = true; if let Some(proxy_config) = proxy_config_opt { let proxy_stream = proxy_config .connect(context, host, port, load_cache) .await?; - let tls_stream = wrap_tls(strict_tls, host, &[], proxy_stream).await?; + let tls_stream = wrap_rustls(host, &[], proxy_stream).await?; Box::new(tls_stream) } else { let tcp_stream = crate::net::connect_tcp(context, host, port, load_cache).await?; - let tls_stream = wrap_tls(strict_tls, host, &[], tcp_stream).await?; + let tls_stream = wrap_rustls(host, &[], tcp_stream).await?; Box::new(tls_stream) } } diff --git a/src/net/proxy.rs b/src/net/proxy.rs index caaebcf33..ef121d222 100644 --- a/src/net/proxy.rs +++ b/src/net/proxy.rs @@ -20,8 +20,9 @@ use url::Url; use crate::config::Config; use crate::context::Context; +use crate::net::connect_tcp; use crate::net::session::SessionStream; -use crate::net::{connect_tcp, wrap_tls}; +use crate::net::tls::wrap_rustls; use crate::sql::Sql; /// Default SOCKS5 port according to [RFC 1928](https://tools.ietf.org/html/rfc1928). @@ -375,7 +376,6 @@ impl ProxyConfig { } ProxyConfig::Https(https_config) => { let load_cache = true; - let strict_tls = true; let tcp_stream = crate::net::connect_tcp( context, &https_config.host, @@ -383,7 +383,7 @@ impl ProxyConfig { load_cache, ) .await?; - let tls_stream = wrap_tls(strict_tls, &https_config.host, &[], tcp_stream).await?; + let tls_stream = wrap_rustls(&https_config.host, &[], tcp_stream).await?; let auth = if let Some((username, password)) = &https_config.user_password { Some((username.as_str(), password.as_str())) } else { diff --git a/src/net/session.rs b/src/net/session.rs index 18a758e06..b6df40ea5 100644 --- a/src/net/session.rs +++ b/src/net/session.rs @@ -1,4 +1,3 @@ -use async_native_tls::TlsStream; use fast_socks5::client::Socks5Stream; use std::pin::Pin; use std::time::Duration; @@ -17,11 +16,16 @@ impl SessionStream for Box { self.as_mut().set_read_timeout(timeout); } } -impl SessionStream for TlsStream { +impl SessionStream for async_native_tls::TlsStream { fn set_read_timeout(&mut self, timeout: Option) { self.get_mut().set_read_timeout(timeout); } } +impl SessionStream for tokio_rustls::client::TlsStream { + fn set_read_timeout(&mut self, timeout: Option) { + self.get_mut().0.set_read_timeout(timeout); + } +} impl SessionStream for BufStream { fn set_read_timeout(&mut self, timeout: Option) { self.get_mut().set_read_timeout(timeout); diff --git a/src/net/tls.rs b/src/net/tls.rs index 30742e268..f30ed5cfd 100644 --- a/src/net/tls.rs +++ b/src/net/tls.rs @@ -1,4 +1,5 @@ //! TLS support. +use std::sync::Arc; use anyhow::Result; use async_native_tls::{Certificate, Protocol, TlsConnector, TlsStream}; @@ -46,7 +47,7 @@ pub async fn wrap_rustls( let mut config = rustls::ClientConfig::builder() .with_root_certificates(root_cert_store) .with_no_client_auth(); - config.alpn_protocols = alpn.into_iter().map(|s| s.as_bytes().to_vec()).collect(); + config.alpn_protocols = alpn.iter().map(|s| s.as_bytes().to_vec()).collect(); let tls = tokio_rustls::TlsConnector::from(Arc::new(config)); let name = rustls_pki_types::ServerName::try_from(hostname)?.to_owned();