From 1a9b71d1389093080ea4352046738780a44138ed Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 27 Dec 2022 22:11:56 +0100 Subject: [PATCH] Support Sendmail (#12) * Refactor Postfix pattern matching * Preliminary support for Sendmail (fixes #11) * Convert regexes into constants * Update regex to support multiple recipients with Sendmail, add test * Update README for Sendmail support --- README.md | 13 ++-- test/TlsIconTest.php | 139 +++++++++++++++++++++++++++++++++++++++---- tls_icon.php | 20 +++---- 3 files changed, 144 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index a5dd179..8818eba 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # Roundcube TLS Icon Displays a small icon after the subject line that displays the (presumed) encryption state of received mails. -This plugin parses the "Received" header for the last hop and checks if TLS was used. This requires TLS logging in the receiving MTA. +This plugin parses the "Received" header for the last hop and checks if TLS was used. This requires TLS logging in the +receiving MTA. -In Postfix this can be enabled by setting [`smtpd_tls_received_header = yes`](https://www.postfix.org/postconf.5.html#smtpd_tls_received_header). The regex used to parse the header has only been tested against Postfix. +In Postfix this can be enabled by +setting [`smtpd_tls_received_header = yes`](https://www.postfix.org/postconf.5.html#smtpd_tls_received_header). Sendmail +should work out of the box. Other MTAs have not been explicitly tested. -Note that while this talks about "encryption", this does not imply security. An encrypted mail may still be insecure, mostly because mailservers generally use "opportunistic TLS", where MITM attacks are possible. -This also only validates the last hop of an email - some emails may run through multiple hops and we don't know anything about the security of these. +Note that while this talks about "encryption", this does not imply security. An encrypted mail may still be insecure, +mostly because mailservers generally use "opportunistic TLS", where MITM attacks are possible. +This also only validates the last hop of an email - some emails may run through multiple hops and we don't know anything +about the security of these. Inspired by [roundcube-easy-unsubscribe](https://github.com/SS88UK/roundcube-easy-unsubscribe) diff --git a/test/TlsIconTest.php b/test/TlsIconTest.php index 51d7494..0218452 100644 --- a/test/TlsIconTest.php +++ b/test/TlsIconTest.php @@ -24,6 +24,13 @@ final class TlsIconTest extends TestCase /** @var string */ private $strInternal = ''; + /** @var string */ + private $strSendmailCryptedTlsv13WithCipherNoVerify = ''; + + /** @var string */ + private $strSendmailCryptedTlsv12WithCipherVerify = ''; + + public function testInstance() { $o = new tls_icon(); @@ -62,7 +69,7 @@ public function testMessageHeadersNoMatching() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'my header', ] @@ -75,7 +82,7 @@ public function testMessageHeadersNoMatching() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'my header', ] @@ -92,7 +99,7 @@ public function testMessageHeadersTlsWithCipher() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) @@ -108,7 +115,7 @@ public function testMessageHeadersTlsWithCipher() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) @@ -128,7 +135,7 @@ public function testMessageHeadersTls() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204]) (using TLSv1.2) (No client certificate requested) @@ -144,7 +151,7 @@ public function testMessageHeadersTls() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'from smtp.github.com (out-21.smtp.github.com [192.30.252.204]) (using TLSv1.2) (No client certificate requested) @@ -164,7 +171,7 @@ public function testMessageHeadersInternal() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'by aaa.bbb.ccc (Postfix, from userid 0) id A70248414D5; Sun, 26 Apr 2020 16:49:01 +0200 (CEST)', @@ -178,7 +185,7 @@ public function testMessageHeadersInternal() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => 'by aaa.bbb.ccc (Postfix, from userid 0) id A70248414D5; Sun, 26 Apr 2020 16:49:01 +0200 (CEST)', @@ -205,7 +212,7 @@ public function testMessageHeadersMultiFromWithConfig() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => $inputHeaders, ] @@ -218,7 +225,7 @@ public function testMessageHeadersMultiFromWithConfig() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => $inputHeaders, ] @@ -244,7 +251,7 @@ public function testMessageHeadersMultiFromWithBadConfig() 'value' => 'Sent to you', ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => $inputHeaders, ] @@ -257,11 +264,119 @@ public function testMessageHeadersMultiFromWithBadConfig() 'html' => 1, ], ], - 'headers' => (object) [ + 'headers' => (object)[ 'others' => [ 'received' => $inputHeaders, ] ] ], $headersProcessed); } + + public function testSendmailTLS13NoVerify() + { + $o = new tls_icon(); + $headersProcessed = $o->message_headers([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you', + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from 69-171-232-143.mail-mail.facebook.com (69-171-232-143.mail-mail.facebook.com [69.171.232.143]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BI73F8b1489360 + (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO) + for ; Sun, 18 Dec 2022 07:03:16 GMT', + ] + ] + ]); + $this->assertEquals([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv13WithCipherNoVerify, + 'html' => 1, + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from 69-171-232-143.mail-mail.facebook.com (69-171-232-143.mail-mail.facebook.com [69.171.232.143]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BI73F8b1489360 + (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO) + for ; Sun, 18 Dec 2022 07:03:16 GMT', + ] + ] + ], $headersProcessed); + } + + public function testSendmailTLS12WithVerify() + { + $o = new tls_icon(); + $headersProcessed = $o->message_headers([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you', + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from smtp.github.com (out-18.smtp.github.com [192.30.252.201]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BGMf4uY685293 + (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) + for ; Fri, 16 Dec 2022 22:41:05 GMT', + ] + ] + ]); + $this->assertEquals([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv12WithCipherVerify, + 'html' => 1, + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from smtp.github.com (out-18.smtp.github.com [192.30.252.201]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BGMf4uY685293 + (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) + for ; Fri, 16 Dec 2022 22:41:05 GMT', + ] + ] + ], $headersProcessed); + } + + public function testSendmailTLS13MultipleRecipients() + { + $o = new tls_icon(); + $headersProcessed = $o->message_headers([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you', + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from mout.kundenserver.de (mout.kundenserver.de [212.227.126.134]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BLGrgYw3602565 + (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO); + Wed, 21 Dec 2022 16:53:42 GMT', + ] + ] + ]); + $this->assertEquals([ + 'output' => [ + 'subject' => [ + 'value' => 'Sent to you' . $this->strSendmailCryptedTlsv13WithCipherNoVerify, + 'html' => 1, + ], + ], + 'headers' => (object)[ + 'others' => [ + 'received' => 'from mout.kundenserver.de (mout.kundenserver.de [212.227.126.134]) + by mail.aegee.org (8.17.1/8.17.1) with ESMTPS id 2BLGrgYw3602565 + (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO); + Wed, 21 Dec 2022 16:53:42 GMT', + ] + ] + ], $headersProcessed); + } } diff --git a/tls_icon.php b/tls_icon.php index cacff54..d05a334 100644 --- a/tls_icon.php +++ b/tls_icon.php @@ -2,6 +2,10 @@ class tls_icon extends rcube_plugin { + const POSTFIX_TLS_REGEX = "/\(using (TLS.*)\) \(/im"; + const POSTFIX_LOCAL_REGEX = "/\([a-zA-Z]*, from userid [0-9]*\)/im"; + const SENDMAIL_TLS_REGEX = "/\(version=(TLS.*)\)(\s+for|;)/im"; + private $message_headers_done = false; private $icon_img; private $rcmail; @@ -55,19 +59,11 @@ public function message_headers($p) return $p; } - if (preg_match_all('/\(using TLS.*.*\) \(/im', $Received, $items, PREG_PATTERN_ORDER)) { - $data = $items[0][0]; - - $needle = '(using '; - $pos = strpos($data, $needle); - $data = substr_replace($data, '', $pos, strlen($needle)); - - $needle = ') ('; - $pos = strrpos($data, $needle); - $data = substr_replace($data, '', $pos, strlen($needle)); - + if (preg_match_all(tls_icon::POSTFIX_TLS_REGEX, $Received, $items, PREG_PATTERN_ORDER) || + preg_match_all(tls_icon::SENDMAIL_TLS_REGEX, $Received, $items, PREG_PATTERN_ORDER)) { + $data = $items[1][0]; $this->icon_img .= ''; - } elseif (preg_match_all('/\([a-zA-Z]*, from userid [0-9]*\)/im', $Received, $items, PREG_PATTERN_ORDER)) { + } elseif (preg_match_all(tls_icon::POSTFIX_LOCAL_REGEX, $Received, $items, PREG_PATTERN_ORDER)) { $this->icon_img .= ''; } else { // TODO: Mails received from localhost but without TLS are currently flagged insecure