use std::sync::Arc; use std::time::SystemTime; use rustls::internal::msgs::enums::AlertDescription; use rustls::server::{AllowAnyAuthenticatedClient, ClientCertVerified, ClientCertVerifier}; use rustls::{Certificate, DistinguishedNames, Error, RootCertStore}; use x509_parser::prelude::{CertificateRevocationList, FromDer, X509Certificate}; use crate::base::cert_utils::parse_pem_certificates; use crate::base::err_utils::{encpasulate_error, new_err}; use crate::tcp_relay_server::server_config::ServerConfig; pub struct CustomCertClientVerifier { upstream_cert_verifier: Box>, crl: Option>, } impl CustomCertClientVerifier { pub fn new(conf: Arc) -> std::io::Result { // Build root certifications list let cert_path = conf .tls_client_auth_root_cert .as_deref() .expect("No root certificates for client authentication provided!"); let cert_file = std::fs::read(cert_path).map_err(|e| { encpasulate_error( e, "Failed to read root certificates for client authentication!", ) })?; let root_certs = parse_pem_certificates(&cert_file).map_err(|e| { encpasulate_error( e, "Failed to read root certificates for server authentication!", ) })?; if root_certs.is_empty() { return Err(new_err("No certificates found for client authentication!")); } let mut store = RootCertStore::empty(); for cert in root_certs { store .add(&cert) .expect("Failed to add certificate to root store"); } // Parse CRL file (if any) let crl = if let Some(crl_file) = &conf.tls_revocation_list { let crl_file = std::fs::read(crl_file) .map_err(|e| encpasulate_error(e, "Failed to open / read CRL file!"))?; let parsed_crl = pem::parse(crl_file) .map_err(|e| encpasulate_error(e, "Failed to decode CRL file!"))?; Some(parsed_crl.contents) } else { None }; Ok(Self { upstream_cert_verifier: Box::new(AllowAnyAuthenticatedClient::new(store)), crl, }) } } impl ClientCertVerifier for CustomCertClientVerifier { fn offer_client_auth(&self) -> bool { true } fn client_auth_mandatory(&self) -> Option { Some(true) } fn client_auth_root_subjects(&self) -> Option { Some(vec![]) } fn verify_client_cert( &self, end_entity: &Certificate, intermediates: &[Certificate], now: SystemTime, ) -> Result { let (_rem, cert) = X509Certificate::from_der(&end_entity.0).expect("Failed to read certificate!"); // Check the certificates sent by the client has been revoked if let Some(crl) = &self.crl { let (_rem, crl) = CertificateRevocationList::from_der(crl).expect("Failed to read CRL!"); for revoked in crl.iter_revoked_certificates() { if revoked.user_certificate == cert.serial { log::error!( "Client attempted to use a revoked certificate! Serial={:?} Subject={}", cert.serial, cert.subject ); return Err(Error::AlertReceived(AlertDescription::CertificateRevoked)); } } } let result = self .upstream_cert_verifier .verify_client_cert(end_entity, intermediates, now); match result.as_ref() { Err(e) => log::error!( "FAILED authentication attempt from Serial={} / Subject={} : {}", cert.serial, cert.subject, e ), Ok(_) => log::info!( "SUCCESSFUL authentication attempt from Serial={} / Subject={}", cert.serial, cert.subject ), } result } }