1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

refactor(ext/tls): use concrete error types (#26174)

This commit is contained in:
Leo Kettmeir 2024-10-12 16:53:38 -07:00 committed by GitHub
parent 2ac699fe6e
commit 64c304a452
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 79 additions and 54 deletions

1
Cargo.lock generated
View file

@ -2117,6 +2117,7 @@ dependencies = [
"rustls-tokio-stream", "rustls-tokio-stream",
"rustls-webpki", "rustls-webpki",
"serde", "serde",
"thiserror",
"tokio", "tokio",
"webpki-roots", "webpki-roots",
] ]

View file

@ -250,7 +250,7 @@ pub fn op_tls_cert_resolver_resolve_error(
#[string] sni: String, #[string] sni: String,
#[string] error: String, #[string] error: String,
) { ) {
lookup.resolve(sni, Err(anyhow!(error))) lookup.resolve(sni, Err(error))
} }
#[op2] #[op2]

View file

@ -21,5 +21,6 @@ rustls-pemfile.workspace = true
rustls-tokio-stream.workspace = true rustls-tokio-stream.workspace = true
rustls-webpki.workspace = true rustls-webpki.workspace = true
serde.workspace = true serde.workspace = true
thiserror.workspace = true
tokio.workspace = true tokio.workspace = true
webpki-roots.workspace = true webpki-roots.workspace = true

View file

@ -9,17 +9,12 @@ pub use rustls_tokio_stream::*;
pub use webpki; pub use webpki;
pub use webpki_roots; pub use webpki_roots;
use deno_core::anyhow::anyhow;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use rustls::client::danger::HandshakeSignatureValid; use rustls::client::danger::HandshakeSignatureValid;
use rustls::client::danger::ServerCertVerified; use rustls::client::danger::ServerCertVerified;
use rustls::client::danger::ServerCertVerifier; use rustls::client::danger::ServerCertVerifier;
use rustls::client::WebPkiServerVerifier; use rustls::client::WebPkiServerVerifier;
use rustls::ClientConfig; use rustls::ClientConfig;
use rustls::DigitallySignedStruct; use rustls::DigitallySignedStruct;
use rustls::Error;
use rustls::RootCertStore; use rustls::RootCertStore;
use rustls_pemfile::certs; use rustls_pemfile::certs;
use rustls_pemfile::ec_private_keys; use rustls_pemfile::ec_private_keys;
@ -35,12 +30,30 @@ use std::sync::Arc;
mod tls_key; mod tls_key;
pub use tls_key::*; pub use tls_key::*;
#[derive(Debug, thiserror::Error)]
pub enum TlsError {
#[error(transparent)]
Rustls(#[from] rustls::Error),
#[error("Unable to add pem file to certificate store: {0}")]
UnableAddPemFileToCert(std::io::Error),
#[error("Unable to decode certificate")]
CertInvalid,
#[error("No certificates found in certificate data")]
CertsNotFound,
#[error("No keys found in key data")]
KeysNotFound,
#[error("Unable to decode key")]
KeyDecode,
}
/// Lazily resolves the root cert store. /// Lazily resolves the root cert store.
/// ///
/// This was done because the root cert store is not needed in all cases /// This was done because the root cert store is not needed in all cases
/// and takes a bit of time to initialize. /// and takes a bit of time to initialize.
pub trait RootCertStoreProvider: Send + Sync { pub trait RootCertStoreProvider: Send + Sync {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError>; fn get_or_try_init(
&self,
) -> Result<&RootCertStore, deno_core::error::AnyError>;
} }
// This extension has no runtime apis, it only exports some shared native functions. // This extension has no runtime apis, it only exports some shared native functions.
@ -77,7 +90,7 @@ impl ServerCertVerifier for NoCertificateVerification {
server_name: &rustls::pki_types::ServerName<'_>, server_name: &rustls::pki_types::ServerName<'_>,
ocsp_response: &[u8], ocsp_response: &[u8],
now: rustls::pki_types::UnixTime, now: rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, Error> { ) -> Result<ServerCertVerified, rustls::Error> {
if self.ic_allowlist.is_empty() { if self.ic_allowlist.is_empty() {
return Ok(ServerCertVerified::assertion()); return Ok(ServerCertVerified::assertion());
} }
@ -89,7 +102,9 @@ impl ServerCertVerifier for NoCertificateVerification {
_ => { _ => {
// NOTE(bartlomieju): `ServerName` is a non-exhaustive enum // NOTE(bartlomieju): `ServerName` is a non-exhaustive enum
// so we have this catch all errors here. // so we have this catch all errors here.
return Err(Error::General("Unknown `ServerName` variant".to_string())); return Err(rustls::Error::General(
"Unknown `ServerName` variant".to_string(),
));
} }
}; };
if self.ic_allowlist.contains(&dns_name_or_ip_address) { if self.ic_allowlist.contains(&dns_name_or_ip_address) {
@ -110,7 +125,7 @@ impl ServerCertVerifier for NoCertificateVerification {
message: &[u8], message: &[u8],
cert: &rustls::pki_types::CertificateDer, cert: &rustls::pki_types::CertificateDer,
dss: &DigitallySignedStruct, dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> { ) -> Result<HandshakeSignatureValid, rustls::Error> {
if self.ic_allowlist.is_empty() { if self.ic_allowlist.is_empty() {
return Ok(HandshakeSignatureValid::assertion()); return Ok(HandshakeSignatureValid::assertion());
} }
@ -126,7 +141,7 @@ impl ServerCertVerifier for NoCertificateVerification {
message: &[u8], message: &[u8],
cert: &rustls::pki_types::CertificateDer, cert: &rustls::pki_types::CertificateDer,
dss: &DigitallySignedStruct, dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> { ) -> Result<HandshakeSignatureValid, rustls::Error> {
if self.ic_allowlist.is_empty() { if self.ic_allowlist.is_empty() {
return Ok(HandshakeSignatureValid::assertion()); return Ok(HandshakeSignatureValid::assertion());
} }
@ -178,7 +193,7 @@ pub fn create_client_config(
unsafely_ignore_certificate_errors: Option<Vec<String>>, unsafely_ignore_certificate_errors: Option<Vec<String>>,
maybe_cert_chain_and_key: TlsKeys, maybe_cert_chain_and_key: TlsKeys,
socket_use: SocketUse, socket_use: SocketUse,
) -> Result<ClientConfig, AnyError> { ) -> Result<ClientConfig, TlsError> {
if let Some(ic_allowlist) = unsafely_ignore_certificate_errors { if let Some(ic_allowlist) = unsafely_ignore_certificate_errors {
let client_config = ClientConfig::builder() let client_config = ClientConfig::builder()
.dangerous() .dangerous()
@ -214,10 +229,7 @@ pub fn create_client_config(
root_cert_store.add(cert)?; root_cert_store.add(cert)?;
} }
Err(e) => { Err(e) => {
return Err(anyhow!( return Err(TlsError::UnableAddPemFileToCert(e));
"Unable to add pem file to certificate store: {}",
e
));
} }
} }
} }
@ -255,74 +267,61 @@ fn add_alpn(client: &mut ClientConfig, socket_use: SocketUse) {
pub fn load_certs( pub fn load_certs(
reader: &mut dyn BufRead, reader: &mut dyn BufRead,
) -> Result<Vec<CertificateDer<'static>>, AnyError> { ) -> Result<Vec<CertificateDer<'static>>, TlsError> {
let certs: Result<Vec<_>, _> = certs(reader).collect(); let certs: Result<Vec<_>, _> = certs(reader).collect();
let certs = certs let certs = certs.map_err(|_| TlsError::CertInvalid)?;
.map_err(|_| custom_error("InvalidData", "Unable to decode certificate"))?;
if certs.is_empty() { if certs.is_empty() {
return Err(cert_not_found_err()); return Err(TlsError::CertsNotFound);
} }
Ok(certs) Ok(certs)
} }
fn key_decode_err() -> AnyError {
custom_error("InvalidData", "Unable to decode key")
}
fn key_not_found_err() -> AnyError {
custom_error("InvalidData", "No keys found in key data")
}
fn cert_not_found_err() -> AnyError {
custom_error("InvalidData", "No certificates found in certificate data")
}
/// Starts with -----BEGIN RSA PRIVATE KEY----- /// Starts with -----BEGIN RSA PRIVATE KEY-----
fn load_rsa_keys( fn load_rsa_keys(
mut bytes: &[u8], mut bytes: &[u8],
) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { ) -> Result<Vec<PrivateKeyDer<'static>>, TlsError> {
let keys: Result<Vec<_>, _> = rsa_private_keys(&mut bytes).collect(); let keys: Result<Vec<_>, _> = rsa_private_keys(&mut bytes).collect();
let keys = keys.map_err(|_| key_decode_err())?; let keys = keys.map_err(|_| TlsError::KeyDecode)?;
Ok(keys.into_iter().map(PrivateKeyDer::Pkcs1).collect()) Ok(keys.into_iter().map(PrivateKeyDer::Pkcs1).collect())
} }
/// Starts with -----BEGIN EC PRIVATE KEY----- /// Starts with -----BEGIN EC PRIVATE KEY-----
fn load_ec_keys( fn load_ec_keys(
mut bytes: &[u8], mut bytes: &[u8],
) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { ) -> Result<Vec<PrivateKeyDer<'static>>, TlsError> {
let keys: Result<Vec<_>, std::io::Error> = let keys: Result<Vec<_>, std::io::Error> =
ec_private_keys(&mut bytes).collect(); ec_private_keys(&mut bytes).collect();
let keys2 = keys.map_err(|_| key_decode_err())?; let keys2 = keys.map_err(|_| TlsError::KeyDecode)?;
Ok(keys2.into_iter().map(PrivateKeyDer::Sec1).collect()) Ok(keys2.into_iter().map(PrivateKeyDer::Sec1).collect())
} }
/// Starts with -----BEGIN PRIVATE KEY----- /// Starts with -----BEGIN PRIVATE KEY-----
fn load_pkcs8_keys( fn load_pkcs8_keys(
mut bytes: &[u8], mut bytes: &[u8],
) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { ) -> Result<Vec<PrivateKeyDer<'static>>, TlsError> {
let keys: Result<Vec<_>, std::io::Error> = let keys: Result<Vec<_>, std::io::Error> =
pkcs8_private_keys(&mut bytes).collect(); pkcs8_private_keys(&mut bytes).collect();
let keys2 = keys.map_err(|_| key_decode_err())?; let keys2 = keys.map_err(|_| TlsError::KeyDecode)?;
Ok(keys2.into_iter().map(PrivateKeyDer::Pkcs8).collect()) Ok(keys2.into_iter().map(PrivateKeyDer::Pkcs8).collect())
} }
fn filter_invalid_encoding_err( fn filter_invalid_encoding_err(
to_be_filtered: Result<HandshakeSignatureValid, Error>, to_be_filtered: Result<HandshakeSignatureValid, rustls::Error>,
) -> Result<HandshakeSignatureValid, Error> { ) -> Result<HandshakeSignatureValid, rustls::Error> {
match to_be_filtered { match to_be_filtered {
Err(Error::InvalidCertificate(rustls::CertificateError::BadEncoding)) => { Err(rustls::Error::InvalidCertificate(
Ok(HandshakeSignatureValid::assertion()) rustls::CertificateError::BadEncoding,
} )) => Ok(HandshakeSignatureValid::assertion()),
res => res, res => res,
} }
} }
pub fn load_private_keys( pub fn load_private_keys(
bytes: &[u8], bytes: &[u8],
) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { ) -> Result<Vec<PrivateKeyDer<'static>>, TlsError> {
let mut keys = load_rsa_keys(bytes)?; let mut keys = load_rsa_keys(bytes)?;
if keys.is_empty() { if keys.is_empty() {
@ -334,7 +333,7 @@ pub fn load_private_keys(
} }
if keys.is_empty() { if keys.is_empty() {
return Err(key_not_found_err()); return Err(TlsError::KeysNotFound);
} }
Ok(keys) Ok(keys)

View file

@ -11,8 +11,6 @@
//! key lookup can handle closing one end of the pair, in which case they will just //! key lookup can handle closing one end of the pair, in which case they will just
//! attempt to clean up the associated resources. //! attempt to clean up the associated resources.
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::futures::future::poll_fn; use deno_core::futures::future::poll_fn;
use deno_core::futures::future::Either; use deno_core::futures::future::Either;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
@ -33,7 +31,19 @@ use tokio::sync::oneshot;
use webpki::types::CertificateDer; use webpki::types::CertificateDer;
use webpki::types::PrivateKeyDer; use webpki::types::PrivateKeyDer;
type ErrorType = Rc<AnyError>; #[derive(Debug, thiserror::Error)]
pub enum TlsKeyError {
#[error(transparent)]
Rustls(#[from] rustls::Error),
#[error("Failed: {0}")]
Failed(ErrorType),
#[error(transparent)]
JoinError(#[from] tokio::task::JoinError),
#[error(transparent)]
RecvError(#[from] tokio::sync::broadcast::error::RecvError),
}
type ErrorType = Arc<Box<str>>;
/// A TLS certificate/private key pair. /// A TLS certificate/private key pair.
/// see https://docs.rs/rustls-pki-types/latest/rustls_pki_types/#cloning-private-keys /// see https://docs.rs/rustls-pki-types/latest/rustls_pki_types/#cloning-private-keys
@ -114,7 +124,7 @@ impl TlsKeyResolver {
&self, &self,
sni: String, sni: String,
alpn: Vec<Vec<u8>>, alpn: Vec<Vec<u8>>,
) -> Result<Arc<ServerConfig>, AnyError> { ) -> Result<Arc<ServerConfig>, TlsKeyError> {
let key = self.resolve(sni).await?; let key = self.resolve(sni).await?;
let mut tls_config = ServerConfig::builder() let mut tls_config = ServerConfig::builder()
@ -183,7 +193,7 @@ impl TlsKeyResolver {
pub fn resolve( pub fn resolve(
&self, &self,
sni: String, sni: String,
) -> impl Future<Output = Result<TlsKey, AnyError>> { ) -> impl Future<Output = Result<TlsKey, TlsKeyError>> {
let mut cache = self.inner.cache.borrow_mut(); let mut cache = self.inner.cache.borrow_mut();
let mut recv = match cache.get(&sni) { let mut recv = match cache.get(&sni) {
None => { None => {
@ -194,7 +204,7 @@ impl TlsKeyResolver {
} }
Some(TlsKeyState::Resolving(recv)) => recv.resubscribe(), Some(TlsKeyState::Resolving(recv)) => recv.resubscribe(),
Some(TlsKeyState::Resolved(res)) => { Some(TlsKeyState::Resolved(res)) => {
return Either::Left(ready(res.clone().map_err(|_| anyhow!("Failed")))); return Either::Left(ready(res.clone().map_err(TlsKeyError::Failed)));
} }
}; };
drop(cache); drop(cache);
@ -212,7 +222,7 @@ impl TlsKeyResolver {
// Someone beat us to it // Someone beat us to it
} }
} }
res.map_err(|_| anyhow!("Failed")) res.map_err(TlsKeyError::Failed)
}); });
Either::Right(async move { handle.await? }) Either::Right(async move { handle.await? })
} }
@ -247,13 +257,13 @@ impl TlsKeyLookup {
} }
/// Resolve a previously polled item. /// Resolve a previously polled item.
pub fn resolve(&self, sni: String, res: Result<TlsKey, AnyError>) { pub fn resolve(&self, sni: String, res: Result<TlsKey, String>) {
_ = self _ = self
.pending .pending
.borrow_mut() .borrow_mut()
.remove(&sni) .remove(&sni)
.unwrap() .unwrap()
.send(res.map_err(Rc::new)); .send(res.map_err(|e| Arc::new(e.into_boxed_str())));
} }
} }

View file

@ -349,6 +349,7 @@ pub fn create_ws_client_config(
TlsKeys::Null, TlsKeys::Null,
socket_use, socket_use,
) )
.map_err(|e| e.into())
} }
/// Headers common to both http/1.1 and h2 requests. /// Headers common to both http/1.1 and h2 requests.

View file

@ -17,6 +17,7 @@ use deno_core::serde_json;
use deno_core::url; use deno_core::url;
use deno_core::ModuleResolutionError; use deno_core::ModuleResolutionError;
use deno_cron::CronError; use deno_cron::CronError;
use deno_tls::TlsError;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::io; use std::io;
@ -157,6 +158,17 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
} }
} }
fn get_tls_error_class(e: &TlsError) -> &'static str {
match e {
TlsError::Rustls(_) => "Error",
TlsError::UnableAddPemFileToCert(e) => get_io_error_class(e),
TlsError::CertInvalid
| TlsError::CertsNotFound
| TlsError::KeysNotFound
| TlsError::KeyDecode => "InvalidData",
}
}
pub fn get_cron_error_class(e: &CronError) -> &'static str { pub fn get_cron_error_class(e: &CronError) -> &'static str {
match e { match e {
CronError::Resource(e) => { CronError::Resource(e) => {
@ -211,6 +223,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| deno_web::get_error_class_name(e)) .or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e)) .or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| deno_websocket::get_network_error_class_name(e)) .or_else(|| deno_websocket::get_network_error_class_name(e))
.or_else(|| e.downcast_ref::<TlsError>().map(get_tls_error_class))
.or_else(|| e.downcast_ref::<CronError>().map(get_cron_error_class)) .or_else(|| e.downcast_ref::<CronError>().map(get_cron_error_class))
.or_else(|| e.downcast_ref::<CanvasError>().map(get_canvas_error)) .or_else(|| e.downcast_ref::<CanvasError>().map(get_canvas_error))
.or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error)) .or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error))