From 353a4a1af3165b2c59319865350d70a99105269c Mon Sep 17 00:00:00 2001 From: TheAifam5 Date: Mon, 9 Aug 2021 16:53:21 +0200 Subject: [PATCH] feat: Add --unsafely-treat-insecure-origin-as-secure flag to disable SSL verification (#11324) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds "--unsafely-treat-insecure-origin-as-secure" flag that allows to disable SSL verification for all domains, or specific domains if they were passed as an argument to the flag. Co-authored-by: Bartek IwaƄczuk --- cli/file_fetcher.rs | 11 +++ cli/flags.rs | 89 ++++++++++++++++++- cli/http_util.rs | 9 +- cli/lsp/registries.rs | 2 + cli/main.rs | 8 ++ cli/program_state.rs | 17 ++++ cli/specifier_handler.rs | 1 + cli/standalone.rs | 3 + cli/tests/cafile_ts_fetch_unsafe_ssl.ts.out | 2 + .../cafile_url_imports_unsafe_ssl.ts.out | 3 + cli/tests/integration/mod.rs | 13 +++ cli/tools/standalone.rs | 5 ++ extensions/fetch/lib.rs | 7 ++ extensions/net/lib.rs | 10 +++ extensions/net/ops_tls.rs | 17 +++- extensions/tls/Cargo.toml | 2 +- extensions/tls/lib.rs | 59 +++++++++++- extensions/websocket/lib.rs | 21 ++++- runtime/build.rs | 7 +- runtime/examples/hello_runtime.rs | 1 + runtime/web_worker.rs | 4 + runtime/worker.rs | 5 ++ 22 files changed, 284 insertions(+), 12 deletions(-) create mode 100644 cli/tests/cafile_ts_fetch_unsafe_ssl.ts.out create mode 100644 cli/tests/cafile_url_imports_unsafe_ssl.ts.out diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 207f08c649..e61c3beb9b 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -223,6 +223,7 @@ impl FileFetcher { allow_remote: bool, root_cert_store: Option, blob_store: BlobStore, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Result { Ok(Self { auth_tokens: AuthTokens::new(env::var(DENO_AUTH_TOKENS).ok()), @@ -235,6 +236,7 @@ impl FileFetcher { root_cert_store, None, None, + unsafely_treat_insecure_origin_as_secure, )?, blob_store, }) @@ -618,6 +620,7 @@ mod tests { true, None, blob_store.clone(), + None, ) .expect("setup failed"); (file_fetcher, temp_dir, blob_store) @@ -1063,6 +1066,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("setup failed"); let result = file_fetcher @@ -1090,6 +1094,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let specifier = @@ -1118,6 +1123,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let result = file_fetcher_02 @@ -1279,6 +1285,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let specifier = @@ -1310,6 +1317,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let result = file_fetcher_02 @@ -1420,6 +1428,7 @@ mod tests { false, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let specifier = @@ -1447,6 +1456,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let file_fetcher_02 = FileFetcher::new( @@ -1455,6 +1465,7 @@ mod tests { true, None, BlobStore::default(), + None, ) .expect("could not create file fetcher"); let specifier = diff --git a/cli/flags.rs b/cli/flags.rs index 1c7eaf9a09..36b326c0bd 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -164,6 +164,7 @@ pub struct Flags { pub repl: bool, pub seed: Option, pub unstable: bool, + pub unsafely_treat_insecure_origin_as_secure: Option>, pub v8_flags: Vec, pub version: bool, pub watch: bool, @@ -216,6 +217,20 @@ impl Flags { _ => {} } + match &self.unsafely_treat_insecure_origin_as_secure { + Some(ic_allowlist) if ic_allowlist.is_empty() => { + args.push("--unsafely-treat-insecure-origin-as-secure".to_string()); + } + Some(ic_allowlist) => { + let s = format!( + "--unsafely-treat-insecure-origin-as-secure={}", + ic_allowlist.join(",") + ); + args.push(s); + } + _ => {} + } + match &self.allow_env { Some(env_allowlist) if env_allowlist.is_empty() => { args.push("--allow-env".to_string()); @@ -1221,6 +1236,16 @@ fn permission_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { .help("Allow network access") .validator(crate::flags_allow_net::validator), ) + .arg( + Arg::with_name("unsafely-treat-insecure-origin-as-secure") + .long("unsafely-treat-insecure-origin-as-secure") + .min_values(0) + .takes_value(true) + .use_delimiter(true) + .require_equals(true) + .help("DANGER: Disables verification of SSL certificates") + .validator(crate::flags_allow_net::validator), + ) .arg( Arg::with_name("allow-env") .long("allow-env") @@ -1879,7 +1904,15 @@ fn permission_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) { crate::flags_allow_net::parse(net_wl.map(ToString::to_string).collect()) .unwrap(); flags.allow_net = Some(net_allowlist); - debug!("net allowlist: {:#?}", &flags.allow_net); + } + + if let Some(ic_wl) = + matches.values_of("unsafely-treat-insecure-origin-as-secure") + { + let ic_allowlist: Vec = + crate::flags_allow_net::parse(ic_wl.map(ToString::to_string).collect()) + .unwrap(); + flags.unsafely_treat_insecure_origin_as_secure = Some(ic_allowlist); } if let Some(env_wl) = matches.values_of("allow-env") { @@ -2723,6 +2756,7 @@ mod tests { repl: true, subcommand: DenoSubcommand::Repl { eval: None }, allow_net: Some(vec![]), + unsafely_treat_insecure_origin_as_secure: None, allow_env: Some(vec![]), allow_run: Some(vec![]), allow_read: Some(vec![]), @@ -3198,7 +3232,7 @@ mod tests { #[test] fn install_with_flags() { #[rustfmt::skip] - let r = flags_from_vec(svec!["deno", "install", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "https://deno.land/std/http/file_server.ts", "foo", "bar"]); + let r = flags_from_vec(svec!["deno", "install", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-treat-insecure-origin-as-secure", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "https://deno.land/std/http/file_server.ts", "foo", "bar"]); assert_eq!( r.unwrap(), Flags { @@ -3222,6 +3256,7 @@ mod tests { seed: Some(1), inspect: Some("127.0.0.1:9229".parse().unwrap()), allow_net: Some(vec![]), + unsafely_treat_insecure_origin_as_secure: Some(vec![]), allow_read: Some(vec![]), ..Flags::default() } @@ -3366,6 +3401,53 @@ mod tests { ); } + #[test] + fn unsafely_treat_insecure_origin_as_secure() { + let r = flags_from_vec(svec![ + "deno", + "run", + "--unsafely-treat-insecure-origin-as-secure", + "script.ts" + ]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Run { + script: "script.ts".to_string(), + }, + unsafely_treat_insecure_origin_as_secure: Some(vec![]), + ..Flags::default() + } + ); + } + + #[test] + fn unsafely_treat_insecure_origin_as_secure_with_ipv6_address() { + let r = flags_from_vec(svec![ + "deno", + "run", + "--unsafely-treat-insecure-origin-as-secure=deno.land,localhost,::,127.0.0.1,[::1],1.2.3.4", + "script.ts" + ]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Run { + script: "script.ts".to_string(), + }, + unsafely_treat_insecure_origin_as_secure: Some(svec![ + "deno.land", + "localhost", + "::", + "127.0.0.1", + "[::1]", + "1.2.3.4" + ]), + ..Flags::default() + } + ); + } + #[test] fn no_remote() { let r = flags_from_vec(svec!["deno", "run", "--no-remote", "script.ts"]); @@ -3845,7 +3927,7 @@ mod tests { #[test] fn compile_with_flags() { #[rustfmt::skip] - let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--output", "colors", "https://deno.land/std/examples/colors.ts", "foo", "bar"]); + let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-treat-insecure-origin-as-secure", "--reload", "--lock", "lock.json", "--lock-write", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--output", "colors", "https://deno.land/std/examples/colors.ts", "foo", "bar"]); assert_eq!( r.unwrap(), Flags { @@ -3866,6 +3948,7 @@ mod tests { cached_only: true, location: Some(Url::parse("https://foo/").unwrap()), allow_read: Some(vec![]), + unsafely_treat_insecure_origin_as_secure: Some(vec![]), allow_net: Some(vec![]), v8_flags: svec!["--help", "--random-seed=1"], seed: Some(1), diff --git a/cli/http_util.rs b/cli/http_util.rs index 6710939238..ac8e1dff60 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -145,7 +145,8 @@ mod tests { use std::fs::read; fn create_test_client(ca_data: Option>) -> Client { - create_http_client("test_client".to_string(), None, ca_data, None).unwrap() + create_http_client("test_client".to_string(), None, ca_data, None, None) + .unwrap() } #[tokio::test] @@ -347,6 +348,7 @@ mod tests { .unwrap(), ), None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -376,6 +378,7 @@ mod tests { None, // This will load mozilla certs by default None, None, + None, ) .unwrap(); @@ -407,6 +410,7 @@ mod tests { Some(RootCertStore::empty()), // no certs loaded at all None, None, + None, ) .unwrap(); @@ -445,6 +449,7 @@ mod tests { .unwrap(), ), None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -484,6 +489,7 @@ mod tests { .unwrap(), ), None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { @@ -537,6 +543,7 @@ mod tests { .unwrap(), ), None, + None, ) .unwrap(); let result = fetch_once(FetchOnceArgs { diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index 6a1dc1a4bc..1bf1400193 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -266,6 +266,7 @@ impl Default for ModuleRegistry { true, None, BlobStore::default(), + None, ) .unwrap(); @@ -285,6 +286,7 @@ impl ModuleRegistry { true, None, BlobStore::default(), + None, ) .context("Error creating file fetcher in module registry.") .unwrap(); diff --git a/cli/main.rs b/cli/main.rs index 77cce1d05b..93b3d50214 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -109,6 +109,10 @@ fn create_web_worker_callback( .log_level .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, + unsafely_treat_insecure_origin_as_secure: program_state + .flags + .unsafely_treat_insecure_origin_as_secure + .clone(), root_cert_store: program_state.root_cert_store.clone(), user_agent: version::get_user_agent(), seed: program_state.flags.seed, @@ -189,6 +193,10 @@ pub fn create_main_worker( .log_level .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, + unsafely_treat_insecure_origin_as_secure: program_state + .flags + .unsafely_treat_insecure_origin_as_secure + .clone(), root_cert_store: program_state.root_cert_store.clone(), user_agent: version::get_user_agent(), seed: program_state.flags.seed, diff --git a/cli/program_state.rs b/cli/program_state.rs index 244351a03d..721ccda9cd 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -1,5 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use crate::colors; use crate::config_file::ConfigFile; use crate::deno_dir; use crate::file_fetcher::CacheSetting; @@ -117,6 +118,21 @@ impl ProgramState { } } + if let Some(insecure_allowlist) = + flags.unsafely_treat_insecure_origin_as_secure.as_ref() + { + let domains = if insecure_allowlist.is_empty() { + "for all domains".to_string() + } else { + format!("for: {}", insecure_allowlist.join(", ")) + }; + let msg = format!( + "DANGER: SSL ceritificate validation is disabled {}", + domains + ); + eprintln!("{}", colors::yellow(msg)); + } + let cache_usage = if flags.cached_only { CacheSetting::Only } else if !flags.cache_blocklist.is_empty() { @@ -137,6 +153,7 @@ impl ProgramState { !flags.no_remote, Some(root_cert_store.clone()), blob_store.clone(), + flags.unsafely_treat_insecure_origin_as_secure.clone(), )?; let lockfile = if let Some(filename) = &flags.lock { diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs index 5fd9d6b256..ddc3adc7e1 100644 --- a/cli/specifier_handler.rs +++ b/cli/specifier_handler.rs @@ -598,6 +598,7 @@ pub mod tests { true, None, BlobStore::default(), + None, ) .expect("could not setup"); let disk_cache = deno_dir.gen_cache; diff --git a/cli/standalone.rs b/cli/standalone.rs index 460ee23d05..ded3c88e8a 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -57,6 +57,7 @@ pub struct Metadata { pub log_level: Option, pub ca_stores: Option>, pub ca_data: Option>, + pub unsafely_treat_insecure_origin_as_secure: Option>, } pub const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd"; @@ -252,6 +253,8 @@ pub async fn run( debug_flag: metadata.log_level.map_or(false, |l| l == log::Level::Debug), user_agent: version::get_user_agent(), unstable: metadata.unstable, + unsafely_treat_insecure_origin_as_secure: metadata + .unsafely_treat_insecure_origin_as_secure, root_cert_store: Some(root_cert_store), seed: metadata.seed, js_error_create_fn: None, diff --git a/cli/tests/cafile_ts_fetch_unsafe_ssl.ts.out b/cli/tests/cafile_ts_fetch_unsafe_ssl.ts.out new file mode 100644 index 0000000000..f0b63833af --- /dev/null +++ b/cli/tests/cafile_ts_fetch_unsafe_ssl.ts.out @@ -0,0 +1,2 @@ +DANGER: SSL ceritificate validation is disabled for all domains +Hello diff --git a/cli/tests/cafile_url_imports_unsafe_ssl.ts.out b/cli/tests/cafile_url_imports_unsafe_ssl.ts.out new file mode 100644 index 0000000000..3f6a0c07f6 --- /dev/null +++ b/cli/tests/cafile_url_imports_unsafe_ssl.ts.out @@ -0,0 +1,3 @@ +DANGER: SSL ceritificate validation is disabled for: localhost +Hello +success diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs index cc016382fa..938d40125b 100644 --- a/cli/tests/integration/mod.rs +++ b/cli/tests/integration/mod.rs @@ -474,6 +474,19 @@ fn broken_stdout() { // http_server: true, // }); +itest!(cafile_url_imports_unsafe_ssl { + args: "run --quiet --reload --unsafely-treat-insecure-origin-as-secure=localhost cafile_url_imports.ts", + output: "cafile_url_imports_unsafe_ssl.ts.out", + http_server: true, +}); + +itest!(cafile_ts_fetch_unsafe_ssl { + args: + "run --quiet --reload --allow-net --unsafely-treat-insecure-origin-as-secure cafile_ts_fetch.ts", + output: "cafile_ts_fetch_unsafe_ssl.ts.out", + http_server: true, +}); + #[test] #[ignore] fn cafile_env_fetch() { diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs index 46ac27b83b..2eaf501614 100644 --- a/cli/tools/standalone.rs +++ b/cli/tools/standalone.rs @@ -99,6 +99,9 @@ pub fn create_standalone_binary( location: flags.location.clone(), permissions: flags.clone().into(), v8_flags: flags.v8_flags.clone(), + unsafely_treat_insecure_origin_as_secure: flags + .unsafely_treat_insecure_origin_as_secure + .clone(), log_level: flags.log_level, ca_stores: flags.ca_stores, ca_data, @@ -223,6 +226,8 @@ pub fn compile_to_runtime_flags( lock: None, log_level: flags.log_level, no_check: false, + unsafely_treat_insecure_origin_as_secure: flags + .unsafely_treat_insecure_origin_as_secure, no_remote: false, prompt: flags.prompt, reload: false, diff --git a/extensions/fetch/lib.rs b/extensions/fetch/lib.rs index 0ac853cbca..cc5954b745 100644 --- a/extensions/fetch/lib.rs +++ b/extensions/fetch/lib.rs @@ -60,6 +60,7 @@ pub fn init( root_cert_store: Option, proxy: Option, request_builder_hook: Option RequestBuilder>, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Extension { Extension::builder() .js(include_js_files!( @@ -87,6 +88,7 @@ pub fn init( root_cert_store.clone(), None, proxy.clone(), + unsafely_treat_insecure_origin_as_secure.clone(), ) .unwrap() }); @@ -95,6 +97,8 @@ pub fn init( root_cert_store: root_cert_store.clone(), proxy: proxy.clone(), request_builder_hook, + unsafely_treat_insecure_origin_as_secure: + unsafely_treat_insecure_origin_as_secure.clone(), }); Ok(()) }) @@ -106,6 +110,7 @@ pub struct HttpClientDefaults { pub root_cert_store: Option, pub proxy: Option, pub request_builder_hook: Option RequestBuilder>, + pub unsafely_treat_insecure_origin_as_secure: Option>, } pub trait FetchPermissions { @@ -532,11 +537,13 @@ where let defaults = state.borrow::(); let cert_data = get_cert_data(args.ca_file.as_deref(), args.ca_data.as_deref())?; + let client = create_http_client( defaults.user_agent.clone(), defaults.root_cert_store.clone(), cert_data, args.proxy, + defaults.unsafely_treat_insecure_origin_as_secure.clone(), ) .unwrap(); diff --git a/extensions/net/lib.rs b/extensions/net/lib.rs index 6b0b728b15..f68034517b 100644 --- a/extensions/net/lib.rs +++ b/extensions/net/lib.rs @@ -94,9 +94,16 @@ pub struct DefaultTlsOptions { pub root_cert_store: Option, } +/// `UnsafelyTreatInsecureOriginAsSecure` is a wrapper struct so it can be placed inside `GothamState`; +/// using type alias for a `Option>` could work, but there's a high chance +/// that there might be another type alias pointing to a `Option>`, which +/// would override previously used alias. +pub struct UnsafelyTreatInsecureOriginAsSecure(Option>); + pub fn init( root_cert_store: Option, unstable: bool, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Extension { let mut ops_to_register = vec![]; ops_to_register.extend(io::init()); @@ -115,6 +122,9 @@ pub fn init( root_cert_store: root_cert_store.clone(), }); state.put(UnstableChecker { unstable }); + state.put(UnsafelyTreatInsecureOriginAsSecure( + unsafely_treat_insecure_origin_as_secure.clone(), + )); Ok(()) }) .build() diff --git a/extensions/net/ops_tls.rs b/extensions/net/ops_tls.rs index 7c45633903..6c26ed7484 100644 --- a/extensions/net/ops_tls.rs +++ b/extensions/net/ops_tls.rs @@ -9,6 +9,7 @@ use crate::resolve_addr::resolve_addr; use crate::resolve_addr::resolve_addr_sync; use crate::DefaultTlsOptions; use crate::NetPermissions; +use crate::UnsafelyTreatInsecureOriginAsSecure; use deno_core::error::bad_resource; use deno_core::error::bad_resource_id; use deno_core::error::custom_error; @@ -720,8 +721,8 @@ where let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; - let tls_config = Arc::new(create_client_config(root_cert_store, ca_data)?); - + let tls_config = + Arc::new(create_client_config(root_cert_store, ca_data, None)?); let tls_stream = TlsStream::new_client_side(tcp_stream, &tls_config, hostname_dns); @@ -760,6 +761,11 @@ where }; let port = args.port; let cert_file = args.cert_file.as_deref(); + let unsafely_treat_insecure_origin_as_secure = state + .borrow() + .borrow::() + .0 + .clone(); if args.cert_chain.is_some() { super::check_unstable2(&state, "ConnectTlsOptions.certChain"); @@ -801,8 +807,11 @@ where let tcp_stream = TcpStream::connect(connect_addr).await?; let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; - - let mut tls_config = create_client_config(root_cert_store, ca_data)?; + let mut tls_config = create_client_config( + root_cert_store, + ca_data, + unsafely_treat_insecure_origin_as_secure, + )?; if args.cert_chain.is_some() || args.private_key.is_some() { let cert_chain = args diff --git a/extensions/tls/Cargo.toml b/extensions/tls/Cargo.toml index ee7be04dc1..e75d0bbfe2 100644 --- a/extensions/tls/Cargo.toml +++ b/extensions/tls/Cargo.toml @@ -17,7 +17,7 @@ path = "lib.rs" deno_core = { version = "0.95.0", path = "../../core" } lazy_static = "1.4.0" reqwest = { version = "0.11.4", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } -rustls = "0.19.0" +rustls = { version = "0.19.1", features = ["dangerous_configuration"] } rustls-native-certs = "0.5.0" serde = { version = "1.0.126", features = ["derive"] } webpki = "0.21.4" diff --git a/extensions/tls/lib.rs b/extensions/tls/lib.rs index f91249792c..2a15b4e757 100644 --- a/extensions/tls/lib.rs +++ b/extensions/tls/lib.rs @@ -16,20 +16,65 @@ use reqwest::header::HeaderMap; use reqwest::header::USER_AGENT; use reqwest::redirect::Policy; use reqwest::Client; +use rustls::internal::msgs::handshake::DigitallySignedStruct; +use rustls::Certificate; use rustls::ClientConfig; +use rustls::HandshakeSignatureValid; use rustls::RootCertStore; +use rustls::ServerCertVerified; +use rustls::ServerCertVerifier; use rustls::StoresClientSessions; +use rustls::TLSError; use serde::Deserialize; use std::collections::HashMap; use std::io::BufReader; use std::io::Cursor; use std::sync::Arc; +use webpki::DNSNameRef; /// This extension has no runtime apis, it only exports some shared native functions. pub fn init() -> Extension { Extension::builder().build() } +pub struct NoCertificateVerification(pub Vec); + +impl ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _roots: &RootCertStore, + _presented_certs: &[Certificate], + dns_name: DNSNameRef<'_>, + _ocsp: &[u8], + ) -> Result { + let dns_name: &str = dns_name.into(); + let dns_name: String = dns_name.to_owned(); + if self.0.is_empty() || self.0.contains(&dns_name) { + Ok(ServerCertVerified::assertion()) + } else { + Err(TLSError::General(dns_name)) + } + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } +} + #[derive(Deserialize, Default, Debug, Clone)] #[serde(rename_all = "camelCase")] #[serde(default)] @@ -80,6 +125,7 @@ pub fn create_default_root_cert_store() -> RootCertStore { pub fn create_client_config( root_cert_store: Option, ca_data: Option>, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Result { let mut tls_config = ClientConfig::new(); tls_config.set_persistence(CLIENT_SESSION_MEMORY_CACHE.clone()); @@ -95,6 +141,12 @@ pub fn create_client_config( } } + if let Some(ic_allowlist) = unsafely_treat_insecure_origin_as_secure { + tls_config.dangerous().set_certificate_verifier(Arc::new( + NoCertificateVerification(ic_allowlist), + )); + } + Ok(tls_config) } @@ -105,8 +157,13 @@ pub fn create_http_client( root_cert_store: Option, ca_data: Option>, proxy: Option, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Result { - let tls_config = create_client_config(root_cert_store, ca_data)?; + let tls_config = create_client_config( + root_cert_store, + ca_data, + unsafely_treat_insecure_origin_as_secure, + )?; let mut headers = HeaderMap::new(); headers.insert(USER_AGENT, user_agent.parse().unwrap()); let mut builder = Client::builder() diff --git a/extensions/websocket/lib.rs b/extensions/websocket/lib.rs index 01f0a523d0..896a5f2e26 100644 --- a/extensions/websocket/lib.rs +++ b/extensions/websocket/lib.rs @@ -54,6 +54,12 @@ pub trait WebSocketPermissions { fn check_net_url(&mut self, _url: &url::Url) -> Result<(), AnyError>; } +/// `UnsafelyTreatInsecureOriginAsSecure` is a wrapper struct so it can be placed inside `GothamState`; +/// using type alias for a `Option>` could work, but there's a high chance +/// that there might be another type alias pointing to a `Option>`, which +/// would override previously used alias. +pub struct UnsafelyTreatInsecureOriginAsSecure(Option>); + /// For use with `op_websocket_*` when the user does not want permissions. pub struct NoWebSocketPermissions; @@ -197,6 +203,11 @@ where ); } + let unsafely_treat_insecure_origin_as_secure = state + .borrow() + .borrow::() + .0 + .clone(); let root_cert_store = state.borrow().borrow::().0.clone(); let user_agent = state.borrow().borrow::().0.clone(); let uri: Uri = args.url.parse()?; @@ -221,7 +232,11 @@ where let socket: MaybeTlsStream = match uri.scheme_str() { Some("ws") => MaybeTlsStream::Plain(tcp_socket), Some("wss") => { - let tls_config = create_client_config(root_cert_store, None)?; + let tls_config = create_client_config( + root_cert_store, + None, + unsafely_treat_insecure_origin_as_secure, + )?; let tls_connector = TlsConnector::from(Arc::new(tls_config)); let dnsname = DNSNameRef::try_from_ascii_str(domain) .map_err(|_| invalid_hostname(domain))?; @@ -377,6 +392,7 @@ pub async fn op_ws_next_event( pub fn init( user_agent: String, root_cert_store: Option, + unsafely_treat_insecure_origin_as_secure: Option>, ) -> Extension { Extension::builder() .js(include_js_files!( @@ -395,6 +411,9 @@ pub fn init( ]) .state(move |state| { state.put::(WsUserAgent(user_agent.clone())); + state.put(UnsafelyTreatInsecureOriginAsSecure( + unsafely_treat_insecure_origin_as_secure.clone(), + )); state.put::(WsRootStore(root_cert_store.clone())); Ok(()) }) diff --git a/runtime/build.rs b/runtime/build.rs index e6f7de6411..3003fb2313 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -48,10 +48,12 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec) { None, None, None, + None, ), deno_websocket::init::( "".to_owned(), None, + None, ), deno_webstorage::init(None), deno_crypto::init(None), @@ -62,7 +64,10 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec) { false, // No --unstable. ), deno_ffi::init::(false), - deno_net::init::(None, false), // No --unstable. + deno_net::init::( + None, false, // No --unstable. + None, + ), deno_http::init(), ]; diff --git a/runtime/examples/hello_runtime.rs b/runtime/examples/hello_runtime.rs index eaedcac10d..e3a8b88b22 100644 --- a/runtime/examples/hello_runtime.rs +++ b/runtime/examples/hello_runtime.rs @@ -27,6 +27,7 @@ async fn main() -> Result<(), AnyError> { args: vec![], debug_flag: false, unstable: false, + unsafely_treat_insecure_origin_as_secure: None, root_cert_store: None, user_agent: "hello_runtime".to_string(), seed: None, diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 3f68fc4e64..1349c510a5 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -253,6 +253,7 @@ pub struct WebWorkerOptions { pub args: Vec, pub debug_flag: bool, pub unstable: bool, + pub unsafely_treat_insecure_origin_as_secure: Option>, pub root_cert_store: Option, pub user_agent: String, pub seed: Option, @@ -304,10 +305,12 @@ impl WebWorker { options.root_cert_store.clone(), None, None, + options.unsafely_treat_insecure_origin_as_secure.clone(), ), deno_websocket::init::( options.user_agent.clone(), options.root_cert_store.clone(), + options.unsafely_treat_insecure_origin_as_secure.clone(), ), deno_broadcast_channel::init( options.broadcast_channel.clone(), @@ -341,6 +344,7 @@ impl WebWorker { deno_net::init::( options.root_cert_store.clone(), options.unstable, + options.unsafely_treat_insecure_origin_as_secure.clone(), ), ops::os::init(), ops::permissions::init(), diff --git a/runtime/worker.rs b/runtime/worker.rs index 69602d0dd6..f8b0edb64d 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -50,6 +50,7 @@ pub struct WorkerOptions { pub args: Vec, pub debug_flag: bool, pub unstable: bool, + pub unsafely_treat_insecure_origin_as_secure: Option>, pub root_cert_store: Option, pub user_agent: String, pub seed: Option, @@ -103,10 +104,12 @@ impl MainWorker { options.root_cert_store.clone(), None, None, + options.unsafely_treat_insecure_origin_as_secure.clone(), ), deno_websocket::init::( options.user_agent.clone(), options.root_cert_store.clone(), + options.unsafely_treat_insecure_origin_as_secure.clone(), ), deno_webstorage::init(options.origin_storage_dir.clone()), deno_crypto::init(options.seed), @@ -131,6 +134,7 @@ impl MainWorker { deno_net::init::( options.root_cert_store.clone(), options.unstable, + options.unsafely_treat_insecure_origin_as_secure.clone(), ), ops::os::init(), ops::permissions::init(), @@ -300,6 +304,7 @@ mod tests { args: vec![], debug_flag: false, unstable: false, + unsafely_treat_insecure_origin_as_secure: None, root_cert_store: None, seed: None, js_error_create_fn: None,