From 4f5884d28b84086ca92d293af27b00266b1d342c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Sch=C3=B6nberger?= Date: Tue, 10 Sep 2024 20:59:48 +0200 Subject: [PATCH] Add config for additional SMTP TLS root certs --- .env.template | 4 ++++ src/config.rs | 26 ++++++++++++++++++++++++++ src/mail.rs | 4 ++++ 3 files changed, 34 insertions(+) diff --git a/.env.template b/.env.template index 0189cc7a..9bbe0fa3 100644 --- a/.env.template +++ b/.env.template @@ -525,6 +525,10 @@ ## Only use this as a last resort if you are not able to use a valid certificate. # SMTP_ACCEPT_INVALID_HOSTNAMES=false +## Accept additional root certs +## Paths to PEM files, separated by semicolons +# SMTP_ADDITIONAL_ROOT_CERTS= + ########################## ### Rocket settings ### ########################## diff --git a/src/config.rs b/src/config.rs index 489a229d..c99e05a2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ use std::process::exit; use std::sync::RwLock; use job_scheduler_ng::Schedule; +use lettre::transport::smtp::client::Certificate; use once_cell::sync::Lazy; use reqwest::Url; @@ -674,6 +675,8 @@ make_config! { smtp_accept_invalid_certs: bool, true, def, false; /// Accept Invalid Hostnames (Know the risks!) |> DANGEROUS: Allow invalid hostnames. This option introduces significant vulnerabilities to man-in-the-middle attacks! smtp_accept_invalid_hostnames: bool, true, def, false; + /// Accept additional root certs |> Paths to PEM files, separated by semicolons + smtp_additional_root_certs: String, true, option; }, /// Email 2FA Settings @@ -886,6 +889,11 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { if cfg._enable_email_2fa && cfg.email_token_size < 6 { err!("`EMAIL_TOKEN_SIZE` has a minimum size of 6") } + + if let Some(additional_root_cert_paths) = &cfg.smtp_additional_root_certs { + let certificates = load_certificates(additional_root_cert_paths.as_str())?; + *SMTP_ADDITIONAL_ROOT_CERTS.write().unwrap() = certificates; + } } if cfg._enable_email_2fa && !(cfg.smtp_host.is_some() || cfg.use_sendmail) { @@ -1018,6 +1026,24 @@ fn generate_smtp_img_src(embed_images: bool, domain: &str) -> String { } } +pub static SMTP_ADDITIONAL_ROOT_CERTS: RwLock> = RwLock::new(Vec::new()); + +fn load_certificates(smtp_additional_root_certs: &str) -> Result, Error> { + smtp_additional_root_certs + .split(';') + .filter(|path| !path.is_empty()) + .map(|path| { + let cert = match std::fs::read(path) { + Ok(cert) => cert, + Err(e) => { + err!(format!("Error loading additional SMTP root certificate file {path}.\n{e}")) + } + }; + Certificate::from_pem(&cert).or_else(|e| err!(format!("Error decoding certificate file {path}.\n{e}"))) + }) + .collect() +} + /// Generate the correct URL for the icon service. /// This will be used within icons.rs to call the external icon service. fn generate_icon_service_url(icon_service: &str) -> String { diff --git a/src/mail.rs b/src/mail.rs index 151554a1..17c50969 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -17,6 +17,7 @@ use crate::{ encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims, generate_verify_email_claims, }, + config::SMTP_ADDITIONAL_ROOT_CERTS, error::Error, CONFIG, }; @@ -46,6 +47,9 @@ fn smtp_transport() -> AsyncSmtpTransport { if CONFIG.smtp_accept_invalid_certs() { tls_parameters = tls_parameters.dangerous_accept_invalid_certs(true); } + for cert in &*SMTP_ADDITIONAL_ROOT_CERTS.read().unwrap() { + tls_parameters = tls_parameters.add_root_certificate(cert.clone()); + } let tls_parameters = tls_parameters.build().unwrap(); if CONFIG.smtp_security() == *"force_tls" {