From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id MKJTJccxq16yTAAA0tVLHw (envelope-from ) for ; Thu, 30 Apr 2020 20:15:03 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id IF81D9Axq16vFQAAB5/wlQ (envelope-from ) for ; Thu, 30 Apr 2020 20:15:12 +0000 Received: from arlo.cworth.org (arlo.cworth.org [50.126.95.6]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 7F3329425D4 for ; Thu, 30 Apr 2020 20:15:11 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id 14C756DE13D5; Thu, 30 Apr 2020 13:14:48 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id K1GUXn3y_wVy; Thu, 30 Apr 2020 13:14:47 -0700 (PDT) Received: from arlo.cworth.org (localhost [IPv6:::1]) by arlo.cworth.org (Postfix) with ESMTP id 5D03F6DE13FF; Thu, 30 Apr 2020 13:14:24 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id 76DD46DE13F9 for ; Thu, 30 Apr 2020 13:14:21 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id SoMEf8wsgPOr for ; Thu, 30 Apr 2020 13:14:19 -0700 (PDT) Received: from che.mayfirst.org (unknown [162.247.75.117]) by arlo.cworth.org (Postfix) with ESMTPS id 8339F6DE13D2 for ; Thu, 30 Apr 2020 13:14:18 -0700 (PDT) DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/simple; d=fifthhorseman.net; i=@fifthhorseman.net; q=dns/txt; s=2019; t=1588277655; h=from : to : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : from; bh=kDTvvUTY6xvu1g8ewErATAqYW9jHB+fabmWz7qTK8ws=; b=zF7+HErpUXQCpTp6ks3bxSQx0Iuy0SO3696B/YgRjZ/FgdyEbkOeooNWeFwQJO7Eh57Q+ LcQOfEgx51rxBToAA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=fifthhorseman.net; i=@fifthhorseman.net; q=dns/txt; s=2019rsa; t=1588277655; h=from : to : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : from; bh=kDTvvUTY6xvu1g8ewErATAqYW9jHB+fabmWz7qTK8ws=; b=h/n6TZgsJk4Ivc7dIOG4zpn3dqyKhVTLibEttxpXlw1DmeVAD9fZff9Q1dY5Hk8BY2ciV jvTv/MjsTinlNGnve1xhlFHolAmPLApND3649Y6bXe5jTLf8hefjDIJTiSZKfWHhPHFMqUH URAL6bNuZDUdn+s8Ckg/8vnscxPwOnpa9b7FyDK13x82E4YxJzUdFSpI++mb+xk3b4hLrEc UYuK3XZjf1PZfzysNC3GOJ8i1RavF/WVi+3WXJJUFGAVwPaZOPZgqcFfSV+cX2mmlH14ijK 3s/eeqYRTuyLkaivksZ+4jh1YCA1mHfF7XB/H7YaO1JYeJGnLieDl/agxf5Q== Received: from fifthhorseman.net (unknown [108.58.6.98]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by che.mayfirst.org (Postfix) with ESMTPSA id 7D9BCF9A6 for ; Thu, 30 Apr 2020 16:14:15 -0400 (EDT) Received: by fifthhorseman.net (Postfix, from userid 1000) id 2757F1FE68; Thu, 30 Apr 2020 16:13:35 -0400 (EDT) From: Daniel Kahn Gillmor To: Notmuch Mail Subject: [PATCH 1/9] lib: index PKCS7 SignedData parts Date: Thu, 30 Apr 2020 16:13:20 -0400 Message-Id: <20200430201328.725651-2-dkg@fifthhorseman.net> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200430201328.725651-1-dkg@fifthhorseman.net> References: <20200430201328.725651-1-dkg@fifthhorseman.net> MIME-Version: 1.0 X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: notmuch-bounces@notmuchmail.org Sender: "notmuch" X-Scanner: scn0 X-Spam-Score: 1.09 Authentication-Results: aspmx1.migadu.com; dkim=fail (body hash did not verify) header.d=fifthhorseman.net header.s=2019 header.b=zF7+HErp; dkim=fail (body hash did not verify) header.d=fifthhorseman.net header.s=2019rsa header.b=h/n6TZgs; dmarc=fail reason="SPF not aligned (relaxed)" header.from=fifthhorseman.net (policy=none); spf=pass (aspmx1.migadu.com: domain of notmuch-bounces@notmuchmail.org designates 50.126.95.6 as permitted sender) smtp.mailfrom=notmuch-bounces@notmuchmail.org X-Scan-Result: default: False [1.09 / 13.00]; RCVD_VIA_SMTP_AUTH(0.00)[]; GENERIC_REPUTATION(0.00)[-0.46702914551072]; DWL_DNSWL_BLOCKED(0.00)[50.126.95.6:from]; IP_REPUTATION_HAM(0.00)[asn: 27017(-0.18), country: US(-0.00), ip: 50.126.95.6(-0.47)]; R_SPF_ALLOW(-0.20)[+a:c]; R_DKIM_REJECT(1.00)[fifthhorseman.net:s=2019,fifthhorseman.net:s=2019rsa]; TO_DN_ALL(0.00)[]; DKIM_TRACE(0.00)[fifthhorseman.net:-]; MX_GOOD(-0.50)[cached: notmuchmail.org]; MAILLIST(-0.20)[mailman]; MIME_TRACE(0.00)[0:+]; RCVD_TLS_LAST(0.00)[]; ASN(0.00)[asn:27017, ipnet:50.126.64.0/18, country:US]; FROM_NEQ_ENVFROM(0.00)[dkg@fifthhorseman.net,notmuch-bounces@notmuchmail.org]; ARC_NA(0.00)[]; URIBL_BLOCKED(0.00)[fifthhorseman.net:email,notmuchmail.org:email]; FROM_HAS_DN(0.00)[]; MIME_GOOD(-0.10)[text/plain]; PREVIOUSLY_DELIVERED(0.00)[notmuch@notmuchmail.org]; HAS_LIST_UNSUB(-0.01)[]; RCPT_COUNT_ONE(0.00)[1]; DNSWL_BLOCKED(0.00)[50.126.95.6:from]; MID_CONTAINS_FROM(1.00)[]; RCVD_COUNT_SEVEN(0.00)[8]; FORGED_SENDER_MAILLIST(0.00)[]; DMARC_POLICY_SOFTFAIL(0.10)[fifthhorseman.net : SPF not aligned (relaxed),none] X-TUID: RW1tdFytvbyD When we are indexing, we should treat SignedData parts the same way that we treat a multipart object, indexing the wrapped part as a distinct MIME object. Unfortunately, this means doing some sort of cryptographic verification whose results we throw away, because GMime doesn't offer us any way to unwrap without doing signature verification. I've opened https://github.com/jstedfast/gmime/issues/67 to request the capability from GMime but for now, we'll just accept the additional performance hit. As we do this indexing, we also apply the "signed" tag, by analogy with how we handle multipart/signed messages. These days, that kind of change should probably be done with a property instead, but that's a different set of changes. This one is just for consistency. Note that we are currently *only* handling signedData parts, which are basically clearsigned messages. PKCS#7 parts can also be envelopedData and authEnvelopedData (which are effectively encryption layers), and compressedData (which afaict isn't implemented anywhere, i've never encountered it). We're laying the groundwork for indexing these other S/MIME types here, but we're only dealing with signedData for now. Signed-off-by: Daniel Kahn Gillmor --- lib/index.cc | 57 ++++++++++++++++++++++++++++++++++++++++++++++ test/T355-smime.sh | 2 -- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/lib/index.cc b/lib/index.cc index 158ba5cf..bbf13dc5 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -372,6 +372,12 @@ _index_encrypted_mime_part (notmuch_message_t *message, notmuch_indexopts_t *ind GMimeMultipartEncrypted *part, _notmuch_message_crypto_t *msg_crypto); +static void +_index_pkcs7_part (notmuch_message_t *message, + notmuch_indexopts_t *indexopts, + GMimeObject *part, + _notmuch_message_crypto_t *msg_crypto); + /* Callback to generate terms for each mime part of a message. */ static void _index_mime_part (notmuch_message_t *message, @@ -466,6 +472,11 @@ _index_mime_part (notmuch_message_t *message, goto DONE; } + if (GMIME_IS_APPLICATION_PKCS7_MIME (part)) { + _index_pkcs7_part (message, indexopts, part, msg_crypto); + goto DONE; + } + if (! (GMIME_IS_PART (part))) { _notmuch_database_log (notmuch_message_get_database (message), "Warning: Not indexing unknown mime part: %s.\n", @@ -608,6 +619,52 @@ _index_encrypted_mime_part (notmuch_message_t *message, } +static void +_index_pkcs7_part (notmuch_message_t *message, + notmuch_indexopts_t *indexopts, + GMimeObject *part, + _notmuch_message_crypto_t *msg_crypto) +{ + GMimeApplicationPkcs7Mime *pkcs7; + GMimeSecureMimeType p7type; + GMimeObject *mimeobj = NULL; + GMimeSignatureList *sigs = NULL; + GError *err = NULL; + notmuch_database_t *notmuch = NULL; + + pkcs7 = GMIME_APPLICATION_PKCS7_MIME (part); + p7type = g_mime_application_pkcs7_mime_get_smime_type (pkcs7); + notmuch = notmuch_message_get_database (message); + _index_content_type (message, part); + + if (p7type == GMIME_SECURE_MIME_TYPE_SIGNED_DATA) { + sigs = g_mime_application_pkcs7_mime_verify (pkcs7, GMIME_VERIFY_NONE, &mimeobj, &err); + if (sigs == NULL) { + _notmuch_database_log (notmuch, "Failed to verify PKCS#7 SignedData during indexing. (%d:%d) [%s]\n", + err->domain, err->code, err->message); + g_error_free (err); + goto DONE; + } + _notmuch_message_add_term (message, "tag", "signed"); + GMimeObject *toindex = mimeobj; + if (_notmuch_message_crypto_potential_payload (msg_crypto, mimeobj, part, 0) && + msg_crypto->decryption_status == NOTMUCH_MESSAGE_DECRYPTED_FULL) { + toindex = _notmuch_repair_crypto_payload_skip_legacy_display (mimeobj); + if (toindex != mimeobj) + notmuch_message_add_property (message, "index.repaired", "skip-protected-headers-legacy-display"); + } + _index_mime_part (message, indexopts, toindex, msg_crypto); + } else { + _notmuch_database_log (notmuch, "Cannot currently handle PKCS#7 smime-type '%s'\n", + g_mime_object_get_content_type_parameter (part, "smime-type")); + } + DONE: + if (mimeobj) + g_object_unref (mimeobj); + if (sigs) + g_object_unref (sigs); +} + static notmuch_status_t _notmuch_message_index_user_headers (notmuch_message_t *message, GMimeMessage *mime_message) { diff --git a/test/T355-smime.sh b/test/T355-smime.sh index 117fa2b9..01e53e33 100755 --- a/test/T355-smime.sh +++ b/test/T355-smime.sh @@ -132,13 +132,11 @@ expected='' test_expect_equal "$expected" "$output" test_begin_subtest "know the MIME type of the embedded part in PKCS#7 SignedData" -test_subtest_known_broken output=$(notmuch search --output=messages 'mimetype:text/plain') expected=id:smime-onepart-signed@protected-headers.example test_expect_equal "$expected" "$output" test_begin_subtest "PKCS#7 SignedData message is tagged 'signed'" -test_subtest_known_broken output=$(notmuch dump id:smime-onepart-signed@protected-headers.example) expected='#notmuch-dump batch-tag:3 config,properties,tags +inbox +signed +unread -- id:smime-onepart-signed@protected-headers.example' -- 2.26.2