From 1e6c852e7bc8fa0518f7bbe233fba8987a35e80b Mon Sep 17 00:00:00 2001 From: orthoxerox Date: Fri, 1 Feb 2019 21:46:12 +0300 Subject: [PATCH] Skip proxies with MITM root certificates by setting RUSTUP_USE_UNSAFE_SSL environment variable --- Cargo.lock | 14 +++++ README.md | 6 +++ rustup-init.sh | 12 ++++- src/download/Cargo.toml | 3 ++ src/download/src/lib.rs | 16 +++++- src/download/tests/download-curl-resume.rs | 2 +- src/download/tests/download-curl-safe.rs | 42 +++++++++++++++ src/download/tests/download-curl-unsafe.rs | 27 ++++++++++ src/download/tests/download-reqwest-resume.rs | 2 +- src/download/tests/download-reqwest-safe.rs | 42 +++++++++++++++ src/download/tests/download-reqwest-unsafe.rs | 27 ++++++++++ src/download/tests/support/cert.p12 | Bin 0 -> 2405 bytes src/download/tests/support/mod.rs | 26 ++++++---- src/download/tests/support/tls_proxy.rs | 49 ++++++++++++++++++ 14 files changed, 252 insertions(+), 16 deletions(-) create mode 100644 src/download/tests/download-curl-safe.rs create mode 100644 src/download/tests/download-curl-unsafe.rs create mode 100644 src/download/tests/download-reqwest-safe.rs create mode 100644 src/download/tests/download-reqwest-unsafe.rs create mode 100644 src/download/tests/support/cert.p12 create mode 100644 src/download/tests/support/tls_proxy.rs diff --git a/Cargo.lock b/Cargo.lock index ee75fe72714..6bf2cdc8710 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,8 +282,11 @@ dependencies = [ "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1549,6 +1552,16 @@ dependencies = [ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-tls" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-udp" version = "0.1.2" @@ -1959,6 +1972,7 @@ dependencies = [ "checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" "checksum tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd8a8b911301c60cbfaa2a6588fb210e5c1038375b8bdecc47aa09a94c3c05f" "checksum tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3a52f00c97fedb6d535d27f65cccb7181c8dd4c6edc3eda9ea93f6d45d05168e" +"checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" "checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" "checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9" "checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65" diff --git a/README.md b/README.md index ff89ca58fff..ffca4818e1a 100644 --- a/README.md +++ b/README.md @@ -580,6 +580,12 @@ Command | Description - `RUSTUP_UPDATE_ROOT` (default `https://static.rust-lang.org/rustup`) Sets the root URL for downloading self-updates. +- `RUSTUP_USE_UNSAFE_SSL` (default: none) + If set, rustup will not validate the SSL certificate when downloading + files. This parameter should be used only in exceptional circumstances + when youre computer is behind a corporate proxy that injects its own + certificates into HTTPS connections. + ## Other installation methods The primary installation method, as described at diff --git a/rustup-init.sh b/rustup-init.sh index cd10a187b37..6f3ec5afa87 100755 --- a/rustup-init.sh +++ b/rustup-init.sh @@ -385,13 +385,21 @@ downloader() { else _dld='curl or wget' # to be used in error message of need_cmd fi + + if [ -n "$RUSTUP_USE_UNSAFE_SSL" ]; then + _curl_unsafe = "--insecure" + _wget_unsafe = "--no-check-certificate" + else + _curl_unsafe = "" + _wget_unsafe = "" + fi if [ "$1" = --check ]; then need_cmd "$_dld" elif [ "$_dld" = curl ]; then - curl -sSfL "$1" -o "$2" + curl -sSfL "$_curl_unsafe" "$1" -o "$2" elif [ "$_dld" = wget ]; then - wget "$1" -O "$2" + wget "$1" "$_wget_unsafe" -O "$2" else err "Unknown downloader" # should not reach here fi diff --git a/src/download/Cargo.toml b/src/download/Cargo.toml index cd240e2bc32..534bbcc0a14 100644 --- a/src/download/Cargo.toml +++ b/src/download/Cargo.toml @@ -26,3 +26,6 @@ reqwest = { version = "0.9", optional = true } futures = "0.1" hyper = "0.12" tempdir = "0.3.4" +tokio = "0.1.11" +tokio-tls = "0.2.1" +native-tls = "0.2.1" \ No newline at end of file diff --git a/src/download/src/lib.rs b/src/download/src/lib.rs index 9c99277a44d..549aba4e66f 100644 --- a/src/download/src/lib.rs +++ b/src/download/src/lib.rs @@ -10,6 +10,7 @@ extern crate lazy_static; #[cfg(feature = "reqwest-backend")] extern crate reqwest; +use std::env; use std::path::Path; use url::Url; @@ -128,6 +129,10 @@ pub fn download_to_path_with_backend( }) } +fn use_unsafe_ssl() -> bool { + env::var_os("RUSTUP_USE_UNSAFE_SSL").is_some() +} + /// Download via libcurl; encrypt with the native (or OpenSSl) TLS /// stack via libcurl #[cfg(feature = "curl-backend")] @@ -136,6 +141,7 @@ pub mod curl { extern crate curl; use self::curl::easy::Easy; + use super::use_unsafe_ssl; use super::Event; use crate::errors::*; use std::cell::RefCell; @@ -175,6 +181,10 @@ pub mod curl { .connect_timeout(Duration::new(30, 0)) .chain_err(|| "failed to set connect timeout")?; + handle + .ssl_verify_peer(!use_unsafe_ssl()) + .chain_err(|| "failed to configure unsafe SSL mode")?; + { let cberr = RefCell::new(None); let mut transfer = handle.transfer(); @@ -254,6 +264,7 @@ pub mod curl { pub mod reqwest_be { extern crate env_proxy; + use super::use_unsafe_ssl; use super::Event; use crate::errors::*; use reqwest::{header, Client, Proxy, Response}; @@ -302,6 +313,7 @@ pub mod reqwest_be { .gzip(false) .proxy(Proxy::custom(env_proxy)) .timeout(Duration::from_secs(30)) + .danger_accept_invalid_certs(use_unsafe_ssl()) .build() }; @@ -374,7 +386,7 @@ pub mod reqwest_be { pub mod curl { use super::Event; - use errors::*; + use crate::errors::*; use url::Url; pub fn download( @@ -390,7 +402,7 @@ pub mod curl { pub mod reqwest_be { use super::Event; - use errors::*; + use crate::errors::*; use url::Url; pub fn download( diff --git a/src/download/tests/download-curl-resume.rs b/src/download/tests/download-curl-resume.rs index a31b3c672fb..c5ddddbce37 100644 --- a/src/download/tests/download-curl-resume.rs +++ b/src/download/tests/download-curl-resume.rs @@ -34,7 +34,7 @@ fn callback_gets_all_data_as_if_the_download_happened_all_at_once() { let target_path = tmpdir.path().join("downloaded"); write_file(&target_path, "123"); - let addr = serve_file(b"xxx45".to_vec()); + let addr = serve_file(b"xxx45".to_vec(), false); let from_url = format!("http://{}", addr).parse().unwrap(); diff --git a/src/download/tests/download-curl-safe.rs b/src/download/tests/download-curl-safe.rs new file mode 100644 index 00000000000..7435a989e0e --- /dev/null +++ b/src/download/tests/download-curl-safe.rs @@ -0,0 +1,42 @@ +#![cfg(feature = "curl-backend")] + +use download::*; + +mod support; +use crate::support::{file_contents, serve_file, tmp_dir}; + +/// There are two separate files because this crate caches curl handles +/// and all tests in one file use either the safe or the unsafe handle + +#[test] +fn downloading_with_no_certificate() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), false); + let from_url = format!("http://{}", addr).parse().unwrap(); + + download_to_path_with_backend(Backend::Curl, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} + +#[test] +#[should_panic] +fn downloading_with_bad_certificate() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), true); + let from_url = format!("https://{}", addr).parse().unwrap(); + + std::env::remove_var("RUSTUP_USE_UNSAFE_SSL"); + + assert_eq!(std::env::var_os("RUSTUP_USE_UNSAFE_SSL").is_none(), true); + + download_to_path_with_backend(Backend::Curl, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} diff --git a/src/download/tests/download-curl-unsafe.rs b/src/download/tests/download-curl-unsafe.rs new file mode 100644 index 00000000000..183b0a8e86a --- /dev/null +++ b/src/download/tests/download-curl-unsafe.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "curl-backend")] + +use download::*; + +mod support; +use crate::support::{file_contents, serve_file, tmp_dir}; + +/// There are two separate files because this crate caches reqwest handles +/// and all tests in one file use either the safe or the unsafe handle + +#[test] +fn downloading_with_bad_certificate_unsafely() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), true); + let from_url = format!("https://{}", addr).parse().unwrap(); + + std::env::set_var("RUSTUP_USE_UNSAFE_SSL", "1"); + + assert_eq!(std::env::var_os("RUSTUP_USE_UNSAFE_SSL").is_some(), true); + + download_to_path_with_backend(Backend::Curl, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} diff --git a/src/download/tests/download-reqwest-resume.rs b/src/download/tests/download-reqwest-resume.rs index 6967b9c6b54..8fc9cad6360 100644 --- a/src/download/tests/download-reqwest-resume.rs +++ b/src/download/tests/download-reqwest-resume.rs @@ -34,7 +34,7 @@ fn callback_gets_all_data_as_if_the_download_happened_all_at_once() { let target_path = tmpdir.path().join("downloaded"); write_file(&target_path, "123"); - let addr = serve_file(b"xxx45".to_vec()); + let addr = serve_file(b"xxx45".to_vec(), false); let from_url = format!("http://{}", addr).parse().unwrap(); diff --git a/src/download/tests/download-reqwest-safe.rs b/src/download/tests/download-reqwest-safe.rs new file mode 100644 index 00000000000..ad4aeb86cf3 --- /dev/null +++ b/src/download/tests/download-reqwest-safe.rs @@ -0,0 +1,42 @@ +#![cfg(feature = "reqwest-backend")] + +use download::*; + +mod support; +use crate::support::{file_contents, serve_file, tmp_dir}; + +/// There are two separate files because this crate caches reqwest handles +/// and all tests in one file use either the safe or the unsafe handle + +#[test] +fn downloading_with_no_certificate() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), false); + let from_url = format!("http://{}", addr).parse().unwrap(); + + download_to_path_with_backend(Backend::Reqwest, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} + +#[test] +#[should_panic] +fn downloading_with_bad_certificate() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), true); + let from_url = format!("https://{}", addr).parse().unwrap(); + + std::env::remove_var("RUSTUP_USE_UNSAFE_SSL"); + + assert_eq!(std::env::var_os("RUSTUP_USE_UNSAFE_SSL").is_none(), true); + + download_to_path_with_backend(Backend::Reqwest, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} diff --git a/src/download/tests/download-reqwest-unsafe.rs b/src/download/tests/download-reqwest-unsafe.rs new file mode 100644 index 00000000000..8c5c3b723f5 --- /dev/null +++ b/src/download/tests/download-reqwest-unsafe.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "reqwest-backend")] + +use download::*; + +mod support; +use crate::support::{file_contents, serve_file, tmp_dir}; + +/// There are two separate files because this crate caches reqwest handles +/// and all tests in one file use either the safe or the unsafe handle + +#[test] +fn downloading_with_bad_certificate_unsafely() { + let tmpdir = tmp_dir(); + let target_path = tmpdir.path().join("downloaded"); + + let addr = serve_file(b"12345".to_vec(), true); + let from_url = format!("https://{}", addr).parse().unwrap(); + + std::env::set_var("RUSTUP_USE_UNSAFE_SSL", "1"); + + assert_eq!(std::env::var_os("RUSTUP_USE_UNSAFE_SSL").is_some(), true); + + download_to_path_with_backend(Backend::Reqwest, &from_url, &target_path, false, None) + .expect("Test download failed"); + + assert_eq!(file_contents(&target_path), "12345"); +} diff --git a/src/download/tests/support/cert.p12 b/src/download/tests/support/cert.p12 new file mode 100644 index 0000000000000000000000000000000000000000..83bad39b622ab0efeff7886395c71ed1fba56063 GIT binary patch literal 2405 zcmY+Ec{CJ?9>>kfgo#YHC|iWVh%yMFvRv7>47s?7tc`A#n;4R1WY2JseV-74z0yZ-=<26bKgDQC>{8 z$8(o&O{t^Vx5$qW=mqOw*9)hXa)W+&TWVSQK4bf4toG*ZzDZt>gr&G6N`>AVDoU#* zy4O0F9o%h~Y63rnY@tO?){T|nd)S_855!a1k{#XJ3=a--lgPEFU2rpkQYJ_=dVuLS z*&v*DQ?NlObB{5@*73b^x>(C-!1?o9tyowO+HD}(1jFa#&@>DmFYJ!=;-Sb<2qVbL zq_T3Fj8hyzZ#Le@dmgEj^ui=0@N0jYQi0`F0rvsuRK!DbS9~xXsShx9 zAc`0;YB>%!@^DrDs)k;6sAsJgY3cq8BOktHeJvZT*}F7Bf(J_d*8D@NI=uBSu(V-i#XRAr@M5fFJ@x4d0*;gasKmF)*CChT>c zy%c84p>)~&Ry4Ch6yTw(rm1m~)63z+lScV)^~;$9oU=e;bob`)nY58PHKSw2+tj&x z&j}Iizdsk1CL!P>17S_W*5&&TNjo=`st-M#!+eyVotjyzQqVnYE`%{~~O#_E?%L#HXxh2S5_;o`m^#2FgLLB5g7za`Q87ureSt$2^@I3|q7UIB66b{VzKdM2$ zsLnYvris?zhyJ1(i~|o{32t&6kuwabQX4Fp3q4+ca#j50MF##*lumW7CzzV}hooy? z8_#8~-BxfpU#Amx(~0rfv9bh-Q0LiQt=T$uplriRlD+8+;@x?D%`5R@tEZBYy8c_W z`p+_h?hhmPB?CC(SC-}#V)T0gsW$>WC9{;eb;cWWma7gUk+0@8X`N<45wQ1@^Ay85 zU?0@NyXUE8`$c&ys6%i);b@K(@fHV97Qe!rWKL*qi-ePoSb1rLpsqsrngL`5EVhzR zhi3E=AX_y?f_5e&qb}Wxdolex#rIcfMy#v5?L&no8L3`bmF<*#3c01-bLY$7;uY(9 zsaIv}BbJSySU)3@A6NNbM++G#|Bi)g1tx2#wB>4lhh!>l++K54Jck**S22{VwOcMw zYDS*_sV%wAjcyUFJ*(xBP)8L{l%I>O!K7{g(gXFdX79| z07HjJ%~P5!49_kix*mVIl*gOdBv~EpJ>UDd>DxK~YO(XjH&)aZq)NVdPkwaeNLk?; z>%8eq&we(2QMo99Z*YJh*tdAT+6Tdryt{Y@9opyNuky~5m&0|O9n8rUM%R%jcgr2T znH*mm1|nKNEoNDu-vHoWjH7r@la?yaaU zTQp68$JHqwnCJ76L>*41ru}e>DWYG@zI8 zt-I^fnqRx>@SA$MXw}3(tNRVHAFoau20D$2zo}4>hWI*q z&1ecJ?LeRb-LY0z(Lp@Pp~Bbv!y=B$ zS7*t>oq{In7m%Y5y(v}LtdZ+(epJ90!?dVCNz*hc2xEDjm=r2z``q)0+&_W;u+n;9 zm7H5riECpj5iHv}*Y2>{ksjpjipN=h8A}?<(=tD|d=OH9J1S8*jTh1MDcWR!kIWHd zRJMCaK;8JdObX%!Pk+&}j|kJ|-q2o+f6q0+Y`!;4osf?);qTI{XgO=fp8dSz;t?Tw zh*f6XBcVgeKG{=QOV&Z;z@3`nA6gFF)zT_wdDI%kvir^_+`m|0UYpwH~;_u literal 0 HcmV?d00001 diff --git a/src/download/tests/support/mod.rs b/src/download/tests/support/mod.rs index 143ce2b5080..6c06d124924 100644 --- a/src/download/tests/support/mod.rs +++ b/src/download/tests/support/mod.rs @@ -1,14 +1,13 @@ -extern crate futures; -extern crate hyper; -extern crate tempdir; - use std::fs::{self, File}; use std::io::{self, Read}; use std::net::SocketAddr; use std::path::Path; -use self::futures::sync::oneshot; -use self::tempdir::TempDir; +use futures::sync::oneshot; +use tempdir::TempDir; + +mod tls_proxy; +use tls_proxy::proxy; pub fn tmp_dir() -> TempDir { TempDir::new("rustup-download-test-").expect("creating tempdir for test") @@ -23,6 +22,7 @@ pub fn file_contents(path: &Path) -> String { result } +#[allow(dead_code)] pub fn write_file(path: &Path, contents: &str) { let mut file = fs::OpenOptions::new() .write(true) @@ -36,11 +36,11 @@ pub fn write_file(path: &Path, contents: &str) { file.sync_data().expect("writing test data"); } -pub fn serve_file(contents: Vec) -> SocketAddr { - use self::futures::Future; +pub fn serve_file(contents: Vec, use_ssl: bool) -> SocketAddr { + use futures::Future; use std::thread; - let addr = ([127, 0, 0, 1], 0).into(); + let addr = ([0, 0, 0, 0], 0).into(); let (addr_tx, addr_rx) = oneshot::channel(); thread::spawn(move || { @@ -57,8 +57,14 @@ pub fn serve_file(contents: Vec) -> SocketAddr { addr_tx.send(addr).unwrap(); hyper::rt::run(server.map_err(|e| panic!(e))); }); + let addr = addr_rx.wait().unwrap(); - addr + + if use_ssl { + proxy(addr) + } else { + addr + } } fn serve_contents( diff --git a/src/download/tests/support/tls_proxy.rs b/src/download/tests/support/tls_proxy.rs new file mode 100644 index 00000000000..238c7ef2db7 --- /dev/null +++ b/src/download/tests/support/tls_proxy.rs @@ -0,0 +1,49 @@ +use native_tls::{Identity, TlsAcceptor}; +use std::io::{Error, ErrorKind}; +use std::net::SocketAddr; +use std::thread; +use tokio::io::copy; +use tokio::net::{TcpListener, TcpStream}; +use tokio::prelude::*; + +pub fn proxy(server_addr: SocketAddr) -> SocketAddr { + let addr = ([0, 0, 0, 0], 0).into(); + let listener = TcpListener::bind(&addr).expect("binding proxy address"); + let addr = listener.local_addr().expect("starting a proxy listener"); + + let der = include_bytes!("cert.p12"); + let cert = Identity::from_pkcs12(der, "rustup").expect("parsing certificate"); + let tls = TlsAcceptor::builder(cert) + .build() + .expect("creating TLS acceptor"); + let tls = tokio_tls::TlsAcceptor::from(tls); + + thread::spawn(move || { + let server = listener.incoming().for_each(move |socket| { + let connection = tls + .accept(socket) + .map_err(|e| Error::new(ErrorKind::PermissionDenied, e)) + .and_then(move |client_socket| { + let server_socket = TcpStream::connect(&server_addr); + (future::ok(client_socket), server_socket) + }) + .and_then(|(client_socket, server_socket)| { + let (client_reader, client_writer) = client_socket.split(); + let (server_reader, server_writer) = server_socket.split(); + let client_to_server = copy(client_reader, server_writer); + let server_to_client = copy(server_reader, client_writer); + client_to_server.join(server_to_client) + }) + .map(|_| ()) + .map_err(|_| ()); + + tokio::spawn(connection); + + Ok(()) + }); + + tokio::run(server.map_err(|_| ())); + }); + + return addr; +}