From 013c5802015698239fa0cd39bcbed7802ad8b85e Mon Sep 17 00:00:00 2001 From: mdecimus Date: Sat, 11 Jan 2025 12:34:48 +0100 Subject: [PATCH] Avoid double encrypting PGP parts encoded as plain text (fixes #1083) --- crates/jmap/src/email/crypto.rs | 36 ++++++- tests/resources/crypto/is_encrypted.txt | 134 +++++++++++++++++++++--- tests/src/jmap/crypto.rs | 2 +- 3 files changed, 156 insertions(+), 16 deletions(-) diff --git a/crates/jmap/src/email/crypto.rs b/crates/jmap/src/email/crypto.rs index fe05f4f05..fcdd826a8 100644 --- a/crates/jmap/src/email/crypto.rs +++ b/crates/jmap/src/email/crypto.rs @@ -17,7 +17,7 @@ use common::{auth::AccessToken, Server}; use directory::backend::internal::manage; use jmap_proto::types::{collection::Collection, property::Property}; use mail_builder::{encoders::base64::base64_encode_mime, mime::make_boundary}; -use mail_parser::{decoders::base64::base64_decode, Message, MessageParser, MimeHeaders}; +use mail_parser::{decoders::base64::base64_decode, Message, MessageParser, MimeHeaders, PartType}; use openpgp::{ parse::Parse, serialize::stream, @@ -372,7 +372,7 @@ impl EncryptMessage for Message<'_> { } fn is_encrypted(&self) -> bool { - self.content_type().is_some_and(|ct| { + if self.content_type().is_some_and(|ct| { let main_type = ct.c_type.as_ref(); let sub_type = ct .c_subtype @@ -390,7 +390,37 @@ impl EncryptMessage for Message<'_> { })))) || (main_type.eq_ignore_ascii_case("multipart") && sub_type.eq_ignore_ascii_case("encrypted")) - }) + }) { + return true; + } + + if self.parts.len() <= 2 { + let mut text_part = None; + let mut is_multipart = false; + + for part in &self.parts { + match &part.body { + PartType::Text(text) => { + text_part = Some(text.as_ref()); + } + PartType::Multipart(_) => { + is_multipart = true; + } + _ => (), + } + } + + match text_part { + Some(text) if self.parts.len() == 1 || is_multipart => { + if text.trim_start().starts_with("-----BEGIN PGP MESSAGE-----") { + return true; + } + } + _ => (), + } + } + + false } } diff --git a/tests/resources/crypto/is_encrypted.txt b/tests/resources/crypto/is_encrypted.txt index 835e3002a..1e73b4590 100644 --- a/tests/resources/crypto/is_encrypted.txt +++ b/tests/resources/crypto/is_encrypted.txt @@ -4,14 +4,14 @@ Content-Type: multipart/encrypted; boundary="17778885806d6b28_46555a54adda6fa5_d83705f326a6b951" body ---- +!!! Subject: FALSE Content-Type: multipart/signed; protocol="application/pkcs7-signature"; boundary="17778885806d6b28_46555a54adda6fa5_d83705f326a6b951" body ---- +!!! Subject: TRUE Content-Type: application/pkcs7-mime; name="smime.p7m"; @@ -21,7 +21,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/pkcs7-signature; name="smime.p7s"; smime-type="signed-data" @@ -30,7 +30,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream; name="smime.p7m" @@ -39,14 +39,14 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream; name="smime.p7m" Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream Content-Disposition: attachment; @@ -54,7 +54,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream Content-Disposition: attachment; @@ -62,7 +62,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream Content-Disposition: attachment; @@ -70,7 +70,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: TRUE Content-Type: application/octet-stream Content-Disposition: attachment; @@ -78,7 +78,7 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: FALSE Content-Type: application/octet-stream Content-Disposition: attachment; @@ -86,16 +86,126 @@ Content-Disposition: attachment; Content-Transfer-Encoding: base64 body ---- +!!! Subject: FALSE Content-Type: application/octet-stream Content-Disposition: attachment Content-Transfer-Encoding: base64 body ---- +!!! Subject: FALSE Content-Type: multipart/mixed; boundary="17778885806d6b28_46555a54adda6fa5_d83705f326a6b951" body +!!! +Subject: TRUE +Content-Type: multipart/mixed; boundary="----sinikael-?=_1-17364333937490.28304631087789" + +------sinikael-?=_1-17364333937490.28304631087789 +Content-Type: text/plain +Content-Transfer-Encoding: quoted-printable + + +-----BEGIN PGP MESSAGE----- +Version: Mailvelope v6.0.1 +Comment: https://mailvelope.com + +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 +5fkw+PwLe2vPtuObvuY+ezbJGb1jV0tWZFXF +=3DaQyM +-----END PGP MESSAGE----- + +------sinikael-?=_1-17364333937490.28304631087789-- +!!! +Subject: TRUE +Content-Type: text/plain +Content-Transfer-Encoding: quoted-printable + + + +-----BEGIN PGP MESSAGE----- +Version: Mailvelope v6.0.1 +Comment: https://mailvelope.com + +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 +5fkw+PwLe2vPtuObvuY+ezbJGb1jV0tWZFXF +=3DaQyM +-----END PGP MESSAGE----- diff --git a/tests/src/jmap/crypto.rs b/tests/src/jmap/crypto.rs index 18aeb5773..8ae333852 100644 --- a/tests/src/jmap/crypto.rs +++ b/tests/src/jmap/crypto.rs @@ -241,7 +241,7 @@ pub fn check_is_encrypted() { ) .unwrap(); - for raw_message in messages.split("---") { + for raw_message in messages.split("!!!") { let is_encrypted = raw_message.contains("TRUE"); let message = MessageParser::new() .parse(raw_message.trim().as_bytes())