diff options
author | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2017-12-08 01:23:53 -0500 |
---|---|---|
committer | David Bremner <david@tethera.net> | 2017-12-08 08:07:53 -0400 |
commit | e4890b5bf9e2260b36bcc36ddb77d8e97e2abe7d (patch) | |
tree | 83c71b87a02c656aee698a5e10700a4ff6e12419 | |
parent | 798aa789b5d117cf11697bc97dd982bd5a2c2ac8 (diff) |
crypto: new decryption policy "auto"
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.
-rw-r--r-- | doc/man1/notmuch-config.rst | 11 | ||||
-rw-r--r-- | lib/index.cc | 3 | ||||
-rw-r--r-- | lib/indexopts.c | 13 | ||||
-rw-r--r-- | lib/notmuch.h | 1 | ||||
-rw-r--r-- | mime-node.c | 7 | ||||
-rw-r--r-- | notmuch-client.h | 4 | ||||
-rw-r--r-- | notmuch.c | 3 | ||||
-rwxr-xr-x | test/T357-index-decryption.sh | 12 | ||||
-rw-r--r-- | util/crypto.c | 9 | ||||
-rw-r--r-- | util/crypto.h | 3 |
10 files changed, 49 insertions, 17 deletions
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index ea3d9754..4835f897 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.decrypt=true`` without considering the security of your index. diff --git a/lib/index.cc b/lib/index.cc index 905366ae..af999bd3 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_decrypt_policy (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 78f53391..a04d1c1c 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 (decrypt_policy && - ((!(strcasecmp(decrypt_policy, "true"))) || - (!(strcasecmp(decrypt_policy, "yes"))) || - (!(strcasecmp(decrypt_policy, "1"))))) - notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_TRUE); + if (decrypt_policy) { + if ((!(strcasecmp(decrypt_policy, "true"))) || + (!(strcasecmp(decrypt_policy, "yes"))) || + (!(strcasecmp(decrypt_policy, "1")))) + notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_TRUE); + else if (!strcasecmp(decrypt_policy, "auto")) + notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_AUTO); + } free (decrypt_policy); return ret; diff --git a/lib/notmuch.h b/lib/notmuch.h index 47633496..ff860e06 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 c4de708b..49d668fe 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 50b69e35..0985a7b0 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. * @@ -103,6 +103,7 @@ const notmuch_opt_desc_t notmuch_shared_indexing_options [] = { .present = &indexing_cli_choices.decrypt_policy_set, .keywords = (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE }, { "true", NOTMUCH_DECRYPT_TRUE }, + { "auto", NOTMUCH_DECRYPT_AUTO }, { 0, 0 } }, .name = "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_decrypt_policy (indexing_cli_choices.opts) == NOTMUCH_DECRYPT_TRUE) { + if (indexing_cli_choices.opts && notmuch_indexopts_get_decrypt_policy (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 15deaa6e..7996ec67 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 --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 <<EOF #notmuch-dump batch-tag:3 config,properties,tags #= simple-encrypted@crypto.notmuchmail.org session-key=9%3AFC09987F5F927CC0CC0EE80A96E4C5BBF4A499818FB591207705DFDDD6112CF9 EOF -notmuch reindex --decrypt=true id:simple-encrypted@crypto.notmuchmail.org +notmuch reindex --decrypt=auto id:simple-encrypted@crypto.notmuchmail.org output=$(notmuch search sekrit) expected='thread:0000000000000001 2016-12-22 [1/1] Daniel Kahn Gillmor; encrypted message (encrypted inbox unread)' if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then diff --git a/util/crypto.c b/util/crypto.c index 476f1879..bb587571 100644 --- a/util/crypto.c +++ b/util/crypto.c @@ -140,13 +140,16 @@ void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto)) #endif GMimeObject * -_notmuch_crypto_decrypt (notmuch_message_t *message, +_notmuch_crypto_decrypt (notmuch_decryption_policy_t decrypt, + notmuch_message_t *message, g_mime_3_unused(GMimeCryptoContext* crypto_ctx), GMimeMultipartEncrypted *part, GMimeDecryptResult **decrypt_result, GError **err) { GMimeObject *ret = NULL; + if (decrypt == NOTMUCH_DECRYPT_FALSE) + return NULL; /* the versions of notmuch that can support session key decryption */ #if HAVE_GMIME_SESSION_KEYS @@ -184,6 +187,10 @@ _notmuch_crypto_decrypt (notmuch_message_t *message, g_error_free (*err); *err = NULL; } + + 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, |