From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id ED56E6DE0F4A for ; Wed, 24 Apr 2019 11:31:25 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org X-Spam-Flag: NO X-Spam-Score: -0.151 X-Spam-Level: X-Spam-Status: No, score=-0.151 tagged_above=-999 required=5 tests=[AWL=0.050, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_PASS=-0.001] autolearn=disabled 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 BoiD45F6AAQi for ; Wed, 24 Apr 2019 11:31:24 -0700 (PDT) Received: from che.mayfirst.org (che.mayfirst.org [162.247.75.118]) by arlo.cworth.org (Postfix) with ESMTPS id 918946DE0F7E for ; Wed, 24 Apr 2019 11:31:23 -0700 (PDT) DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/simple; d=fifthhorseman.net; i=@fifthhorseman.net; q=dns/txt; s=2019; t=1556130682; h=from : to : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : from; bh=ivbfu3zIiPcrYXuw7UD0HUC3+1vmcpotqvFX8/MFN48=; b=83D+ZPPi7DPeoXi0npqcShsBNBo4W1GD5yM5bC5xULzOQcBoNw13lRg2 mdby7SuO1CPWuRIuTIy37XQF6GdDDg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=fifthhorseman.net; i=@fifthhorseman.net; q=dns/txt; s=2019rsa; t=1556130682; h=from : to : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : from; bh=ivbfu3zIiPcrYXuw7UD0HUC3+1vmcpotqvFX8/MFN48=; b=QchPZ30w+GgFOsz6aRR3CMOesXb08hFf26ITJcFTgFtVEtjIIqwaQ+Z6 vWrUnjp46LEVnE9Jiep1Hz8uE7co6l43pj+LzBifoZQksye69KoK8L2HBS RPtthISAKOkbExwiTg79aFgKDawjXreWSzn8HGPy7/+Nw0JUnQm456hvEl 7wFCDQzqnmiin/8PoM/p41RGE4VJkyWQ3KBBQIb/TtAEhyv/EVVFf8LYhY iCB0QXmW4ocxNj5a1xOcIEkhioXHCI1p+zfBN8tmhCOBY5v45sQXSh4Ghu Ooo9sMqEVVRStHdaf9wfq3ZEAWOiy/GhTY8Wi2fvF1/RuY7A2EWZ3Q== Received: from fifthhorseman.net (unknown [38.109.115.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by che.mayfirst.org (Postfix) with ESMTPSA id 71902F9A1 for ; Wed, 24 Apr 2019 14:31:22 -0400 (EDT) Received: by fifthhorseman.net (Postfix, from userid 1000) id 53CA01FE6C; Wed, 24 Apr 2019 14:31:18 -0400 (EDT) From: Daniel Kahn Gillmor To: Notmuch Mail Subject: [PATCH 1/4] util/crypto: _notmuch_message_crypto: tracks message-wide crypto state Date: Wed, 24 Apr 2019 14:31:10 -0400 Message-Id: <20190424183113.29242-2-dkg@fifthhorseman.net> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190424183113.29242-1-dkg@fifthhorseman.net> References: <20190424183113.29242-1-dkg@fifthhorseman.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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: , X-List-Received-Date: Wed, 24 Apr 2019 18:31:26 -0000 E-mail encryption and signatures reported by notmuch are at the MIME part level. This makes sense in the dirty details, but for users we need to have a per-message conception of the cryptographic state of the e-mail. (see https://dkg.fifthhorseman.net/blog/e-mail-cryptography.html for more discussion of why this is important). The object created in this patch is a useful for tracking the cryptographic state of the underlying message as a whole, based on a depth-first search of the message's MIME structure. This object stores a signature list of the message, but we don't handle it yet. Further patches in this series will make use of the signature list. --- util/crypto.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ util/crypto.h | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/util/crypto.c b/util/crypto.c index 9d3b6dad..9512fd67 100644 --- a/util/crypto.c +++ b/util/crypto.c @@ -219,3 +219,92 @@ _notmuch_crypto_decrypt (bool *attempted, #endif return ret; } + + +static int +_notmuch_message_crypto_cleanup (_notmuch_message_crypto_t *msg_crypto) +{ + if (!msg_crypto) + return 0; + if (msg_crypto->sig_list) + g_object_unref (msg_crypto->sig_list); + return 0; +} + +_notmuch_message_crypto_t * +_notmuch_message_crypto_new (void *ctx) +{ + _notmuch_message_crypto_t *ret = talloc_zero (ctx, _notmuch_message_crypto_t); + talloc_set_destructor (ret, _notmuch_message_crypto_cleanup); + return ret; +} + + +notmuch_status_t +_notmuch_message_crypto_potential_sig_list (_notmuch_message_crypto_t *msg_crypto, GMimeSignatureList *sigs) +{ + if (!msg_crypto) + return NOTMUCH_STATUS_NULL_POINTER; + + /* Signatures that arrive after a payload part during DFS are not + * part of the cryptographic envelope: */ + if (msg_crypto->payload_encountered) + return NOTMUCH_STATUS_SUCCESS; + + if (msg_crypto->sig_list) + g_object_unref (msg_crypto->sig_list); + + msg_crypto->sig_list = sigs; + if (sigs) + g_object_ref (sigs); + + if (msg_crypto->decryption_status == NOTMUCH_MESSAGE_DECRYPTED_FULL) + msg_crypto->signature_encrypted = true; + + return NOTMUCH_STATUS_SUCCESS; +} + + +notmuch_status_t +_notmuch_message_crypto_potential_payload (_notmuch_message_crypto_t *msg_crypto, GMimeObject *payload, GMimeObject *parent, int childnum) +{ + if (!msg_crypto || !payload) + return NOTMUCH_STATUS_NULL_POINTER; + + /* only fire on the first payload part encountered */ + if (msg_crypto->payload_encountered) + return NOTMUCH_STATUS_SUCCESS; + + /* the first child of multipart/encrypted that matches the + * encryption protocol should be "control information" metadata, + * not payload. So we skip it. (see + * https://tools.ietf.org/html/rfc1847#page-8) */ + if (parent && GMIME_IS_MULTIPART_ENCRYPTED (parent) && childnum == GMIME_MULTIPART_ENCRYPTED_VERSION) { + const char *enc_type = g_mime_object_get_content_type_parameter (parent, "protocol"); + GMimeContentType *ct = g_mime_object_get_content_type (payload); + if (ct && enc_type) { + const char *part_type = g_mime_content_type_to_string (ct); + if (part_type && strcmp (part_type, enc_type) == 0) + return NOTMUCH_STATUS_SUCCESS; + } + } + + msg_crypto->payload_encountered = true; + + return NOTMUCH_STATUS_SUCCESS; +} + + +notmuch_status_t +_notmuch_message_crypto_successful_decryption (_notmuch_message_crypto_t *msg_crypto) +{ + if (!msg_crypto) + return NOTMUCH_STATUS_NULL_POINTER; + + if (!msg_crypto->payload_encountered) + msg_crypto->decryption_status = NOTMUCH_MESSAGE_DECRYPTED_FULL; + else if (msg_crypto->decryption_status == NOTMUCH_MESSAGE_DECRYPTED_NONE) + msg_crypto->decryption_status = NOTMUCH_MESSAGE_DECRYPTED_PARTIAL; + + return NOTMUCH_STATUS_SUCCESS; +} diff --git a/util/crypto.h b/util/crypto.h index 1a90f0e0..c09c136e 100644 --- a/util/crypto.h +++ b/util/crypto.h @@ -38,6 +38,71 @@ _notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto, void _notmuch_crypto_cleanup (_notmuch_crypto_t *crypto); +/* The user probably wants to know if the entire message was in the + * clear. When replying, the MUA probably wants to know whether there + * was any part decrypted in the message. And when displaying to the + * user, we probably only want to display "encrypted message" if the + * entire message was covered by encryption. */ +typedef enum { + NOTMUCH_MESSAGE_DECRYPTED_NONE = 0, + NOTMUCH_MESSAGE_DECRYPTED_PARTIAL, + NOTMUCH_MESSAGE_DECRYPTED_FULL, +} _notmuch_message_decryption_status_t; + +/* description of the cryptographic state of a given message overall; + * for use by simple user agents. + */ +typedef struct _notmuch_message_crypto { + /* encryption status: partial, full, none */ + _notmuch_message_decryption_status_t decryption_status; + /* FIXME: can we show what key(s) a fully-encrypted message was + * encrypted to? This data is not necessarily cryptographically + * reliable; even when we decrypt, we might not know which public + * key was used (e.g. if we're using a session key). */ + + /* signature status of the whole message (either the whole message + * is signed, or it is not) -- this means that partially-signed + * messages will get no signature status. */ + GMimeSignatureList * sig_list; + /* if part of the message was signed, and the MUA is clever, it + * can determine on its own exactly which part and try to make + * more sense of it. */ + + /* mark this flag once we encounter a payload (i.e. something that + * is not part of the cryptographic envelope) */ + bool payload_encountered; + + /* if both signed and encrypted, was the signature encrypted? */ + bool signature_encrypted; +} _notmuch_message_crypto_t; + + +/* _notmuch_message_crypto_t objects should be released with + * talloc_free (), or they will be released along with their parent + * context. + */ +_notmuch_message_crypto_t * +_notmuch_message_crypto_new (void *ctx); + +/* call potential_sig_list during a depth-first-search on a message to + * consider a particular signature as relevant for the message. + */ +notmuch_status_t +_notmuch_message_crypto_potential_sig_list (_notmuch_message_crypto_t *msg_crypto, GMimeSignatureList *sigs); + +/* call successful_decryption during a depth-first-search on a message + * to indicate that a part was successfully decrypted. + */ +notmuch_status_t +_notmuch_message_crypto_successful_decryption (_notmuch_message_crypto_t *msg_crypto); + +/* call potential_payload during a depth-first-search on a message + * when encountering a message part that is not part of the envelope. + */ +notmuch_status_t +_notmuch_message_crypto_potential_payload (_notmuch_message_crypto_t *msg_crypto, GMimeObject *payload, GMimeObject *parent, int childnum); + + #ifdef __cplusplus } #endif -- 2.20.1