session keys followup, version 3

classic Classic list List threaded Threaded
23 messages Options
12
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

session keys followup, version 3

The first part of the session-keys series has already landed --
thanks to everyone who reviewed it and shepherded it on its way!

This is a respin of the remaining patches in the series, introducing
very minor changes from the previous series: typo corrections, and a
fix to the test suite to mark session-key-dependent subtests as broken
when notmuch is built against an older version of GMime.

Happy hacking,

      --dkg

history:

v1 was introduced in id:[hidden email]

v2 was introduced in id:[hidden email]

the first 6 patches of v2 have already landed, so the numbers in this
series are 6 less than previously ("v3 01/15" corresponds to "v2
07/21", etc)


_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 01/15] indexing: Change from try_decrypt to decrypt

the command-line interface for indexing (reindex, new, insert) used
--try-decrypt; and the configuration records used index.try_decrypt.
But by comparison with "show" and "reply", there doesn't seem to be
any reason for the "try" prefix.

This changeset adjusts the command-line interface and the
configuration interface.

For the moment, i've left indexopts_{set,get}_try_decrypt alone.  The
subsequent changeset will address those.
---
 NEWS                               |  4 ++--
 completion/notmuch-completion.bash | 12 ++++++------
 doc/man1/notmuch-config.rst        |  4 ++--
 doc/man1/notmuch-insert.rst        |  6 +++---
 doc/man1/notmuch-new.rst           |  6 +++---
 doc/man1/notmuch-reindex.rst       |  6 +++---
 doc/man7/notmuch-properties.rst    |  2 +-
 lib/indexopts.c                    | 14 +++++++-------
 notmuch-config.c                   |  2 +-
 notmuch.c                          |  4 ++--
 test/T357-index-decryption.sh      | 18 +++++++++---------
 test/test-lib.sh                   |  2 +-
 12 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/NEWS b/NEWS
index 412c678d..0465b9e8 100644
--- a/NEWS
+++ b/NEWS
@@ -19,9 +19,9 @@ Indexing cleartext of encrypted e-mails
   It's now possible to include the cleartext of encrypted e-mails in
   the notmuch index.  This makes it possible to search your encrypted
   e-mails with the same ease as searching cleartext.  This can be done
-  on a per-message basis with the --try-decrypt argument to indexing
+  on a per-message basis with the --decrypt argument to indexing
   commands (new, insert, reindex), or by default by running "notmuch
-  config set index.try_decrypt true".
+  config set index.decrypt true".
 
   Note that the contents of the index are sufficient to roughly
   reconstruct the cleartext of the message itself, so please ensure
diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 7aae4297..e462a82a 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -287,7 +287,7 @@ _notmuch_insert()
  sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
     return
     ;;
- --try-decrypt)
+ --decrypt)
     COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
     return
     ;;
@@ -296,7 +296,7 @@ _notmuch_insert()
     ! $split &&
     case "${cur}" in
  --*)
-    local options="--create-folder --folder= --keep --no-hooks --try-decrypt= ${_notmuch_shared_options}"
+    local options="--create-folder --folder= --keep --no-hooks --decrypt= ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     return
@@ -319,7 +319,7 @@ _notmuch_new()
 
     $split &&
     case "${prev}" in
- --try-decrypt)
+ --decrypt)
     COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
     return
     ;;
@@ -328,7 +328,7 @@ _notmuch_new()
     ! $split &&
     case "${cur}" in
  -*)
-    local options="--no-hooks --try-decrypt= --quiet ${_notmuch_shared_options}"
+    local options="--no-hooks --decrypt= --quiet ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
     ;;
@@ -437,7 +437,7 @@ _notmuch_reindex()
 
     $split &&
     case "${prev}" in
- --try-decrypt)
+ --decrypt)
     COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
     return
     ;;
@@ -446,7 +446,7 @@ _notmuch_reindex()
     ! $split &&
     case "${cur}" in
  -*)
-    local options="--try-decrypt= ${_notmuch_shared_options}"
+    local options="--decrypt= ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6961737f..ea3d9754 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -138,7 +138,7 @@ The available configuration items are described below.
 
         Default: ``gpg``.
 
-    **index.try_decrypt**
+    **index.decrypt**
 
         **[STORED IN DATABASE]**
         When indexing an encrypted e-mail message, if this variable is
@@ -146,7 +146,7 @@ The available configuration items are described below.
         the cleartext.  Be aware that the 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
+        DO NOT USE ``index.decrypt=true`` without considering the
         security of your index.
 
         Default: ``false``.
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index c500b251..eb9ff11b 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -51,7 +51,7 @@ Supported options for **insert** include
     ``--no-hooks``
         Prevent hooks from being run.
 
-    ``--try-decrypt=(true|false)``
+    ``--decrypt=(true|false)``
 
         If true and the message is encrypted, try to decrypt the
         message while indexing.  If decryption is successful, index
@@ -60,10 +60,10 @@ Supported options for **insert** include
         that the 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
-        ``--try-decrypt=true`` without considering the security of
+        ``--decrypt=true`` without considering the security of
         your index.
 
-        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+        See also ``index.decrypt`` in **notmuch-config(1)**.
 
 EXIT STATUS
 ===========
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index bc26aa48..1df86f06 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,17 +43,17 @@ Supported options for **new** include
     ``--quiet``
         Do not print progress or results.
 
-    ``--try-decrypt=(true|false)``
+    ``--decrypt=(true|false)``
 
         If true, when encountering an encrypted message, try to
         decrypt it while indexing.  If decryption is successful, index
         the cleartext itself.  Be aware that the 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 ``--try-decrypt=true`` without
+        protected.  DO NOT USE ``--decrypt=true`` without
         considering the security of your index.
 
-        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+        See also ``index.decrypt`` in **notmuch-config(1)**.
 
 EXIT STATUS
 ===========
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index 21f6c7a9..782b0d7b 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -21,17 +21,17 @@ messages using the supplied options.
 
 Supported options for **reindex** include
 
-    ``--try-decrypt=(true|false)``
+    ``--decrypt=(true|false)``
 
         If true, when encountering an encrypted message, try to
         decrypt it while reindexing.  If decryption is successful,
         index the cleartext itself.  Be aware that the 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 ``--try-decrypt=true`` without
+        protected. DO NOT USE ``--decrypt=true`` without
         considering the security of your index.
 
-        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+        See also ``index.decrypt`` in **notmuch-config(1)**.
 
 SEE ALSO
 ========
diff --git a/doc/man7/notmuch-properties.rst b/doc/man7/notmuch-properties.rst
index 9e033e14..05444f6c 100644
--- a/doc/man7/notmuch-properties.rst
+++ b/doc/man7/notmuch-properties.rst
@@ -70,7 +70,7 @@ of its normal activity.
     properties will be set on the message as a whole.
 
     If notmuch never tried to decrypt an encrypted message during
-    indexing (which is the default, see ``index.try_decrypt`` in
+    indexing (which is the default, see ``index.decrypt`` in
     **notmuch-config(1)**), then this property will not be set on that
     message.
 
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 15c31d24..ca6bf6c9 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -27,18 +27,18 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
     if (!ret)
  return ret;
 
-    char * try_decrypt;
-    notmuch_status_t err = notmuch_database_get_config (db, "index.try_decrypt", &try_decrypt);
+    char * decrypt;
+    notmuch_status_t err = notmuch_database_get_config (db, "index.decrypt", &decrypt);
     if (err)
  return ret;
 
-    if (try_decrypt &&
- ((!(strcasecmp(try_decrypt, "true"))) ||
- (!(strcasecmp(try_decrypt, "yes"))) ||
- (!(strcasecmp(try_decrypt, "1")))))
+    if (decrypt &&
+ ((!(strcasecmp(decrypt, "true"))) ||
+ (!(strcasecmp(decrypt, "yes"))) ||
+ (!(strcasecmp(decrypt, "1")))))
  notmuch_indexopts_set_try_decrypt (ret, true);
 
-    free (try_decrypt);
+    free (decrypt);
     return ret;
 }
 
diff --git a/notmuch-config.c b/notmuch-config.c
index 1cba2661..e1b16609 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -813,7 +813,7 @@ static bool
 _stored_in_db (const char *item)
 {
     const char * db_configs[] = {
- "index.try_decrypt",
+ "index.decrypt",
     };
     if (STRNCMP_LITERAL (item, "query.") == 0)
  return true;
diff --git a/notmuch.c b/notmuch.c
index fa866d86..7ee3ad0b 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -101,7 +101,7 @@ struct _notmuch_client_indexing_cli_choices indexing_cli_choices = { };
 const notmuch_opt_desc_t  notmuch_shared_indexing_options [] = {
     { .opt_bool = &indexing_cli_choices.try_decrypt,
       .present = &indexing_cli_choices.try_decrypt_set,
-      .name = "try-decrypt" },
+      .name = "decrypt" },
     { }
 };
 
@@ -117,7 +117,7 @@ notmuch_process_shared_indexing_options (notmuch_database_t *notmuch, g_mime_3_u
     return NOTMUCH_STATUS_OUT_OF_MEMORY;
  status = notmuch_indexopts_set_try_decrypt (indexing_cli_choices.opts, indexing_cli_choices.try_decrypt);
  if (status != NOTMUCH_STATUS_SUCCESS) {
-    fprintf (stderr, "Error: Failed to set try_decrypt to %s. (%s)\n",
+    fprintf (stderr, "Error: Failed to set index decryption policy to %s. (%s)\n",
      indexing_cli_choices.try_decrypt ? "True" : "False", notmuch_status_to_string (status));
     notmuch_indexopts_destroy (indexing_cli_choices.opts);
     indexing_cli_choices.opts = NULL;
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 3efaa61f..2047d145 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -29,7 +29,7 @@ test_expect_equal \
 # create a test encrypted message that is indexed in the clear
 test_begin_subtest 'emacs delivery of encrypted message'
 test_expect_success \
-'emacs_fcc_message --try-decrypt=true \
+'emacs_fcc_message --decrypt=true \
     "test encrypted message for cleartext index 002" \
     "This is a test encrypted message with a wumpus.\n" \
     "(mml-secure-message-encrypt)"'
@@ -71,8 +71,8 @@ test_expect_equal \
 
 # 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 --try-decrypt"
-notmuch insert --folder=sent --try-decrypt <<<"$contents"
+test_begin_subtest "message cleartext is present after reinserting with --decrypt"
+notmuch insert --folder=sent --decrypt <<<"$contents"
 output=$(notmuch search wumpus)
 expected='thread:0000000000000003   2000-01-01 [1/1(2)] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox unread)'
 test_expect_equal \
@@ -93,8 +93,8 @@ test_expect_equal \
 # try inserting it with decryption, should appear as a single copy
 # (note: i think thread id skips 4 because of duplicate message-id
 # insertion, above)
-test_begin_subtest "message cleartext is present with insert --try-decrypt"
-notmuch insert --folder=sent --try-decrypt <<<"$contents"
+test_begin_subtest "message cleartext is present with insert --decrypt"
+notmuch insert --folder=sent --decrypt <<<"$contents"
 output=$(notmuch search wumpus)
 expected='thread:0000000000000005   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox unread)'
 test_expect_equal \
@@ -113,9 +113,9 @@ test_expect_equal \
     "$output" \
     "$expected"
 
-# see if first message shows up after reindexing with --try-decrypt=true (same $expected, untouched):
+# see if first message shows up after reindexing with --decrypt=true (same $expected, untouched):
 test_begin_subtest 'reindex old messages'
-test_expect_success 'notmuch reindex --try-decrypt=true tag:encrypted and not property:index.decryption=success'
+test_expect_success 'notmuch reindex --decrypt=true tag:encrypted and not property:index.decryption=success'
 test_begin_subtest "reindexed encrypted message, including cleartext"
 output=$(notmuch search wumpus)
 test_expect_equal \
@@ -159,7 +159,7 @@ test_expect_equal \
 add_email_corpus crypto
 
 test_begin_subtest "indexing message fails when secret key not available"
-notmuch reindex --try-decrypt id:[hidden email]
+notmuch reindex --decrypt id:[hidden email]
 output=$(notmuch dump )
 expected='#notmuch-dump batch-tag:3 config,properties,tags
 +encrypted +inbox +unread -- id:[hidden email]
@@ -180,7 +180,7 @@ notmuch restore <<EOF
 #notmuch-dump batch-tag:3 config,properties,tags
 #= [hidden email] session-key=9%3AFC09987F5F927CC0CC0EE80A96E4C5BBF4A499818FB591207705DFDDD6112CF9
 EOF
-notmuch reindex --try-decrypt id:[hidden email]
+notmuch reindex --decrypt id:[hidden email]
 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/test/test-lib.sh b/test/test-lib.sh
index 4c8607a1..5b212514 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -347,7 +347,7 @@ emacs_deliver_message ()
 # before sending, which is useful to doing things like attaching files
 # to the message and encrypting/signing.
 #
-# If any GNU-style long-arguments (like --quiet or --try-decrypt=true) are
+# If any GNU-style long-arguments (like --quiet or --decrypt=true) are
 # at the head of the argument list, they are sent directly to "notmuch
 # new" after message delivery
 emacs_fcc_message ()
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 02/15] indexopts: change _try_decrypt to _decrypt_policy

In reply to this post by Daniel Kahn Gillmor
This terminology makes it clearer what's going on at the API layer,
and paves the way for future changesets that offer more nuanced
decryption policy.
---
 lib/index.cc     |  2 +-
 lib/indexopts.c  | 10 +++++-----
 lib/notmuch.h    |  8 ++++----
 notmuch-client.h |  4 ++--
 notmuch.c        | 12 ++++++------
 5 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/lib/index.cc b/lib/index.cc
index 6eb60f30..ff14e408 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -525,7 +525,7 @@ _index_encrypted_mime_part (notmuch_message_t *message,
     notmuch_database_t * notmuch = NULL;
     GMimeObject *clear = NULL;
 
-    if (!indexopts || !notmuch_indexopts_get_try_decrypt (indexopts))
+    if (!indexopts || !notmuch_indexopts_get_decrypt_policy (indexopts))
  return;
 
     notmuch = _notmuch_message_database (message);
diff --git a/lib/indexopts.c b/lib/indexopts.c
index ca6bf6c9..0f65b97c 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -36,24 +36,24 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
  ((!(strcasecmp(decrypt, "true"))) ||
  (!(strcasecmp(decrypt, "yes"))) ||
  (!(strcasecmp(decrypt, "1")))))
- notmuch_indexopts_set_try_decrypt (ret, true);
+ notmuch_indexopts_set_decrypt_policy (ret, true);
 
     free (decrypt);
     return ret;
 }
 
 notmuch_status_t
-notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
-   notmuch_bool_t try_decrypt)
+notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
+      notmuch_bool_t decrypt_policy)
 {
     if (!indexopts)
  return NOTMUCH_STATUS_NULL_POINTER;
-    indexopts->crypto.decrypt = try_decrypt;
+    indexopts->crypto.decrypt = decrypt_policy;
     return NOTMUCH_STATUS_SUCCESS;
 }
 
 notmuch_bool_t
-notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts)
+notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t *indexopts)
 {
     if (!indexopts)
  return false;
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 2c5dcab5..ef463090 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2244,17 +2244,17 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db);
  * @since libnotmuch 5.1 (notmuch 0.26)
  */
 notmuch_status_t
-notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
-   notmuch_bool_t try_decrypt);
+notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
+      notmuch_bool_t decrypt_policy);
 
 /**
  * Return whether to decrypt encrypted parts while indexing.
- * see notmuch_indexopts_set_try_decrypt.
+ * see notmuch_indexopts_set_decrypt_policy.
  *
  * @since libnotmuch 5.1 (notmuch 0.26)
  */
 notmuch_bool_t
-notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts);
+notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t *indexopts);
 
 /**
  * Destroy a notmuch_indexopts_t object.
diff --git a/notmuch-client.h b/notmuch-client.h
index f7524e59..fc981459 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -500,8 +500,8 @@ int notmuch_minimal_options (const char* subcommand_name,
 /* the state chosen by the user invoking one of the notmuch
  * subcommands that does indexing */
 struct _notmuch_client_indexing_cli_choices {
-    bool try_decrypt;
-    bool try_decrypt_set;
+    bool decrypt_policy;
+    bool decrypt_policy_set;
     notmuch_indexopts_t * opts;
 };
 extern struct _notmuch_client_indexing_cli_choices indexing_cli_choices;
diff --git a/notmuch.c b/notmuch.c
index 7ee3ad0b..89d5e77b 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -99,8 +99,8 @@ int notmuch_minimal_options (const char *subcommand_name,
 
 struct _notmuch_client_indexing_cli_choices indexing_cli_choices = { };
 const notmuch_opt_desc_t  notmuch_shared_indexing_options [] = {
-    { .opt_bool = &indexing_cli_choices.try_decrypt,
-      .present = &indexing_cli_choices.try_decrypt_set,
+    { .opt_bool = &indexing_cli_choices.decrypt_policy,
+      .present = &indexing_cli_choices.decrypt_policy_set,
       .name = "decrypt" },
     { }
 };
@@ -111,21 +111,21 @@ notmuch_process_shared_indexing_options (notmuch_database_t *notmuch, g_mime_3_u
 {
     if (indexing_cli_choices.opts == NULL)
  indexing_cli_choices.opts = notmuch_database_get_default_indexopts (notmuch);
-    if (indexing_cli_choices.try_decrypt_set) {
+    if (indexing_cli_choices.decrypt_policy_set) {
  notmuch_status_t status;
  if (indexing_cli_choices.opts == NULL)
     return NOTMUCH_STATUS_OUT_OF_MEMORY;
- status = notmuch_indexopts_set_try_decrypt (indexing_cli_choices.opts, indexing_cli_choices.try_decrypt);
+ status = notmuch_indexopts_set_decrypt_policy (indexing_cli_choices.opts, indexing_cli_choices.decrypt_policy);
  if (status != NOTMUCH_STATUS_SUCCESS) {
     fprintf (stderr, "Error: Failed to set index decryption policy to %s. (%s)\n",
-     indexing_cli_choices.try_decrypt ? "True" : "False", notmuch_status_to_string (status));
+     indexing_cli_choices.decrypt_policy ? "True" : "False", notmuch_status_to_string (status));
     notmuch_indexopts_destroy (indexing_cli_choices.opts);
     indexing_cli_choices.opts = NULL;
     return status;
  }
     }
 #if (GMIME_MAJOR_VERSION < 3)
-    if (indexing_cli_choices.opts && notmuch_indexopts_get_try_decrypt (indexing_cli_choices.opts)) {
+    if (indexing_cli_choices.opts && notmuch_indexopts_get_decrypt_policy (indexing_cli_choices.opts)) {
  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"
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 03/15] lib: convert notmuch decryption policy to an enum

In reply to this post by Daniel Kahn Gillmor
Future patches in this series will introduce new policies; this merely
readies the way for them.

We also convert --try-decrypt to a keyword argument instead of a boolean.
---
 lib/index.cc                  |  2 +-
 lib/indexopts.c               | 21 +++++++++++----------
 lib/notmuch.h                 | 14 ++++++++++++--
 mime-node.c                   |  4 ++--
 notmuch-client.h              |  9 +++++----
 notmuch-reply.c               |  6 +++++-
 notmuch-show.c                | 10 +++++++---
 notmuch.c                     | 13 ++++++++-----
 test/T357-index-decryption.sh | 12 ++++++------
 util/crypto.h                 |  2 +-
 10 files changed, 58 insertions(+), 35 deletions(-)

diff --git a/lib/index.cc b/lib/index.cc
index ff14e408..905366ae 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -525,7 +525,7 @@ _index_encrypted_mime_part (notmuch_message_t *message,
     notmuch_database_t * notmuch = NULL;
     GMimeObject *clear = NULL;
 
-    if (!indexopts || !notmuch_indexopts_get_decrypt_policy (indexopts))
+    if (!indexopts || (notmuch_indexopts_get_decrypt_policy (indexopts) == NOTMUCH_DECRYPT_FALSE))
  return;
 
     notmuch = _notmuch_message_database (message);
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 0f65b97c..78f53391 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -26,25 +26,26 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
     notmuch_indexopts_t *ret = talloc_zero (db, notmuch_indexopts_t);
     if (!ret)
  return ret;
+    ret->crypto.decrypt = NOTMUCH_DECRYPT_FALSE;
 
-    char * decrypt;
-    notmuch_status_t err = notmuch_database_get_config (db, "index.decrypt", &decrypt);
+    char * decrypt_policy;
+    notmuch_status_t err = notmuch_database_get_config (db, "index.decrypt", &decrypt_policy);
     if (err)
  return ret;
 
-    if (decrypt &&
- ((!(strcasecmp(decrypt, "true"))) ||
- (!(strcasecmp(decrypt, "yes"))) ||
- (!(strcasecmp(decrypt, "1")))))
- notmuch_indexopts_set_decrypt_policy (ret, true);
+    if (decrypt_policy &&
+ ((!(strcasecmp(decrypt_policy, "true"))) ||
+ (!(strcasecmp(decrypt_policy, "yes"))) ||
+ (!(strcasecmp(decrypt_policy, "1")))))
+ notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_TRUE);
 
-    free (decrypt);
+    free (decrypt_policy);
     return ret;
 }
 
 notmuch_status_t
 notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
-      notmuch_bool_t decrypt_policy)
+      notmuch_decryption_policy_t decrypt_policy)
 {
     if (!indexopts)
  return NOTMUCH_STATUS_NULL_POINTER;
@@ -52,7 +53,7 @@ notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
     return NOTMUCH_STATUS_SUCCESS;
 }
 
-notmuch_bool_t
+notmuch_decryption_policy_t
 notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t *indexopts)
 {
     if (!indexopts)
diff --git a/lib/notmuch.h b/lib/notmuch.h
index ef463090..47633496 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2233,6 +2233,16 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list);
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db);
 
+/**
+ * Stating a policy about how to decrypt messages.
+ *
+ * See index.decrypt in notmuch-config(1) for more details.
+ */
+typedef enum {
+    NOTMUCH_DECRYPT_FALSE,
+    NOTMUCH_DECRYPT_TRUE,
+} notmuch_decryption_policy_t;
+
 /**
  * Specify whether to decrypt encrypted parts while indexing.
  *
@@ -2245,7 +2255,7 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db);
  */
 notmuch_status_t
 notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
-      notmuch_bool_t decrypt_policy);
+      notmuch_decryption_policy_t decrypt_policy);
 
 /**
  * Return whether to decrypt encrypted parts while indexing.
@@ -2253,7 +2263,7 @@ notmuch_indexopts_set_decrypt_policy (notmuch_indexopts_t *indexopts,
  *
  * @since libnotmuch 5.1 (notmuch 0.26)
  */
-notmuch_bool_t
+notmuch_decryption_policy_t
 notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t *indexopts);
 
 /**
diff --git a/mime-node.c b/mime-node.c
index daaae9f8..c4de708b 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -270,7 +270,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)
+    if ((GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt == NOTMUCH_DECRYPT_TRUE))
  || (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 +286,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) {
+    if (GMIME_IS_MULTIPART_ENCRYPTED (part) && (node->ctx->crypto->decrypt == NOTMUCH_DECRYPT_TRUE)) {
  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 fc981459..50b69e35 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -414,9 +414,10 @@ 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 true, encrypted child parts will be
- * decrypted.  If the crypto contexts (crypto->gpgctx or
- * crypto->pkcs7) are NULL, they will be lazily initialized.
+ * verified. If crypto->decrypt is NOTMUCH_DECRYPT_TRUE, encrypted
+ * child parts will be decrypted.  If the crypto contexts
+ * (crypto->gpgctx or crypto->pkcs7) are NULL, they will be lazily
+ * initialized.
  *
  * Return value:
  *
@@ -500,7 +501,7 @@ int notmuch_minimal_options (const char* subcommand_name,
 /* the state chosen by the user invoking one of the notmuch
  * subcommands that does indexing */
 struct _notmuch_client_indexing_cli_choices {
-    bool decrypt_policy;
+    int decrypt_policy;
     bool decrypt_policy_set;
     notmuch_indexopts_t * opts;
 };
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 2c7cc4eb..eec34bed 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -700,9 +700,11 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
     int opt_index;
     notmuch_show_params_t params = {
  .part = -1,
+ .crypto = { .decrypt = NOTMUCH_DECRYPT_FALSE },
     };
     int format = FORMAT_DEFAULT;
     int reply_all = true;
+    bool decrypt = false;
 
     notmuch_opt_desc_t options[] = {
  { .opt_keyword = &format, .name = "format", .keywords =
@@ -716,7 +718,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
   (notmuch_keyword_t []){ { "all", true },
   { "sender", false },
   { 0, 0 } } },
- { .opt_bool = &params.crypto.decrypt, .name = "decrypt" },
+ { .opt_bool = &decrypt, .name = "decrypt" },
  { .opt_inherit = notmuch_shared_options },
  { }
     };
@@ -726,6 +728,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
  return EXIT_FAILURE;
 
     notmuch_process_shared_options (argv[0]);
+    if (decrypt)
+ params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
 
     notmuch_exit_if_unsupported_format ();
 
diff --git a/notmuch-show.c b/notmuch-show.c
index 4b89be0a..e840a470 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1105,11 +1105,13 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
  .part = -1,
  .omit_excluded = true,
  .output_body = true,
+ .crypto = { .decrypt = NOTMUCH_DECRYPT_FALSE },
     };
     int format = NOTMUCH_FORMAT_NOT_SPECIFIED;
     bool exclude = true;
     bool entire_thread_set = false;
     bool single_message;
+    bool decrypt = false;
 
     notmuch_opt_desc_t options[] = {
  { .opt_keyword = &format, .name = "format", .keywords =
@@ -1124,7 +1126,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
  { .opt_bool = &params.entire_thread, .name = "entire-thread",
   .present = &entire_thread_set },
  { .opt_int = &params.part, .name = "part" },
- { .opt_bool = &params.crypto.decrypt, .name = "decrypt" },
+ { .opt_bool = &decrypt, .name = "decrypt" },
  { .opt_bool = &params.crypto.verify, .name = "verify" },
  { .opt_bool = &params.output_body, .name = "body" },
  { .opt_bool = &params.include_html, .name = "include-html" },
@@ -1138,9 +1140,11 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_process_shared_options (argv[0]);
 
-    /* decryption implies verification */
-    if (params.crypto.decrypt)
+    if (decrypt) {
+ params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
+ /* decryption implies verification */
  params.crypto.verify = true;
+    }
 
     /* specifying a part implies single message display */
     single_message = params.part >= 0;
diff --git a/notmuch.c b/notmuch.c
index 89d5e77b..7d5eeae4 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -99,8 +99,11 @@ int notmuch_minimal_options (const char *subcommand_name,
 
 struct _notmuch_client_indexing_cli_choices indexing_cli_choices = { };
 const notmuch_opt_desc_t  notmuch_shared_indexing_options [] = {
-    { .opt_bool = &indexing_cli_choices.decrypt_policy,
-      .present = &indexing_cli_choices.decrypt_policy_set,
+    { .opt_keyword = &indexing_cli_choices.decrypt_policy,
+      .present = &indexing_cli_choices.decrypt_policy_set, .keywords =
+      (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE },
+      { "true", NOTMUCH_DECRYPT_TRUE },
+      { 0, 0 } },
       .name = "decrypt" },
     { }
 };
@@ -117,15 +120,15 @@ notmuch_process_shared_indexing_options (notmuch_database_t *notmuch, g_mime_3_u
     return NOTMUCH_STATUS_OUT_OF_MEMORY;
  status = notmuch_indexopts_set_decrypt_policy (indexing_cli_choices.opts, indexing_cli_choices.decrypt_policy);
  if (status != NOTMUCH_STATUS_SUCCESS) {
-    fprintf (stderr, "Error: Failed to set index decryption policy to %s. (%s)\n",
-     indexing_cli_choices.decrypt_policy ? "True" : "False", notmuch_status_to_string (status));
+    fprintf (stderr, "Error: Failed to set index decryption policy to %d. (%s)\n",
+     indexing_cli_choices.decrypt_policy, notmuch_status_to_string (status));
     notmuch_indexopts_destroy (indexing_cli_choices.opts);
     indexing_cli_choices.opts = NULL;
     return status;
  }
     }
 #if (GMIME_MAJOR_VERSION < 3)
-    if (indexing_cli_choices.opts && notmuch_indexopts_get_decrypt_policy (indexing_cli_choices.opts)) {
+    if (indexing_cli_choices.opts && notmuch_indexopts_get_decrypt_policy (indexing_cli_choices.opts) == NOTMUCH_DECRYPT_TRUE) {
  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 2047d145..15deaa6e 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -71,8 +71,8 @@ test_expect_equal \
 
 # 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"
-notmuch insert --folder=sent --decrypt <<<"$contents"
+test_begin_subtest "message cleartext is present after reinserting with --decrypt=true"
+notmuch insert --folder=sent --decrypt=true <<<"$contents"
 output=$(notmuch search wumpus)
 expected='thread:0000000000000003   2000-01-01 [1/1(2)] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox unread)'
 test_expect_equal \
@@ -93,8 +93,8 @@ test_expect_equal \
 # try inserting it with decryption, should appear as a single copy
 # (note: i think thread id skips 4 because of duplicate message-id
 # insertion, above)
-test_begin_subtest "message cleartext is present with insert --decrypt"
-notmuch insert --folder=sent --decrypt <<<"$contents"
+test_begin_subtest "message cleartext is present with insert --decrypt=true"
+notmuch insert --folder=sent --decrypt=true <<<"$contents"
 output=$(notmuch search wumpus)
 expected='thread:0000000000000005   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox unread)'
 test_expect_equal \
@@ -159,7 +159,7 @@ test_expect_equal \
 add_email_corpus crypto
 
 test_begin_subtest "indexing message fails when secret key not available"
-notmuch reindex --decrypt id:[hidden email]
+notmuch reindex --decrypt=true id:[hidden email]
 output=$(notmuch dump )
 expected='#notmuch-dump batch-tag:3 config,properties,tags
 +encrypted +inbox +unread -- id:[hidden email]
@@ -180,7 +180,7 @@ notmuch restore <<EOF
 #notmuch-dump batch-tag:3 config,properties,tags
 #= [hidden email] session-key=9%3AFC09987F5F927CC0CC0EE80A96E4C5BBF4A499818FB591207705DFDDD6112CF9
 EOF
-notmuch reindex --decrypt id:[hidden email]
+notmuch reindex --decrypt=true id:[hidden email]
 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.h b/util/crypto.h
index 0c62ac70..b23ca747 100644
--- a/util/crypto.h
+++ b/util/crypto.h
@@ -7,7 +7,7 @@
 
 typedef struct _notmuch_crypto {
     bool verify;
-    bool decrypt;
+    notmuch_decryption_policy_t decrypt;
 #if (GMIME_MAJOR_VERSION < 3)
     GMimeCryptoContext* gpgctx;
     GMimeCryptoContext* pkcs7ctx;
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 04/15] crypto: new decryption policy "auto"

In reply to this post by Daniel Kahn Gillmor
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                 |  9 ++++++++-
 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.
  *
diff --git a/notmuch.c b/notmuch.c
index 7d5eeae4..1d283e26 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -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
 #= [hidden email] session-key=9%3AFC09987F5F927CC0CC0EE80A96E4C5BBF4A499818FB591207705DFDDD6112CF9
 EOF
-notmuch reindex --decrypt=true id:[hidden email]
+notmuch reindex --decrypt=auto id:[hidden email]
 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,
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 05/15] cli/reply: use decryption policy "auto" by default.

In reply to this post by Daniel Kahn Gillmor
If the user doesn't specify --decrypt= at all, but a stashed session
key is known to notmuch, when replying to an encrypted message,
notmuch should just go ahead and decrypt.

The user can disable this at the command line with --decrypt=false,
though it's not clear why they would ever want to do that.
---
 completion/notmuch-completion.bash |  6 +++++-
 doc/man1/notmuch-reply.rst         |  6 +++++-
 notmuch-reply.c                    |  9 +++++----
 test/T357-index-decryption.sh      | 10 ++++++++++
 4 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index e462a82a..1cd616b3 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -350,12 +350,16 @@ _notmuch_reply()
     COMPREPLY=( $( compgen -W "all sender" -- "${cur}" ) )
     return
     ;;
+ --decrypt)
+    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    return
+    ;;
     esac
 
     ! $split &&
     case "${cur}" in
  -*)
-    local options="--format= --format-version= --reply-to= --decrypt ${_notmuch_shared_options}"
+    local options="--format= --format-version= --reply-to= --decrypt= ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-reply.rst b/doc/man1/notmuch-reply.rst
index b6aec3c8..ede77930 100644
--- a/doc/man1/notmuch-reply.rst
+++ b/doc/man1/notmuch-reply.rst
@@ -80,8 +80,12 @@ Supported options for **reply** include
         multipart/encrypted part will be replaced by the decrypted
         content.
 
+        If a session key is already known for the message, then it
+        will be decrypted automatically unless the user explicitly
+        sets ``--decrypt=false``.
+
         Decryption expects a functioning **gpg-agent(1)** to provide any
-        needed credentials. Without one, the decryption will fail.
+        needed credentials. Without one, the decryption will likely fail.
 
 See **notmuch-search-terms(7)** for details of the supported syntax for
 <search-terms>.
diff --git a/notmuch-reply.c b/notmuch-reply.c
index eec34bed..fd990a9a 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -700,11 +700,12 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
     int opt_index;
     notmuch_show_params_t params = {
  .part = -1,
- .crypto = { .decrypt = NOTMUCH_DECRYPT_FALSE },
+ .crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
     };
     int format = FORMAT_DEFAULT;
     int reply_all = true;
     bool decrypt = false;
+    bool decrypt_set = false;
 
     notmuch_opt_desc_t options[] = {
  { .opt_keyword = &format, .name = "format", .keywords =
@@ -718,7 +719,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
   (notmuch_keyword_t []){ { "all", true },
   { "sender", false },
   { 0, 0 } } },
- { .opt_bool = &decrypt, .name = "decrypt" },
+ { .opt_bool = &decrypt, .name = "decrypt", .present = &decrypt_set },
  { .opt_inherit = notmuch_shared_options },
  { }
     };
@@ -728,8 +729,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
  return EXIT_FAILURE;
 
     notmuch_process_shared_options (argv[0]);
-    if (decrypt)
- params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
+    if (decrypt_set)
+ params.crypto.decrypt = decrypt ? NOTMUCH_DECRYPT_TRUE : NOTMUCH_DECRYPT_FALSE;
 
     notmuch_exit_if_unsupported_format ();
 
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 7996ec67..31991e22 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -200,6 +200,16 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "notmuch reply should show cleartext if session key is present"
+output=$(notmuch reply id:[hidden email] | grep '^>')
+expected='> This is a top sekrit message.'
+if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then
+    test_subtest_known_broken
+fi
+test_expect_equal \
+    "$output" \
+    "$expected"
+
 
 # TODO: test removal of a message from the message store between
 # indexing and reindexing.
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 06/15] cli/show: use decryption policy "auto" by default.

In reply to this post by Daniel Kahn Gillmor
When showing a message, if the user doesn't specify --decrypt= at all,
but a stashed session key is known to notmuch, notmuch should just go
ahead and try to decrypt the message with the session key (without
bothering the user for access to their asymmetric secret key).

The user can disable this at the command line with --decrypt=false if
they really don't want to look at the e-mail that they've asked
notmuch to show them.

and of course, "notmuch show --decrypt" still works for accessing the
user's secret keys if necessary.
---
 completion/notmuch-completion.bash |  4 ++--
 doc/man1/notmuch-show.rst          |  4 ++++
 notmuch-show.c                     | 17 +++++++++++------
 test/T357-index-decryption.sh      | 17 +++++++++++++++++
 4 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 1cd616b3..f94dbeed 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -517,7 +517,7 @@ _notmuch_show()
     COMPREPLY=( $( compgen -W "text json sexp mbox raw" -- "${cur}" ) )
     return
     ;;
- --exclude|--body)
+ --exclude|--body|--decrypt)
     COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
     return
     ;;
@@ -526,7 +526,7 @@ _notmuch_show()
     ! $split &&
     case "${cur}" in
  -*)
-    local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt --include-html ${_notmuch_shared_options}"
+    local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt= --include-html ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-show.rst b/doc/man1/notmuch-show.rst
index 7ba091cf..64caa7a6 100644
--- a/doc/man1/notmuch-show.rst
+++ b/doc/man1/notmuch-show.rst
@@ -123,6 +123,10 @@ Supported options for **show** include
         multipart/encrypted part will be replaced by the decrypted
         content.
 
+        If a session key is already known for the message, then it
+        will be decrypted automatically unless the user explicitly
+        sets ``--decrypt=false``.
+
         Decryption expects a functioning **gpg-agent(1)** to provide any
         needed credentials. Without one, the decryption will fail.
 
diff --git a/notmuch-show.c b/notmuch-show.c
index e840a470..591889a9 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1105,13 +1105,14 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
  .part = -1,
  .omit_excluded = true,
  .output_body = true,
- .crypto = { .decrypt = NOTMUCH_DECRYPT_FALSE },
+ .crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
     };
     int format = NOTMUCH_FORMAT_NOT_SPECIFIED;
     bool exclude = true;
     bool entire_thread_set = false;
     bool single_message;
     bool decrypt = false;
+    bool decrypt_set = false;
 
     notmuch_opt_desc_t options[] = {
  { .opt_keyword = &format, .name = "format", .keywords =
@@ -1126,7 +1127,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
  { .opt_bool = &params.entire_thread, .name = "entire-thread",
   .present = &entire_thread_set },
  { .opt_int = &params.part, .name = "part" },
- { .opt_bool = &decrypt, .name = "decrypt" },
+ { .opt_bool = &decrypt, .name = "decrypt", .present = &decrypt_set },
  { .opt_bool = &params.crypto.verify, .name = "verify" },
  { .opt_bool = &params.output_body, .name = "body" },
  { .opt_bool = &params.include_html, .name = "include-html" },
@@ -1140,10 +1141,14 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_process_shared_options (argv[0]);
 
-    if (decrypt) {
- params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
- /* decryption implies verification */
- params.crypto.verify = true;
+    if (decrypt_set) {
+ if (decrypt) {
+    params.crypto.decrypt = NOTMUCH_DECRYPT_TRUE;
+    /* decryption implies verification */
+    params.crypto.verify = true;
+ } else {
+    params.crypto.decrypt = NOTMUCH_DECRYPT_FALSE;
+ }
     }
 
     /* specifying a part implies single message display */
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 31991e22..c3730488 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -210,6 +210,23 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "notmuch show should show cleartext if session key is present"
+output=$(notmuch show id:[hidden email] | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
+expected='This is a top sekrit message.'
+if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then
+    test_subtest_known_broken
+fi
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "notmuch show should show nothing if decryption is explicitly disallowed"
+output=$(notmuch show --decrypt=false id:[hidden email] | 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"
+
 
 # TODO: test removal of a message from the message store between
 # indexing and reindexing.
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 07/15] cli/show, reply: document use of stashed session keys in notmuch-properties

In reply to this post by Daniel Kahn Gillmor
The stashed session keys are stored internally as notmuch properties.
So a user or developer who is reading about those properties might
want to understand how they fit into the bigger picture.

Note here that decrypting with a stored session key no longer needs
-decrypt for "notmuch show" and "notmuch reply".
---
 doc/man7/notmuch-properties.rst | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/doc/man7/notmuch-properties.rst b/doc/man7/notmuch-properties.rst
index 05444f6c..1a3f690e 100644
--- a/doc/man7/notmuch-properties.rst
+++ b/doc/man7/notmuch-properties.rst
@@ -77,9 +77,12 @@ of its normal activity.
 **session-key**
 
     When **notmuch-show(1)** or **nomtuch-reply** encounters a message
-    with an encrypted part and ``--decrypt`` is set, if notmuch finds a
-    ``session-key`` property associated with the message, it will try
-    that stashed session key for decryption.
+    with an encrypted part, if notmuch finds a ``session-key``
+    property associated with the message, it will try that stashed
+    session key for decryption.
+
+    If you do not want to use any stashed session keys that might be
+    present, you should pass those programs ``--decrypt=false``.
 
     Using a stashed session key with "notmuch show" will speed up
     rendering of long encrypted threads.  It also allows the user to
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 08/15] cli/new, insert, reindex: update documentation for --decrypt=auto

In reply to this post by Daniel Kahn Gillmor
we also include --decrypt=auto in the tab completion.
---
 completion/notmuch-completion.bash |  6 +++---
 doc/man1/notmuch-insert.rst        | 16 ++++++++++------
 doc/man1/notmuch-new.rst           | 10 +++++++---
 doc/man1/notmuch-reindex.rst       | 23 ++++++++++++++---------
 4 files changed, 34 insertions(+), 21 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index f94dbeed..272131e6 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -288,7 +288,7 @@ _notmuch_insert()
     return
     ;;
  --decrypt)
-    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
     return
     ;;
     esac
@@ -320,7 +320,7 @@ _notmuch_new()
     $split &&
     case "${prev}" in
  --decrypt)
-    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
     return
     ;;
     esac
@@ -442,7 +442,7 @@ _notmuch_reindex()
     $split &&
     case "${prev}" in
  --decrypt)
-    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    COMPREPLY=( $( compgen -W "true false auto" -- "${cur}" ) )
     return
     ;;
     esac
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index eb9ff11b..b22be863 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -51,14 +51,18 @@ Supported options for **insert** include
     ``--no-hooks``
         Prevent hooks from being run.
 
-    ``--decrypt=(true|false)``
+    ``--decrypt=(true|auto|false)``
 
-        If true and the message is encrypted, try to decrypt the
-        message while indexing.  If decryption is successful, index
+        If ``true`` and the message is encrypted, try to decrypt the
+        message while indexing.  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.  Either way, the 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
+        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
         notmuch message index is adequately protected. DO NOT USE
         ``--decrypt=true`` without considering the security of
         your index.
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 1df86f06..71df31d7 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,11 +43,15 @@ Supported options for **new** include
     ``--quiet``
         Do not print progress or results.
 
-    ``--decrypt=(true|false)``
+    ``--decrypt=(true|auto|false)``
 
-        If true, when encountering an encrypted message, try to
+        If ``true``, when encountering an encrypted message, try to
         decrypt it while indexing.  If decryption is successful, index
-        the cleartext itself.  Be aware that the index is likely
+        the cleartext itself.  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.
+
+        Be aware that the 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 ``--decrypt=true`` without
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index 782b0d7b..d87e9d85 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -21,15 +21,20 @@ messages using the supplied options.
 
 Supported options for **reindex** include
 
-    ``--decrypt=(true|false)``
-
-        If true, when encountering an encrypted message, try to
-        decrypt it while reindexing.  If decryption is successful,
-        index the cleartext itself.  Be aware that the 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 ``--decrypt=true`` without
-        considering the security of your index.
+    ``--decrypt=(true|auto|false)``
+
+        If ``true``, when encountering an encrypted message, try to
+        decrypt it while reindexing.  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.
+
+        Be aware that the 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
+        ``--decrypt=true`` without considering the security of your
+        index.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 09/15] crypto: record whether an actual decryption attempt happened

In reply to this post by Daniel Kahn Gillmor
In our consolidation of _notmuch_crypto_decrypt, the callers lost
track a little bit of whether any actual decryption was attempted.

Now that we have the more-subtle "auto" policy, it's possible that
_notmuch_crypto_decrypt could be called without having any actual
decryption take place.

This change lets the callers be a little bit smarter about whether or
not any decryption was actually attempted.
---
 lib/index.cc  | 17 ++++++++++++-----
 mime-node.c   |  4 ++--
 util/crypto.c |  7 ++++++-
 util/crypto.h |  3 ++-
 4 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/lib/index.cc b/lib/index.cc
index af999bd3..3914012a 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -548,12 +548,19 @@ _index_encrypted_mime_part (notmuch_message_t *message,
  }
     }
 #endif
-    clear = _notmuch_crypto_decrypt (notmuch_indexopts_get_decrypt_policy (indexopts),
+    bool attempted = false;
+    clear = _notmuch_crypto_decrypt (&attempted, 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);
- g_error_free(err);
+    if (!attempted)
+ return;
+    if (err || !clear) {
+ if (err) {
+    _notmuch_database_log (notmuch, "Failed to decrypt during indexing. (%d:%d) [%s]\n",
+   err->domain, err->code, err->message);
+    g_error_free(err);
+ } else {
+    _notmuch_database_log (notmuch, "Failed to decrypt during indexing. (unknown error)\n");
+ }
  /* Indicate that we failed to decrypt during indexing */
  status = notmuch_message_add_property (message, "index.decryption", "failure");
  if (status)
diff --git a/mime-node.c b/mime-node.c
index 49d668fe..11df082b 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -204,8 +204,8 @@ node_decrypt_and_verify (mime_node_t *node, GMimeObject *part,
     if (parent->envelope_file)
  break;
 
- node->decrypt_attempted = true;
- node->decrypted_child = _notmuch_crypto_decrypt (node->ctx->crypto->decrypt,
+ node->decrypted_child = _notmuch_crypto_decrypt (&node->decrypt_attempted,
+ node->ctx->crypto->decrypt,
  parent ? parent->envelope_file : NULL,
  cryptoctx, encrypteddata, &decrypt_result, &err);
     }
diff --git a/util/crypto.c b/util/crypto.c
index bb587571..338f1d5d 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -140,7 +140,8 @@ void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto))
 #endif
 
 GMimeObject *
-_notmuch_crypto_decrypt (notmuch_decryption_policy_t decrypt,
+_notmuch_crypto_decrypt (bool *attempted,
+ notmuch_decryption_policy_t decrypt,
  notmuch_message_t *message,
  g_mime_3_unused(GMimeCryptoContext* crypto_ctx),
  GMimeMultipartEncrypted *part,
@@ -162,6 +163,8 @@ _notmuch_crypto_decrypt (notmuch_decryption_policy_t decrypt,
  g_error_free (*err);
  *err = NULL;
     }
+    if (attempted)
+ *attempted = true;
 #if (GMIME_MAJOR_VERSION < 3)
     ret = g_mime_multipart_encrypted_decrypt_session (part,
       crypto_ctx,
@@ -191,6 +194,8 @@ _notmuch_crypto_decrypt (notmuch_decryption_policy_t decrypt,
     if (decrypt == NOTMUCH_DECRYPT_AUTO)
  return ret;
 
+    if (attempted)
+ *attempted = true;
 #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 dc95b4ca..c384601c 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_decryption_policy_t decrypt,
+_notmuch_crypto_decrypt (bool *attempted,
+ notmuch_decryption_policy_t decrypt,
  notmuch_message_t *message,
  GMimeCryptoContext* crypto_ctx,
  GMimeMultipartEncrypted *part,
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 10/15] cli/new, insert, reindex: change index.decrypt to "auto" by default

In reply to this post by Daniel Kahn Gillmor
The new "auto" decryption policy is not only good for "notmuch show"
and "notmuch reindex".  It's also useful for indexing messages --
there's no good reason to not try to go ahead and index the cleartext
of a message that we have a stashed session key for.

This change updates the defaults and tunes the test suite to make sure
that they have taken effect.
---
 doc/man1/notmuch-config.rst   | 2 +-
 lib/indexopts.c               | 8 +++++---
 test/T357-index-decryption.sh | 4 ++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 4835f897..1a9e08a3 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -154,7 +154,7 @@ The available configuration items are described below.
         DO NOT USE ``index.decrypt=true`` without considering the
         security of your index.
 
-        Default: ``false``.
+        Default: ``auto``.
 
     **built_with.<name>**
 
diff --git a/lib/indexopts.c b/lib/indexopts.c
index a04d1c1c..26a31e89 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -26,7 +26,7 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
     notmuch_indexopts_t *ret = talloc_zero (db, notmuch_indexopts_t);
     if (!ret)
  return ret;
-    ret->crypto.decrypt = NOTMUCH_DECRYPT_FALSE;
+    ret->crypto.decrypt = NOTMUCH_DECRYPT_AUTO;
 
     char * decrypt_policy;
     notmuch_status_t err = notmuch_database_get_config (db, "index.decrypt", &decrypt_policy);
@@ -38,8 +38,10 @@ notmuch_database_get_default_indexopts (notmuch_database_t *db)
     (!(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);
+ else if ((!(strcasecmp(decrypt_policy, "false"))) ||
+ (!(strcasecmp(decrypt_policy, "no"))) ||
+ (!(strcasecmp(decrypt_policy, "0"))))
+    notmuch_indexopts_set_decrypt_policy (ret, NOTMUCH_DECRYPT_FALSE);
     }
 
     free (decrypt_policy);
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index c3730488..bd213415 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -142,7 +142,7 @@ test_expect_equal \
 
 # 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_expect_success 'notmuch reindex tag:encrypted and property:index.decryption=success'
 test_begin_subtest "reindexed encrypted messages, decrypting only with session keys"
 output=$(notmuch search wumpus)
 expected=''
@@ -190,7 +190,7 @@ notmuch restore <<EOF
 #notmuch-dump batch-tag:3 config,properties,tags
 #= [hidden email] session-key=9%3AFC09987F5F927CC0CC0EE80A96E4C5BBF4A499818FB591207705DFDDD6112CF9
 EOF
-notmuch reindex --decrypt=auto id:[hidden email]
+notmuch reindex id:[hidden email]
 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
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 11/15] cli/reindex: destroy stashed session keys when --decrypt=false

In reply to this post by Daniel Kahn Gillmor
There are some situations where the user wants to get rid of the
cleartext index of a message.  For example, if they're indexing
encrypted messages normally, but suddenly they run across a message
that they really don't want any trace of in their index.

In that case, the natural thing to do is:

   notmuch reindex --decrypt=false id:[hidden email]

But of course, clearing the cleartext index without clearing the
stashed session key is just silly.  So we do the expected thing and
also destroy any stashed session keys while we're destroying the index
of the cleartext.

Note that stashed session keys are stored in the xapian database, but
xapian does not currently allow safe deletion (see
https://trac.xapian.org/ticket/742).

As a workaround, after removing session keys and cleartext material
from the database, the user probably should do something like "notmuch
compact" to try to purge whatever recoverable data is left in the
xapian freelist.  This problem really needs to be addressed within
xapian, though, if we want it fixed right.
---
 doc/man1/notmuch-reindex.rst  |  3 +++
 lib/message.cc                |  5 +++++
 test/T357-index-decryption.sh | 17 +++++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index d87e9d85..e8174f39 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -30,6 +30,9 @@ Supported options for **reindex** include
         the user's secret keys.  If decryption is successful, index
         the cleartext itself.
 
+        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
         notmuch message index is adequately protected. DO NOT USE
diff --git a/lib/message.cc b/lib/message.cc
index 12743460..d5db89b6 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -2002,6 +2002,11 @@ notmuch_message_reindex (notmuch_message_t *message,
     ret = notmuch_message_remove_all_properties_with_prefix (message, "index.");
     if (ret)
  goto DONE; /* XXX TODO: distinguish from other error returns above? */
+    if (indexopts && notmuch_indexopts_get_decrypt_policy (indexopts) == NOTMUCH_DECRYPT_FALSE) {
+ ret = notmuch_message_remove_all_properties (message, "session-key");
+ if (ret)
+    goto DONE;
+    }
 
     /* re-add the filenames with the associated indexopts */
     for (; notmuch_filenames_valid (orig_filenames);
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index bd213415..9f46a01b 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -227,6 +227,23 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "purging stashed session keys should lose access to the cleartext"
+notmuch reindex --decrypt=false id:[hidden email]
+output=$(notmuch search sekrit)
+expected=''
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "and cleartext should be unrecoverable now that there are no stashed session keys"
+notmuch dump
+notmuch reindex --decrypt=true id:[hidden email]
+output=$(notmuch search sekrit)
+expected=''
+test_expect_equal \
+    "$output" \
+    "$expected"
+
 
 # TODO: test removal of a message from the message store between
 # indexing and reindexing.
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 12/15] crypto: actually stash session keys when decrypt=true

In reply to this post by Daniel Kahn Gillmor
If you're going to store the cleartext index of an encrypted message,
in most situations you might just as well store the session key.
Doing this storage has efficiency and recoverability advantages.

Combined with a schedule of regular OpenPGP subkey rotation and
destruction, this can also offer security benefits, like "deletable
e-mail", which is the store-and-forward analog to "forward secrecy".

But wait, i hear you saying, i have a special need to store cleartext
indexes but it's really bad for me to store session keys!  Maybe
(let's imagine) i get lots of e-mails with incriminating photos
attached, and i want to be able to search for them by the text in the
e-mail, but i don't want someone with access to the index to be
actually able to see the photos themselves.

Fret not, the next patch in this series will support your wacky
uncommon use case.
---
 doc/man1/notmuch-config.rst     | 10 ++++++----
 doc/man1/notmuch-insert.rst     | 13 +++++++------
 doc/man1/notmuch-new.rst        | 21 +++++++++++----------
 doc/man1/notmuch-reindex.rst    | 10 +++++-----
 doc/man7/notmuch-properties.rst |  4 ++++
 lib/index.cc                    | 18 +++++++++++++++++-
 test/T357-index-decryption.sh   | 24 +++++++++++++++++++++++-
 util/crypto.c                   | 16 +++++++++++++++-
 8 files changed, 88 insertions(+), 28 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 1a9e08a3..dabf269f 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -143,10 +143,12 @@ 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.  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.
+        index the cleartext, stashing a copy of any discovered session
+        keys for the message.  If ``auto``, it will try to index the
+        cleartext if a stashed session key is already known for the message
+        (e.g. from a previous copy), but will not try to access your
+        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
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index b22be863..214f261b 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -54,12 +54,13 @@ Supported options for **insert** include
     ``--decrypt=(true|auto|false)``
 
         If ``true`` and the message is encrypted, try to decrypt the
-        message while indexing.  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.  Either way, the message is always
-        stored to disk in its original form (ciphertext).
+        message while indexing, storing 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.  Either way, the
+        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
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 71df31d7..27676a19 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -46,16 +46,17 @@ Supported options for **new** include
     ``--decrypt=(true|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while indexing.  If decryption is successful, index
-        the cleartext itself.  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.
-
-        Be aware that the 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 ``--decrypt=true`` without
-        considering the security of your index.
+        decrypt it while indexing, and store 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
+        cleartext of the message.
+
+        Be aware that the index is likely sufficient (and the 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.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index e8174f39..47790871 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -24,11 +24,11 @@ Supported options for **reindex** include
     ``--decrypt=(true|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while reindexing.  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.
+        decrypt it while reindexing, storing 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.
 
         If ``false``, notmuch reindex will also delete any stashed
         session keys for all messages matching the search terms.
diff --git a/doc/man7/notmuch-properties.rst b/doc/man7/notmuch-properties.rst
index 1a3f690e..07d36a1a 100644
--- a/doc/man7/notmuch-properties.rst
+++ b/doc/man7/notmuch-properties.rst
@@ -98,6 +98,10 @@ of its normal activity.
     message.  This includes attachments, cryptographic signatures, and
     other material that cannot be reconstructed from the index alone.
 
+    See ``index.decrypt`` in **notmuch-config(1)** for more
+    details about how to set notmuch's policy on when to store session
+    keys.
+
     The session key should be in the ASCII text form produced by
     GnuPG.  For OpenPGP, that consists of a decimal representation of
     the hash algorithm used (identified by number from RFC 4880,
diff --git a/lib/index.cc b/lib/index.cc
index 3914012a..0ad683fa 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -549,11 +549,15 @@ _index_encrypted_mime_part (notmuch_message_t *message,
     }
 #endif
     bool attempted = false;
+    GMimeDecryptResult *decrypt_result = NULL;
+    bool get_sk = (HAVE_GMIME_SESSION_KEYS && notmuch_indexopts_get_decrypt_policy (indexopts) == NOTMUCH_DECRYPT_TRUE);
     clear = _notmuch_crypto_decrypt (&attempted, notmuch_indexopts_get_decrypt_policy (indexopts),
-     message, crypto_ctx, encrypted_data, NULL, &err);
+     message, crypto_ctx, encrypted_data, get_sk ? &decrypt_result : NULL, &err);
     if (!attempted)
  return;
     if (err || !clear) {
+ if (decrypt_result)
+    g_object_unref (decrypt_result);
  if (err) {
     _notmuch_database_log (notmuch, "Failed to decrypt during indexing. (%d:%d) [%s]\n",
    err->domain, err->code, err->message);
@@ -568,6 +572,18 @@ _index_encrypted_mime_part (notmuch_message_t *message,
   "property (%d)\n", status);
  return;
     }
+    if (decrypt_result) {
+#if HAVE_GMIME_SESSION_KEYS
+ if (get_sk) {
+    status = notmuch_message_add_property (message, "session-key",
+   g_mime_decrypt_result_get_session_key (decrypt_result));
+    if (status)
+ _notmuch_database_log (notmuch, "failed to add session-key "
+       "property (%d)\n", status);
+ }
+#endif
+ g_object_unref (decrypt_result);
+    }
     _index_mime_part (message, indexopts, clear);
     g_object_unref (clear);
 
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 9f46a01b..fcecb1d9 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -48,6 +48,17 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "show the message body of the encrypted message"
+notmuch dump wumpus
+output=$(notmuch show wumpus | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
+expected='This is a test encrypted message with a wumpus.'
+if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then
+    test_subtest_known_broken
+fi
+test_expect_equal \
+    "$output" \
+    "$expected"
+
 
 test_begin_subtest "message should go away after deletion"
 # cache the message in an env var and remove it:
@@ -129,10 +140,21 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+# try a simple reindex
+test_begin_subtest 'reindex in auto mode'
+test_expect_success 'notmuch reindex tag:encrypted and property:index.decryption=success'
+test_begin_subtest "reindexed encrypted messages, should not have changed"
+output=$(notmuch search wumpus)
+if [ $NOTMUCH_HAVE_GMIME_SESSION_KEYS -eq 0 ]; then
+    test_subtest_known_broken
+fi
+test_expect_equal \
+    "$output" \
+    "$expected"
 
 # try to remove cleartext indexing
 test_begin_subtest 'reindex without cleartext'
-test_expect_success 'notmuch reindex tag:encrypted and property:index.decryption=success'
+test_expect_success 'notmuch reindex --decrypt=false tag:encrypted and property:index.decryption=success'
 test_begin_subtest "reindexed encrypted messages, without cleartext"
 output=$(notmuch search wumpus)
 expected=''
diff --git a/util/crypto.c b/util/crypto.c
index 338f1d5d..066dea6e 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -197,10 +197,24 @@ _notmuch_crypto_decrypt (bool *attempted,
     if (attempted)
  *attempted = true;
 #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);
+    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);
+#endif
     ret = g_mime_multipart_encrypted_decrypt(part, crypto_ctx,
      decrypt_result, err);
+#if HAVE_GMIME_SESSION_KEYS
+    if (newgetsk != oldgetsk)
+ g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, oldgetsk, NULL);
+#endif
 #else
-    ret = g_mime_multipart_encrypted_decrypt(part, GMIME_DECRYPT_NONE, NULL,
+    GMimeDecryptFlags flags = GMIME_DECRYPT_NONE;
+    if (decrypt_result)
+ flags |= GMIME_DECRYPT_EXPORT_SESSION_KEY;
+    ret = g_mime_multipart_encrypted_decrypt(part, flags, NULL,
      decrypt_result, err);
 #endif
     return ret;
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 13/15] crypto: add --decrypt=nostash to avoid stashing session keys

In reply to this post by Daniel Kahn Gillmor
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.
---
 completion/notmuch-completion.bash |  6 +++---
 doc/man1/notmuch-config.rst        | 17 ++++++++++++-----
 doc/man1/notmuch-insert.rst        | 16 ++++++++++------
 doc/man1/notmuch-new.rst           |  7 ++++---
 doc/man1/notmuch-reindex.rst       | 16 ++++++++++------
 lib/indexopts.c                    |  2 ++
 lib/notmuch.h                      |  1 +
 notmuch-reply.c                    |  2 +-
 notmuch-show.c                     |  3 ++-
 notmuch.c                          |  1 +
 test/T357-index-decryption.sh      | 23 +++++++++++++++++++++++
 util/crypto.c                      |  4 ++--
 12 files changed, 71 insertions(+), 27 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 272131e6..948c153b 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index dabf269f..773fd9da 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -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``.
 
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index 214f261b..1a3dfe98 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -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)**.
 
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 27676a19..3ddd4621 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -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)**.
 
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index 47790871..8b3083cf 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -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)**.
 
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 26a31e89..b78a57b6 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -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);
diff --git a/lib/notmuch.h b/lib/notmuch.h
index ff860e06..39759b7a 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2242,6 +2242,7 @@ typedef enum {
     NOTMUCH_DECRYPT_FALSE,
     NOTMUCH_DECRYPT_TRUE,
     NOTMUCH_DECRYPT_AUTO,
+    NOTMUCH_DECRYPT_NOSTASH,
 } notmuch_decryption_policy_t;
 
 /**
diff --git a/notmuch-reply.c b/notmuch-reply.c
index fd990a9a..5cdf642b 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -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 ();
 
diff --git a/notmuch-show.c b/notmuch-show.c
index 591889a9..2ad50007 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1143,7 +1143,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 {
diff --git a/notmuch.c b/notmuch.c
index 1d283e26..0becd87e 100644
--- 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" },
     { }
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index fcecb1d9..6b8a8261 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -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"
diff --git a/util/crypto.c b/util/crypto.c
index 066dea6e..9d3b6dad 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -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);
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 14/15] docs: clean up documentation about decryption policies

In reply to this post by Daniel Kahn Gillmor
Now that the range of sensible decryption policies has come into full
view, we take a bit of space to document the distinctions.

Most people will use either "auto" or "true" -- but we provide "false"
and "nostash" to handle use cases that might reasonably be requested.

Note also that these can be combined in sensible ways.  Like, if your
mail comes in regularly to a service that doesn't have access to your
secret keys, but does have access to your index, and you feel
comfortable adding selected encrypted messages to the index after
you've read them, you could stay in "auto" normally, and then when you
find yourself reading an indexable message (e.g. one you want to be
able to search for in the future, and that you don't mind exposing to
whatever entities have access to your inde), you can do:

    notmuch reindex --decrypt=true id:[hidden email]

That leaves your default the same (still "auto") but you get the
cleartext index and stashed session key benefits for that particular
message.
---
 doc/man1/notmuch-config.rst | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 773fd9da..3ba849b2 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -142,7 +142,9 @@ The available configuration items are described below.
 
         **[STORED IN DATABASE]**
 
-        One of ``false``, ``auto``, ``nostash``, or ``true``.
+        Policy for decrypting encrypted messages during indexing.
+        Must be 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
@@ -156,6 +158,34 @@ The available configuration items are described below.
         ``nostash`` is the same as ``true`` except that it will not
         stash newly-discovered session keys in the database.
 
+        From the command line (i.e. during **notmuch-new(1)**,
+        **notmuch-insert(1)**, or **notmuch-reindex(1)**), the user
+        can override the database's stored decryption policy with the
+        ``--decrypt=`` option.
+
+        Here is a table that summarizes the functionality of each of
+        these policies:
+
+        +------------------------+-------+------+---------+------+
+        |                        | false | auto | nostash | true |
+        +========================+=======+======+=========+======+
+        | Index cleartext using  |       |  X   |    X    |  X   |
+        | stashed session keys   |       |      |         |      |
+        +------------------------+-------+------+---------+------+
+        | Index cleartext        |       |      |    X    |  X   |
+        | using secret keys      |       |      |         |      |
+        +------------------------+-------+------+---------+------+
+        | Stash session keys     |       |      |         |  X   |
+        +------------------------+-------+------+---------+------+
+        | Delete stashed session |   X   |      |         |      |
+        | keys on reindex        |       |      |         |      |
+        +------------------------+-------+------+---------+------+
+
+        Stashed session keys are kept in the database as properties
+        associated with the message.  See ``session-key`` in
+        **notmuch-properties(7)** for more details about how they can
+        be useful.
+
         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
@@ -201,5 +231,6 @@ SEE ALSO
 **notmuch-restore(1)**,
 **notmuch-search(1)**,
 **notmuch-search-terms(7)**,
+**notmuch-properties(7)**,
 **notmuch-show(1)**,
 **notmuch-tag(1)**
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 15/15] python: add decrypt_policy argument to Database.index_file()

In reply to this post by Daniel Kahn Gillmor
We adopt a pythonic idiom here with an optional argument, rather than
exposing the user to the C indexopts object directly.
---
 bindings/python/notmuch/database.py | 45 +++++++++++++++++++++++++++++++++++--
 bindings/python/notmuch/globals.py  |  5 +++++
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py
index 1279804a..2a07e346 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -28,6 +28,7 @@ from .globals import (
     _str,
     NotmuchDatabaseP,
     NotmuchDirectoryP,
+    NotmuchIndexoptsP,
     NotmuchMessageP,
     NotmuchTagsP,
 )
@@ -72,6 +73,9 @@ class Database(object):
     MODE = Enum(['READ_ONLY', 'READ_WRITE'])
     """Constants: Mode in which to open the database"""
 
+    DECRYPTION_POLICY = Enum(['FALSE', 'TRUE', 'AUTO', 'NOSTASH'])
+    """Constants: policies for decrypting messages during indexing"""
+
     """notmuch_database_get_directory"""
     _get_directory = nmlib.notmuch_database_get_directory
     _get_directory.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchDirectoryP)]
@@ -400,13 +404,25 @@ class Database(object):
         # return the Directory, init it with the absolute path
         return Directory(abs_dirpath, dir_p, self)
 
+    _get_default_indexopts = nmlib.notmuch_database_get_default_indexopts
+    _get_default_indexopts.argtypes = [NotmuchDatabaseP]
+    _get_default_indexopts.restype = NotmuchIndexoptsP
+
+    _indexopts_set_decrypt_policy = nmlib.notmuch_indexopts_set_decrypt_policy
+    _indexopts_set_decrypt_policy.argtypes = [NotmuchIndexoptsP, c_uint]
+    _indexopts_set_decrypt_policy.restype = None
+
+    _indexopts_destroy = nmlib.notmuch_indexopts_destroy
+    _indexopts_destroy.argtypes = [NotmuchIndexoptsP]
+    _indexopts_destroy.restype = None
+
     _index_file = nmlib.notmuch_database_index_file
     _index_file.argtypes = [NotmuchDatabaseP, c_char_p,
                              c_void_p,
                              POINTER(NotmuchMessageP)]
     _index_file.restype = c_uint
 
-    def index_file(self, filename, sync_maildir_flags=False):
+    def index_file(self, filename, sync_maildir_flags=False, decrypt_policy=None):
         """Adds a new message to the database
 
         :param filename: should be a path relative to the path of the
@@ -427,6 +443,23 @@ class Database(object):
             API. You might want to look into the underlying method
             :meth:`Message.maildir_flags_to_tags`.
 
+        :param decrypt_policy: If the message contains any encrypted
+            parts, and decrypt_policy is set to
+            :attr:`DECRYPTION_POLICY`.TRUE, notmuch will try to
+            decrypt the message and index the cleartext, stashing any
+            discovered session keys.  If it is set to
+            :attr:`DECRYPTION_POLICY`.FALSE, it will never try to
+            decrypt during indexing.  If it is set to
+            :attr:`DECRYPTION_POLICY`.AUTO, then it will try to use
+            any stashed session keys it knows about, but will not try
+            to access the user's secret keys.
+            :attr:`DECRYPTION_POLICY`.NOSTASH behaves the same as
+            :attr:`DECRYPTION_POLICY`.TRUE except that no session keys
+            are stashed in the database.  If decrypt_policy is set to
+            None (the default), then the database itself will decide
+            whether to decrypt, based on the `index.decrypt`
+            configuration setting (see notmuch-config(1)).
+
         :returns: On success, we return
 
            1) a :class:`Message` object that can be used for things
@@ -457,7 +490,15 @@ class Database(object):
         """
         self._assert_db_is_initialized()
         msg_p = NotmuchMessageP()
-        status = self._index_file(self._db, _str(filename), c_void_p(None), byref(msg_p))
+        indexopts = c_void_p(None)
+        if decrypt_policy is not None:
+            indexopts = self._get_default_indexopts(self._db)
+            self._indexopts_set_decrypt_policy(indexopts, decrypt_policy)
+
+        status = self._index_file(self._db, _str(filename), indexopts, byref(msg_p))
+
+        if indexopts:
+            self._indexopts_destroy(indexopts)
 
         if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
             raise NotmuchError(status)
diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index b1eec2cf..71426c84 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -88,3 +88,8 @@ NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
 class NotmuchFilenamesS(Structure):
     pass
 NotmuchFilenamesP = POINTER(NotmuchFilenamesS)
+
+
+class NotmuchIndexoptsS(Structure):
+    pass
+NotmuchIndexoptsP = POINTER(NotmuchIndexoptsS)
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
David Bremner-2 David Bremner-2
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 14/15] docs: clean up documentation about decryption policies

In reply to this post by Daniel Kahn Gillmor
Daniel Kahn Gillmor <[hidden email]> writes:

> Now that the range of sensible decryption policies has come into full
> view, we take a bit of space to document the distinctions.

pushed 1-14, but holding you to your promise of a test for the python
bindings part.

d
_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

[PATCH v4] python: add decrypt_policy argument to Database.index_file()

In reply to this post by Daniel Kahn Gillmor
We adopt a pythonic idiom here with an optional argument, rather than
exposing the user to the C indexopts object directly.

This now includes a simple test to ensure that the decrypt_policy
argument works as expected.
---
 bindings/python/notmuch/database.py | 45 +++++++++++++++++++++++++++++++++++--
 bindings/python/notmuch/globals.py  |  5 +++++
 test/T390-python.sh                 | 39 ++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py
index 1279804a..2a07e346 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -28,6 +28,7 @@ from .globals import (
     _str,
     NotmuchDatabaseP,
     NotmuchDirectoryP,
+    NotmuchIndexoptsP,
     NotmuchMessageP,
     NotmuchTagsP,
 )
@@ -72,6 +73,9 @@ class Database(object):
     MODE = Enum(['READ_ONLY', 'READ_WRITE'])
     """Constants: Mode in which to open the database"""
 
+    DECRYPTION_POLICY = Enum(['FALSE', 'TRUE', 'AUTO', 'NOSTASH'])
+    """Constants: policies for decrypting messages during indexing"""
+
     """notmuch_database_get_directory"""
     _get_directory = nmlib.notmuch_database_get_directory
     _get_directory.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchDirectoryP)]
@@ -400,13 +404,25 @@ class Database(object):
         # return the Directory, init it with the absolute path
         return Directory(abs_dirpath, dir_p, self)
 
+    _get_default_indexopts = nmlib.notmuch_database_get_default_indexopts
+    _get_default_indexopts.argtypes = [NotmuchDatabaseP]
+    _get_default_indexopts.restype = NotmuchIndexoptsP
+
+    _indexopts_set_decrypt_policy = nmlib.notmuch_indexopts_set_decrypt_policy
+    _indexopts_set_decrypt_policy.argtypes = [NotmuchIndexoptsP, c_uint]
+    _indexopts_set_decrypt_policy.restype = None
+
+    _indexopts_destroy = nmlib.notmuch_indexopts_destroy
+    _indexopts_destroy.argtypes = [NotmuchIndexoptsP]
+    _indexopts_destroy.restype = None
+
     _index_file = nmlib.notmuch_database_index_file
     _index_file.argtypes = [NotmuchDatabaseP, c_char_p,
                              c_void_p,
                              POINTER(NotmuchMessageP)]
     _index_file.restype = c_uint
 
-    def index_file(self, filename, sync_maildir_flags=False):
+    def index_file(self, filename, sync_maildir_flags=False, decrypt_policy=None):
         """Adds a new message to the database
 
         :param filename: should be a path relative to the path of the
@@ -427,6 +443,23 @@ class Database(object):
             API. You might want to look into the underlying method
             :meth:`Message.maildir_flags_to_tags`.
 
+        :param decrypt_policy: If the message contains any encrypted
+            parts, and decrypt_policy is set to
+            :attr:`DECRYPTION_POLICY`.TRUE, notmuch will try to
+            decrypt the message and index the cleartext, stashing any
+            discovered session keys.  If it is set to
+            :attr:`DECRYPTION_POLICY`.FALSE, it will never try to
+            decrypt during indexing.  If it is set to
+            :attr:`DECRYPTION_POLICY`.AUTO, then it will try to use
+            any stashed session keys it knows about, but will not try
+            to access the user's secret keys.
+            :attr:`DECRYPTION_POLICY`.NOSTASH behaves the same as
+            :attr:`DECRYPTION_POLICY`.TRUE except that no session keys
+            are stashed in the database.  If decrypt_policy is set to
+            None (the default), then the database itself will decide
+            whether to decrypt, based on the `index.decrypt`
+            configuration setting (see notmuch-config(1)).
+
         :returns: On success, we return
 
            1) a :class:`Message` object that can be used for things
@@ -457,7 +490,15 @@ class Database(object):
         """
         self._assert_db_is_initialized()
         msg_p = NotmuchMessageP()
-        status = self._index_file(self._db, _str(filename), c_void_p(None), byref(msg_p))
+        indexopts = c_void_p(None)
+        if decrypt_policy is not None:
+            indexopts = self._get_default_indexopts(self._db)
+            self._indexopts_set_decrypt_policy(indexopts, decrypt_policy)
+
+        status = self._index_file(self._db, _str(filename), indexopts, byref(msg_p))
+
+        if indexopts:
+            self._indexopts_destroy(indexopts)
 
         if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
             raise NotmuchError(status)
diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index b1eec2cf..71426c84 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -88,3 +88,8 @@ NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
 class NotmuchFilenamesS(Structure):
     pass
 NotmuchFilenamesP = POINTER(NotmuchFilenamesS)
+
+
+class NotmuchIndexoptsS(Structure):
+    pass
+NotmuchIndexoptsP = POINTER(NotmuchIndexoptsS)
diff --git a/test/T390-python.sh b/test/T390-python.sh
index a93a7f34..04a19fca 100755
--- a/test/T390-python.sh
+++ b/test/T390-python.sh
@@ -5,6 +5,7 @@ test_description="python bindings"
 test_require_external_prereq ${NOTMUCH_PYTHON}
 
 add_email_corpus
+add_gnupg_home
 
 test_begin_subtest "compare thread ids"
 test_python <<EOF
@@ -74,4 +75,42 @@ EOF
 notmuch search --sort=oldest-first --output=messages "tučňáččí" | sed s/^id:// > EXPECTED
 test_expect_equal_file EXPECTED OUTPUT
 
+mkdir -p "${MAIL_DIR}/cur"
+fname="${MAIL_DIR}/cur/simplemsg.eml"
+cat <<EOF > "$fname"
+From: [hidden email]
+To: [hidden email]
+Subject: encrypted message
+Date: $(date -R)
+Message-ID: <[hidden email]>
+MIME-Version: 1.0
+Content-Type: multipart/encrypted; boundary="=-=-=";
+ protocol="application/pgp-encrypted"
+
+--=-=-=
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=-=-=
+Content-Type: application/octet-stream
+
+$(printf 'Content-Type: text/plain\n\nThis is the sekrit message\n' | gpg --no-tty --batch --quiet --trust-model=always --encrypt --armor --recipient [hidden email])
+--=-=-=--
+EOF
+
+test_begin_subtest "index message with decryption"
+test_python <<EOF
+import notmuch
+db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
+(m, status) = db.index_file('$fname', decrypt_policy=notmuch.Database.DECRYPTION_POLICY.TRUE)
+if status == notmuch.errors.STATUS.DUPLICATE_MESSAGE_ID:
+   print("got duplicate message")
+q_new = notmuch.Query(db, 'sekrit')
+for m in q_new.search_messages():
+    print(m.get_filename())
+EOF
+echo "$fname" > EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
--
2.15.1

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 14/15] docs: clean up documentation about decryption policies

In reply to this post by David Bremner-2
On Fri 2017-12-08 22:27:37 -0400, David Bremner wrote:
> Daniel Kahn Gillmor <[hidden email]> writes:
>
>> Now that the range of sensible decryption policies has come into full
>> view, we take a bit of space to document the distinctions.
>
> pushed 1-14, but holding you to your promise of a test for the python
> bindings part.

id:[hidden email] should fulfil that
promise.  please let me know if you need anything else!

thanks for the review and the merges.

       --dkg

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch

signature.asc (847 bytes) Download Attachment
David Bremner-2 David Bremner-2
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v4] python: add decrypt_policy argument to Database.index_file()

In reply to this post by Daniel Kahn Gillmor
Daniel Kahn Gillmor <[hidden email]> writes:

> +Date: $(date -R)
> +Message-ID: <[hidden email]>
> +MIME-Version: 1.0
> +Content-Type: multipart/encrypted; boundary="=-=-=";
> + protocol="application/pgp-encrypted"
> +

Can you explain why you explicitely set the date here? it seems to make
the test less reproducible. In the end it doesn't matter much because
your test ignores the date, but I feel like I'm missing something.

d


_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
12