From d58fd763e5b6c814d7d35fa9a8f7845d910770a4 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 13 Apr 2021 13:30:41 -0600 Subject: [PATCH] add support for a certificate trust list --- src/database.rs | 12 +++++++++++- src/database/globals.rs | 20 +++++++++++++++++++- src/database/proxy.rs | 32 +++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/database.rs b/src/database.rs index bdff386..82221b3 100644 --- a/src/database.rs +++ b/src/database.rs @@ -38,7 +38,15 @@ use std::{ use tokio::sync::{OwnedRwLockReadGuard, RwLock as TokioRwLock, Semaphore}; use tracing::{debug, error, warn}; -use self::proxy::ProxyConfig; +use self::proxy::{IncludeExclude, ProxyConfig}; + +#[derive(Debug, Clone, Deserialize)] +pub struct CertificateTrustList(IncludeExclude); +impl CertificateTrustList { + pub fn contains(&self, domain: &str) -> bool { + self.0.includes(domain) + } +} #[derive(Clone, Debug, Deserialize)] pub struct Config { @@ -64,6 +72,8 @@ pub struct Config { pub tracing_flame: bool, #[serde(default)] proxy: ProxyConfig, + #[serde(default)] + trust_certificates: Option, jwt_secret: Option, #[serde(default = "Vec::new")] trusted_servers: Vec>, diff --git a/src/database/globals.rs b/src/database/globals.rs index 823ce34..513a0ff 100644 --- a/src/database/globals.rs +++ b/src/database/globals.rs @@ -19,7 +19,7 @@ use tokio::sync::{broadcast, watch::Receiver, Mutex as TokioMutex, Semaphore}; use tracing::{error, info}; use trust_dns_resolver::TokioAsyncResolver; -use super::abstraction::Tree; +use super::{abstraction::Tree, CertificateTrustList}; pub const COUNTER: &[u8] = b"c"; @@ -53,6 +53,7 @@ pub struct Globals { struct MatrixServerVerifier { inner: WebPKIVerifier, + trust_list: Option, tls_name_override: Arc>, } @@ -66,6 +67,14 @@ impl ServerCertVerifier for MatrixServerVerifier { ocsp_response: &[u8], ) -> std::result::Result { if let Some(override_name) = self.tls_name_override.read().unwrap().get(dns_name.into()) { + if self + .trust_list + .as_ref() + .map(|tl| tl.contains(override_name.as_ref().into())) + .unwrap_or_default() + { + return Ok(rustls::ServerCertVerified::assertion()); + } let result = self.inner.verify_server_cert( roots, presented_certs, @@ -80,6 +89,14 @@ impl ServerCertVerifier for MatrixServerVerifier { dns_name ); } + if self + .trust_list + .as_ref() + .map(|tl| tl.contains(dns_name.into())) + .unwrap_or_default() + { + return Ok(rustls::ServerCertVerified::assertion()); + } self.inner .verify_server_cert(roots, presented_certs, dns_name, ocsp_response) } @@ -164,6 +181,7 @@ impl Globals { let tls_name_override = Arc::new(RwLock::new(TlsNameMap::new())); let verifier = Arc::new(MatrixServerVerifier { inner: WebPKIVerifier::new(), + trust_list: config.trust_certificates.clone(), tls_name_override: tls_name_override.clone(), }); let mut tlsconfig = rustls::ClientConfig::new(); diff --git a/src/database/proxy.rs b/src/database/proxy.rs index 78e9d2b..03e62e7 100644 --- a/src/database/proxy.rs +++ b/src/database/proxy.rs @@ -58,14 +58,28 @@ impl Default for ProxyConfig { pub struct PartialProxyConfig { #[serde(deserialize_with = "crate::utils::deserialize_from_str")] url: Url, + #[serde(flatten)] + include_exclude: IncludeExclude, +} +impl PartialProxyConfig { + pub fn for_url(&self, url: &Url) -> Option<&Url> { + if self.include_exclude.includes(url.domain()?) { + Some(&self.url) + } else { + None + } + } +} + +#[derive(Debug, Clone, Deserialize, Default)] +pub struct IncludeExclude { #[serde(default)] include: Vec, #[serde(default)] exclude: Vec, } -impl PartialProxyConfig { - pub fn for_url(&self, url: &Url) -> Option<&Url> { - let domain = url.domain()?; +impl IncludeExclude { + pub fn includes(&self, domain: &str) -> bool { let mut included_because = None; // most specific reason it was included let mut excluded_because = None; // most specific reason it was excluded if self.include.is_empty() { @@ -89,9 +103,9 @@ impl PartialProxyConfig { } } match (included_because, excluded_because) { - (Some(a), Some(b)) if a.more_specific_than(b) => Some(&self.url), // included for a more specific reason than excluded - (Some(_), None) => Some(&self.url), - _ => None, + (Some(a), Some(b)) if a.more_specific_than(b) => true, // included for a more specific reason than excluded + (Some(_), None) => true, + _ => false, } } } @@ -99,9 +113,9 @@ impl PartialProxyConfig { /// A domain name, that optionally allows a * as its first subdomain. #[derive(Clone, Debug)] pub enum WildCardedDomain { - WildCard, - WildCarded(String), - Exact(String), + WildCard, // * + WildCarded(String), // *.foo + Exact(String), // foo.bar } impl WildCardedDomain { pub fn matches(&self, domain: &str) -> bool {