Check for encryption
- collect body of incoming mail. - check if incoming mail is already encrypted - dont touch it if it is. - check if recipients of that mail can encrypt.
This commit is contained in:
+31
-2
@@ -7,9 +7,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow;
|
use anyhow;
|
||||||
use sequoia_openpgp::parse::Parse;
|
use bytes::Bytes;
|
||||||
|
use sequoia_openpgp::parse::{PacketParser, PacketParserResult, Parse};
|
||||||
use sequoia_openpgp::policy::StandardPolicy;
|
use sequoia_openpgp::policy::StandardPolicy;
|
||||||
use sequoia_openpgp::{Fingerprint, Cert};
|
use sequoia_openpgp::{Fingerprint, Cert, Packet};
|
||||||
use sequoia_openpgp::cert::raw::RawCertParser;
|
use sequoia_openpgp::cert::raw::RawCertParser;
|
||||||
use sequoia_cert_store::{Store, CertStore, LazyCert};
|
use sequoia_cert_store::{Store, CertStore, LazyCert};
|
||||||
use sequoia_wot::{self as wot};
|
use sequoia_wot::{self as wot};
|
||||||
@@ -125,3 +126,31 @@ pub fn get_introducers<'c>(cert_store: &CertStore<'c>, policy: &StandardPolicy,
|
|||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_encrypted(body: &Bytes) -> bool {
|
||||||
|
|
||||||
|
let mut ppr = match PacketParser::from_bytes(body) {
|
||||||
|
Ok(pp) => pp,
|
||||||
|
Err(_) => return false, // no OpenPGP artifacts
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut encrypted: bool = false;
|
||||||
|
|
||||||
|
while let PacketParserResult::Some(pp) = ppr {
|
||||||
|
if let Packet::SEIP(_) = pp.packet {
|
||||||
|
encrypted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the whole message, don't stop at first SEIP
|
||||||
|
ppr = match pp.recurse() {
|
||||||
|
Ok((_, next_ppr)) => next_ppr,
|
||||||
|
Err(_) => return false, // broken artifact -> not encrypted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let PacketParserResult::EOF(eof) = ppr {
|
||||||
|
if eof.is_message().is_ok() && encrypted {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|||||||
+34
-3
@@ -27,6 +27,8 @@ use indymilter::{
|
|||||||
use crate::types::husk_context::HuskContext;
|
use crate::types::husk_context::HuskContext;
|
||||||
use crate::config::{HuskConfig, HuskConfigContainer};
|
use crate::config::{HuskConfig, HuskConfigContainer};
|
||||||
use crate::mail;
|
use crate::mail;
|
||||||
|
use crate::crypto;
|
||||||
|
use crate::types::mail_context::ProtectionPossibility;
|
||||||
|
|
||||||
pub struct Daemon { }
|
pub struct Daemon { }
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ impl Daemon {
|
|||||||
async fn handle_data(cx: &mut Context<HuskContext<'_>>) -> Status {
|
async fn handle_data(cx: &mut Context<HuskContext<'_>>) -> Status {
|
||||||
log::debug!("DATA");
|
log::debug!("DATA");
|
||||||
|
|
||||||
if let Some(ref mut context) = cx.data {
|
if let Some(ref mut _context) = cx.data {
|
||||||
// XXX: check for early accepting
|
// XXX: check for early accepting
|
||||||
// if there are no recipients with authenticated certificates
|
// if there are no recipients with authenticated certificates
|
||||||
// tell the MTA that this milter is done and the mail can be further
|
// tell the MTA that this milter is done and the mail can be further
|
||||||
@@ -176,10 +178,15 @@ impl Daemon {
|
|||||||
Status::Continue
|
Status::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_body(_cx: &mut Context<HuskContext<'_>>, chunk: Bytes) -> Status {
|
async fn handle_body(cx: &mut Context<HuskContext<'_>>, chunk: Bytes) -> Status {
|
||||||
log::debug!("BODY: chunk with {:?} bytes received", &chunk.len());
|
log::debug!("BODY: chunk with {:?} bytes received", &chunk.len());
|
||||||
|
|
||||||
Status::Continue
|
if let Some(ref mut context) = cx.data {
|
||||||
|
context.mail.add_chunk(chunk);
|
||||||
|
Status::Continue
|
||||||
|
} else {
|
||||||
|
Status::Tempfail
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_eom(cx: &mut EomContext<HuskContext<'_>>) -> Status {
|
async fn handle_eom(cx: &mut EomContext<HuskContext<'_>>) -> Status {
|
||||||
@@ -188,6 +195,30 @@ impl Daemon {
|
|||||||
if let Some(ref mut context) = cx.data {
|
if let Some(ref mut context) = cx.data {
|
||||||
log::debug!("Mail from {:?} complete", context.mail.sender);
|
log::debug!("Mail from {:?} complete", context.mail.sender);
|
||||||
log::debug!("recipients: {:?}", context.mail.recipients);
|
log::debug!("recipients: {:?}", context.mail.recipients);
|
||||||
|
log::debug!("body size: {}", context.mail.body_size);
|
||||||
|
|
||||||
|
let body = context.mail.collect_body();
|
||||||
|
|
||||||
|
// check if mail is already encrypted. If it is, keep it as such.
|
||||||
|
if crypto::is_encrypted(&body) {
|
||||||
|
log::info!("encrypted mail detected");
|
||||||
|
return Status::Accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// possible protection
|
||||||
|
// none -> accept
|
||||||
|
// partial -> accept (later: split)
|
||||||
|
// full -> encrypt
|
||||||
|
// check mailbody
|
||||||
|
// is encrypted -> accept
|
||||||
|
// else -> encrypt
|
||||||
|
|
||||||
|
let protection = ProtectionPossibility::from(&context.mail);
|
||||||
|
|
||||||
|
if protection == ProtectionPossibility::Full {
|
||||||
|
// encrypt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Status::Continue
|
Status::Continue
|
||||||
|
|||||||
@@ -5,16 +5,25 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use bytes::{Bytes, BytesMut};
|
||||||
use sequoia_cert_store::LazyCert;
|
use sequoia_cert_store::LazyCert;
|
||||||
|
|
||||||
use crate::types::recipient::Recipient;
|
use crate::types::recipient::Recipient;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ProtectionPossibility {
|
||||||
|
NoProtection,
|
||||||
|
Partial,
|
||||||
|
Full
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct MailContext<'mc> {
|
pub struct MailContext<'mc> {
|
||||||
pub sender: Option<String>,
|
pub sender: Option<String>,
|
||||||
pub recipients: Vec<Recipient<'mc>>,
|
pub recipients: Vec<Recipient<'mc>>,
|
||||||
// header
|
// header
|
||||||
// body
|
body: Vec<Bytes>,
|
||||||
// body size
|
pub body_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mc> MailContext<'mc> {
|
impl<'mc> MailContext<'mc> {
|
||||||
@@ -23,6 +32,8 @@ impl<'mc> MailContext<'mc> {
|
|||||||
MailContext {
|
MailContext {
|
||||||
sender: None,
|
sender: None,
|
||||||
recipients: Vec::new(),
|
recipients: Vec::new(),
|
||||||
|
body: Vec::new(),
|
||||||
|
body_size: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,4 +50,39 @@ impl<'mc> MailContext<'mc> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_chunk(&mut self, chunk: Bytes) {
|
||||||
|
self.body_size += chunk.len();
|
||||||
|
self.body.push(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_body(&self) -> Bytes {
|
||||||
|
let mut buffer = BytesMut::with_capacity(self.body_size);
|
||||||
|
for chunk in &self.body {
|
||||||
|
buffer.extend_from_slice(&chunk[..]);
|
||||||
|
}
|
||||||
|
buffer.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MailContext<'_>> for ProtectionPossibility {
|
||||||
|
fn from(mail_context: &MailContext<'_>) -> ProtectionPossibility {
|
||||||
|
let mut can_encrypt = false;
|
||||||
|
let mut cannot_encrypt = false;
|
||||||
|
|
||||||
|
for rcpt in mail_context.recipients.iter() {
|
||||||
|
if rcpt.can_encrypt() {
|
||||||
|
can_encrypt = true;
|
||||||
|
} else {
|
||||||
|
cannot_encrypt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match (can_encrypt, cannot_encrypt) {
|
||||||
|
(true, false) => ProtectionPossibility::Full,
|
||||||
|
(true, true) => ProtectionPossibility::Partial,
|
||||||
|
(false, true) => ProtectionPossibility::NoProtection,
|
||||||
|
(false, false) => ProtectionPossibility::NoProtection, // this should not occure
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,13 @@ impl TryFrom<CString> for Recipient<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Recipient<'_> {
|
||||||
|
pub fn can_encrypt(&self) -> bool {
|
||||||
|
if self.certs.len() == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user