diff --git a/Cargo.lock b/Cargo.lock index 9ea47a34..076e8b03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10447,6 +10447,7 @@ dependencies = [ "hyper-util", "log 0.4.29", "mime_guess", + "native-tls", "regex 1.11.1", "reqwest", "serde", diff --git a/crates/yaak-http/Cargo.toml b/crates/yaak-http/Cargo.toml index 34ebba4b..16b9d393 100644 --- a/crates/yaak-http/Cargo.toml +++ b/crates/yaak-http/Cargo.toml @@ -18,8 +18,9 @@ zstd = "0.13" hyper-util = { version = "0.1.17", default-features = false, features = ["client-legacy"] } log = { workspace = true } mime_guess = "2.0.5" +native-tls = "0.2" regex = "1.11.1" -reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "socks", "http2", "stream"] } +reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "native-tls", "socks", "http2", "stream"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } diff --git a/crates/yaak-http/src/client.rs b/crates/yaak-http/src/client.rs index 26be82c9..4f5bf423 100644 --- a/crates/yaak-http/src/client.rs +++ b/crates/yaak-http/src/client.rs @@ -6,6 +6,56 @@ use std::sync::Arc; use yaak_models::models::DnsOverride; use yaak_tls::{ClientCertificateConfig, get_tls_config}; +/// Build a native-tls connector for maximum compatibility when certificate +/// validation is disabled. Unlike rustls, native-tls uses the OS TLS stack +/// (Secure Transport on macOS, SChannel on Windows, OpenSSL on Linux) which +/// supports TLS 1.0+ for legacy servers. +fn build_native_tls_connector( + client_cert: Option, +) -> Result { + let mut builder = native_tls::TlsConnector::builder(); + builder.danger_accept_invalid_certs(true); + builder.danger_accept_invalid_hostnames(true); + builder.min_protocol_version(Some(native_tls::Protocol::Tlsv10)); + + if let Some(identity) = build_native_tls_identity(client_cert)? { + builder.identity(identity); + } + + Ok(builder.build()?) +} + +fn build_native_tls_identity( + client_cert: Option, +) -> Result> { + let config = match client_cert { + None => return Ok(None), + Some(c) => c, + }; + + // Try PFX/PKCS12 first + if let Some(pfx_path) = &config.pfx_file { + if !pfx_path.is_empty() { + let pfx_data = std::fs::read(pfx_path)?; + let password = config.passphrase.as_deref().unwrap_or(""); + let identity = native_tls::Identity::from_pkcs12(&pfx_data, password)?; + return Ok(Some(identity)); + } + } + + // Try CRT + KEY files + if let (Some(crt_path), Some(key_path)) = (&config.crt_file, &config.key_file) { + if !crt_path.is_empty() && !key_path.is_empty() { + let crt_data = std::fs::read(crt_path)?; + let key_data = std::fs::read(key_path)?; + let identity = native_tls::Identity::from_pkcs8(&crt_data, &key_data)?; + return Ok(Some(identity)); + } + } + + Ok(None) +} + #[derive(Clone)] pub struct HttpConnectionProxySettingAuth { pub user: String, @@ -51,10 +101,17 @@ impl HttpConnectionOptions { // This is needed so we can emit DNS timing events for each request .pool_max_idle_per_host(0); - // Configure TLS with optional client certificate - let config = - get_tls_config(self.validate_certificates, true, self.client_certificate.clone())?; - client = client.use_preconfigured_tls(config); + // Configure TLS + if self.validate_certificates { + // Use rustls with platform certificate verification (TLS 1.2+ only) + let config = get_tls_config(true, true, self.client_certificate.clone())?; + client = client.use_preconfigured_tls(config); + } else { + // Use native TLS for maximum compatibility (supports TLS 1.0+) + let connector = + build_native_tls_connector(self.client_certificate.clone())?; + client = client.use_preconfigured_tls(connector); + } // Configure DNS resolver - keep a reference to configure per-request let resolver = LocalhostResolver::new(self.dns_overrides.clone()); diff --git a/crates/yaak-http/src/error.rs b/crates/yaak-http/src/error.rs index bfb063a0..ce72bd13 100644 --- a/crates/yaak-http/src/error.rs +++ b/crates/yaak-http/src/error.rs @@ -9,6 +9,12 @@ pub enum Error { #[error(transparent)] TlsError(#[from] yaak_tls::error::Error), + #[error("Native TLS error: {0}")] + NativeTlsError(#[from] native_tls::Error), + + #[error("IO error: {0}")] + IoError(#[from] std::io::Error), + #[error("Request failed with {0:?}")] RequestError(String),