From 037466e9cdec913d0f146532fde28b26093267f1 Mon Sep 17 00:00:00 2001 From: Aleksei Kosyrev Date: Wed, 18 May 2022 14:32:12 +0300 Subject: [PATCH] fix(ext/tls): ability to ignore IP-address certificate errors (#14610) --- Cargo.lock | 4 +- cli/tests/integration/mod.rs | 6 ++ cli/tests/testdata/ip_address_unsafe_ssl.ts | 2 + .../testdata/ip_address_unsafe_ssl.ts.out | 2 + cli/tests/unit/tls_test.ts | 11 +- ext/tls/lib.rs | 102 ++++++++++++++---- 6 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 cli/tests/testdata/ip_address_unsafe_ssl.ts create mode 100644 cli/tests/testdata/ip_address_unsafe_ssl.ts.out diff --git a/Cargo.lock b/Cargo.lock index 2f81d70e6c..916b2fcb91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3373,9 +3373,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +checksum = "a024a432ae760ab3bff924ad91ce1cfa52cb57ed16e1ef32d0d249cfee1a6c13" dependencies = [ "log", "ring", diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs index 7acd926619..7de4418743 100644 --- a/cli/tests/integration/mod.rs +++ b/cli/tests/integration/mod.rs @@ -523,6 +523,12 @@ itest!(deno_land_unsafe_ssl { output: "deno_land_unsafe_ssl.ts.out", }); +itest!(ip_address_unsafe_ssl { + args: + "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=1.1.1.1 ip_address_unsafe_ssl.ts", + output: "ip_address_unsafe_ssl.ts.out", +}); + itest!(localhost_unsafe_ssl { args: "run --quiet --reload --allow-net --unsafely-ignore-certificate-errors=deno.land cafile_url_imports.ts", diff --git a/cli/tests/testdata/ip_address_unsafe_ssl.ts b/cli/tests/testdata/ip_address_unsafe_ssl.ts new file mode 100644 index 0000000000..a3268888fb --- /dev/null +++ b/cli/tests/testdata/ip_address_unsafe_ssl.ts @@ -0,0 +1,2 @@ +const r = await fetch("https://1.1.1.1"); +console.log(r.status); diff --git a/cli/tests/testdata/ip_address_unsafe_ssl.ts.out b/cli/tests/testdata/ip_address_unsafe_ssl.ts.out new file mode 100644 index 0000000000..d4ebb2617d --- /dev/null +++ b/cli/tests/testdata/ip_address_unsafe_ssl.ts.out @@ -0,0 +1,2 @@ +DANGER: TLS certificate validation is disabled for: 1.1.1.1 +200 diff --git a/cli/tests/unit/tls_test.ts b/cli/tests/unit/tls_test.ts index 07ffcd487a..05eced64e3 100644 --- a/cli/tests/unit/tls_test.ts +++ b/cli/tests/unit/tls_test.ts @@ -36,18 +36,9 @@ Deno.test({ permissions: { net: false } }, async function connectTLSNoPerm() { Deno.test( { permissions: { read: true, net: true } }, async function connectTLSInvalidHost() { - const listener = await Deno.listenTls({ - hostname: "localhost", - port: 3567, - certFile: "cli/tests/testdata/tls/localhost.crt", - keyFile: "cli/tests/testdata/tls/localhost.key", - }); - await assertRejects(async () => { - await Deno.connectTls({ hostname: "127.0.0.1", port: 3567 }); + await Deno.connectTls({ hostname: "256.0.0.0", port: 3567 }); }, TypeError); - - listener.close(); }, ); diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 66545ec7db..42ea5b05d4 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -12,10 +12,12 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::Extension; +use rustls::client::HandshakeSignatureValid; use rustls::client::ServerCertVerified; use rustls::client::ServerCertVerifier; use rustls::client::StoresClientSessions; use rustls::client::WebPkiVerifier; +use rustls::internal::msgs::handshake::DigitallySignedStruct; use rustls::Certificate; use rustls::ClientConfig; use rustls::Error; @@ -38,6 +40,22 @@ pub fn init() -> Extension { Extension::builder().build() } +struct DefaultSignatureVerification; + +impl ServerCertVerifier for DefaultSignatureVerification { + fn verify_server_cert( + &self, + _end_entity: &Certificate, + _intermediates: &[Certificate], + _server_name: &ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: SystemTime, + ) -> Result { + Err(Error::General("Should not be used".to_string())) + } +} + pub struct NoCertificateVerification(pub Vec); impl ServerCertVerifier for NoCertificateVerification { @@ -50,27 +68,60 @@ impl ServerCertVerifier for NoCertificateVerification { ocsp_response: &[u8], now: SystemTime, ) -> Result { - if let ServerName::DnsName(dns_name) = server_name { - let dns_name = dns_name.as_ref().to_owned(); - if self.0.is_empty() || self.0.contains(&dns_name) { - Ok(ServerCertVerified::assertion()) - } else { - let root_store = create_default_root_cert_store(); - let verifier = WebPkiVerifier::new(root_store, None); - verifier.verify_server_cert( - end_entity, - intermediates, - server_name, - scts, - ocsp_response, - now, - ) - } - } else { - // NOTE(bartlomieju): `ServerName` is a non-exhaustive enum - // so we have this catch all error here. - Err(Error::General("Unknown `ServerName` variant".to_string())) + if self.0.is_empty() { + return Ok(ServerCertVerified::assertion()); } + let dns_name_or_ip_address = match server_name { + ServerName::DnsName(dns_name) => dns_name.as_ref().to_owned(), + ServerName::IpAddress(ip_address) => ip_address.to_string(), + _ => { + // NOTE(bartlomieju): `ServerName` is a non-exhaustive enum + // so we have this catch all errors here. + return Err(Error::General("Unknown `ServerName` variant".to_string())); + } + }; + if self.0.contains(&dns_name_or_ip_address) { + Ok(ServerCertVerified::assertion()) + } else { + let root_store = create_default_root_cert_store(); + let verifier = WebPkiVerifier::new(root_store, None); + verifier.verify_server_cert( + end_entity, + intermediates, + server_name, + scts, + ocsp_response, + now, + ) + } + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &rustls::Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + if self.0.is_empty() { + return Ok(HandshakeSignatureValid::assertion()); + } + filter_invalid_encoding_err( + DefaultSignatureVerification.verify_tls12_signature(message, cert, dss), + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &rustls::Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + if self.0.is_empty() { + return Ok(HandshakeSignatureValid::assertion()); + } + filter_invalid_encoding_err( + DefaultSignatureVerification.verify_tls13_signature(message, cert, dss), + ) } } @@ -233,6 +284,17 @@ fn load_pkcs8_keys(mut bytes: &[u8]) -> Result, AnyError> { Ok(keys.into_iter().map(PrivateKey).collect()) } +fn filter_invalid_encoding_err( + to_be_filtered: Result, +) -> Result { + match to_be_filtered { + Err(Error::InvalidCertificateEncoding) => { + Ok(HandshakeSignatureValid::assertion()) + } + res => res, + } +} + pub fn load_private_keys(bytes: &[u8]) -> Result, AnyError> { let mut keys = load_rsa_keys(bytes)?;