mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 17:36:29 +03:00
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.
This commit is contained in:
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -1325,6 +1325,8 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"rust-hsluv",
|
"rust-hsluv",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
"sanitize-filename",
|
"sanitize-filename",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1341,12 +1343,14 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-io-timeout",
|
"tokio-io-timeout",
|
||||||
|
"tokio-rustls",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-tar",
|
"tokio-tar",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4992,9 +4996,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.10"
|
version = "0.23.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
|
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -5030,9 +5034,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.7.0"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-platform-verifier"
|
name = "rustls-platform-verifier"
|
||||||
@@ -5063,9 +5067,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.102.4"
|
version = "0.102.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
|
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
@@ -6561,9 +6565,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.26.1"
|
version = "0.26.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
|
checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ rand = { workspace = true }
|
|||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
rusqlite = { workspace = true, features = ["sqlcipher"] }
|
rusqlite = { workspace = true, features = ["sqlcipher"] }
|
||||||
rust-hsluv = "0.1"
|
rust-hsluv = "0.1"
|
||||||
|
rustls-pki-types = "1.8.0"
|
||||||
|
rustls = { version = "0.23.13", default-features = false }
|
||||||
sanitize-filename = { workspace = true }
|
sanitize-filename = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
@@ -97,6 +99,7 @@ tagger = "4.3.4"
|
|||||||
textwrap = "0.16.1"
|
textwrap = "0.16.1"
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tokio-io-timeout = "1.2.0"
|
tokio-io-timeout = "1.2.0"
|
||||||
|
tokio-rustls = { version = "0.26.0", default-features = false }
|
||||||
tokio-stream = { version = "0.1.15", features = ["fs"] }
|
tokio-stream = { version = "0.1.15", features = ["fs"] }
|
||||||
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
@@ -104,6 +107,7 @@ tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
|
|||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
url = "2"
|
url = "2"
|
||||||
uuid = { version = "1", features = ["serde", "v4"] }
|
uuid = { version = "1", features = ["serde", "v4"] }
|
||||||
|
webpki-roots = "0.26.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
|
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
|
||||||
@@ -197,7 +201,6 @@ yerpc = "0.6.2"
|
|||||||
default = ["vendored"]
|
default = ["vendored"]
|
||||||
internals = []
|
internals = []
|
||||||
vendored = [
|
vendored = [
|
||||||
"async-native-tls/vendored",
|
|
||||||
"rusqlite/bundled-sqlcipher-vendored-openssl"
|
"rusqlite/bundled-sqlcipher-vendored-openssl"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use serde::Serialize;
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::net::proxy::ProxyConfig;
|
use crate::net::proxy::ProxyConfig;
|
||||||
use crate::net::session::SessionStream;
|
use crate::net::session::SessionStream;
|
||||||
use crate::net::tls::wrap_tls;
|
use crate::net::tls::wrap_rustls;
|
||||||
|
|
||||||
/// HTTP(S) GET response.
|
/// HTTP(S) GET response.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -67,17 +67,16 @@ where
|
|||||||
"https" => {
|
"https" => {
|
||||||
let port = parsed_url.port_u16().unwrap_or(443);
|
let port = parsed_url.port_u16().unwrap_or(443);
|
||||||
let load_cache = true;
|
let load_cache = true;
|
||||||
let strict_tls = true;
|
|
||||||
|
|
||||||
if let Some(proxy_config) = proxy_config_opt {
|
if let Some(proxy_config) = proxy_config_opt {
|
||||||
let proxy_stream = proxy_config
|
let proxy_stream = proxy_config
|
||||||
.connect(context, host, port, load_cache)
|
.connect(context, host, port, load_cache)
|
||||||
.await?;
|
.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)
|
Box::new(tls_stream)
|
||||||
} else {
|
} else {
|
||||||
let tcp_stream = crate::net::connect_tcp(context, host, port, load_cache).await?;
|
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)
|
Box::new(tls_stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ use url::Url;
|
|||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::net::connect_tcp;
|
||||||
use crate::net::session::SessionStream;
|
use crate::net::session::SessionStream;
|
||||||
use crate::net::{connect_tcp, wrap_tls};
|
use crate::net::tls::wrap_rustls;
|
||||||
use crate::sql::Sql;
|
use crate::sql::Sql;
|
||||||
|
|
||||||
/// Default SOCKS5 port according to [RFC 1928](https://tools.ietf.org/html/rfc1928).
|
/// Default SOCKS5 port according to [RFC 1928](https://tools.ietf.org/html/rfc1928).
|
||||||
@@ -375,7 +376,6 @@ impl ProxyConfig {
|
|||||||
}
|
}
|
||||||
ProxyConfig::Https(https_config) => {
|
ProxyConfig::Https(https_config) => {
|
||||||
let load_cache = true;
|
let load_cache = true;
|
||||||
let strict_tls = true;
|
|
||||||
let tcp_stream = crate::net::connect_tcp(
|
let tcp_stream = crate::net::connect_tcp(
|
||||||
context,
|
context,
|
||||||
&https_config.host,
|
&https_config.host,
|
||||||
@@ -383,7 +383,7 @@ impl ProxyConfig {
|
|||||||
load_cache,
|
load_cache,
|
||||||
)
|
)
|
||||||
.await?;
|
.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 {
|
let auth = if let Some((username, password)) = &https_config.user_password {
|
||||||
Some((username.as_str(), password.as_str()))
|
Some((username.as_str(), password.as_str()))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use async_native_tls::TlsStream;
|
|
||||||
use fast_socks5::client::Socks5Stream;
|
use fast_socks5::client::Socks5Stream;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -17,11 +16,16 @@ impl SessionStream for Box<dyn SessionStream> {
|
|||||||
self.as_mut().set_read_timeout(timeout);
|
self.as_mut().set_read_timeout(timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: SessionStream> SessionStream for TlsStream<T> {
|
impl<T: SessionStream> SessionStream for async_native_tls::TlsStream<T> {
|
||||||
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
self.get_mut().set_read_timeout(timeout);
|
self.get_mut().set_read_timeout(timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<T: SessionStream> SessionStream for tokio_rustls::client::TlsStream<T> {
|
||||||
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
|
self.get_mut().0.set_read_timeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<T: SessionStream> SessionStream for BufStream<T> {
|
impl<T: SessionStream> SessionStream for BufStream<T> {
|
||||||
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
self.get_mut().set_read_timeout(timeout);
|
self.get_mut().set_read_timeout(timeout);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! TLS support.
|
//! TLS support.
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_native_tls::{Certificate, Protocol, TlsConnector, TlsStream};
|
use async_native_tls::{Certificate, Protocol, TlsConnector, TlsStream};
|
||||||
@@ -46,7 +47,7 @@ pub async fn wrap_rustls<T: AsyncRead + AsyncWrite + Unpin>(
|
|||||||
let mut config = rustls::ClientConfig::builder()
|
let mut config = rustls::ClientConfig::builder()
|
||||||
.with_root_certificates(root_cert_store)
|
.with_root_certificates(root_cert_store)
|
||||||
.with_no_client_auth();
|
.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 tls = tokio_rustls::TlsConnector::from(Arc::new(config));
|
||||||
let name = rustls_pki_types::ServerName::try_from(hostname)?.to_owned();
|
let name = rustls_pki_types::ServerName::try_from(hostname)?.to_owned();
|
||||||
|
|||||||
Reference in New Issue
Block a user