From aa605f7e8a4c5e046503d61fdb953721c32f9d3a Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 11 May 2018 02:57:59 -0400 Subject: [PATCH] cli/show: enable --decrypt=stash Add fancy new feature, which makes "notmuch show" capable of actually indexing messages that it just decrypted. This enables a workflow where messages can come in in the background and be indexed using "--decrypt=auto". But when showing an encrypted message for the first time, it gets automatically indexed. This is something of a departure for "notmuch show" -- in particular, because it requires read/write access to the database. However, this might be a common use case -- people get mail delivered and indexed in the background, but only want access to their secret key to happen when they're directly interacting with notmuch itself. In such a scenario, they couldn't search newly-delivered, encrypted messages, but they could search for them once they've read them. Documentation of this new feature also uses a table form, similar to that found in the description of index.decrypt in notmuch-config(1). A notmuch UI that wants to facilitate this workflow while also offering an interactive search interface might instead make use of these additional commands while the user is at the console: Count received encrypted messages (if > 0, there are some things we haven't yet tried to index, and therefore can't yet search): notmuch count tag:encrypted and \ not property:index.decryption=success and \ not property:index.decryption=failure Reindex those messages: notmuch reindex --try-decrypt=true tag:encrypted and \ not property:index.decryption=success and \ not property:index.decryption=failure --- completion/notmuch-completion.bash | 2 +- doc/man1/notmuch-show.rst | 38 ++++++++++++++++++++++++++---- notmuch-show.c | 9 +++++-- test/T357-index-decryption.sh | 18 ++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 249b9664..15425697 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -522,7 +522,7 @@ _notmuch_show() return ;; --decrypt) - COMPREPLY=( $( compgen -W "true auto false" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "true auto false stash" -- "${cur}" ) ) return ;; esac diff --git a/doc/man1/notmuch-show.rst b/doc/man1/notmuch-show.rst index 2b825ccc..b2667537 100644 --- a/doc/man1/notmuch-show.rst +++ b/doc/man1/notmuch-show.rst @@ -110,7 +110,7 @@ Supported options for **show** include supported with --format=json and --format=sexp), and the multipart/signed part will be replaced by the signed data. -``--decrypt=(false|auto|true)`` +``--decrypt=(false|auto|true|stash)`` If ``true``, decrypt any MIME encrypted parts found in the selected content (i.e. "multipart/encrypted" parts). Status of the decryption will be reported (currently only supported @@ -118,17 +118,45 @@ Supported options for **show** include decryption the multipart/encrypted part will be replaced by the decrypted content. + ``stash`` behaves like ``true``, but upon successful decryption it + will also stash the message's session key in the database, and + index the cleartext of the message, enabling automatic decryption + in the future. + If ``auto``, and a session key is already known for the message, then it will be decrypted, but notmuch will not try to access the user's keys. Use ``false`` to avoid even automatic decryption. - Non-automatic decryption expects a functioning - **gpg-agent(1)** to provide any needed credentials. Without - one, the decryption will fail. + Non-automatic decryption (``stash`` or ``true``, in the absence of + a stashed session key) expects a functioning **gpg-agent(1)** to + provide any needed credentials. Without one, the decryption will + fail. - Note: ``true`` implies --verify. + Note: setting either ``true`` or ``stash`` here implies + ``--verify``. + + Here is a table that summarizes each of these policies: + + +------------------------+-------+------+------+-------+ + | | false | auto | true | stash | + +========================+=======+======+======+=======+ + | Show cleartext if | | X | X | X | + | session key is | | | | | + | already known | | | | | + +------------------------+-------+------+------+-------+ + | Use secret keys to | | | X | X | + | show cleartext | | | | | + +------------------------+-------+------+------+-------+ + | Stash any newly | | | | X | + | recovered session keys,| | | | | + | reindexing message if | | | | | + | found | | | | | + +------------------------+-------+------+------+-------+ + + Note: ``--decrypt=stash`` requires write access to the database. + Otherwise, ``notmuch show`` operates entirely in read-only mode. Default: ``auto`` diff --git a/notmuch-show.c b/notmuch-show.c index 3d10f3b2..1072ea55 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1124,6 +1124,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE }, { "auto", NOTMUCH_DECRYPT_AUTO }, { "true", NOTMUCH_DECRYPT_NOSTASH }, + { "stash", NOTMUCH_DECRYPT_TRUE }, { 0, 0 } } }, { .opt_bool = ¶ms.crypto.verify, .name = "verify" }, { .opt_bool = ¶ms.output_body, .name = "body" }, @@ -1139,7 +1140,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_process_shared_options (argv[0]); /* explicit decryption implies verification */ - if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH) + if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH || + params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) params.crypto.verify = true; /* specifying a part implies single message display */ @@ -1202,8 +1204,11 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) params.crypto.gpgpath = notmuch_config_get_crypto_gpg_path (config); #endif + notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; + if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) + mode = NOTMUCH_DATABASE_MODE_READ_WRITE; if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) + mode, ¬much)) return EXIT_FAILURE; notmuch_exit_if_unmatched_db_uuid (notmuch); diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh index ad6c3616..c5435f4f 100755 --- a/test/T357-index-decryption.sh +++ b/test/T357-index-decryption.sh @@ -80,6 +80,24 @@ test_expect_equal \ "$output" \ "$expected" +# show the message using stashing decryption +test_begin_subtest "stash decryption during show" +output=$(notmuch show --decrypt=stash tag:encrypted subject:002 | notmuch_show_part 3) +expected='This is a test encrypted message with a wumpus.' +test_expect_equal \ + "$output" \ + "$expected" + +test_begin_subtest "search should now find the contents" +output=$(notmuch search wumpus) +expected='thread:0000000000000003 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox unread)' +if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then + test_subtest_known_broken +fi +test_expect_equal \ + "$output" \ + "$expected" + # try reinserting it with decryption, should appear again, but now we # have two copies of the message: test_begin_subtest "message cleartext is present after reinserting with --decrypt=true"