From 715664273b6b8662539d69aba3b674d8ef4329ee Mon Sep 17 00:00:00 2001 From: link2xt Date: Fri, 13 May 2022 21:50:52 +0000 Subject: [PATCH] Repair encrypted mails turned into attachments Google Workspace has an option "Append footer" which appends standard footer defined by administrator to all outgoing messages. However, there is no plain text part in encrypted messages sent by Delta Chat, so Google Workspace turn the message into multipart/mixed MIME, where the first part is an empty plaintext part with a footer and the second part is the original encrypted message. This commit makes Delta Chat attempt to repair such messages, similarly to how it already repairs "Mixed Up" MIME structure in `get_mixed_up_mime`. --- CHANGELOG.md | 3 + src/e2ee.rs | 49 +++++++++++- .../message/google-workspace-mixed-up.eml | 78 +++++++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 test-data/message/google-workspace-mixed-up.eml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b81228ec..8e60a3f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ ### Removed - node: remove unmaintained coverage scripts +### Fixes +- repair encrypted mails "mixed up" by Google Workspace "Append footer" function #3315 + ## 1.80.0 diff --git a/src/e2ee.rs b/src/e2ee.rs index 55bfc162f..26fc5cf15 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -266,13 +266,40 @@ fn get_mixed_up_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail< } } +/// Returns a reference to the encrypted payload of a message turned into attachment. +/// +/// Google Workspace has an option "Append footer" which appends standard footer defined +/// by administrator to all outgoing messages. However, there is no plain text part in +/// encrypted messages sent by Delta Chat, so Google Workspace turns the message into +/// multipart/mixed MIME, where the first part is an empty plaintext part with a footer +/// and the second part is the original encrypted message. +fn get_attachment_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> { + if mail.ctype.mimetype != "multipart/mixed" { + return None; + } + if let [first_part, second_part] = &mail.subparts[..] { + if first_part.ctype.mimetype == "text/plain" + && second_part.ctype.mimetype == "multipart/encrypted" + { + get_autocrypt_mime(second_part) + } else { + None + } + } else { + None + } +} + async fn decrypt_if_autocrypt_message( context: &Context, mail: &ParsedMail<'_>, private_keyring: Keyring, public_keyring_for_validate: Keyring, ) -> Result, HashSet)>> { - let encrypted_data_part = match get_autocrypt_mime(mail).or_else(|| get_mixed_up_mime(mail)) { + let encrypted_data_part = match get_autocrypt_mime(mail) + .or_else(|| get_mixed_up_mime(mail)) + .or_else(|| get_attachment_mime(mail)) + { None => { // not an autocrypt mime message, abort and ignore return Ok(None); @@ -389,6 +416,7 @@ pub async fn ensure_secret_key_exists(context: &Context) -> Result { #[cfg(test)] mod tests { use crate::chat; + use crate::dc_receive_imf::dc_receive_imf; use crate::message::{Message, Viewtype}; use crate::param::Param; use crate::peerstate::ToSave; @@ -592,8 +620,8 @@ Sent with my Delta Chat Messenger: https://delta.chat"; assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); } - #[test] - fn test_mixed_up_mime() -> Result<()> { + #[async_std::test] + async fn test_mixed_up_mime() -> Result<()> { // "Mixed Up" mail as received when sending an encrypted // message using Delta Chat Desktop via ProtonMail IMAP/SMTP // Bridge. @@ -601,6 +629,7 @@ Sent with my Delta Chat Messenger: https://delta.chat"; let mail = mailparse::parse_mail(mixed_up_mime)?; assert!(get_autocrypt_mime(&mail).is_none()); assert!(get_mixed_up_mime(&mail).is_some()); + assert!(get_attachment_mime(&mail).is_none()); // Same "Mixed Up" mail repaired by Thunderbird 78.9.0. // @@ -611,6 +640,20 @@ Sent with my Delta Chat Messenger: https://delta.chat"; let mail = mailparse::parse_mail(repaired_mime)?; assert!(get_autocrypt_mime(&mail).is_some()); assert!(get_mixed_up_mime(&mail).is_none()); + assert!(get_attachment_mime(&mail).is_none()); + + // Another form of "Mixed Up" mail created by Google Workspace, + // where original message is turned into attachment to empty plaintext message. + let attachment_mime = include_bytes!("../test-data/message/google-workspace-mixed-up.eml"); + let mail = mailparse::parse_mail(attachment_mime)?; + assert!(get_autocrypt_mime(&mail).is_none()); + assert!(get_mixed_up_mime(&mail).is_none()); + assert!(get_attachment_mime(&mail).is_some()); + + let bob = TestContext::new_bob().await; + dc_receive_imf(&bob, attachment_mime, false).await?; + let msg = bob.get_last_msg().await; + assert_eq!(msg.text.as_deref(), Some("Hello from Thunderbird!")); Ok(()) } diff --git a/test-data/message/google-workspace-mixed-up.eml b/test-data/message/google-workspace-mixed-up.eml new file mode 100644 index 000000000..8ca6dcb17 --- /dev/null +++ b/test-data/message/google-workspace-mixed-up.eml @@ -0,0 +1,78 @@ +Subject: ... +MIME-Version: 1.0 +Date: Tue, 10 May 2022 08:24:11 +0000 +Chat-Version: 1.0 +Message-ID: +To: Bob +From: +Content-Type: multipart/mixed; boundary="000000000000b4907a05dea40c88" + +--000000000000b4907a05dea40c88 +Content-Type: text/plain; charset="UTF-8" +Content-Disposition: inline + + +-- + + + + + + + +--000000000000b4907a05dea40c88 +Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; + boundary="bi1vjiRd9u4EaEuno1ejliJactdnFl" + + +--bi1vjiRd9u4EaEuno1ejliJactdnFl +Content-Type: application/pgp-encrypted +Content-Description: PGP/MIME version identification + +Version: 1 + + +--bi1vjiRd9u4EaEuno1ejliJactdnFl +Content-Type: application/octet-stream; name="encrypted.asc" +Content-Description: OpenPGP encrypted message +Content-Disposition: inline; filename="encrypted.asc"; + +-----BEGIN PGP MESSAGE----- + +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 +=dcQr +-----END PGP MESSAGE----- + + +--bi1vjiRd9u4EaEuno1ejliJactdnFl-- + + +--000000000000b4907a05dea40c88--