diff --git a/src/crypto.rs b/src/crypto.rs index 2c84748..5a6f2a6 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -8,14 +8,17 @@ use std::sync::Arc; use anyhow; use sequoia_openpgp::parse::Parse; +use sequoia_openpgp::policy::StandardPolicy; use sequoia_openpgp::{Fingerprint, Cert}; use sequoia_openpgp::cert::raw::RawCertParser; use sequoia_cert_store::{Store, CertStore, LazyCert}; use sequoia_wot::{self as wot}; use wot::store::Store as _; +use wot::{Depth, Path}; use crate::types::errors::HuskError; use crate::types::husk_context::HuskContext; +use crate::types::introducer::Introducer; pub fn get_local_trust_root(cert_store: &CertStore) -> anyhow::Result { @@ -71,7 +74,7 @@ pub fn get_certificates<'hc>(context: &HuskContext<'hc>, email: &str) if let Some(userid) = some_userid { let paths = n.authenticate(userid, &fpr, wot::FULLY_TRUSTED); if paths.len() > 0 { - log::debug!("authenticated!!!"); + log::debug!("{} authenticated!!!", email); for (path, _) in paths.iter() { if let Ok(cert) = cert_store.lookup_by_cert_fpr(&path.target().fingerprint()) { result.push(cert); @@ -82,3 +85,43 @@ pub fn get_certificates<'hc>(context: &HuskContext<'hc>, email: &str) } result } + +pub fn get_introducers<'c>(cert_store: &CertStore<'c>, policy: &StandardPolicy, local_trust_root: Fingerprint) -> Vec { + + let mut result = Vec::new(); + + let trust_roots = vec![(local_trust_root, wot::FULLY_TRUSTED)]; + let wot_store = wot::store::CertStore::from_store(cert_store, policy, None); + let n = wot::NetworkBuilder::rooted(&wot_store, &*trust_roots) + .build(); + + n.certified_userids().iter().for_each(|(fpr, user_id)| { + let paths = n.authenticate(user_id, fpr, wot::FULLY_TRUSTED); + + // find the longest path (highest trust depth) + let mut depth = Depth::Limit(0); + let mut target: Option<&Path> = None; + paths.iter().for_each(|(path, amount)| { + if *amount == wot::FULLY_TRUSTED { + let d = path.residual_depth(); + if d > depth { + depth = d; + target = Some(path); + } + } + }); + + if depth > Depth::Limit(0) { + if let Some(p) = target { + if let Some(c) = p.certifications().last() { + result.push( Introducer { + cert: p.target().clone(), + certification: c.clone(), + }); + } + } + } + }); + + result +} diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index d552ad9..90ac1a9 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -93,6 +93,7 @@ impl Daemon { // setup Context match HuskContext::new(&config) { Ok(context) => { + log::debug!("found introducers: {:?}", context.introducers); cx.data = Some(context).take(); Status::Continue }, @@ -149,10 +150,18 @@ impl Daemon { } } - async fn handle_data(_cx: &mut Context>) -> Status { + async fn handle_data(cx: &mut Context>) -> Status { log::debug!("DATA"); - Status::Continue + if let Some(ref mut context) = cx.data { + // XXX: check for early accepting + // if there are no recipients with authenticated certificates + // tell the MTA that this milter is done and the mail can be further + // processed. + Status::Continue + } else { + Status::Tempfail + } } async fn handle_header(_cx: &mut Context>, name: CString, value: CString) -> Status { diff --git a/src/types/husk_context.rs b/src/types/husk_context.rs index 234d524..5e6e7e6 100644 --- a/src/types/husk_context.rs +++ b/src/types/husk_context.rs @@ -14,10 +14,13 @@ use sequoia_directories::Home; use crate::{config::HuskConfig, crypto}; use crate::types::mail_context::MailContext; +use crate::types::introducer::Introducer; + pub struct HuskContext<'hc> { pub policy: StandardPolicy<'hc>, pub cert_store: CertStore<'hc>, pub local_trust_root: Cert, + pub introducers: Vec, pub mail: MailContext<'hc>, } @@ -33,11 +36,13 @@ impl<'hc> HuskContext<'hc> { let policy = StandardPolicy::new(); let local_trust_root = crypto::get_local_trust_root(&cert_store)?; + let introducers = crypto::get_introducers(&cert_store, &policy, local_trust_root.fingerprint()); Ok(HuskContext { policy, cert_store, local_trust_root, + introducers, mail: MailContext::new(), }) } diff --git a/src/types/introducer.rs b/src/types/introducer.rs new file mode 100644 index 0000000..fe0e349 --- /dev/null +++ b/src/types/introducer.rs @@ -0,0 +1,33 @@ +// +// husk-milter +// +// introducers +// + +use sequoia_wot as wot; +use wot::{CertSynopsis, Certification}; +use sequoia_openpgp::{policy::StandardPolicy, Cert, KeyID}; + +#[derive(Debug, Clone)] +pub struct Introducer { + pub cert: CertSynopsis, + pub certification: Certification, +} + +impl Introducer { + + pub fn can_introduce(&self, email: &str) -> bool { + if let Some(regexset) = self.certification.regular_expressions() { + if regexset.matches_everything() { + // return false, because unlimited certifications are + // ignored. + false + } else { + regexset.is_match(email) + } + } else { + false + } + } +} + diff --git a/src/types/mod.rs b/src/types/mod.rs index 4ba6c8e..16cda95 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,3 +2,4 @@ pub mod husk_context; pub mod mail_context; pub mod errors; pub mod recipient; +pub mod introducer;