crypto: add --decrypt=nostash to avoid stashing session keys
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Fri, 8 Dec 2017 06:24:02 +0000 (01:24 -0500)
committerDavid Bremner <david@tethera.net>
Fri, 8 Dec 2017 12:08:47 +0000 (08:08 -0400)
Here's the configuration choice for people who want a cleartext index,
but don't want stashed session keys.

Interestingly, this "nostash" decryption policy is actually the same
policy that should be used by "notmuch show" and "notmuch reply",
since they never modify the index or database when they are invoked
with --decrypt.

We take advantage of this parallel to tune the behavior of those
programs so that we're not requesting session keys from GnuPG during
"show" and "reply" that we would then otherwise just throw away.

12 files changed:
completion/notmuch-completion.bash
doc/man1/notmuch-config.rst
doc/man1/notmuch-insert.rst
doc/man1/notmuch-new.rst
doc/man1/notmuch-reindex.rst
lib/indexopts.c
lib/notmuch.h
notmuch-reply.c
notmuch-show.c
notmuch.c
test/T357-index-decryption.sh
util/crypto.c

index 272131e63576bb9c9b2198636c39d20e8a19835a..948c153be4b3eeb56db38cdb9420a8f6c025add0 100644 (file)
@@ -288,7 +288,7 @@ _notmuch_insert()
            return
            ;;
        --decrypt)
-           COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
+           COMPREPLY=( $( compgen -W "true false auto nostash" -- "${cur}" ) )
            return
            ;;
     esac
@@ -320,7 +320,7 @@ _notmuch_new()
     $split &&
     case "${prev}" in
        --decrypt)
-           COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
+           COMPREPLY=( $( compgen -W "true false auto nostash" -- "${cur}" ) )
            return
            ;;
     esac
@@ -442,7 +442,7 @@ _notmuch_reindex()
     $split &&
     case "${prev}" in
        --decrypt)
-           COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
+           COMPREPLY=( $( compgen -W "true false auto nostash" -- "${cur}" ) )
            return
            ;;
     esac
index dabf269f141c38b996a4be424262d47ec0c17dc6..773fd9da2cd735cbc38c5157c0ae158e3ded8012 100644 (file)
@@ -141,6 +141,9 @@ The available configuration items are described below.
     **index.decrypt**
 
         **[STORED IN DATABASE]**
+
+        One of ``false``, ``auto``, ``nostash``, or ``true``.
+
         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, stashing a copy of any discovered session
@@ -150,11 +153,15 @@ The available configuration items are described below.
         secret keys.  Use ``false`` to avoid decrypting even when a
         stashed session key is already present.
 
-        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.
+        ``nostash`` is the same as ``true`` except that it will not
+        stash newly-discovered session keys in the database.
+
+        Be aware that the notmuch index is likely sufficient (and a
+        stashed session key is certainly 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`` or ``index.decrypt=nostash`` without
+        considering the security of your index.
 
         Default: ``auto``.
 
index 214f261ba6b00f95a3eb036f535c9ca992978708..1a3dfe98299ae21b6501de65cad97ef9f91a18bb 100644 (file)
@@ -51,10 +51,10 @@ Supported options for **insert** include
     ``--no-hooks``
         Prevent hooks from being run.
 
-    ``--decrypt=(true|auto|false)``
+    ``--decrypt=(true|nostash|auto|false)``
 
         If ``true`` and the message is encrypted, try to decrypt the
-        message while indexing, storing any session keys discovered.
+        message while indexing, stashing any session keys discovered.
         If ``auto``, and notmuch already knows about a session key for
         the message, it will try decrypting using that session key but
         will not try to access the user's secret keys.  If decryption
@@ -62,11 +62,15 @@ Supported options for **insert** include
         message is always stored to disk in its original form
         (ciphertext).
 
-        Be aware that the index is likely sufficient to reconstruct
-        the cleartext of the message itself, so please ensure that the
+        ``nostash`` is the same as ``true`` except that it will not
+        stash newly-discovered session keys in the database.
+
+        Be aware that the index is likely sufficient (and a stashed
+        session key is certainly sufficient) to reconstruct the
+        cleartext of the message itself, so please ensure that the
         notmuch message index is adequately protected. DO NOT USE
-        ``--decrypt=true`` without considering the security of
-        your index.
+        ``--decrypt=true`` or ``--decrypt=nostash`` without
+        considering the security of your index.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
index 27676a196994919f92bc831e4d5fb45d1b3aa4cb..3ddd4621a3806c87bc2d54f2391033a6aeb04c92 100644 (file)
@@ -43,10 +43,10 @@ Supported options for **new** include
     ``--quiet``
         Do not print progress or results.
 
-    ``--decrypt=(true|auto|false)``
+    ``--decrypt=(true|nostash|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while indexing, and store any discovered session
+        decrypt it while indexing, and stash any discovered session
         keys.  If ``auto``, try to use any session key already known
         to belong to this message, but do not attempt to use the
         user's secret keys.  If decryption is successful, index the
@@ -56,7 +56,8 @@ Supported options for **new** include
         key is certainly sufficient) to reconstruct the cleartext of
         the message itself, so please ensure that the notmuch message
         index is adequately protected.  DO NOT USE ``--decrypt=true``
-        without considering the security of your index.
+        or ``--decrypt=nostash`` without considering the security of
+        your index.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
index 477908713e89885f509c6ece7ec0ae15d5438cd5..8b3083cf41e97535a4e397ba4677078b8e2c71ad 100644 (file)
@@ -21,23 +21,27 @@ messages using the supplied options.
 
 Supported options for **reindex** include
 
-    ``--decrypt=(true|auto|false)``
+    ``--decrypt=(true|nostash|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while reindexing, storing any session keys
+        decrypt it while reindexing, stashing any session keys
         discovered.  If ``auto``, and notmuch already knows about a
         session key for the message, it will try decrypting using that
         session key but will not try to access the user's secret keys.
         If decryption is successful, index the cleartext itself.
 
+        ``nostash`` is the same as ``true`` except that it will not
+        stash newly-discovered session keys in the database.
+
         If ``false``, notmuch reindex will also delete any stashed
         session keys for all messages matching the search terms.
 
-        Be aware that the index is likely sufficient to reconstruct
-        the cleartext of the message itself, so please ensure that the
+        Be aware that the index is likely sufficient (and a stashed
+        session key is certainly sufficient) to reconstruct the
+        cleartext of the message itself, so please ensure that the
         notmuch message index is adequately protected. DO NOT USE
-        ``--decrypt=true`` without considering the security of your
-        index.
+        ``--decrypt=true`` or ``--decrypt=nostash`` without
+        considering the security of your index.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
index 26a31e8926103c780e4422f7734e1b9868900cf4..b78a57b6840996d524e1cdcc9fade0609876f5d2 100644 (file)
@@ -42,6 +42,8 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
                 (!(strcasecmp(decrypt_policy, "no"))) ||
                 (!(strcasecmp(decrypt_policy, "0"))))
            notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_FALSE);
+       else if (!strcasecmp(decrypt_policy, "nostash"))
+           notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_NOSTASH);
     }
 
     free (decrypt_policy);
index ff860e06a4987fb4872be7212b66414fea998b22..39759b7ac6d5ba73fe9d3c3636f9dd6ff41e79bc 100644 (file)
@@ -2242,6 +2242,7 @@ typedef enum {
     NOTMUCH_DECRYPT_FALSE,
     NOTMUCH_DECRYPT_TRUE,
     NOTMUCH_DECRYPT_AUTO,
+    NOTMUCH_DECRYPT_NOSTASH,
 } notmuch_decryption_policy_t;
 
 /**
index fd990a9a7547fcec7dd50880add4256a7908dae8..5cdf642be2ce276fd47630fec3df92cf4fa89e16 100644 (file)
@@ -730,7 +730,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_process_shared_options (argv[0]);
     if (decrypt_set)
-       params.crypto.decrypt = decrypt ? NOTMUCH_DECRYPT_TRUE : NOTMUCH_DECRYPT_FALSE;
+       params.crypto.decrypt = decrypt ? NOTMUCH_DECRYPT_NOSTASH : NOTMUCH_DECRYPT_FALSE;
 
     notmuch_exit_if_unsupported_format ();
 
index c8f5a48f78312744505e4637683a1a5fdeab6377..4e22424bb713cb03ae00f0710a3af838089b0e87 100644 (file)
@@ -1121,7 +1121,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 
     if (decrypt_set) {
        if (decrypt) {
-           params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
+           /* we do not need or want to ask for session keys */
+           params.crypto.decrypt = NOTMUCH_DECRYPT_NOSTASH;
            /* decryption implies verification */
            params.crypto.verify = true;
        } else {
index 1d283e260f1828ce784dae12d7e53b8e554011b1..0becd87e7841e739c8ddf2094bb5b942518b75f5 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -104,6 +104,7 @@ const notmuch_opt_desc_t  notmuch_shared_indexing_options [] = {
       (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE },
                              { "true", NOTMUCH_DECRYPT_TRUE },
                              { "auto", NOTMUCH_DECRYPT_AUTO },
+                             { "nostash", NOTMUCH_DECRYPT_NOSTASH },
                              { 0, 0 } },
       .name = "decrypt" },
     { }
index fcecb1d9f4b19f6c76cc7be8d6e61b8a636e5029..6b8a826186eaa1d98293bfa765adb7ec4d2af3d7 100755 (executable)
@@ -188,6 +188,29 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "index cleartext without keeping session keys"
+test_expect_success "notmuch reindex --decrypt=nostash tag:blarney"
+
+test_begin_subtest "Ensure that the indexed terms are present"
+output=$(notmuch search wumpus)
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "show one of the messages with --decrypt"
+output=$(notmuch show --decrypt thread:0000000000000001 | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
+expected='This is a test encrypted message with a wumpus.'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "Ensure that we cannot show the message without --decrypt"
+output=$(notmuch show thread:0000000000000001 | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
+expected='Non-text part: application/octet-stream'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
 add_email_corpus crypto
 
 test_begin_subtest "indexing message fails when secret key not available"
index 066dea6e1a0380fa8b3844380269669c987e90ab..9d3b6dad9d17e4b32f645d0d5c0a2fbbf9fddf48 100644 (file)
@@ -199,7 +199,7 @@ _notmuch_crypto_decrypt (bool *attempted,
 #if (GMIME_MAJOR_VERSION < 3)
 #if HAVE_GMIME_SESSION_KEYS
     gboolean oldgetsk = g_mime_crypto_context_get_retrieve_session_key (crypto_ctx);
-    gboolean newgetsk = (decrypt_result);
+    gboolean newgetsk = (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result);
     if (newgetsk != oldgetsk)
        /* This could return an error, but we can't do anything about it, so ignore it */
        g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, newgetsk, NULL);
@@ -212,7 +212,7 @@ _notmuch_crypto_decrypt (bool *attempted,
 #endif
 #else
     GMimeDecryptFlags flags = GMIME_DECRYPT_NONE;
-    if (decrypt_result)
+    if (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result)
        flags |= GMIME_DECRYPT_EXPORT_SESSION_KEY;
     ret = g_mime_multipart_encrypted_decrypt(part, flags, NULL,
                                             decrypt_result, err);