diff --git a/crates/common/src/config/smtp/auth.rs b/crates/common/src/config/smtp/auth.rs index 69844191..a0901271 100644 --- a/crates/common/src/config/smtp/auth.rs +++ b/crates/common/src/config/smtp/auth.rs @@ -194,66 +194,6 @@ pub fn rsa_key_parse(private_key: &[u8]) -> trc::Result> { }) } -/*impl ArcSealer { - pub fn new(selector: String, domain: String, signature: DkimSignature) -> trc::Result { - let mut errors = vec![]; - if !signature.validate(&mut errors) { - return Err(trc::DkimEvent::BuildError - .reason("DKIM signature validation failed") - .details( - errors - .into_iter() - .map(|v| trc::Value::from(v.to_string())) - .collect::>(), - )); - } - - match signature { - DkimSignature::Dkim1Ed25519Sha256(signature) => { - let private_key = simple_pem_parse(&signature.private_key).ok_or_else(|| { - trc::DkimEvent::BuildError - .reason("Failed to parse ED25519 private key PEM") - .details("Invalid PEM format") - })?; - let key = - Ed25519Key::from_pkcs8_maybe_unchecked_der(&private_key).map_err(|err| { - trc::DkimEvent::BuildError - .reason(err) - .details("Failed to build ED25519 key") - })?; - - Ok(ArcSealer::Ed25519Sha256(build_dkim1_sealer( - domain, selector, signature, key, - ))) - } - DkimSignature::Dkim1RsaSha256(signature) => { - let key = PrivatePkcs1KeyDer::from_pem_slice(signature.private_key.as_bytes()) - .map(PrivateKeyDer::Pkcs1) - .or_else(|_| { - PrivatePkcs8KeyDer::from_pem_slice(signature.private_key.as_bytes()) - .map(PrivateKeyDer::Pkcs8) - }) - .map_err(|err| { - trc::DkimEvent::BuildError - .reason(err) - .details("Failed to build RSA key") - }) - .and_then(|key| { - RsaKey::::from_key_der(key).map_err(|err| { - trc::DkimEvent::BuildError - .reason(err) - .details("Failed to build RSA key") - }) - })?; - - Ok(ArcSealer::RsaSha256(build_dkim1_sealer( - domain, selector, signature, key, - ))) - } - } - } -}*/ - pub fn simple_pem_parse(contents: &str) -> Option> { let mut contents = contents.as_bytes().iter().copied(); let mut base64 = vec![]; @@ -338,59 +278,6 @@ fn build_dkim1_signer( signer } -/*fn build_dkim1_sealer>( - domain: String, - selector: String, - mut signature: Dkim1Signature, - key: T, -) -> mail_auth::arc::ArcSealer { - if !signature - .headers - .iter() - .any(|h| h.eq_ignore_ascii_case("DKIM-Signature")) - { - signature - .headers - .push_unchecked("DKIM-Signature".to_string()); - } - - let mut sealer = mail_auth::arc::ArcSealer::from_key(key) - .domain(domain) - .selector(selector) - .headers(signature.headers); - - match signature.canonicalization { - enums::DkimCanonicalization::RelaxedRelaxed => { - sealer = sealer - .body_canonicalization(Canonicalization::Relaxed) - .header_canonicalization(Canonicalization::Relaxed); - } - enums::DkimCanonicalization::SimpleSimple => { - sealer = sealer - .body_canonicalization(Canonicalization::Simple) - .header_canonicalization(Canonicalization::Simple); - } - enums::DkimCanonicalization::RelaxedSimple => { - sealer = sealer - .body_canonicalization(Canonicalization::Simple) - .header_canonicalization(Canonicalization::Relaxed); - } - enums::DkimCanonicalization::SimpleRelaxed => { - sealer = sealer - .body_canonicalization(Canonicalization::Relaxed) - .header_canonicalization(Canonicalization::Simple); - } - } - - if let Some(expire) = signature.expire { - sealer = sealer.expiration(expire.into_inner().as_secs()); - } - - sealer -} - -*/ - impl<'x> TryFrom> for VerifyStrategy { type Error = (); diff --git a/crates/common/src/config/smtp/mod.rs b/crates/common/src/config/smtp/mod.rs index 7d98b069..72c94017 100644 --- a/crates/common/src/config/smtp/mod.rs +++ b/crates/common/src/config/smtp/mod.rs @@ -14,8 +14,11 @@ use self::{ auth::MailAuthConfig, queue::QueueConfig, report::ReportConfig, resolver::Resolvers, session::SessionConfig, }; -use crate::expr::Expression; -use registry::{schema::structs::Rate, types::id::ObjectId}; +use crate::{config::smtp::queue::RequireOptional, expr::Expression}; +use registry::{ + schema::{properties::ObjectType, structs::Rate}, + types::id::ObjectId, +}; use store::registry::bootstrap::Bootstrap; #[derive(Clone)] @@ -49,12 +52,31 @@ pub const THROTTLE_HELO_DOMAIN: u16 = 1 << 9; impl SmtpConfig { pub async fn parse(bp: &mut Bootstrap) -> Self { - Self { + let config = Self { session: SessionConfig::parse(bp).await, queue: QueueConfig::parse(bp).await, resolvers: Resolvers::parse(bp).await, mail_auth: MailAuthConfig::parse(bp).await, report: ReportConfig::parse(bp).await, + }; + + if !config.resolvers.dnssec_available + && config + .queue + .tls_strategy + .values() + .any(|t| !matches!(t.dane, RequireOptional::Disable)) + { + bp.build_warning( + ObjectType::DnsResolver.singleton(), + concat!( + "The configured DNS resolver cannot validate DNSSEC. ", + "DANE has been disabled to avoid deferring mail. ", + "Configure a DNSSEC-validating resolver to enable DANE." + ), + ); } + + config } } diff --git a/crates/common/src/config/smtp/resolver.rs b/crates/common/src/config/smtp/resolver.rs index 20f11604..0b72642a 100644 --- a/crates/common/src/config/smtp/resolver.rs +++ b/crates/common/src/config/smtp/resolver.rs @@ -13,14 +13,13 @@ use mail_auth::{ ResolverConfig, ResolverOpts, }, net::runtime::TokioRuntimeProvider, - proto::rr::{Name, RecordType}, system_conf::read_system_conf, }, }; use registry::schema::{ - enums::{DnsResolverProtocol, MtaRequiredOrOptional, PolicyEnforcement}, + enums::{DnsResolverProtocol, PolicyEnforcement}, prelude::ObjectType, - structs::{DnsResolver, MtaSts, MtaTlsStrategy, SystemSettings}, + structs::{DnsResolver, MtaSts, SystemSettings}, }; use serde::{Deserialize, Serialize}; use std::{ @@ -223,40 +222,24 @@ impl Resolvers { .expect("Failed to build DNSSEC resolver"), }; - let uses_dane = bp - .list_infallible::() - .await - .iter() - .any(|obj| obj.object.dane != MtaRequiredOrOptional::Disable); - - let dnssec_available = if uses_dane && !cfg!(any(test, feature = "test_mode")) { - let available = dnssec_capable(&dnssec.resolver).await; - if !available { - bp.build_warning( - ObjectType::DnsResolver.singleton(), - concat!( - "The configured DNS resolver cannot validate DNSSEC. ", - "DANE has been disabled to avoid deferring mail. ", - "Configure a DNSSEC-validating resolver to enable DANE." - ), - ); - } - available - } else { - true - }; - Resolvers { dns: MessageAuthenticator::new(resolver_config, opts).unwrap(), + #[cfg(not(feature = "test_mode"))] + dnssec_available: dnssec_capable(&dnssec.resolver).await, + #[cfg(feature = "test_mode")] + dnssec_available: true, dnssec, - dnssec_available, } } } +#[cfg(not(feature = "test_mode"))] async fn dnssec_capable(resolver: &TokioResolver) -> bool { resolver - .lookup(Name::root(), RecordType::DNSKEY) + .lookup( + hickory_proto::rr::Name::root(), + hickory_proto::rr::RecordType::DNSKEY, + ) .await .is_ok_and(|lookup| { lookup