From 5bdc13a076bf1c96f45e89bffcbbbb2310787f96 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 9 Oct 2020 15:44:51 +0200 Subject: [PATCH] Re-add rustls support and enable it by default rustls is seen as mature enough for curl to depend on it optionally, and it recently has had an audit. This commit adds back rustls support removed by 86bb1850b2c6c6fa7cf4bbfb2e2d50f0833f0127 and enables it by default. You can opt out of rustls use by setting the RUSTUP_AVOID_RUSTLS env variable. --- Cargo.lock | 95 +++++++++++++++++++++++ Cargo.toml | 9 ++- download/Cargo.toml | 6 +- download/src/lib.rs | 66 +++++++++++++--- download/tests/download-reqwest-resume.rs | 12 ++- src/utils/utils.rs | 19 ++++- 6 files changed, 186 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 013c2c2a67a..119a85386c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,6 +948,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" +dependencies = [ + "bytes 0.5.6", + "futures-util", + "hyper", + "log", + "rustls", + "tokio", + "tokio-rustls", + "webpki", +] + [[package]] name = "hyper-tls" version = "0.4.3" @@ -1654,6 +1670,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1664,15 +1681,18 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "rustls", "serde", "serde_urlencoded", "tokio", + "tokio-rustls", "tokio-socks", "tokio-tls", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] @@ -1685,6 +1705,21 @@ dependencies = [ "rand", ] +[[package]] +name = "ring" +version = "0.16.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -1735,6 +1770,19 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "rustls" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rustup" version = "1.22.1" @@ -1818,6 +1866,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "0.4.4" @@ -2168,6 +2226,18 @@ dependencies = [ "slab", ] +[[package]] +name = "tokio-rustls" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" +dependencies = [ + "futures-core", + "rustls", + "tokio", + "webpki", +] + [[package]] name = "tokio-socks" version = "0.2.2" @@ -2311,6 +2381,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.1.1" @@ -2466,6 +2542,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" +dependencies = [ + "webpki", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 15f130aa4fa..185f28942d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,14 @@ version = "1.22.1" [features] curl-backend = ["download/curl-backend"] -default = ["curl-backend", "reqwest-backend"] +default = ["curl-backend", "reqwest-backend", "reqwest-default-tls", "reqwest-rustls-tls"] + reqwest-backend = ["download/reqwest-backend"] vendored-openssl = ['openssl/vendored'] + +reqwest-default-tls = ["download/reqwest-default-tls"] +reqwest-rustls-tls = ["download/reqwest-rustls-tls"] + # Include in the default set to disable self-update and uninstall. no-self-update = [] @@ -25,7 +30,7 @@ anyhow = "1.0.31" cfg-if = "0.1" chrono = "0.4" clap = "2" -download = {path = "download"} +download = { path = "download", default-features = false } error-chain = "0.12" flate2 = "1" git-testament = "0.1.4" diff --git a/download/Cargo.toml b/download/Cargo.toml index 8aefdaf7269..bd53b4fe13c 100644 --- a/download/Cargo.toml +++ b/download/Cargo.toml @@ -9,10 +9,12 @@ license = "MIT/Apache-2.0" [features] -default = ["reqwest-backend"] +default = ["reqwest-backend", "reqwest-rustls-tls", "reqwest-default-tls"] curl-backend = ["curl"] reqwest-backend = ["reqwest", "env_proxy", "lazy_static"] +reqwest-default-tls = ["reqwest/default-tls"] +reqwest-rustls-tls = ["reqwest/rustls-tls"] [dependencies] error-chain = "0.12" @@ -20,7 +22,7 @@ url = "2.1" curl = { version = "0.4.11", optional = true } env_proxy = { version = "0.4.1", optional = true } lazy_static = { version = "1.0", optional = true } -reqwest = { version = "0.10", features = ["blocking", "gzip", "socks"], optional = true } +reqwest = { version = "0.10", default-features = false, features = ["blocking", "gzip", "socks"], optional = true } [dev-dependencies] hyper = { version = "0.13", default-features = false, features = ["tcp"] } diff --git a/download/src/lib.rs b/download/src/lib.rs index 2f83ae24caf..c22b5bedb05 100644 --- a/download/src/lib.rs +++ b/download/src/lib.rs @@ -10,7 +10,13 @@ pub use crate::errors::*; #[derive(Debug, Copy, Clone)] pub enum Backend { Curl, - Reqwest, + Reqwest(TlsBackend), +} + +#[derive(Debug, Copy, Clone)] +pub enum TlsBackend { + Rustls, + Default, } #[derive(Debug, Copy, Clone)] @@ -30,7 +36,7 @@ fn download_with_backend( ) -> Result<()> { match backend { Backend::Curl => curl::download(url, resume_from, callback), - Backend::Reqwest => reqwest_be::download(url, resume_from, callback), + Backend::Reqwest(tls) => reqwest_be::download(url, resume_from, callback, tls), } } @@ -249,9 +255,10 @@ pub mod curl { #[cfg(feature = "reqwest-backend")] pub mod reqwest_be { use super::Event; + use super::TlsBackend; use crate::errors::*; use lazy_static::lazy_static; - use reqwest::blocking::{Client, Response}; + use reqwest::blocking::{Client, ClientBuilder, Response}; use reqwest::{header, Proxy}; use std::io; use std::time::Duration; @@ -261,13 +268,15 @@ pub mod reqwest_be { url: &Url, resume_from: u64, callback: &dyn Fn(Event<'_>) -> Result<()>, + tls: TlsBackend, ) -> Result<()> { // Short-circuit reqwest for the "file:" URL scheme if download_from_file_url(url, resume_from, callback)? { return Ok(()); } - let mut res = request(url, resume_from).chain_err(|| "failed to make network request")?; + let mut res = + request(url, resume_from, tls).chain_err(|| "failed to make network request")?; if !res.status().is_success() { let code: u16 = res.status().into(); @@ -295,13 +304,34 @@ pub mod reqwest_be { } } + fn client_generic() -> ClientBuilder { + Client::builder() + .gzip(false) + .proxy(Proxy::custom(env_proxy)) + .timeout(Duration::from_secs(30)) + } + #[cfg(feature = "reqwest-rustls-tls")] lazy_static! { - static ref CLIENT: Client = { + static ref CLIENT_RUSTLS_TLS: Client = { let catcher = || { - Client::builder() - .gzip(false) - .proxy(Proxy::custom(env_proxy)) - .timeout(Duration::from_secs(30)) + client_generic().use_rustls_tls() + .build() + }; + + // woah, an unwrap?! + // It's OK. This is the same as what is happening in curl. + // + // The curl::Easy::new() internally assert!s that the initialized + // Easy is not null. Inside reqwest, the errors here would be from + // the TLS library returning a null pointer as well. + catcher().unwrap() + }; + } + #[cfg(feature = "reqwest-default-tls")] + lazy_static! { + static ref CLIENT_DEFAULT_TLS: Client = { + let catcher = || { + client_generic() .build() }; @@ -319,8 +349,22 @@ pub mod reqwest_be { env_proxy::for_url(url).to_url() } - fn request(url: &Url, resume_from: u64) -> reqwest::Result { - let mut req = CLIENT.get(url.as_str()); + fn request(url: &Url, resume_from: u64, backend: TlsBackend) -> reqwest::Result { + let client: &Client = match backend { + #[cfg(feature = "reqwest-rustls-tls")] + TlsBackend::Rustls => &CLIENT_RUSTLS_TLS, + #[cfg(not(feature = "reqwest-rustls-tls"))] + TlsBackend::Default => { + return Err(ErrorKind::BackendUnavailable("reqwest rustls").into()); + } + #[cfg(feature = "reqwest-default-tls")] + TlsBackend::Default => &CLIENT_DEFAULT_TLS, + #[cfg(not(feature = "reqwest-default-tls"))] + TlsBackend::Default => { + return Err(ErrorKind::BackendUnavailable("reqwest default TLS").into()); + } + }; + let mut req = client.get(url.as_str()); if resume_from != 0 { req = req.header(header::RANGE, format!("bytes={}-", resume_from)); diff --git a/download/tests/download-reqwest-resume.rs b/download/tests/download-reqwest-resume.rs index 0b235289313..021e428b328 100644 --- a/download/tests/download-reqwest-resume.rs +++ b/download/tests/download-reqwest-resume.rs @@ -20,8 +20,14 @@ fn resume_partial_from_file_url() { write_file(&target_path, "123"); let from_url = Url::from_file_path(&from_path).unwrap(); - download_to_path_with_backend(Backend::Reqwest, &from_url, &target_path, true, None) - .expect("Test download failed"); + download_to_path_with_backend( + Backend::Reqwest(TlsBackend::Default), + &from_url, + &target_path, + true, + None, + ) + .expect("Test download failed"); assert_eq!(std::fs::read_to_string(&target_path).unwrap(), "12345"); } @@ -41,7 +47,7 @@ fn callback_gets_all_data_as_if_the_download_happened_all_at_once() { let received_in_callback = Mutex::new(Vec::new()); download_to_path_with_backend( - Backend::Reqwest, + Backend::Reqwest(TlsBackend::Default), &from_url, &target_path, true, diff --git a/src/utils/utils.rs b/src/utils/utils.rs index 528bd51d600..ac9acc78ef4 100644 --- a/src/utils/utils.rs +++ b/src/utils/utils.rs @@ -207,7 +207,7 @@ fn download_file_( notify_handler: &dyn Fn(Notification<'_>), ) -> Result<()> { use download::download_to_path_with_backend; - use download::{Backend, Event}; + use download::{Backend, Event, TlsBackend}; use sha2::Digest; use std::cell::RefCell; @@ -241,12 +241,25 @@ fn download_file_( // Download the file - // Keep the hyper env var around for a bit + // Keep the curl env var around for a bit let use_curl_backend = process().var_os("RUSTUP_USE_CURL").is_some(); + let avoid_rustls = process().var_os("RUSTUP_AVOID_RUSTLS").is_some(); let (backend, notification) = if use_curl_backend { (Backend::Curl, Notification::UsingCurl) } else { - (Backend::Reqwest, Notification::UsingReqwest) + let tls_backend = if avoid_rustls { + TlsBackend::Default + } else { + #[cfg(feature = "reqwest-rustls-tls")] + { + TlsBackend::Rustls + } + #[cfg(not(feature = "reqwest-rustls-tls"))] + { + TlsBackend::Default + } + }; + (Backend::Reqwest(tls_backend), Notification::UsingReqwest) }; notify_handler(notification); let res =