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 1211C6DE12D9 for ; Tue, 24 Oct 2017 23:52:30 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org X-Spam-Flag: NO X-Spam-Score: -0.037 X-Spam-Level: X-Spam-Status: No, score=-0.037 tagged_above=-999 required=5 tests=[AWL=-0.037] 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 AOi9V_6c6IN5 for ; Tue, 24 Oct 2017 23:52:28 -0700 (PDT) Received: from che.mayfirst.org (che.mayfirst.org [162.247.75.118]) by arlo.cworth.org (Postfix) with ESMTP id C240C6DE1006 for ; Tue, 24 Oct 2017 23:52:13 -0700 (PDT) Received: from fifthhorseman.net (unknown [38.109.115.130]) by che.mayfirst.org (Postfix) with ESMTPSA id A72D0F9AC for ; Wed, 25 Oct 2017 02:52:12 -0400 (EDT) Received: by fifthhorseman.net (Postfix, from userid 1000) id DA9BE21327; Wed, 25 Oct 2017 02:52:06 -0400 (EDT) From: Daniel Kahn Gillmor To: Notmuch Mail Subject: [PATCH 07/18] crypto: new decryption policy "auto" Date: Wed, 25 Oct 2017 02:51:52 -0400 Message-Id: <20171025065203.24403-8-dkg@fifthhorseman.net> X-Mailer: git-send-email 2.14.2 In-Reply-To: <20171025065203.24403-1-dkg@fifthhorseman.net> References: <20171025065203.24403-1-dkg@fifthhorseman.net> X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.23 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, 25 Oct 2017 06:52:30 -0000 This new automatic decryption policy should make it possible to decrypt messages that we have stashed session keys for, without incurring a call to the user's asymmetric keys. --- doc/man1/notmuch-config.rst | 11 ++++++++--- lib/index.cc | 3 ++- lib/indexopts.c | 13 ++++++++----- lib/notmuch.h | 1 + mime-node.c | 7 ++++--- notmuch-client.h | 4 +++- notmuch.c | 3 ++- test/T357-index-decryption.sh | 12 +++++++++++- util/crypto.c | 8 +++++++- util/crypto.h | 3 ++- 10 files changed, 48 insertions(+), 17 deletions(-) diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index 6961737f..14642062 100644 --- a/doc/man1/notmuch-config.rst +++ b/doc/man1/notmuch-config.rst @@ -142,9 +142,14 @@ The available configuration items are described below. **[STORED IN DATABASE]** When indexing an encrypted e-mail message, if this variable is - set to true, notmuch will try to decrypt the message and index - the cleartext. Be aware that the index is likely sufficient - to reconstruct the cleartext of the message itself, so please + set to ``true``, notmuch will try to decrypt the message and + index the cleartext. If ``auto``, it will try to index the + cleartext if a stashed session key is already known for the message, + but will not try to access your secret keys. Use ``false`` to + avoid decrypting even when a session key is already known. + + Be aware that the notmuch index is likely sufficient to + reconstruct the cleartext of the message itself, so please ensure that the notmuch message index is adequately protected. DO NOT USE ``index.try_decrypt=true`` without considering the security of your index. diff --git a/lib/index.cc b/lib/index.cc index 483fe31d..d9a0018c 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -548,7 +548,8 @@ _index_encrypted_mime_part (notmuch_message_t *message, } } #endif - clear = _notmuch_crypto_decrypt (message, crypto_ctx, encrypted_data, NULL, &err); + clear = _notmuch_crypto_decrypt (notmuch_indexopts_get_try_decrypt (indexopts), + message, crypto_ctx, encrypted_data, NULL, &err); if (err) { _notmuch_database_log (notmuch, "Failed to decrypt during indexing. (%d:%d) [%s]\n", err->domain, err->code, err->message); diff --git a/lib/indexopts.c b/lib/indexopts.c index 23557143..93a2c9eb 100644 --- a/lib/indexopts.c +++ b/lib/indexopts.c @@ -33,11 +33,14 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db) if (err) return ret; - if (try_decrypt && - ((!(strcasecmp(try_decrypt, "true"))) || - (!(strcasecmp(try_decrypt, "yes"))) || - (!(strcasecmp(try_decrypt, "1"))))) - notmuch_indexopts_set_try_decrypt (ret, NOTMUCH_DECRYPT_TRUE); + if (try_decrypt) { + if ((!(strcasecmp(try_decrypt, "true"))) || + (!(strcasecmp(try_decrypt, "yes"))) || + (!(strcasecmp(try_decrypt, "1")))) + notmuch_indexopts_set_try_decrypt (ret, NOTMUCH_DECRYPT_TRUE); + else if (!strcasecmp(try_decrypt, "auto")) + notmuch_indexopts_set_try_decrypt (ret, NOTMUCH_DECRYPT_AUTO); + } free (try_decrypt); return ret; diff --git a/lib/notmuch.h b/lib/notmuch.h index 26fe8f81..7b1c61ad 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2241,6 +2241,7 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db); typedef enum { NOTMUCH_DECRYPT_FALSE, NOTMUCH_DECRYPT_TRUE, + NOTMUCH_DECRYPT_AUTO, } notmuch_decryption_policy_t; /** diff --git a/mime-node.c b/mime-node.c index eb6a16c0..815c1787 100644 --- a/mime-node.c +++ b/mime-node.c @@ -205,7 +205,8 @@ node_decrypt_and_verify (mime_node_t *node, GMimeObject *part, break; node->decrypt_attempted = true; - node->decrypted_child = _notmuch_crypto_decrypt (parent ? parent->envelope_file : NULL, + node->decrypted_child = _notmuch_crypto_decrypt (node->ctx->crypto->decrypt, + parent ? parent->envelope_file : NULL, cryptoctx, encrypteddata, &decrypt_result, &err); } if (! node->decrypted_child) { @@ -270,7 +271,7 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part) } #if (GMIME_MAJOR_VERSION < 3) - if ((GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt == NOTMUCH_DECRYPT_TRUE)) + if ((GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt != NOTMUCH_DECRYPT_FALSE)) || (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->crypto->verify)) { GMimeContentType *content_type = g_mime_object_get_content_type (part); const char *protocol = g_mime_content_type_get_parameter (content_type, "protocol"); @@ -286,7 +287,7 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part) #endif /* Handle PGP/MIME parts */ - if (GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt == NOTMUCH_DECRYPT_TRUE)) { + if (GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt != NOTMUCH_DECRYPT_FALSE)) { if (node->nchildren != 2) { /* this violates RFC 3156 section 4, so we won't bother with it. */ fprintf (stderr, "Error: %d part(s) for a multipart/encrypted " diff --git a/notmuch-client.h b/notmuch-client.h index 79b826c1..6aec9974 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -415,7 +415,9 @@ struct mime_node { /* Construct a new MIME node pointing to the root message part of * message. If crypto->verify is true, signed child parts will be * verified. If crypto->decrypt is NOTMUCH_DECRYPT_TRUE, encrypted - * child parts will be decrypted. If the crypto contexts + * child parts will be decrypted using either stored session keys or + * asymmetric crypto. If crypto->decrypt is NOTMUCH_DECRYPT_AUTO, + * only session keys will be tried. If the crypto contexts * (crypto->gpgctx or crypto->pkcs7) are NULL, they will be lazily * initialized. * diff --git a/notmuch.c b/notmuch.c index 1abd8288..62706748 100644 --- a/notmuch.c +++ b/notmuch.c @@ -103,6 +103,7 @@ const notmuch_opt_desc_t notmuch_shared_indexing_options [] = { .present = &indexing_cli_choices.try_decrypt_set, .keywords = (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE }, { "true", NOTMUCH_DECRYPT_TRUE }, + { "auto", NOTMUCH_DECRYPT_AUTO }, { 0, 0 } }, .name = "try-decrypt" }, { } @@ -128,7 +129,7 @@ notmuch_process_shared_indexing_options (notmuch_database_t *notmuch, g_mime_3_u } } #if (GMIME_MAJOR_VERSION < 3) - if (indexing_cli_choices.opts && notmuch_indexopts_get_try_decrypt (indexing_cli_choices.opts) == NOTMUCH_DECRYPT_TRUE) { + if (indexing_cli_choices.opts && notmuch_indexopts_get_try_decrypt (indexing_cli_choices.opts) != NOTMUCH_DECRYPT_FALSE) { const char* gpg_path = notmuch_config_get_crypto_gpg_path (config); if (gpg_path && strcmp(gpg_path, "gpg")) fprintf (stderr, "Warning: deprecated crypto.gpg_path is set to '%s'\n" diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh index 670d1ae9..d453d568 100755 --- a/test/T357-index-decryption.sh +++ b/test/T357-index-decryption.sh @@ -140,6 +140,16 @@ test_expect_equal \ "$output" \ "$expected" +# ensure no session keys are present: +test_begin_subtest 'reindex using only session keys' +test_expect_success 'notmuch reindex --try-decrypt=auto tag:encrypted and property:index.decryption=success' +test_begin_subtest "reindexed encrypted messages, decrypting only with session keys" +output=$(notmuch search wumpus) +expected='' +test_expect_equal \ + "$output" \ + "$expected" + # and the same search, but by property ($expected is untouched): test_begin_subtest "emacs search by property with both messages unindexed" output=$(notmuch search property:index.decryption=success) @@ -180,7 +190,7 @@ notmuch restore <= 3 || (GMIME_MAJOR_VERSION == 2 && GMIME_MINOR_VERSION == 6 && GMIME_MICRO_VERSION >= 21)) @@ -176,6 +179,9 @@ _notmuch_crypto_decrypt (notmuch_message_t *message, } #endif + if (decrypt == NOTMUCH_DECRYPT_AUTO) + return ret; + #if (GMIME_MAJOR_VERSION < 3) ret = g_mime_multipart_encrypted_decrypt(part, crypto_ctx, decrypt_result, err); diff --git a/util/crypto.h b/util/crypto.h index b23ca747..dc95b4ca 100644 --- a/util/crypto.h +++ b/util/crypto.h @@ -16,7 +16,8 @@ typedef struct _notmuch_crypto { } _notmuch_crypto_t; GMimeObject * -_notmuch_crypto_decrypt (notmuch_message_t *message, +_notmuch_crypto_decrypt (notmuch_decryption_policy_t decrypt, + notmuch_message_t *message, GMimeCryptoContext* crypto_ctx, GMimeMultipartEncrypted *part, GMimeDecryptResult **decrypt_result, -- 2.14.2