From 4d915f9800435bf13057d41af8d708abd34dbfa8 Mon Sep 17 00:00:00 2001 From: adb Date: Tue, 28 Jan 2025 05:48:07 +0100 Subject: [PATCH] improve secure-join message detection (#473) --- CHANGELOG.md | 3 ++ chatmaild/src/chatmaild/filtermail.py | 29 +++++++++++++++---- .../tests/mail-data/securejoin-vc-fake.eml | 21 ++++++++++++++ .../tests/mail-data/securejoin-vc.eml | 21 ++++++++++++++ .../src/chatmaild/tests/test_filtermail.py | 15 ++++++++++ 5 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 chatmaild/src/chatmaild/tests/mail-data/securejoin-vc-fake.eml create mode 100644 chatmaild/src/chatmaild/tests/mail-data/securejoin-vc.eml diff --git a/CHANGELOG.md b/CHANGELOG.md index a9786355..f5c13806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ - migration guide: let opendkim own the DKIM keys directory ([#468](https://github.com/deltachat/chatmail/pull/468)) +- improve secure-join message detection + ([#473](https://github.com/deltachat/chatmail/pull/473)) + ## 1.5.0 2024-12-20 - cmdeploy dns: always show recommended DNS records diff --git a/chatmaild/src/chatmaild/filtermail.py b/chatmaild/src/chatmaild/filtermail.py index 47ab791a..fcbb9c0b 100644 --- a/chatmaild/src/chatmaild/filtermail.py +++ b/chatmaild/src/chatmaild/filtermail.py @@ -100,6 +100,27 @@ def check_armored_payload(payload: str): return False +def is_securejoin(message): + if message.get("secure-join") not in ["vc-request", "vg-request"]: + return False + if not message.is_multipart(): + return False + parts_count = 0 + for part in message.iter_parts(): + parts_count += 1 + if parts_count > 1: + return False + if part.is_multipart(): + return False + if part.get_content_type() != "text/plain": + return False + + payload = part.get_payload().strip().lower() + if payload not in ("secure-join: vc-request", "secure-join: vg-request"): + return False + return True + + def check_encrypted(message): """Check that the message is an OpenPGP-encrypted message. @@ -203,11 +224,7 @@ class BeforeQueueHandler: passthrough_recipients = self.config.passthrough_recipients - is_securejoin = message.get("secure-join") in [ - "vc-request", - "vg-request", - ] - if is_securejoin: + if mail_encrypted or is_securejoin(message): return for recipient in envelope.rcpt_tos: @@ -222,7 +239,7 @@ class BeforeQueueHandler: _recipient_addr, recipient_domain = res is_outgoing = recipient_domain != envelope_from_domain - if is_outgoing and not mail_encrypted: + if is_outgoing: print("Rejected unencrypted mail.", file=sys.stderr) return f"500 Invalid unencrypted mail to <{recipient}>" diff --git a/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc-fake.eml b/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc-fake.eml new file mode 100644 index 00000000..73e43798 --- /dev/null +++ b/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc-fake.eml @@ -0,0 +1,21 @@ +Subject: Message from {from_addr} +From: <{from_addr}> +To: <{to_addr}> +Date: Sun, 15 Oct 2023 16:43:25 +0000 +Message-ID: +Chat-Version: 1.0 +Secure-Join: vc-request +Secure-Join-Invitenumber: RANDOM-TOKEN +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi" + + +--Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi +Content-Type: text/plain; charset=utf-8 + +Buy viagra! + + +--Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi-- + + diff --git a/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc.eml b/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc.eml new file mode 100644 index 00000000..a0b14966 --- /dev/null +++ b/chatmaild/src/chatmaild/tests/mail-data/securejoin-vc.eml @@ -0,0 +1,21 @@ +Subject: Message from {from_addr} +From: <{from_addr}> +To: <{to_addr}> +Date: Sun, 15 Oct 2023 16:43:25 +0000 +Message-ID: +Chat-Version: 1.0 +Secure-Join: vc-request +Secure-Join-Invitenumber: RANDOM-TOKEN +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi" + + +--Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi +Content-Type: text/plain; charset=utf-8 + +Secure-Join: vc-request + + +--Gl92xgZjOShJ5PGHntqYkoo2OK2Dvi-- + + diff --git a/chatmaild/src/chatmaild/tests/test_filtermail.py b/chatmaild/src/chatmaild/tests/test_filtermail.py index 2bd5b2b1..989378c4 100644 --- a/chatmaild/src/chatmaild/tests/test_filtermail.py +++ b/chatmaild/src/chatmaild/tests/test_filtermail.py @@ -6,6 +6,7 @@ from chatmaild.filtermail import ( check_armored_payload, check_encrypted, common_encrypted_subjects, + is_securejoin, ) @@ -55,6 +56,20 @@ def test_filtermail_no_encryption_detection(maildata): assert not check_encrypted(msg) +def test_filtermail_securejoin_detection(maildata): + msg = maildata( + "securejoin-vc.eml", from_addr="some@example.org", to_addr="other@example.org" + ) + assert is_securejoin(msg) + + msg = maildata( + "securejoin-vc-fake.eml", + from_addr="some@example.org", + to_addr="other@example.org", + ) + assert not is_securejoin(msg) + + def test_filtermail_encryption_detection(maildata): for subject in common_encrypted_subjects: msg = maildata(