From b0888df59b3d4d39addb920151fb1338d039471f Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 20 Jul 2023 16:47:55 -0700 Subject: [PATCH] fix(util): use rustls as the curl TLS backend Making this change allows cargo to be used on Windows 7 and 8.1 against the current Fastly TLS configuration. This does the minimal amount of plumbing required for curl to be built with rustls as the TLS backend, and for rustls to be configured with the system store's root certificate bundle. (Using webpki_roots would be inappropriate, since enterprise users may have custom CA roots configured.) I've made no effort to make this configurable thus far. Presumably we'd only want to do this on Windows, if at all. Technically, this would fix #8154. --- Cargo.lock | 274 ++++++++++++++++++++++++++++++++- Cargo.toml | 6 +- src/cargo/util/config/mod.rs | 41 +++++ src/cargo/util/network/http.rs | 2 + 4 files changed, 320 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee6bcaee389..d68b74691c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,45 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "atty" version = "0.2.14" @@ -134,6 +173,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.2" @@ -271,7 +316,7 @@ name = "cargo" version = "0.74.0" dependencies = [ "anyhow", - "base64", + "base64 0.21.2", "bytesize", "cargo-platform 0.1.4", "cargo-test-macro", @@ -314,6 +359,7 @@ dependencies = [ "pulldown-cmark", "rand", "rustfix", + "rustls-native-certs", "same-file", "semver", "serde", @@ -336,6 +382,7 @@ dependencies = [ "url", "walkdir", "windows-sys 0.48.0", + "x509-parser", ] [[package]] @@ -748,10 +795,17 @@ dependencies = [ "libz-sys", "openssl-sys", "pkg-config", + "rustls-ffi", "vcpkg", "winapi", ] +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + [[package]] name = "der" version = "0.7.6" @@ -763,6 +817,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "diff" version = "0.1.13" @@ -781,6 +849,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1593,7 +1672,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a39ffed9a9078ed700605e064b15d7c6ae50aa65e7faa36ca6919e8081df15" dependencies = [ - "base64", + "base64 0.21.2", "bstr", "curl", "gix-command", @@ -2173,6 +2252,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -2193,6 +2293,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -2202,6 +2323,15 @@ dependencies = [ "libc", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -2541,6 +2671,16 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.60" @@ -2749,6 +2889,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2767,6 +2922,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.37.20" @@ -2781,6 +2945,63 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-ffi" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +dependencies = [ + "libc", + "log", + "num_enum", + "rustls", + "rustls-pemfile 0.2.1", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.3", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.2", +] + [[package]] name = "rusty-fork" version = "0.3.0" @@ -2823,6 +3044,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" version = "0.7.2" @@ -3057,6 +3288,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" version = "0.7.2" @@ -3355,6 +3592,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.4.0" @@ -3581,6 +3824,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3753,6 +4006,23 @@ dependencies = [ "memchr", ] +[[package]] +name = "x509-parser" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab0c2f54ae1d92f4fcb99c0b7ccf0b1e3451cbd395e5f115ccbdbcb18d4f634" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "xtask-build-man" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 63e06adb53a..696657f59e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ clap = "4.2.0" core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] } crates-io = { version = "0.37.0", path = "crates/crates-io" } criterion = { version = "0.5.1", features = ["html_reports"] } -curl = "0.4.44" +curl = { version = "0.4.44", default-features = false, features = ["rustls", "static-curl"] } curl-sys = "0.4.63" env_logger = "0.10.0" filetime = "0.2.9" @@ -71,6 +71,7 @@ proptest = "1.1.0" pulldown-cmark = { version = "0.9.2", default-features = false } rand = "0.8.5" rustfix = "0.6.0" +rustls-native-certs = "0.6.3" same-file = "1.0.6" security-framework = "2.0.0" semver = { version = "1.0.3", features = ["serde"] } @@ -96,6 +97,7 @@ url = "2.2.2" varisat = "0.2.1" walkdir = "2.3.1" windows-sys = "0.48" +x509-parser = "0.15.0" [package] name = "cargo" @@ -154,6 +156,7 @@ pretty_env_logger = { workspace = true, optional = true } pulldown-cmark.workspace = true rand.workspace = true rustfix.workspace = true +rustls-native-certs.workspace = true semver.workspace = true serde = { workspace = true, features = ["derive"] } serde-value.workspace = true @@ -173,6 +176,7 @@ unicode-width.workspace = true unicode-xid.workspace = true url.workspace = true walkdir.workspace = true +x509-parser.workspace = true [target.'cfg(not(windows))'.dependencies] openssl = { workspace = true, optional = true } diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 4e6bca30212..48746d782e9 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -79,8 +79,11 @@ use crate::util::{internal, toml as cargo_toml}; use crate::util::{try_canonicalize, validate_package_name}; use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc}; use anyhow::{anyhow, bail, format_err, Context as _}; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; use cargo_util::paths; use curl::easy::Easy; +use itertools::Itertools; use lazycell::LazyCell; use serde::de::IntoDeserializer as _; use serde::Deserialize; @@ -223,6 +226,7 @@ pub struct Config { doc_extern_map: LazyCell, progress_config: ProgressConfig, env_config: LazyCell, + tls_roots: LazyCell, /// This should be false if: /// - this is an artifact of the rustc distribution process for "stable" or for "beta" /// - this is an `#[test]` that does not opt in with `enable_nightly_features` @@ -311,6 +315,7 @@ impl Config { doc_extern_map: LazyCell::new(), progress_config: ProgressConfig::default(), env_config: LazyCell::new(), + tls_roots: LazyCell::new(), nightly_features_allowed: matches!(&*features::channel(), "nightly" | "dev"), ws_roots: RefCell::new(HashMap::new()), } @@ -1788,6 +1793,42 @@ impl Config { Ok(env_config) } + pub fn tls_roots(&self) -> CargoResult<&[u8]> { + Ok(self + .tls_roots + .try_borrow_with(|| -> CargoResult { + // We want to use the certificates from the system store to respect any custom root + // CAs the user might need, but also want to use rustls to avoid platform specific + // problems with schannel. + let native_certs = rustls_native_certs::load_native_certs()?; + let mut certs = Vec::with_capacity(native_certs.len()); + for cert in native_certs { + // rustls_native_certs gives us DER encoded certificates, but we need PEM + // encoded certificates for curl. The conversion isn't terribly difficult: it's + // basically just base64 encoding, wrapping at 64 characters, and enclosing + // each certificate with BEGIN and END markers. + let der = cert.0; + let pem = format!( + "-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----", + String::from_utf8( + Itertools::intersperse( + STANDARD.encode(der).into_bytes().chunks(64), + &[b'\n'], + ) + .flatten() + .copied() + .collect::>(), + )? + ); + + certs.push(pem); + } + + Ok(certs.into_iter().join("\n\n")) + })? + .as_bytes()) + } + /// This is used to validate the `term` table has valid syntax. /// /// This is necessary because loading the term settings happens very diff --git a/src/cargo/util/network/http.rs b/src/cargo/util/network/http.rs index f077ce2b640..562cb146134 100644 --- a/src/cargo/util/network/http.rs +++ b/src/cargo/util/network/http.rs @@ -133,6 +133,8 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult< handle.ssl_min_max_version(SslVersion::Default, SslVersion::Tlsv12)?; } + handle.ssl_cainfo_blob(config.tls_roots()?)?; + if let Some(true) = http.debug { handle.verbose(true)?; log::debug!("{:#?}", curl::Version::get());