diff options
author | Aleksei Kosyrev <albnnc@gmail.com> | 2022-05-18 14:32:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-18 13:32:12 +0200 |
commit | 037466e9cdec913d0f146532fde28b26093267f1 (patch) | |
tree | c30c4aea060be94e3fcbc24b6723327e4b84379c | |
parent | 4d8261070095e49de68ca21ac3b564887039bd24 (diff) |
fix(ext/tls): ability to ignore IP-address certificate errors (#14610)
-rw-r--r-- | Cargo.lock | 4 | ||||
-rw-r--r-- | cli/tests/integration/mod.rs | 6 | ||||
-rw-r--r-- | cli/tests/testdata/ip_address_unsafe_ssl.ts | 2 | ||||
-rw-r--r-- | cli/tests/testdata/ip_address_unsafe_ssl.ts.out | 2 | ||||
-rw-r--r-- | cli/tests/unit/tls_test.ts | 11 | ||||
-rw-r--r-- | ext/tls/lib.rs | 98 |
6 files changed, 93 insertions, 30 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2f81d70e6..916b2fcb9 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 7acd92661..7de441874 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 000000000..a3268888f --- /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 000000000..d4ebb2617 --- /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 07ffcd487..05eced64e 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 66545ec7d..42ea5b05d 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<Item = &[u8]>, + _ocsp_response: &[u8], + _now: SystemTime, + ) -> Result<ServerCertVerified, Error> { + Err(Error::General("Should not be used".to_string())) + } +} + pub struct NoCertificateVerification(pub Vec<String>); impl ServerCertVerifier for NoCertificateVerification { @@ -50,28 +68,61 @@ impl ServerCertVerifier for NoCertificateVerification { ocsp_response: &[u8], now: SystemTime, ) -> Result<ServerCertVerified, Error> { - 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, - ) + 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 { - // NOTE(bartlomieju): `ServerName` is a non-exhaustive enum - // so we have this catch all error here. - Err(Error::General("Unknown `ServerName` variant".to_string())) + 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<HandshakeSignatureValid, Error> { + 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<HandshakeSignatureValid, Error> { + if self.0.is_empty() { + return Ok(HandshakeSignatureValid::assertion()); + } + filter_invalid_encoding_err( + DefaultSignatureVerification.verify_tls13_signature(message, cert, dss), + ) + } } #[derive(Deserialize, Default, Debug, Clone)] @@ -233,6 +284,17 @@ fn load_pkcs8_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { Ok(keys.into_iter().map(PrivateKey).collect()) } +fn filter_invalid_encoding_err( + to_be_filtered: Result<HandshakeSignatureValid, Error>, +) -> Result<HandshakeSignatureValid, Error> { + match to_be_filtered { + Err(Error::InvalidCertificateEncoding) => { + Ok(HandshakeSignatureValid::assertion()) + } + res => res, + } +} + pub fn load_private_keys(bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { let mut keys = load_rsa_keys(bytes)?; |