[PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted

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

[PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted

This makes no functional changes, but prepares the codebase for a
cleaner changeset for dealing with indexing some encrypted messages in
the clear.
---
 lib/index.cc | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/lib/index.cc b/lib/index.cc
index 2b98b588..3ed3b2c6 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -384,27 +384,29 @@ _index_mime_part (notmuch_message_t *message,
  GMimeMultipart *multipart = GMIME_MULTIPART (part);
  int i;
 
- if (GMIME_IS_MULTIPART_SIGNED (multipart))
-  _notmuch_message_add_term (message, "tag", "signed");
-
- if (GMIME_IS_MULTIPART_ENCRYPTED (multipart))
-  _notmuch_message_add_term (message, "tag", "encrypted");
-
- for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
-    if (GMIME_IS_MULTIPART_SIGNED (multipart)) {
- /* Don't index the signature. */
- if (i == 1)
-    continue;
- if (i > 1)
-    _notmuch_database_log (_notmuch_message_database (message),
-  "Warning: Unexpected extra parts of multipart/signed. Indexing anyway.\n");
-    }
-    if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
- /* Don't index encrypted parts. */
- continue;
-    }
+ if (GMIME_IS_MULTIPART_SIGNED (multipart)) {
+    _notmuch_message_add_term (message, "tag", "signed");
+    /* FIXME: should we try to validate the signature? */
+    
+    /* FIXME: is it always just the first part that is signed
+     in all multipart/signed messages? it is for
+     application/pgp-signature and
+     application/pkcs7-signature, which is all gmime can
+     handle anyway. */
     _index_mime_part (message,
-      g_mime_multipart_get_part (multipart, i));
+      g_mime_multipart_get_part (multipart, 0));
+    
+    if (g_mime_multipart_get_count (multipart) > 2)
+ _notmuch_database_log (_notmuch_message_database (message),
+       "Warning: Unexpected extra parts of multipart/signed. Indexing anyway.\n");
+ } else if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
+    /* Don't index encrypted parts */
+    _notmuch_message_add_term (message, "tag", "encrypted");
+ } else {
+    for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
+ _index_mime_part (message,
+  g_mime_multipart_get_part (multipart, i));
+    }
  }
  return;
     }
--
2.14.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
|

[PATCH 02/10] crypto: Move crypto.c into libutil

This prepares us for using the crypto object in both the library and
the client.

i've prefixed notmuch_crypto with _ to indicate that while this can be
built into the library when needed, it's not something to be exported
or used externally.
---
 Makefile.local            |  1 -
 mime-node.c               | 12 ++++++------
 notmuch-client.h          | 27 +++------------------------
 notmuch-reply.c           |  2 +-
 notmuch-show.c            |  2 +-
 util/Makefile.local       |  2 +-
 crypto.c => util/crypto.c | 45 +++++++++++++++++++++++++--------------------
 util/crypto.h             | 31 +++++++++++++++++++++++++++++++
 8 files changed, 68 insertions(+), 54 deletions(-)
 rename crypto.c => util/crypto.c (79%)
 create mode 100644 util/crypto.h

diff --git a/Makefile.local b/Makefile.local
index 9d9c52c2..9505b7fe 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -246,7 +246,6 @@ notmuch_client_srcs = \
  sprinter-text.c \
  query-string.c \
  mime-node.c \
- crypto.c \
  tag-util.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
diff --git a/mime-node.c b/mime-node.c
index 24d73afa..d9ff7de1 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -33,7 +33,7 @@ typedef struct mime_node_context {
     GMimeMessage *mime_message;
 
     /* Context provided by the caller. */
-    notmuch_crypto_t *crypto;
+    _notmuch_crypto_t *crypto;
 } mime_node_context_t;
 
 static int
@@ -56,7 +56,7 @@ _mime_node_context_free (mime_node_context_t *res)
 
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
- notmuch_crypto_t *crypto, mime_node_t **root_out)
+ _notmuch_crypto_t *crypto, mime_node_t **root_out)
 {
     const char *filename = notmuch_message_get_filename (message);
     mime_node_context_t *mctx;
@@ -171,7 +171,7 @@ set_signature_list_destructor (mime_node_t *node)
 /* Verify a signed mime node (GMime 2.6) */
 static void
 node_verify (mime_node_t *node, GMimeObject *part,
-     g_mime_3_unused(notmuch_crypto_context_t *cryptoctx))
+     g_mime_3_unused(GMimeCryptoContext *cryptoctx))
 {
     GError *err = NULL;
 
@@ -192,7 +192,7 @@ node_verify (mime_node_t *node, GMimeObject *part,
 /* Decrypt and optionally verify an encrypted mime node (GMime 2.6) */
 static void
 node_decrypt_and_verify (mime_node_t *node, GMimeObject *part,
- g_mime_3_unused(notmuch_crypto_context_t *cryptoctx))
+ g_mime_3_unused(GMimeCryptoContext *cryptoctx))
 {
     GError *err = NULL;
     GMimeDecryptResult *decrypt_result = NULL;
@@ -227,7 +227,7 @@ static mime_node_t *
 _mime_node_create (mime_node_t *parent, GMimeObject *part)
 {
     mime_node_t *node = talloc_zero (parent, mime_node_t);
-    notmuch_crypto_context_t *cryptoctx = NULL;
+    GMimeCryptoContext *cryptoctx = NULL;
 
     /* Set basic node properties */
     node->part = part;
@@ -265,7 +265,7 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part)
  || (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");
- cryptoctx = notmuch_crypto_get_context (node->ctx->crypto, protocol);
+ cryptoctx = _notmuch_crypto_get_gmime_context (node->ctx->crypto, protocol);
  if (!cryptoctx)
     return NULL;
     }
diff --git a/notmuch-client.h b/notmuch-client.h
index 9d0f367d..76e69501 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -31,10 +31,6 @@
 
 #include "gmime-extra.h"
 
-typedef GMimeCryptoContext notmuch_crypto_context_t;
-/* This is automatically included only since gmime 2.6.10 */
-#include <gmime/gmime-pkcs7-context.h>
-
 #include "notmuch.h"
 
 /* This is separate from notmuch-private.h because we're trying to
@@ -54,6 +50,7 @@ typedef GMimeCryptoContext notmuch_crypto_context_t;
 #include <ctype.h>
 
 #include "talloc-extra.h"
+#include "crypto.h"
 
 #define unused(x) x __attribute__ ((unused))
 
@@ -71,22 +68,12 @@ typedef struct notmuch_show_format {
       const struct notmuch_show_params *params);
 } notmuch_show_format_t;
 
-typedef struct notmuch_crypto {
-    notmuch_bool_t verify;
-    notmuch_bool_t decrypt;
-#if (GMIME_MAJOR_VERSION < 3)
-    notmuch_crypto_context_t* gpgctx;
-    notmuch_crypto_context_t* pkcs7ctx;
-    const char *gpgpath;
-#endif
-} notmuch_crypto_t;
-
 typedef struct notmuch_show_params {
     notmuch_bool_t entire_thread;
     notmuch_bool_t omit_excluded;
     notmuch_bool_t output_body;
     int part;
-    notmuch_crypto_t crypto;
+    _notmuch_crypto_t crypto;
     notmuch_bool_t include_html;
     GMimeStream *out_stream;
 } notmuch_show_params_t;
@@ -180,14 +167,6 @@ typedef struct _notmuch_config notmuch_config_t;
 void
 notmuch_exit_if_unsupported_format (void);
 
-#if (GMIME_MAJOR_VERSION <3)
-notmuch_crypto_context_t *
-notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol);
-#endif
-
-int
-notmuch_crypto_cleanup (notmuch_crypto_t *crypto);
-
 int
 notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]);
 
@@ -448,7 +427,7 @@ struct mime_node {
  */
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
- notmuch_crypto_t *crypto, mime_node_t **node_out);
+ _notmuch_crypto_t *crypto, mime_node_t **node_out);
 
 /* Return a new MIME node for the requested child part of parent.
  * parent will be used as the talloc context for the returned child
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 929f3077..00fff3ca 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -759,7 +759,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
     if (do_reply (config, query, &params, format, reply_all) != 0)
  return EXIT_FAILURE;
 
-    notmuch_crypto_cleanup (&params.crypto);
+    _notmuch_crypto_cleanup (&params.crypto);
     notmuch_query_destroy (query);
     notmuch_database_destroy (notmuch);
 
diff --git a/notmuch-show.c b/notmuch-show.c
index cdcc2a98..a35b11ad 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1241,7 +1241,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     g_mime_stream_flush (params.out_stream);
     g_object_unref (params.out_stream);
 
-    notmuch_crypto_cleanup (&params.crypto);
+    _notmuch_crypto_cleanup (&params.crypto);
     notmuch_query_destroy (query);
     notmuch_database_destroy (notmuch);
 
diff --git a/util/Makefile.local b/util/Makefile.local
index 3027880b..ba03230e 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,7 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)
 
 libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \
   $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \
- $(dir)/util.c $(dir)/gmime-extra.c
+ $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c
 
 libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o)
 
diff --git a/crypto.c b/util/crypto.c
similarity index 79%
rename from crypto.c
rename to util/crypto.c
index cc45b885..97e8c8f4 100644
--- a/crypto.c
+++ b/util/crypto.c
@@ -16,18 +16,26 @@
  * along with this program.  If not, see https://www.gnu.org/licenses/ .
  *
  * Authors: Jameson Rollins <[hidden email]>
+ *          Daniel Kahn Gillmor <[hidden email]>
  */
 
-#include "notmuch-client.h"
+#include "notmuch.h"
+#include "crypto.h"
+#include "lib/notmuch-private.h"
+#include <string.h>
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
+
 #if (GMIME_MAJOR_VERSION < 3)
 /* Create a GPG context (GMime 2.6) */
-static notmuch_crypto_context_t *
-create_gpg_context (notmuch_crypto_t *crypto)
+static GMimeCryptoContext*
+create_gpg_context (_notmuch_crypto_t *crypto)
 {
-    notmuch_crypto_context_t *gpgctx;
+    GMimeCryptoContext *gpgctx;
 
-    if (crypto->gpgctx)
+    if (crypto->gpgctx) {
  return crypto->gpgctx;
+    }
 
     /* TODO: GMimePasswordRequestFunc */
     gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
@@ -40,14 +48,14 @@ create_gpg_context (notmuch_crypto_t *crypto)
     g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
     g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
 
-    return gpgctx;
+    return crypto->gpgctx;
 }
 
 /* Create a PKCS7 context (GMime 2.6) */
-static notmuch_crypto_context_t *
-create_pkcs7_context (notmuch_crypto_t *crypto)
+static GMimeCryptoContext*
+create_pkcs7_context (_notmuch_crypto_t *crypto)
 {
-    notmuch_crypto_context_t *pkcs7ctx;
+    GMimeCryptoContext *pkcs7ctx;
 
     if (crypto->pkcs7ctx)
  return crypto->pkcs7ctx;
@@ -63,11 +71,11 @@ create_pkcs7_context (notmuch_crypto_t *crypto)
     g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) pkcs7ctx,
    FALSE);
 
-    return pkcs7ctx;
+    return crypto->pkcs7ctx;
 }
 static const struct {
     const char *protocol;
-    notmuch_crypto_context_t *(*get_context) (notmuch_crypto_t *crypto);
+    GMimeCryptoContext *(*get_context) (_notmuch_crypto_t *crypto);
 } protocols[] = {
     {
  .protocol = "application/pgp-signature",
@@ -89,10 +97,10 @@ static const struct {
 
 /* for the specified protocol return the context pointer (initializing
  * if needed) */
-notmuch_crypto_context_t *
-notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
+GMimeCryptoContext *
+_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol)
 {
-    notmuch_crypto_context_t *cryptoctx = NULL;
+    GMimeCryptoContext *cryptoctx = NULL;
     size_t i;
 
     if (! protocol) {
@@ -117,8 +125,8 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
     return NULL;
 }
 
-int
-notmuch_crypto_cleanup (notmuch_crypto_t *crypto)
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto)
 {
     if (crypto->gpgctx) {
  g_object_unref (crypto->gpgctx);
@@ -129,12 +137,9 @@ notmuch_crypto_cleanup (notmuch_crypto_t *crypto)
  g_object_unref (crypto->pkcs7ctx);
  crypto->pkcs7ctx = NULL;
     }
-
-    return 0;
 }
 #else
-int notmuch_crypto_cleanup (unused(notmuch_crypto_t *crypto))
+void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto))
 {
-    return 0;
 }
 #endif
diff --git a/util/crypto.h b/util/crypto.h
new file mode 100644
index 00000000..6d15a6ae
--- /dev/null
+++ b/util/crypto.h
@@ -0,0 +1,31 @@
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include "notmuch.h"
+#if (GMIME_MAJOR_VERSION < 3)
+#include "gmime-extra.h"
+#include <gmime/gmime.h>
+/* This is automatically included only since gmime 2.6.10 */
+#include <gmime/gmime-pkcs7-context.h>
+#endif
+
+typedef struct _notmuch_crypto {
+    notmuch_bool_t verify;
+    notmuch_bool_t decrypt;
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeCryptoContext* gpgctx;
+    GMimeCryptoContext* pkcs7ctx;
+    const char *gpgpath;
+#endif
+} _notmuch_crypto_t;
+
+
+#if (GMIME_MAJOR_VERSION < 3)
+GMimeCryptoContext *
+_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol);
+#endif
+
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto);
+
+#endif
--
2.14.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
|

[PATCH 03/10] crypto: make shared crypto code behave library-like

In reply to this post by Daniel Kahn Gillmor
If we're going to reuse the crypto code across both the library and
the client, then it needs to report error states properly and not
write to stderr.
---
 lib/database.cc |  6 ++++
 lib/notmuch.h   | 17 +++++++++++
 mime-node.c     |  7 ++++-
 util/crypto.c   | 89 ++++++++++++++++++++++++++++-----------------------------
 util/crypto.h   |  6 ++--
 5 files changed, 76 insertions(+), 49 deletions(-)

diff --git a/lib/database.cc b/lib/database.cc
index 79eb3d69..82a3d463 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -413,6 +413,12 @@ notmuch_status_to_string (notmuch_status_t status)
  return "Operation requires a database upgrade";
     case NOTMUCH_STATUS_PATH_ERROR:
  return "Path supplied is illegal for this function";
+    case NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL:
+ return "Crypto protocol missing, malformed, or unintelligible";
+    case NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION:
+ return "Crypto engine initialization failure";
+    case NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL:
+ return "Unknown crypto protocol";
     default:
     case NOTMUCH_STATUS_LAST_STATUS:
  return "Unknown error status value";
diff --git a/lib/notmuch.h b/lib/notmuch.h
index f26565f3..6c76fb40 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -191,6 +191,23 @@ typedef enum _notmuch_status {
      * function, in a way not covered by a more specific argument.
      */
     NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
+    /**
+     * A MIME object claimed to have cryptographic protection which
+     * notmuch tried to handle, but the protocol was not specified in
+     * an intelligible way.
+     */
+    NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL,
+    /**
+     * Notmuch attempted to do crypto processing, but could not
+     * initialize the engine needed to do so.
+     */
+    NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION,
+    /**
+     * A MIME object claimed to have cryptographic protection, and
+     * notmuch attempted to process it, but the specific protocol was
+     * something that notmuch doesn't know how to handle.
+     */
+    NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL,
     /**
      * Not an actual status value. Just a way to find out how many
      * valid status values there are.
diff --git a/mime-node.c b/mime-node.c
index d9ff7de1..6cd7d2de 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -265,7 +265,12 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part)
  || (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");
- cryptoctx = _notmuch_crypto_get_gmime_context (node->ctx->crypto, protocol);
+ notmuch_status_t status;
+ status = _notmuch_crypto_get_gmime_ctx_for_protocol (node->ctx->crypto,
+     protocol, &cryptoctx);
+ if (status) /* this is a warning, not an error */
+    fprintf (stderr, "Warning: %s (%s).\n", notmuch_status_to_string (status),
+     protocol ? protocol : "(NULL)");
  if (!cryptoctx)
     return NULL;
     }
diff --git a/util/crypto.c b/util/crypto.c
index 97e8c8f4..e7908197 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -27,86 +27,86 @@
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
 
 #if (GMIME_MAJOR_VERSION < 3)
-/* Create a GPG context (GMime 2.6) */
-static GMimeCryptoContext*
-create_gpg_context (_notmuch_crypto_t *crypto)
+/* Create or pass on a GPG context (GMime 2.6) */
+static notmuch_status_t
+get_gpg_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *gpgctx;
+    if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
 
     if (crypto->gpgctx) {
- return crypto->gpgctx;
+ *ctx = crypto->gpgctx;
+ return NOTMUCH_STATUS_SUCCESS;
     }
 
     /* TODO: GMimePasswordRequestFunc */
-    gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
-    if (! gpgctx) {
- fprintf (stderr, "Failed to construct gpg context.\n");
- return NULL;
+    crypto->gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
+    if (! crypto->gpgctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
     }
-    crypto->gpgctx = gpgctx;
 
-    g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
-    g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
+    g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) crypto->gpgctx, TRUE);
+    g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) crypto->gpgctx, FALSE);
 
-    return crypto->gpgctx;
+    *ctx = crypto->gpgctx;
+    return NOTMUCH_STATUS_SUCCESS;
 }
 
-/* Create a PKCS7 context (GMime 2.6) */
-static GMimeCryptoContext*
-create_pkcs7_context (_notmuch_crypto_t *crypto)
+/* Create or pass on a PKCS7 context (GMime 2.6) */
+static notmuch_status_t
+get_pkcs7_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *pkcs7ctx;
+    if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
 
-    if (crypto->pkcs7ctx)
- return crypto->pkcs7ctx;
+    if (crypto->pkcs7ctx) {
+ *ctx = crypto->pkcs7ctx;
+ return NOTMUCH_STATUS_SUCCESS;
+    }
 
     /* TODO: GMimePasswordRequestFunc */
-    pkcs7ctx = g_mime_pkcs7_context_new (NULL);
-    if (! pkcs7ctx) {
- fprintf (stderr, "Failed to construct pkcs7 context.\n");
- return NULL;
+    crypto->pkcs7ctx = g_mime_pkcs7_context_new (NULL);
+    if (! crypto->pkcs7ctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
     }
-    crypto->pkcs7ctx = pkcs7ctx;
 
-    g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) pkcs7ctx,
+    g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) crypto->pkcs7ctx,
    FALSE);
 
-    return crypto->pkcs7ctx;
+    *ctx = crypto->pkcs7ctx;
+    return NOTMUCH_STATUS_SUCCESS;
 }
 static const struct {
     const char *protocol;
-    GMimeCryptoContext *(*get_context) (_notmuch_crypto_t *crypto);
+    notmuch_status_t (*get_context) (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx);
 } protocols[] = {
     {
  .protocol = "application/pgp-signature",
- .get_context = create_gpg_context,
+ .get_context = get_gpg_context,
     },
     {
  .protocol = "application/pgp-encrypted",
- .get_context = create_gpg_context,
+ .get_context = get_gpg_context,
     },
     {
  .protocol = "application/pkcs7-signature",
- .get_context = create_pkcs7_context,
+ .get_context = get_pkcs7_context,
     },
     {
  .protocol = "application/x-pkcs7-signature",
- .get_context = create_pkcs7_context,
+ .get_context = get_pkcs7_context,
     },
 };
 
 /* for the specified protocol return the context pointer (initializing
  * if needed) */
-GMimeCryptoContext *
-_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol)
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+    const char *protocol,
+    GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *cryptoctx = NULL;
-    size_t i;
-
-    if (! protocol) {
- fprintf (stderr, "Cryptographic protocol is empty.\n");
- return cryptoctx;
-    }
+    if (! protocol)
+ return NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL;
 
     /* As per RFC 1847 section 2.1: "the [protocol] value token is
      * comprised of the type and sub-type tokens of the Content-Type".
@@ -114,15 +114,12 @@ _notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protoc
      * parameter names as defined in this document are
      * case-insensitive."  Thus, we use strcasecmp for the protocol.
      */
-    for (i = 0; i < ARRAY_SIZE (protocols); i++) {
+    for (size_t i = 0; i < ARRAY_SIZE (protocols); i++) {
  if (strcasecmp (protocol, protocols[i].protocol) == 0)
-    return protocols[i].get_context (crypto);
+    return protocols[i].get_context (crypto, ctx);
     }
 
-    fprintf (stderr, "Unknown or unsupported cryptographic protocol %s.\n",
-     protocol);
-
-    return NULL;
+    return NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL;
 }
 
 void
diff --git a/util/crypto.h b/util/crypto.h
index 6d15a6ae..d653ffb4 100644
--- a/util/crypto.h
+++ b/util/crypto.h
@@ -21,8 +21,10 @@ typedef struct _notmuch_crypto {
 
 
 #if (GMIME_MAJOR_VERSION < 3)
-GMimeCryptoContext *
-_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol);
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+    const char *protocol,
+    GMimeCryptoContext **ctx);
 #endif
 
 void
--
2.14.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
|

[PATCH 04/10] tests: prepare for more crypto tests (using add_gnupg_home)

In reply to this post by Daniel Kahn Gillmor
Move add_gnupg_home to test-lib.sh to prepare it for reuse.
---
 test/T350-crypto.sh | 17 -----------------
 test/test-lib.sh    | 15 +++++++++++++++
 2 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh
index 1d408af7..e1b8fd83 100755
--- a/test/T350-crypto.sh
+++ b/test/T350-crypto.sh
@@ -7,23 +7,6 @@
 test_description='PGP/MIME signature verification and decryption'
 . ./test-lib.sh || exit 1
 
-add_gnupg_home ()
-{
-    local output
-    [ -d ${GNUPGHOME} ] && return
-    _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
-    at_exit_function _gnupg_exit
-    mkdir -m 0700 "$GNUPGHOME"
-    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
-    test_debug "cat $GNUPGHOME/import.log"
-    if (gpg --quick-random --version >/dev/null 2>&1) ; then
- echo quick-random >> "$GNUPGHOME"/gpg.conf
-    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
- echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
-    fi
-    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
-}
-
 ##################################################
 
 add_gnupg_home
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 35024649..b8427d97 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -93,6 +93,21 @@ unset GREP_OPTIONS
 # For emacsclient
 unset ALTERNATE_EDITOR
 
+add_gnupg_home ()
+{
+    local output
+    [ -d ${GNUPGHOME} ] && return
+    mkdir -m 0700 "$GNUPGHOME"
+    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
+    test_debug "cat $GNUPGHOME/import.log"
+    if (gpg --quick-random --version >/dev/null 2>&1) ; then
+ echo quick-random >> "$GNUPGHOME"/gpg.conf
+    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
+ echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
+    fi
+    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
+}
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
--
2.14.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
|

[PATCH 05/10] index: implement notmuch_indexopts_t with try_decrypt

In reply to this post by Daniel Kahn Gillmor
This is currently mostly a wrapper around _notmuch_crypto_t that keeps
its internals private and doesn't expose any of the GMime API.
However, non-crypto indexing options might also be added later
(e.g. filters or other transformations).
---
 lib/add-message.cc    |  9 ++++++++-
 lib/indexopts.c       | 22 ++++++++++++++++++++--
 lib/notmuch-private.h |  7 +++++++
 lib/notmuch.h         | 19 +++++++++++++++++++
 4 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index 73bde7fa..1fd91c14 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -460,7 +460,7 @@ _notmuch_database_link_message (notmuch_database_t *notmuch,
 notmuch_status_t
 notmuch_database_index_file (notmuch_database_t *notmuch,
      const char *filename,
-     notmuch_indexopts_t unused (*indexopts),
+     notmuch_indexopts_t *indexopts,
      notmuch_message_t **message_ret)
 {
     notmuch_message_file_t *message_file;
@@ -468,6 +468,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS, ret2;
     notmuch_private_status_t private_status;
     notmuch_bool_t is_ghost = FALSE, is_new = FALSE;
+    notmuch_indexopts_t *def_indexopts = NULL;
 
     const char *date;
     const char *from, *to, *subject;
@@ -540,6 +541,9 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
  if (is_new || is_ghost)
     _notmuch_message_set_header_values (message, date, from, subject);
 
+ if (!indexopts)
+    indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch);
+
  ret = _notmuch_message_index_file (message, message_file);
  if (ret)
     goto DONE;
@@ -557,6 +561,9 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
     }
 
   DONE:
+    if (def_indexopts)
+ notmuch_indexopts_destroy (def_indexopts);
+
     if (message) {
  if ((ret == NOTMUCH_STATUS_SUCCESS ||
      ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) && message_ret)
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 2f9b841b..1162900c 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -21,9 +21,27 @@
 #include "notmuch-private.h"
 
 notmuch_indexopts_t *
-notmuch_database_get_default_indexopts (notmuch_database_t unused (*db))
+notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-    return NULL;
+    return talloc_zero (db, notmuch_indexopts_t);
+}
+
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+   notmuch_bool_t try_decrypt)
+{
+    if (!indexopts)
+ return NOTMUCH_STATUS_NULL_POINTER;
+    indexopts->crypto.decrypt = try_decrypt;
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_bool_t
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts)
+{
+    if (!indexopts)
+ return FALSE;
+    return indexopts->crypto.decrypt;
 }
 
 void
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index b187a80f..3168cf3c 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -51,6 +51,7 @@ NOTMUCH_BEGIN_DECLS
 #include "xutil.h"
 #include "error_util.h"
 #include "string-util.h"
+#include "crypto.h"
 
 #ifdef DEBUG
 # define DEBUG_DATABASE_SANITY 1
@@ -632,6 +633,12 @@ _notmuch_thread_create (void *ctx,
  notmuch_exclude_t omit_exclude,
  notmuch_sort_t sort);
 
+/* param.c */
+
+typedef struct _notmuch_indexopts {
+    _notmuch_crypto_t crypto;
+} notmuch_indexopts_t;
+
 NOTMUCH_END_DECLS
 
 #ifdef __cplusplus
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 6c76fb40..8baa76ab 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2214,6 +2214,25 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list);
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db);
 
+/**
+ * Specify whether to decrypt encrypted parts while indexing.
+ *
+ * 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 SET THIS FLAG TO TRUE
+ * without considering the security of your index.
+ */
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+   notmuch_bool_t try_decrypt);
+
+/**
+ * Return whether to decrypt encrypted parts while indexing.
+ * see notmuch_indexopts_set_try_decrypt.
+ */
+notmuch_bool_t
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts);
+
 /**
  * Destroy a notmuch_indexopts_t object.
  *
--
2.14.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
|

[PATCH 06/10] crypto: index encrypted parts when indexopts try_decrypt is set.

In reply to this post by Daniel Kahn Gillmor
If we see index options that ask us to decrypt when indexing a
message, and we encounter an encrypted part, we'll try to descend into
it.

If we can decrypt, we add the property index-decryption=success.

If we can't decrypt (or recognize the encrypted type of mail), we add
the property index-decryption=failure.

Note that a single message may have both values of the
"index-decryption" property: "success" and "failure".  For example,
consider a message that includes multiple layers of encryption.  If we
manage to decrypt the outer layer ("index-decryption=success"), but
fail on the inner layer ("index-decryption=failure").

Before re-indexing, we wipe this automatically-added property, so that
it will subsequently correspond to the actual semantics of the stored
index.
---
 lib/add-message.cc    |  2 +-
 lib/index.cc          | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/message.cc        | 14 +++++++--
 lib/notmuch-private.h |  1 +
 4 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index 1fd91c14..66eb0a1f 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -544,7 +544,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
  if (!indexopts)
     indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch);
 
- ret = _notmuch_message_index_file (message, message_file);
+ ret = _notmuch_message_index_file (message, indexopts, message_file);
  if (ret)
     goto DONE;
 
diff --git a/lib/index.cc b/lib/index.cc
index 3ed3b2c6..2efac307 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -351,9 +351,17 @@ _index_address_list (notmuch_message_t *message,
     }
 }
 
+static void
+_index_encrypted_mime_part (notmuch_message_t *message, notmuch_indexopts_t *indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeContentType *content_type,
+#endif
+    GMimeMultipartEncrypted *part);
+
 /* Callback to generate terms for each mime part of a message. */
 static void
 _index_mime_part (notmuch_message_t *message,
+  notmuch_indexopts_t *indexopts,
   GMimeObject *part)
 {
     GMimeStream *stream, *filter;
@@ -394,17 +402,23 @@ _index_mime_part (notmuch_message_t *message,
      application/pkcs7-signature, which is all gmime can
      handle anyway. */
     _index_mime_part (message,
+      indexopts,
       g_mime_multipart_get_part (multipart, 0));
     
     if (g_mime_multipart_get_count (multipart) > 2)
  _notmuch_database_log (_notmuch_message_database (message),
        "Warning: Unexpected extra parts of multipart/signed. Indexing anyway.\n");
  } else if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
-    /* Don't index encrypted parts */
     _notmuch_message_add_term (message, "tag", "encrypted");
+    _index_encrypted_mime_part(message, indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+       content_type,
+#endif
+       GMIME_MULTIPART_ENCRYPTED (part));
  } else {
     for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
  _index_mime_part (message,
+  indexopts,
   g_mime_multipart_get_part (multipart, i));
     }
  }
@@ -416,7 +430,7 @@ _index_mime_part (notmuch_message_t *message,
 
  mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
 
- _index_mime_part (message, g_mime_message_get_mime_part (mime_message));
+ _index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message));
 
  return;
     }
@@ -486,8 +500,74 @@ _index_mime_part (notmuch_message_t *message,
     }
 }
 
+/* descend (if desired) into the cleartext part of an encrypted MIME
+ * part while indexing. */
+static void
+_index_encrypted_mime_part (notmuch_message_t *message,
+    notmuch_indexopts_t *indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeContentType *content_type,
+#endif
+    GMimeMultipartEncrypted *encrypted_data)
+{
+    notmuch_status_t status;
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeCryptoContext* crypto_ctx = NULL;
+    const char *protocol = NULL;
+#endif
+    GError *err = NULL;
+    notmuch_database_t * notmuch = NULL;
+    GMimeObject *clear = NULL;
+
+    if (!indexopts || !notmuch_indexopts_get_try_decrypt (indexopts))
+ return;
+
+    notmuch = _notmuch_message_database (message);
+
+#if (GMIME_MAJOR_VERSION < 3)
+    protocol = g_mime_content_type_get_parameter (content_type, "protocol");
+    status = _notmuch_crypto_get_gmime_ctx_for_protocol (&(indexopts->crypto),
+ protocol, &crypto_ctx);
+    if (status) {
+ _notmuch_database_log (notmuch, "Warning: setup failed for decrypting "
+       "during indexing. (%d)\n", status);
+ status = notmuch_message_add_property (message, "index-decryption", "failure");
+ if (status)
+    _notmuch_database_log (notmuch, "failed to add index-decryption "
+   "property (%d)\n", status);
+ return;
+    }
+#endif
+    /* we don't need the GMimeDecryptResult, because we're not looking
+     * at validating signatures, and we don't care about indexing who
+     * the message was ostensibly encrypted to.
+     */
+    clear = g_mime_multipart_encrypted_decrypt(encrypted_data, crypto_ctx,
+       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);
+ /* Indicate that we failed to decrypt during indexing */
+ status = notmuch_message_add_property (message, "index-decryption", "failure");
+ if (status)
+    _notmuch_database_log (notmuch, "failed to add index-decryption "
+   "property (%d)\n", status);
+ return;
+    }
+    _index_mime_part (message, indexopts, clear);
+    g_object_unref (clear);
+    
+    status = notmuch_message_add_property (message, "index-decryption", "success");
+    if (status)
+ _notmuch_database_log (notmuch, "failed to add index-decryption "
+       "property (%d)\n", status);
+
+}
+
 notmuch_status_t
 _notmuch_message_index_file (notmuch_message_t *message,
+     notmuch_indexopts_t *indexopts,
      notmuch_message_file_t *message_file)
 {
     GMimeMessage *mime_message;
@@ -515,7 +595,7 @@ _notmuch_message_index_file (notmuch_message_t *message,
     subject = g_mime_message_get_subject (mime_message);
     _notmuch_message_gen_terms (message, "subject", subject);
 
-    _index_mime_part (message, g_mime_message_get_mime_part (mime_message));
+    _index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message));
 
     return NOTMUCH_STATUS_SUCCESS;
 }
diff --git a/lib/message.cc b/lib/message.cc
index 0e3b5a4f..2ffa25a3 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -1961,7 +1961,7 @@ _notmuch_message_frozen (notmuch_message_t *message)
 
 notmuch_status_t
 notmuch_message_reindex (notmuch_message_t *message,
- notmuch_indexopts_t unused (*indexopts))
+ notmuch_indexopts_t *indexopts)
 {
     notmuch_database_t *notmuch = NULL;
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
@@ -1969,6 +1969,7 @@ notmuch_message_reindex (notmuch_message_t *message,
     notmuch_filenames_t *orig_filenames = NULL;
     const char *orig_thread_id = NULL;
     notmuch_message_file_t *message_file = NULL;
+    const char *autoproperties[] = { "index-decryption" };
 
     int found = 0;
 
@@ -1999,6 +2000,15 @@ notmuch_message_reindex (notmuch_message_t *message,
  goto DONE;
     }
 
+    /* all automatically-added properties should be removed before re-indexing */
+    for (size_t i = 0; i < ARRAY_SIZE (autoproperties); i++) {
+ ret = notmuch_message_remove_all_properties (message, autoproperties[i]);
+ if (ret) {
+    INTERNAL_ERROR ("failed to remove automatically-added property '%s'", autoproperties[i]);
+    goto DONE;
+ }
+    }
+
     /* re-add the filenames with the associated indexopts */
     for (; notmuch_filenames_valid (orig_filenames);
  notmuch_filenames_move_to_next (orig_filenames)) {
@@ -2038,7 +2048,7 @@ notmuch_message_reindex (notmuch_message_t *message,
  if (found == 0)
     _notmuch_message_set_header_values (message, date, from, subject);
 
- ret = _notmuch_message_index_file (message, message_file);
+ ret = _notmuch_message_index_file (message, indexopts, message_file);
 
  if (ret == NOTMUCH_STATUS_FILE_ERROR)
     continue;
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 3168cf3c..362106c8 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -447,6 +447,7 @@ _notmuch_database_link_message_to_parents (notmuch_database_t *notmuch,
 
 notmuch_status_t
 _notmuch_message_index_file (notmuch_message_t *message,
+     notmuch_indexopts_t *indexopts,
      notmuch_message_file_t *message_file);
 
 /* messages.c */
--
2.14.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
|

[PATCH 07/10] Define new config option index.try_decrypt

In reply to this post by Daniel Kahn Gillmor
By default, notmuch won't try to decrypt on indexing.  With this
patch, we make it possible to indicate a per-database preference using
the config variable "index.try_decrypt", which by default will be
false.
---
 doc/man1/notmuch-config.rst | 12 ++++++++++++
 lib/indexopts.c             | 16 +++++++++++++++-
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f..6f35d127 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -134,6 +134,18 @@ The available configuration items are described below.
 
         Default: ``gpg``.
 
+    **index.try_decrypt**
+
+        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
+        ensure that the notmuch message index is adequately protected.
+        DO NOT USE ``index.try_decrypt=true`` without considering the
+        security of your index.
+
+        Default: ``false``.
+
     **built_with.<name>**
 
         Compile time feature <name>. Current possibilities include
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 1162900c..5bd396ff 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -23,7 +23,21 @@
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-    return talloc_zero (db, notmuch_indexopts_t);
+    notmuch_indexopts_t *ret = talloc_zero (db, notmuch_indexopts_t);
+    if (ret) {
+ char * try_decrypt;
+ notmuch_status_t err;
+ if (!(err = notmuch_database_get_config (db, "index.try_decrypt", &try_decrypt))) {
+    if (try_decrypt &&
+ ((!(strcasecmp(try_decrypt, "true"))) ||
+ (!(strcasecmp(try_decrypt, "yes"))) ||
+ (!(strcasecmp(try_decrypt, "1")))))
+ notmuch_indexopts_set_try_decrypt (ret, TRUE);
+
+    free (try_decrypt);
+ }
+    }
+    return ret;
 }
 
 notmuch_status_t
--
2.14.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
|

[PATCH 08/10] add --try-decrypt=(true|false) to notmuch new

In reply to this post by Daniel Kahn Gillmor
Try to decrypt any encrypted parts of newly-discovered messages while
indexing them.  The cleartext of any successfully-decrypted messages
will be indexed, with tags applied in the same form as from notmuch
insert --try-decrypt=true.

Note: if the deprecated crypto.gpg_path configuration option is set to
anything other than "gpg", we ignore it (and print a warning on
stderr, if built against gmime < 3.0).

We also add a new test making use of this functionality.  This
requires a bit of reorganization, because we need to allow passing
--long-arguments to "notmuch new" via emacs_fcc_message
---
 completion/notmuch-completion.bash | 13 ++++++++--
 doc/man1/notmuch-new.rst           | 12 +++++++++
 notmuch-new.c                      | 29 +++++++++++++++++++++-
 test/T357-index-decryption.sh      | 51 ++++++++++++++++++++++++++++++++++++++
 test/test-lib.sh                   | 11 +++++++-
 5 files changed, 112 insertions(+), 4 deletions(-)
 create mode 100755 test/T357-index-decryption.sh

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 5201be63..17be6b8f 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -311,11 +311,20 @@ _notmuch_insert()
 _notmuch_new()
 {
     local cur prev words cword split
-    _init_completion || return
+    _init_completion -s || return
+
+    $split &&
+    case "${prev}" in
+ --try-decrypt)
+    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    return
+    ;;
+    esac
 
+    ! $split &&
     case "${cur}" in
  -*)
-    local options="--no-hooks --quiet ${_notmuch_shared_options}"
+    local options="--no-hooks --try-decrypt= --quiet ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 6acfa112..c255f980 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,6 +43,18 @@ Supported options for **new** include
     ``--quiet``
         Do not print progress or results.
 
+    ``--try-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
+        considering the security of your index.
+
+        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 EXIT STATUS
 ===========
 
diff --git a/notmuch-new.c b/notmuch-new.c
index e011788d..b5405996 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -49,6 +49,7 @@ typedef struct {
     size_t new_tags_length;
     const char **new_ignore;
     size_t new_ignore_length;
+    notmuch_indexopts_t *indexopts;
 
     int total_files;
     int processed_files;
@@ -261,7 +262,7 @@ add_file (notmuch_database_t *notmuch, const char *filename,
     if (status)
  goto DONE;
 
-    status = notmuch_database_index_file (notmuch, filename, NULL, &message);
+    status = notmuch_database_index_file (notmuch, filename, state->indexopts, &message);
     switch (status) {
     /* Success. */
     case NOTMUCH_STATUS_SUCCESS:
@@ -952,6 +953,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
     unsigned int i;
     notmuch_bool_t timer_is_active = FALSE;
     notmuch_bool_t no_hooks = FALSE;
+    int try_decrypt = -1;
     notmuch_bool_t quiet = FALSE, verbose = FALSE;
     notmuch_status_t status;
 
@@ -960,6 +962,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
  { NOTMUCH_OPT_BOOLEAN,  &verbose, "verbose", 'v', 0 },
  { NOTMUCH_OPT_BOOLEAN,  &add_files_state.debug, "debug", 'd', 0 },
  { NOTMUCH_OPT_BOOLEAN,  &no_hooks, "no-hooks", 'n', 0 },
+ { NOTMUCH_OPT_BOOLEAN,  &try_decrypt, "try-decrypt", 0, 0 },
  { NOTMUCH_OPT_INHERIT, (void *) &notmuch_shared_options, NULL, 0, 0 },
  { 0, 0, 0, 0, 0 }
     };
@@ -1077,6 +1080,30 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
     if (notmuch == NULL)
  return EXIT_FAILURE;
 
+    add_files_state.indexopts = notmuch_database_get_default_indexopts (notmuch);
+    if (!add_files_state.indexopts) {
+ fprintf (stderr, "Error: could not create index options.\n");
+ return EXIT_FAILURE;
+    }
+    if (try_decrypt == TRUE || try_decrypt == FALSE) {
+ status = notmuch_indexopts_set_try_decrypt (add_files_state.indexopts, try_decrypt);
+ if (status != NOTMUCH_STATUS_SUCCESS) {
+    fprintf (stderr, "Error: Failed to set try_decrypt to %s. (%s)\n",
+     try_decrypt ? "True" : "False", notmuch_status_to_string (status));
+    notmuch_indexopts_destroy (add_files_state.indexopts);
+    return EXIT_FAILURE;
+ }
+    }
+#if (GMIME_MAJOR_VERSION < 3)
+    if (notmuch_indexopts_get_try_decrypt (add_files_state.indexopts)) {
+ 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"
+     "\tbut ignoring (use $PATH instead)\n", gpg_path);
+    }
+#endif
+
+    
     /* Set up our handler for SIGINT. We do this after having
      * potentially done a database upgrade we this interrupt handler
      * won't support. */
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
new file mode 100755
index 00000000..7bbd81f6
--- /dev/null
+++ b/test/T357-index-decryption.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+# TODO: test index-decryption-failed
+
+test_description='indexing decrypted mail'
+. ./test-lib.sh || exit 1
+
+##################################################
+
+add_gnupg_home
+# get key fingerprint
+FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | grep '^fpr:' | cut -d: -f10)
+
+# create a test encrypted message
+test_begin_subtest 'emacs delivery of encrypted message'
+test_expect_success \
+'emacs_fcc_message \
+    "test encrypted message for cleartext index 001" \
+    "This is a test encrypted message with a wumpus.\n" \
+    "(mml-secure-message-encrypt)"'
+
+test_begin_subtest "search for unindexed cleartext"
+output=$(notmuch search wumpus)
+expected=''
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# 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 \
+    "test encrypted message for cleartext index 002" \
+    "This is a test encrypted message with a wumpus.\n" \
+    "(mml-secure-message-encrypt)"'
+
+test_begin_subtest "emacs delivery of encrypted message, indexed cleartext"
+output=$(notmuch search wumpus)
+expected='thread:0000000000000002   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox)'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# and the same search, but by property ($expected is untouched):
+test_begin_subtest "emacs search by property for one message"
+output=$(notmuch search property:index-decryption=success)
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index b8427d97..6a47354f 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -338,8 +338,17 @@ emacs_deliver_message ()
 # Accepts arbitrary extra emacs/elisp functions to modify the 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
+# at the head of the argument list, they are sent directly to "notmuch
+# new" after message delivery
 emacs_fcc_message ()
 {
+    local nmn_args=''
+    while [[ "$1" =~ ^-- ]]; do
+        nmn_args="$nmn_args $1"
+        shift
+    done
     local subject="$1"
     local body="$2"
     shift 2
@@ -358,7 +367,7 @@ emacs_fcc_message ()
    (insert \"${body}\")
    $@
    (notmuch-mua-send-and-exit))" || return 1
-    notmuch new >/dev/null
+    notmuch new $nmn_args >/dev/null
 }
 
 # Add an existing, fixed corpus of email to the database.
--
2.14.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
|

[PATCH 09/10] add --try-decrypt=(true|false) to notmuch insert

In reply to this post by Daniel Kahn Gillmor
Allow an incoming message to be delivered while indexing the
cleartext, on a per-message basis.

This requires the secret keys for the message to be available.  For
the moment, the most functional approach is to ensure that gpg-agent
is running and knows about any secret keys that might be useful to
decrypt incoming mail.

Any additional recommendations for how to phrase the caveat for this
option are welcome.

Note: if the deprecated crypto.gpg_path is set to anything other than
"gpg", we ignore it (and print a warning on stderr, if built against
gmime < 3.0).
---
 completion/notmuch-completion.bash |  6 +++++-
 doc/man1/notmuch-insert.rst        | 14 ++++++++++++++
 notmuch-insert.c                   | 32 +++++++++++++++++++++++++++++---
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 17be6b8f..72a75a94 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -287,12 +287,16 @@ _notmuch_insert()
  sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
     return
     ;;
+ --try-decrypt)
+    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    return
+    ;;
     esac
 
     ! $split &&
     case "${cur}" in
  --*)
-    local options="--create-folder --folder= --keep --no-hooks ${_notmuch_shared_options}"
+    local options="--create-folder --folder= --keep --no-hooks --try-decrypt= ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     return
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index f79600d6..647dac06 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -50,6 +50,20 @@ Supported options for **insert** include
     ``--no-hooks``
         Prevent hooks from being run.
 
+    ``--try-decrypt=(true|false)``
+
+        If true and the message is encrypted, try to decrypt the
+        message while indexing.  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
+        notmuch message index is adequately protected. DO NOT USE
+        ``--try-decrypt=true`` without considering the security of
+        your index.
+
+        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 EXIT STATUS
 ===========
 
diff --git a/notmuch-insert.c b/notmuch-insert.c
index 648bd944..c46a3278 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -379,12 +379,13 @@ FAIL:
  */
 static notmuch_status_t
 add_file (notmuch_database_t *notmuch, const char *path, tag_op_list_t *tag_ops,
-  notmuch_bool_t synchronize_flags, notmuch_bool_t keep)
+  notmuch_bool_t synchronize_flags, notmuch_bool_t keep,
+  notmuch_indexopts_t *indexopts)
 {
     notmuch_message_t *message;
     notmuch_status_t status;
 
-    status = notmuch_database_index_file (notmuch, path, NULL, &message);
+    status = notmuch_database_index_file (notmuch, path, indexopts, &message);
     if (status == NOTMUCH_STATUS_SUCCESS) {
  status = tag_op_list_apply (message, tag_ops, 0);
  if (status) {
@@ -456,17 +457,20 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
     notmuch_bool_t create_folder = FALSE;
     notmuch_bool_t keep = FALSE;
     notmuch_bool_t no_hooks = FALSE;
+    int try_decrypt = -1;
     notmuch_bool_t synchronize_flags;
     const char *maildir;
     char *newpath;
     int opt_index;
     unsigned int i;
+    notmuch_indexopts_t *indexopts;
 
     notmuch_opt_desc_t options[] = {
  { NOTMUCH_OPT_STRING, &folder, "folder", 0, 0 },
  { NOTMUCH_OPT_BOOLEAN, &create_folder, "create-folder", 0, 0 },
  { NOTMUCH_OPT_BOOLEAN, &keep, "keep", 0, 0 },
  { NOTMUCH_OPT_BOOLEAN,  &no_hooks, "no-hooks", 'n', 0 },
+ { NOTMUCH_OPT_BOOLEAN,  &try_decrypt, "try-decrypt", 0, 0 },
  { NOTMUCH_OPT_INHERIT, (void *) &notmuch_shared_options, NULL, 0, 0 },
  { NOTMUCH_OPT_END, 0, 0, 0, 0 }
     };
@@ -547,9 +551,31 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_exit_if_unmatched_db_uuid (notmuch);
 
+    indexopts = notmuch_database_get_default_indexopts (notmuch);
+    if (!indexopts) {
+ fprintf (stderr, "Error: could not create index options.\n");
+ return EXIT_FAILURE;
+    }
+    if (try_decrypt == TRUE || try_decrypt == FALSE) {
+ status = notmuch_indexopts_set_try_decrypt (indexopts, try_decrypt);
+ if (status != NOTMUCH_STATUS_SUCCESS) {
+    fprintf (stderr, "Error: Failed to set try_decrypt to %s. (%s)\n",
+     try_decrypt ? "True" : "False", notmuch_status_to_string (status));
+    notmuch_indexopts_destroy (indexopts);
+    return EXIT_FAILURE;
+ }
+    }
+#if (GMIME_MAJOR_VERSION < 3)
+    if (notmuch_indexopts_get_try_decrypt (indexopts)) {
+ 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"
+     "\tbut ignoring (use $PATH instead)\n", gpg_path);
+    }
+#endif
 
     /* Index the message. */
-    status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep);
+    status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexopts);
 
     /* Commit changes. */
     close_status = notmuch_database_destroy (notmuch);
--
2.14.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
|

[PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex

In reply to this post by Daniel Kahn Gillmor
Try to decrypt any encrypted parts of newly-discovered messages while
re-indexing them.  The cleartext of any successfully-decrypted
messages will be indexed, with tags applied in the same form as from
notmuch insert --try-decrypt=true.

Note: if the deprecated crypto.gpg_path configuration option is set to
anything other than "gpg", we ignore it (and print a warning on
stderr, if built against gmime < 3.0).
---
 completion/notmuch-completion.bash | 10 +++++-
 doc/man1/notmuch-reindex.rst       | 14 ++++++++
 notmuch-reindex.c                  | 23 ++++++++++++++
 test/T357-index-decryption.sh      | 65 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 72a75a94..ef79affe 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -435,10 +435,18 @@ _notmuch_reindex()
     local cur prev words cword split
     _init_completion -s || return
 
+    $split &&
+    case "${prev}" in
+ --try-decrypt)
+    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    return
+    ;;
+    esac
+    
     ! $split &&
     case "${cur}" in
  -*)
-    local options="${_notmuch_shared_options}"
+    local options="--try-decrypt= ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index e39cc4ee..60a060a7 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -19,6 +19,20 @@ The **reindex** command searches for all messages matching the
 supplied search terms, and re-creates the full-text index on these
 messages using the supplied options.
 
+Supported options for **reindex** include
+
+    ``--try-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
+        considering the security of your index.
+
+        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 SEE ALSO
 ========
 
diff --git a/notmuch-reindex.c b/notmuch-reindex.c
index bceac722..83cd0a57 100644
--- a/notmuch-reindex.c
+++ b/notmuch-reindex.c
@@ -90,6 +90,8 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[])
     int opt_index;
     int ret;
     notmuch_indexopts_t *indexopts = NULL;
+    int try_decrypt = -1;
+    notmuch_status_t status;
 
     /* Set up our handler for SIGINT */
     memset (&action, 0, sizeof (struct sigaction));
@@ -99,6 +101,7 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[])
     sigaction (SIGINT, &action, NULL);
 
     notmuch_opt_desc_t options[] = {
+ { NOTMUCH_OPT_BOOLEAN, &try_decrypt, "try-decrypt", 0, 0 },
  { NOTMUCH_OPT_INHERIT, (void *) &notmuch_shared_options, NULL, 0, 0 },
  { 0, 0, 0, 0, 0 }
     };
@@ -115,6 +118,26 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_exit_if_unmatched_db_uuid (notmuch);
 
+    indexopts = notmuch_database_get_default_indexopts (notmuch);
+    if (!indexopts)
+ return EXIT_FAILURE;
+
+    if (try_decrypt == TRUE || try_decrypt == FALSE) {
+ status = notmuch_indexopts_set_try_decrypt (indexopts, try_decrypt);
+ if (status)
+    fprintf (stderr, "Warning: failed to set --try-decrypt to %d (%s)\n",
+     try_decrypt, notmuch_status_to_string (status));
+    }
+
+#if (GMIME_MAJOR_VERSION < 3)
+    if (notmuch_indexopts_get_try_decrypt (indexopts)) {
+ 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"
+     "\tbut ignoring (use $PATH instead)\n", gpg_path);
+    }
+#endif
+
     query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
     if (query_string == NULL) {
  fprintf (stderr, "Out of memory\n");
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 7bbd81f6..3d18f5af 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -48,4 +48,69 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+# add a tag to all messages to ensure that it stays after reindexing
+test_begin_subtest 'tagging all messages'
+test_expect_success 'notmuch tag +blarney "encrypted message"'
+test_begin_subtest "verify that tags have not changed"
+output=$(notmuch search tag:blarney)
+expected='thread:0000000000000001   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 001 (blarney encrypted inbox)
+thread:0000000000000002   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (blarney encrypted inbox)'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# see if first message shows up after reindexing with --try-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_begin_subtest "reindexed encrypted message, including cleartext"
+output=$(notmuch search wumpus)
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# and the same search, but by property ($expected is untouched):
+test_begin_subtest "emacs search by property for both messages"
+output=$(notmuch search property:index-decryption=success)
+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_begin_subtest "reindexed encrypted messages, without cleartext"
+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)
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# ensure that the tags remain even when we are dropping the cleartext.
+test_begin_subtest "verify that tags remain without cleartext"
+output=$(notmuch search tag:blarney)
+expected='thread:0000000000000001   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 001 (blarney encrypted inbox)
+thread:0000000000000002   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (blarney encrypted inbox)'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+
+# TODO: test removal of a message from the message store between
+# indexing and reindexing.
+
+# TODO: insert the same message into the message store twice, index,
+# remove one of them from the message store, and then reindex.
+# reindexing should return a failure but the message should still be
+# present? -- or what should the semantics be if you ask to reindex a
+# message whose underlying files have been renamed or moved or
+# removed?
+
 test_done
--
2.14.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 07/10] Define new config option index.try_decrypt

In reply to this post by Daniel Kahn Gillmor
On Tue 2017-09-12 19:01:50 -0400, Daniel Kahn Gillmor wrote:
> By default, notmuch won't try to decrypt on indexing.  With this
> patch, we make it possible to indicate a per-database preference using
> the config variable "index.try_decrypt", which by default will be
> false.

I've just discovered that this particular patch doesn't work properly.

Despite doing "notmuch config set index.try_decrypt true", i get the
empty string when i invoke (in C): notmuch_config_get(db,
"index.try_decrypt").

I think that means i've misunderstood the notmuch config subsystem,
unfortunately.  I would be happy to hear any suggestions for what i
should be doing differently!

The rest of the series should still hold up, though.

    --dkg

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

signature.asc (847 bytes) Download Attachment
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

cleartext-indexing

In reply to this post by Daniel Kahn Gillmor
This series is now rebased on top of Jani's unobjectionable 2-commit
"lib: signature content type indexing" series (as well as my third
commit on that thread), id:[hidden email]

I welcome review -- some flavor of this series has been kicking around
for over a year now, and it would be great to get more parts of it
merged.

Regards,

        --dkg

_______________________________________________
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 v2 01/10] crypto: Move crypto.c into libutil

This prepares us for using the crypto object in both the library and
the client.

i've prefixed notmuch_crypto with _ to indicate that while this can be
built into the library when needed, it's not something to be exported
or used externally.
---
 Makefile.local            |  1 -
 mime-node.c               | 12 ++++++------
 notmuch-client.h          | 27 +++------------------------
 notmuch-reply.c           |  2 +-
 notmuch-show.c            |  2 +-
 util/Makefile.local       |  2 +-
 crypto.c => util/crypto.c | 45 +++++++++++++++++++++++++--------------------
 util/crypto.h             | 31 +++++++++++++++++++++++++++++++
 8 files changed, 68 insertions(+), 54 deletions(-)
 rename crypto.c => util/crypto.c (79%)
 create mode 100644 util/crypto.h

diff --git a/Makefile.local b/Makefile.local
index 9d9c52c2..9505b7fe 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -246,7 +246,6 @@ notmuch_client_srcs = \
  sprinter-text.c \
  query-string.c \
  mime-node.c \
- crypto.c \
  tag-util.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
diff --git a/mime-node.c b/mime-node.c
index 24d73afa..d9ff7de1 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -33,7 +33,7 @@ typedef struct mime_node_context {
     GMimeMessage *mime_message;
 
     /* Context provided by the caller. */
-    notmuch_crypto_t *crypto;
+    _notmuch_crypto_t *crypto;
 } mime_node_context_t;
 
 static int
@@ -56,7 +56,7 @@ _mime_node_context_free (mime_node_context_t *res)
 
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
- notmuch_crypto_t *crypto, mime_node_t **root_out)
+ _notmuch_crypto_t *crypto, mime_node_t **root_out)
 {
     const char *filename = notmuch_message_get_filename (message);
     mime_node_context_t *mctx;
@@ -171,7 +171,7 @@ set_signature_list_destructor (mime_node_t *node)
 /* Verify a signed mime node (GMime 2.6) */
 static void
 node_verify (mime_node_t *node, GMimeObject *part,
-     g_mime_3_unused(notmuch_crypto_context_t *cryptoctx))
+     g_mime_3_unused(GMimeCryptoContext *cryptoctx))
 {
     GError *err = NULL;
 
@@ -192,7 +192,7 @@ node_verify (mime_node_t *node, GMimeObject *part,
 /* Decrypt and optionally verify an encrypted mime node (GMime 2.6) */
 static void
 node_decrypt_and_verify (mime_node_t *node, GMimeObject *part,
- g_mime_3_unused(notmuch_crypto_context_t *cryptoctx))
+ g_mime_3_unused(GMimeCryptoContext *cryptoctx))
 {
     GError *err = NULL;
     GMimeDecryptResult *decrypt_result = NULL;
@@ -227,7 +227,7 @@ static mime_node_t *
 _mime_node_create (mime_node_t *parent, GMimeObject *part)
 {
     mime_node_t *node = talloc_zero (parent, mime_node_t);
-    notmuch_crypto_context_t *cryptoctx = NULL;
+    GMimeCryptoContext *cryptoctx = NULL;
 
     /* Set basic node properties */
     node->part = part;
@@ -265,7 +265,7 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part)
  || (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");
- cryptoctx = notmuch_crypto_get_context (node->ctx->crypto, protocol);
+ cryptoctx = _notmuch_crypto_get_gmime_context (node->ctx->crypto, protocol);
  if (!cryptoctx)
     return NULL;
     }
diff --git a/notmuch-client.h b/notmuch-client.h
index 9d0f367d..76e69501 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -31,10 +31,6 @@
 
 #include "gmime-extra.h"
 
-typedef GMimeCryptoContext notmuch_crypto_context_t;
-/* This is automatically included only since gmime 2.6.10 */
-#include <gmime/gmime-pkcs7-context.h>
-
 #include "notmuch.h"
 
 /* This is separate from notmuch-private.h because we're trying to
@@ -54,6 +50,7 @@ typedef GMimeCryptoContext notmuch_crypto_context_t;
 #include <ctype.h>
 
 #include "talloc-extra.h"
+#include "crypto.h"
 
 #define unused(x) x __attribute__ ((unused))
 
@@ -71,22 +68,12 @@ typedef struct notmuch_show_format {
       const struct notmuch_show_params *params);
 } notmuch_show_format_t;
 
-typedef struct notmuch_crypto {
-    notmuch_bool_t verify;
-    notmuch_bool_t decrypt;
-#if (GMIME_MAJOR_VERSION < 3)
-    notmuch_crypto_context_t* gpgctx;
-    notmuch_crypto_context_t* pkcs7ctx;
-    const char *gpgpath;
-#endif
-} notmuch_crypto_t;
-
 typedef struct notmuch_show_params {
     notmuch_bool_t entire_thread;
     notmuch_bool_t omit_excluded;
     notmuch_bool_t output_body;
     int part;
-    notmuch_crypto_t crypto;
+    _notmuch_crypto_t crypto;
     notmuch_bool_t include_html;
     GMimeStream *out_stream;
 } notmuch_show_params_t;
@@ -180,14 +167,6 @@ typedef struct _notmuch_config notmuch_config_t;
 void
 notmuch_exit_if_unsupported_format (void);
 
-#if (GMIME_MAJOR_VERSION <3)
-notmuch_crypto_context_t *
-notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol);
-#endif
-
-int
-notmuch_crypto_cleanup (notmuch_crypto_t *crypto);
-
 int
 notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]);
 
@@ -448,7 +427,7 @@ struct mime_node {
  */
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
- notmuch_crypto_t *crypto, mime_node_t **node_out);
+ _notmuch_crypto_t *crypto, mime_node_t **node_out);
 
 /* Return a new MIME node for the requested child part of parent.
  * parent will be used as the talloc context for the returned child
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 929f3077..00fff3ca 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -759,7 +759,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
     if (do_reply (config, query, &params, format, reply_all) != 0)
  return EXIT_FAILURE;
 
-    notmuch_crypto_cleanup (&params.crypto);
+    _notmuch_crypto_cleanup (&params.crypto);
     notmuch_query_destroy (query);
     notmuch_database_destroy (notmuch);
 
diff --git a/notmuch-show.c b/notmuch-show.c
index cdcc2a98..a35b11ad 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1241,7 +1241,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     g_mime_stream_flush (params.out_stream);
     g_object_unref (params.out_stream);
 
-    notmuch_crypto_cleanup (&params.crypto);
+    _notmuch_crypto_cleanup (&params.crypto);
     notmuch_query_destroy (query);
     notmuch_database_destroy (notmuch);
 
diff --git a/util/Makefile.local b/util/Makefile.local
index 3027880b..ba03230e 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,7 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)
 
 libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \
   $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \
- $(dir)/util.c $(dir)/gmime-extra.c
+ $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c
 
 libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o)
 
diff --git a/crypto.c b/util/crypto.c
similarity index 79%
rename from crypto.c
rename to util/crypto.c
index cc45b885..97e8c8f4 100644
--- a/crypto.c
+++ b/util/crypto.c
@@ -16,18 +16,26 @@
  * along with this program.  If not, see https://www.gnu.org/licenses/ .
  *
  * Authors: Jameson Rollins <[hidden email]>
+ *          Daniel Kahn Gillmor <[hidden email]>
  */
 
-#include "notmuch-client.h"
+#include "notmuch.h"
+#include "crypto.h"
+#include "lib/notmuch-private.h"
+#include <string.h>
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
+
 #if (GMIME_MAJOR_VERSION < 3)
 /* Create a GPG context (GMime 2.6) */
-static notmuch_crypto_context_t *
-create_gpg_context (notmuch_crypto_t *crypto)
+static GMimeCryptoContext*
+create_gpg_context (_notmuch_crypto_t *crypto)
 {
-    notmuch_crypto_context_t *gpgctx;
+    GMimeCryptoContext *gpgctx;
 
-    if (crypto->gpgctx)
+    if (crypto->gpgctx) {
  return crypto->gpgctx;
+    }
 
     /* TODO: GMimePasswordRequestFunc */
     gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
@@ -40,14 +48,14 @@ create_gpg_context (notmuch_crypto_t *crypto)
     g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
     g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
 
-    return gpgctx;
+    return crypto->gpgctx;
 }
 
 /* Create a PKCS7 context (GMime 2.6) */
-static notmuch_crypto_context_t *
-create_pkcs7_context (notmuch_crypto_t *crypto)
+static GMimeCryptoContext*
+create_pkcs7_context (_notmuch_crypto_t *crypto)
 {
-    notmuch_crypto_context_t *pkcs7ctx;
+    GMimeCryptoContext *pkcs7ctx;
 
     if (crypto->pkcs7ctx)
  return crypto->pkcs7ctx;
@@ -63,11 +71,11 @@ create_pkcs7_context (notmuch_crypto_t *crypto)
     g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) pkcs7ctx,
    FALSE);
 
-    return pkcs7ctx;
+    return crypto->pkcs7ctx;
 }
 static const struct {
     const char *protocol;
-    notmuch_crypto_context_t *(*get_context) (notmuch_crypto_t *crypto);
+    GMimeCryptoContext *(*get_context) (_notmuch_crypto_t *crypto);
 } protocols[] = {
     {
  .protocol = "application/pgp-signature",
@@ -89,10 +97,10 @@ static const struct {
 
 /* for the specified protocol return the context pointer (initializing
  * if needed) */
-notmuch_crypto_context_t *
-notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
+GMimeCryptoContext *
+_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol)
 {
-    notmuch_crypto_context_t *cryptoctx = NULL;
+    GMimeCryptoContext *cryptoctx = NULL;
     size_t i;
 
     if (! protocol) {
@@ -117,8 +125,8 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
     return NULL;
 }
 
-int
-notmuch_crypto_cleanup (notmuch_crypto_t *crypto)
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto)
 {
     if (crypto->gpgctx) {
  g_object_unref (crypto->gpgctx);
@@ -129,12 +137,9 @@ notmuch_crypto_cleanup (notmuch_crypto_t *crypto)
  g_object_unref (crypto->pkcs7ctx);
  crypto->pkcs7ctx = NULL;
     }
-
-    return 0;
 }
 #else
-int notmuch_crypto_cleanup (unused(notmuch_crypto_t *crypto))
+void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto))
 {
-    return 0;
 }
 #endif
diff --git a/util/crypto.h b/util/crypto.h
new file mode 100644
index 00000000..6d15a6ae
--- /dev/null
+++ b/util/crypto.h
@@ -0,0 +1,31 @@
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include "notmuch.h"
+#if (GMIME_MAJOR_VERSION < 3)
+#include "gmime-extra.h"
+#include <gmime/gmime.h>
+/* This is automatically included only since gmime 2.6.10 */
+#include <gmime/gmime-pkcs7-context.h>
+#endif
+
+typedef struct _notmuch_crypto {
+    notmuch_bool_t verify;
+    notmuch_bool_t decrypt;
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeCryptoContext* gpgctx;
+    GMimeCryptoContext* pkcs7ctx;
+    const char *gpgpath;
+#endif
+} _notmuch_crypto_t;
+
+
+#if (GMIME_MAJOR_VERSION < 3)
+GMimeCryptoContext *
+_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol);
+#endif
+
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto);
+
+#endif
--
2.14.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
|

[PATCH v2 02/10] crypto: make shared crypto code behave library-like

In reply to this post by Daniel Kahn Gillmor
If we're going to reuse the crypto code across both the library and
the client, then it needs to report error states properly and not
write to stderr.
---
 lib/database.cc |  6 ++++
 lib/notmuch.h   | 17 +++++++++++
 mime-node.c     |  7 ++++-
 util/crypto.c   | 89 ++++++++++++++++++++++++++++-----------------------------
 util/crypto.h   |  6 ++--
 5 files changed, 76 insertions(+), 49 deletions(-)

diff --git a/lib/database.cc b/lib/database.cc
index 79eb3d69..82a3d463 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -413,6 +413,12 @@ notmuch_status_to_string (notmuch_status_t status)
  return "Operation requires a database upgrade";
     case NOTMUCH_STATUS_PATH_ERROR:
  return "Path supplied is illegal for this function";
+    case NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL:
+ return "Crypto protocol missing, malformed, or unintelligible";
+    case NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION:
+ return "Crypto engine initialization failure";
+    case NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL:
+ return "Unknown crypto protocol";
     default:
     case NOTMUCH_STATUS_LAST_STATUS:
  return "Unknown error status value";
diff --git a/lib/notmuch.h b/lib/notmuch.h
index f26565f3..6c76fb40 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -191,6 +191,23 @@ typedef enum _notmuch_status {
      * function, in a way not covered by a more specific argument.
      */
     NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
+    /**
+     * A MIME object claimed to have cryptographic protection which
+     * notmuch tried to handle, but the protocol was not specified in
+     * an intelligible way.
+     */
+    NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL,
+    /**
+     * Notmuch attempted to do crypto processing, but could not
+     * initialize the engine needed to do so.
+     */
+    NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION,
+    /**
+     * A MIME object claimed to have cryptographic protection, and
+     * notmuch attempted to process it, but the specific protocol was
+     * something that notmuch doesn't know how to handle.
+     */
+    NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL,
     /**
      * Not an actual status value. Just a way to find out how many
      * valid status values there are.
diff --git a/mime-node.c b/mime-node.c
index d9ff7de1..6cd7d2de 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -265,7 +265,12 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part)
  || (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");
- cryptoctx = _notmuch_crypto_get_gmime_context (node->ctx->crypto, protocol);
+ notmuch_status_t status;
+ status = _notmuch_crypto_get_gmime_ctx_for_protocol (node->ctx->crypto,
+     protocol, &cryptoctx);
+ if (status) /* this is a warning, not an error */
+    fprintf (stderr, "Warning: %s (%s).\n", notmuch_status_to_string (status),
+     protocol ? protocol : "(NULL)");
  if (!cryptoctx)
     return NULL;
     }
diff --git a/util/crypto.c b/util/crypto.c
index 97e8c8f4..e7908197 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -27,86 +27,86 @@
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
 
 #if (GMIME_MAJOR_VERSION < 3)
-/* Create a GPG context (GMime 2.6) */
-static GMimeCryptoContext*
-create_gpg_context (_notmuch_crypto_t *crypto)
+/* Create or pass on a GPG context (GMime 2.6) */
+static notmuch_status_t
+get_gpg_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *gpgctx;
+    if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
 
     if (crypto->gpgctx) {
- return crypto->gpgctx;
+ *ctx = crypto->gpgctx;
+ return NOTMUCH_STATUS_SUCCESS;
     }
 
     /* TODO: GMimePasswordRequestFunc */
-    gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
-    if (! gpgctx) {
- fprintf (stderr, "Failed to construct gpg context.\n");
- return NULL;
+    crypto->gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
+    if (! crypto->gpgctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
     }
-    crypto->gpgctx = gpgctx;
 
-    g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
-    g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
+    g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) crypto->gpgctx, TRUE);
+    g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) crypto->gpgctx, FALSE);
 
-    return crypto->gpgctx;
+    *ctx = crypto->gpgctx;
+    return NOTMUCH_STATUS_SUCCESS;
 }
 
-/* Create a PKCS7 context (GMime 2.6) */
-static GMimeCryptoContext*
-create_pkcs7_context (_notmuch_crypto_t *crypto)
+/* Create or pass on a PKCS7 context (GMime 2.6) */
+static notmuch_status_t
+get_pkcs7_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *pkcs7ctx;
+    if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
 
-    if (crypto->pkcs7ctx)
- return crypto->pkcs7ctx;
+    if (crypto->pkcs7ctx) {
+ *ctx = crypto->pkcs7ctx;
+ return NOTMUCH_STATUS_SUCCESS;
+    }
 
     /* TODO: GMimePasswordRequestFunc */
-    pkcs7ctx = g_mime_pkcs7_context_new (NULL);
-    if (! pkcs7ctx) {
- fprintf (stderr, "Failed to construct pkcs7 context.\n");
- return NULL;
+    crypto->pkcs7ctx = g_mime_pkcs7_context_new (NULL);
+    if (! crypto->pkcs7ctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
     }
-    crypto->pkcs7ctx = pkcs7ctx;
 
-    g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) pkcs7ctx,
+    g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) crypto->pkcs7ctx,
    FALSE);
 
-    return crypto->pkcs7ctx;
+    *ctx = crypto->pkcs7ctx;
+    return NOTMUCH_STATUS_SUCCESS;
 }
 static const struct {
     const char *protocol;
-    GMimeCryptoContext *(*get_context) (_notmuch_crypto_t *crypto);
+    notmuch_status_t (*get_context) (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx);
 } protocols[] = {
     {
  .protocol = "application/pgp-signature",
- .get_context = create_gpg_context,
+ .get_context = get_gpg_context,
     },
     {
  .protocol = "application/pgp-encrypted",
- .get_context = create_gpg_context,
+ .get_context = get_gpg_context,
     },
     {
  .protocol = "application/pkcs7-signature",
- .get_context = create_pkcs7_context,
+ .get_context = get_pkcs7_context,
     },
     {
  .protocol = "application/x-pkcs7-signature",
- .get_context = create_pkcs7_context,
+ .get_context = get_pkcs7_context,
     },
 };
 
 /* for the specified protocol return the context pointer (initializing
  * if needed) */
-GMimeCryptoContext *
-_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol)
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+    const char *protocol,
+    GMimeCryptoContext **ctx)
 {
-    GMimeCryptoContext *cryptoctx = NULL;
-    size_t i;
-
-    if (! protocol) {
- fprintf (stderr, "Cryptographic protocol is empty.\n");
- return cryptoctx;
-    }
+    if (! protocol)
+ return NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL;
 
     /* As per RFC 1847 section 2.1: "the [protocol] value token is
      * comprised of the type and sub-type tokens of the Content-Type".
@@ -114,15 +114,12 @@ _notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protoc
      * parameter names as defined in this document are
      * case-insensitive."  Thus, we use strcasecmp for the protocol.
      */
-    for (i = 0; i < ARRAY_SIZE (protocols); i++) {
+    for (size_t i = 0; i < ARRAY_SIZE (protocols); i++) {
  if (strcasecmp (protocol, protocols[i].protocol) == 0)
-    return protocols[i].get_context (crypto);
+    return protocols[i].get_context (crypto, ctx);
     }
 
-    fprintf (stderr, "Unknown or unsupported cryptographic protocol %s.\n",
-     protocol);
-
-    return NULL;
+    return NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL;
 }
 
 void
diff --git a/util/crypto.h b/util/crypto.h
index 6d15a6ae..d653ffb4 100644
--- a/util/crypto.h
+++ b/util/crypto.h
@@ -21,8 +21,10 @@ typedef struct _notmuch_crypto {
 
 
 #if (GMIME_MAJOR_VERSION < 3)
-GMimeCryptoContext *
-_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol);
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+    const char *protocol,
+    GMimeCryptoContext **ctx);
 #endif
 
 void
--
2.14.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
|

[PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home)

In reply to this post by Daniel Kahn Gillmor
Move add_gnupg_home to test-lib.sh to prepare it for reuse.
---
 test/T350-crypto.sh | 17 -----------------
 test/test-lib.sh    | 15 +++++++++++++++
 2 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh
index 1d408af7..e1b8fd83 100755
--- a/test/T350-crypto.sh
+++ b/test/T350-crypto.sh
@@ -7,23 +7,6 @@
 test_description='PGP/MIME signature verification and decryption'
 . ./test-lib.sh || exit 1
 
-add_gnupg_home ()
-{
-    local output
-    [ -d ${GNUPGHOME} ] && return
-    _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
-    at_exit_function _gnupg_exit
-    mkdir -m 0700 "$GNUPGHOME"
-    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
-    test_debug "cat $GNUPGHOME/import.log"
-    if (gpg --quick-random --version >/dev/null 2>&1) ; then
- echo quick-random >> "$GNUPGHOME"/gpg.conf
-    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
- echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
-    fi
-    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
-}
-
 ##################################################
 
 add_gnupg_home
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 35024649..b8427d97 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -93,6 +93,21 @@ unset GREP_OPTIONS
 # For emacsclient
 unset ALTERNATE_EDITOR
 
+add_gnupg_home ()
+{
+    local output
+    [ -d ${GNUPGHOME} ] && return
+    mkdir -m 0700 "$GNUPGHOME"
+    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
+    test_debug "cat $GNUPGHOME/import.log"
+    if (gpg --quick-random --version >/dev/null 2>&1) ; then
+ echo quick-random >> "$GNUPGHOME"/gpg.conf
+    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
+ echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
+    fi
+    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
+}
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
--
2.14.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
|

[PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt

In reply to this post by Daniel Kahn Gillmor
This is currently mostly a wrapper around _notmuch_crypto_t that keeps
its internals private and doesn't expose any of the GMime API.
However, non-crypto indexing options might also be added later
(e.g. filters or other transformations).
---
 lib/add-message.cc    |  9 ++++++++-
 lib/indexopts.c       | 22 ++++++++++++++++++++--
 lib/notmuch-private.h |  7 +++++++
 lib/notmuch.h         | 19 +++++++++++++++++++
 4 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index 73bde7fa..1fd91c14 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -460,7 +460,7 @@ _notmuch_database_link_message (notmuch_database_t *notmuch,
 notmuch_status_t
 notmuch_database_index_file (notmuch_database_t *notmuch,
      const char *filename,
-     notmuch_indexopts_t unused (*indexopts),
+     notmuch_indexopts_t *indexopts,
      notmuch_message_t **message_ret)
 {
     notmuch_message_file_t *message_file;
@@ -468,6 +468,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS, ret2;
     notmuch_private_status_t private_status;
     notmuch_bool_t is_ghost = FALSE, is_new = FALSE;
+    notmuch_indexopts_t *def_indexopts = NULL;
 
     const char *date;
     const char *from, *to, *subject;
@@ -540,6 +541,9 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
  if (is_new || is_ghost)
     _notmuch_message_set_header_values (message, date, from, subject);
 
+ if (!indexopts)
+    indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch);
+
  ret = _notmuch_message_index_file (message, message_file);
  if (ret)
     goto DONE;
@@ -557,6 +561,9 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
     }
 
   DONE:
+    if (def_indexopts)
+ notmuch_indexopts_destroy (def_indexopts);
+
     if (message) {
  if ((ret == NOTMUCH_STATUS_SUCCESS ||
      ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) && message_ret)
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 2f9b841b..1162900c 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -21,9 +21,27 @@
 #include "notmuch-private.h"
 
 notmuch_indexopts_t *
-notmuch_database_get_default_indexopts (notmuch_database_t unused (*db))
+notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-    return NULL;
+    return talloc_zero (db, notmuch_indexopts_t);
+}
+
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+   notmuch_bool_t try_decrypt)
+{
+    if (!indexopts)
+ return NOTMUCH_STATUS_NULL_POINTER;
+    indexopts->crypto.decrypt = try_decrypt;
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_bool_t
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts)
+{
+    if (!indexopts)
+ return FALSE;
+    return indexopts->crypto.decrypt;
 }
 
 void
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index b187a80f..3168cf3c 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -51,6 +51,7 @@ NOTMUCH_BEGIN_DECLS
 #include "xutil.h"
 #include "error_util.h"
 #include "string-util.h"
+#include "crypto.h"
 
 #ifdef DEBUG
 # define DEBUG_DATABASE_SANITY 1
@@ -632,6 +633,12 @@ _notmuch_thread_create (void *ctx,
  notmuch_exclude_t omit_exclude,
  notmuch_sort_t sort);
 
+/* param.c */
+
+typedef struct _notmuch_indexopts {
+    _notmuch_crypto_t crypto;
+} notmuch_indexopts_t;
+
 NOTMUCH_END_DECLS
 
 #ifdef __cplusplus
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 6c76fb40..8baa76ab 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2214,6 +2214,25 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list);
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db);
 
+/**
+ * Specify whether to decrypt encrypted parts while indexing.
+ *
+ * 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 SET THIS FLAG TO TRUE
+ * without considering the security of your index.
+ */
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+   notmuch_bool_t try_decrypt);
+
+/**
+ * Return whether to decrypt encrypted parts while indexing.
+ * see notmuch_indexopts_set_try_decrypt.
+ */
+notmuch_bool_t
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts);
+
 /**
  * Destroy a notmuch_indexopts_t object.
  *
--
2.14.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
|

[PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set.

In reply to this post by Daniel Kahn Gillmor
If we see index options that ask us to decrypt when indexing a
message, and we encounter an encrypted part, we'll try to descend into
it.

If we can decrypt, we add the property index-decryption=success.

If we can't decrypt (or recognize the encrypted type of mail), we add
the property index-decryption=failure.

Note that a single message may have both values of the
"index-decryption" property: "success" and "failure".  For example,
consider a message that includes multiple layers of encryption.  If we
manage to decrypt the outer layer ("index-decryption=success"), but
fail on the inner layer ("index-decryption=failure").

Before re-indexing, we wipe this automatically-added property, so that
it will subsequently correspond to the actual semantics of the stored
index.
---
 lib/add-message.cc    |   2 +-
 lib/index.cc          | 103 +++++++++++++++++++++++++++++++++++++++++++++-----
 lib/message.cc        |  14 ++++++-
 lib/notmuch-private.h |   1 +
 4 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index 1fd91c14..66eb0a1f 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -544,7 +544,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
  if (!indexopts)
     indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch);
 
- ret = _notmuch_message_index_file (message, message_file);
+ ret = _notmuch_message_index_file (message, indexopts, message_file);
  if (ret)
     goto DONE;
 
diff --git a/lib/index.cc b/lib/index.cc
index ceb444df..285928f7 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -364,9 +364,17 @@ _index_content_type (notmuch_message_t *message, GMimeObject *part)
     }
 }
 
+static void
+_index_encrypted_mime_part (notmuch_message_t *message, notmuch_indexopts_t *indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeContentType *content_type,
+#endif
+    GMimeMultipartEncrypted *part);
+
 /* Callback to generate terms for each mime part of a message. */
 static void
 _index_mime_part (notmuch_message_t *message,
+  notmuch_indexopts_t *indexopts,
   GMimeObject *part)
 {
     GMimeStream *stream, *filter;
@@ -409,17 +417,26 @@ _index_mime_part (notmuch_message_t *message,
  }
     }
     if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
- /* Don't index encrypted parts, but index their content type. */
- _index_content_type (message,
-     g_mime_multipart_get_part (multipart, i));
- if ((i != GMIME_MULTIPART_ENCRYPTED_VERSION) &&
-    (i != GMIME_MULTIPART_ENCRYPTED_CONTENT)) {
-    _notmuch_database_log (_notmuch_message_database (message),
-   "Warning: Unexpected extra parts of multipart/encrypted.\n");
+ if (i == GMIME_MULTIPART_ENCRYPTED_CONTENT) {
+    _index_encrypted_mime_part(message, indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+       content_type,
+#endif
+       GMIME_MULTIPART_ENCRYPTED (part));
+ } else {
+    /* Don't index the non-content parts of an
+     * encrypted message, but do index their content
+     * type. */
+    _index_content_type (message,
+ g_mime_multipart_get_part (multipart, i));
+    if (i != GMIME_MULTIPART_ENCRYPTED_VERSION) {
+ _notmuch_database_log (_notmuch_message_database (message),
+       "Warning: Unexpected extra parts of multipart/encrypted.\n");
+    }
  }
  continue;
     }
-    _index_mime_part (message,
+    _index_mime_part (message, indexopts,
       g_mime_multipart_get_part (multipart, i));
  }
  return;
@@ -430,7 +447,7 @@ _index_mime_part (notmuch_message_t *message,
 
  mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
 
- _index_mime_part (message, g_mime_message_get_mime_part (mime_message));
+ _index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message));
 
  return;
     }
@@ -502,8 +519,74 @@ _index_mime_part (notmuch_message_t *message,
     }
 }
 
+/* descend (if desired) into the cleartext part of an encrypted MIME
+ * part while indexing. */
+static void
+_index_encrypted_mime_part (notmuch_message_t *message,
+    notmuch_indexopts_t *indexopts,
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeContentType *content_type,
+#endif
+    GMimeMultipartEncrypted *encrypted_data)
+{
+    notmuch_status_t status;
+#if (GMIME_MAJOR_VERSION < 3)
+    GMimeCryptoContext* crypto_ctx = NULL;
+    const char *protocol = NULL;
+#endif
+    GError *err = NULL;
+    notmuch_database_t * notmuch = NULL;
+    GMimeObject *clear = NULL;
+
+    if (!indexopts || !notmuch_indexopts_get_try_decrypt (indexopts))
+ return;
+
+    notmuch = _notmuch_message_database (message);
+
+#if (GMIME_MAJOR_VERSION < 3)
+    protocol = g_mime_content_type_get_parameter (content_type, "protocol");
+    status = _notmuch_crypto_get_gmime_ctx_for_protocol (&(indexopts->crypto),
+ protocol, &crypto_ctx);
+    if (status) {
+ _notmuch_database_log (notmuch, "Warning: setup failed for decrypting "
+       "during indexing. (%d)\n", status);
+ status = notmuch_message_add_property (message, "index-decryption", "failure");
+ if (status)
+    _notmuch_database_log (notmuch, "failed to add index-decryption "
+   "property (%d)\n", status);
+ return;
+    }
+#endif
+    /* we don't need the GMimeDecryptResult, because we're not looking
+     * at validating signatures, and we don't care about indexing who
+     * the message was ostensibly encrypted to.
+     */
+    clear = g_mime_multipart_encrypted_decrypt(encrypted_data, crypto_ctx,
+       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);
+ /* Indicate that we failed to decrypt during indexing */
+ status = notmuch_message_add_property (message, "index-decryption", "failure");
+ if (status)
+    _notmuch_database_log (notmuch, "failed to add index-decryption "
+   "property (%d)\n", status);
+ return;
+    }
+    _index_mime_part (message, indexopts, clear);
+    g_object_unref (clear);
+    
+    status = notmuch_message_add_property (message, "index-decryption", "success");
+    if (status)
+ _notmuch_database_log (notmuch, "failed to add index-decryption "
+       "property (%d)\n", status);
+
+}
+
 notmuch_status_t
 _notmuch_message_index_file (notmuch_message_t *message,
+     notmuch_indexopts_t *indexopts,
      notmuch_message_file_t *message_file)
 {
     GMimeMessage *mime_message;
@@ -531,7 +614,7 @@ _notmuch_message_index_file (notmuch_message_t *message,
     subject = g_mime_message_get_subject (mime_message);
     _notmuch_message_gen_terms (message, "subject", subject);
 
-    _index_mime_part (message, g_mime_message_get_mime_part (mime_message));
+    _index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message));
 
     return NOTMUCH_STATUS_SUCCESS;
 }
diff --git a/lib/message.cc b/lib/message.cc
index 0e3b5a4f..2ffa25a3 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -1961,7 +1961,7 @@ _notmuch_message_frozen (notmuch_message_t *message)
 
 notmuch_status_t
 notmuch_message_reindex (notmuch_message_t *message,
- notmuch_indexopts_t unused (*indexopts))
+ notmuch_indexopts_t *indexopts)
 {
     notmuch_database_t *notmuch = NULL;
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
@@ -1969,6 +1969,7 @@ notmuch_message_reindex (notmuch_message_t *message,
     notmuch_filenames_t *orig_filenames = NULL;
     const char *orig_thread_id = NULL;
     notmuch_message_file_t *message_file = NULL;
+    const char *autoproperties[] = { "index-decryption" };
 
     int found = 0;
 
@@ -1999,6 +2000,15 @@ notmuch_message_reindex (notmuch_message_t *message,
  goto DONE;
     }
 
+    /* all automatically-added properties should be removed before re-indexing */
+    for (size_t i = 0; i < ARRAY_SIZE (autoproperties); i++) {
+ ret = notmuch_message_remove_all_properties (message, autoproperties[i]);
+ if (ret) {
+    INTERNAL_ERROR ("failed to remove automatically-added property '%s'", autoproperties[i]);
+    goto DONE;
+ }
+    }
+
     /* re-add the filenames with the associated indexopts */
     for (; notmuch_filenames_valid (orig_filenames);
  notmuch_filenames_move_to_next (orig_filenames)) {
@@ -2038,7 +2048,7 @@ notmuch_message_reindex (notmuch_message_t *message,
  if (found == 0)
     _notmuch_message_set_header_values (message, date, from, subject);
 
- ret = _notmuch_message_index_file (message, message_file);
+ ret = _notmuch_message_index_file (message, indexopts, message_file);
 
  if (ret == NOTMUCH_STATUS_FILE_ERROR)
     continue;
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 3168cf3c..362106c8 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -447,6 +447,7 @@ _notmuch_database_link_message_to_parents (notmuch_database_t *notmuch,
 
 notmuch_status_t
 _notmuch_message_index_file (notmuch_message_t *message,
+     notmuch_indexopts_t *indexopts,
      notmuch_message_file_t *message_file);
 
 /* messages.c */
--
2.14.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
|

[PATCH v2 06/10] config: indexing defaults will be stored in the database.

In reply to this post by Daniel Kahn Gillmor
At indexing time, the database needs to know its internal defaults.
It shouldn't be contingent on an external config file (since that
can't be retrieved from the database object itself).

This behaves the same as the query.* configurations, which are also
stored in the database itself, so we're not introducing any new
dependencies.
---
 notmuch-config.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/notmuch-config.c b/notmuch-config.c
index cb9529b9..06c54544 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -809,6 +809,7 @@ _item_split (char *item, char **group, char **key)
 
 #define BUILT_WITH_PREFIX "built_with."
 #define QUERY_PREFIX "query."
+#define INDEX_PREFIX "index."
 
 static int
 _print_db_config(notmuch_config_t *config, const char *name)
@@ -859,6 +860,8 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
  notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false");
     } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
  return _print_db_config (config, item);
+    } else if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
+ return _print_db_config (config, item);
     } else {
  char **value;
  size_t i, length;
@@ -931,6 +934,9 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
     if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
  return _set_db_config (config, item, argc, argv);
     }
+    if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
+ return _set_db_config (config, item, argc, argv);
+    }
 
     if (_item_split (item, &group, &key))
  return 1;
--
2.14.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
|

[PATCH v2 07/10] config: define new option index.try_decrypt

In reply to this post by Daniel Kahn Gillmor
By default, notmuch won't try to decrypt on indexing.  With this
patch, we make it possible to indicate a per-database preference using
the config variable "index.try_decrypt", which by default will be
false.
---
 doc/man1/notmuch-config.rst | 12 ++++++++++++
 lib/indexopts.c             | 16 +++++++++++++++-
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f..6f35d127 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -134,6 +134,18 @@ The available configuration items are described below.
 
         Default: ``gpg``.
 
+    **index.try_decrypt**
+
+        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
+        ensure that the notmuch message index is adequately protected.
+        DO NOT USE ``index.try_decrypt=true`` without considering the
+        security of your index.
+
+        Default: ``false``.
+
     **built_with.<name>**
 
         Compile time feature <name>. Current possibilities include
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 1162900c..5bd396ff 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -23,7 +23,21 @@
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-    return talloc_zero (db, notmuch_indexopts_t);
+    notmuch_indexopts_t *ret = talloc_zero (db, notmuch_indexopts_t);
+    if (ret) {
+ char * try_decrypt;
+ notmuch_status_t err;
+ if (!(err = notmuch_database_get_config (db, "index.try_decrypt", &try_decrypt))) {
+    if (try_decrypt &&
+ ((!(strcasecmp(try_decrypt, "true"))) ||
+ (!(strcasecmp(try_decrypt, "yes"))) ||
+ (!(strcasecmp(try_decrypt, "1")))))
+ notmuch_indexopts_set_try_decrypt (ret, TRUE);
+
+    free (try_decrypt);
+ }
+    }
+    return ret;
 }
 
 notmuch_status_t
--
2.14.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
|

[PATCH v2 08/10] cli/new: add --try-decrypt=(true|false)

In reply to this post by Daniel Kahn Gillmor
Try to decrypt any encrypted parts of newly-discovered messages while
indexing them.  The cleartext of any successfully-decrypted messages
will be indexed, with tags applied in the same form as from notmuch
insert --try-decrypt=true.

Note: if the deprecated crypto.gpg_path configuration option is set to
anything other than "gpg", we ignore it (and print a warning on
stderr, if built against gmime < 3.0).

We also add a new test making use of this functionality.  This
requires a bit of reorganization, because we need to allow passing
--long-arguments to "notmuch new" via emacs_fcc_message
---
 completion/notmuch-completion.bash | 13 ++++++++--
 doc/man1/notmuch-new.rst           | 12 +++++++++
 notmuch-new.c                      | 29 +++++++++++++++++++++-
 test/T357-index-decryption.sh      | 51 ++++++++++++++++++++++++++++++++++++++
 test/test-lib.sh                   | 11 +++++++-
 5 files changed, 112 insertions(+), 4 deletions(-)
 create mode 100755 test/T357-index-decryption.sh

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 5201be63..17be6b8f 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -311,11 +311,20 @@ _notmuch_insert()
 _notmuch_new()
 {
     local cur prev words cword split
-    _init_completion || return
+    _init_completion -s || return
+
+    $split &&
+    case "${prev}" in
+ --try-decrypt)
+    COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+    return
+    ;;
+    esac
 
+    ! $split &&
     case "${cur}" in
  -*)
-    local options="--no-hooks --quiet ${_notmuch_shared_options}"
+    local options="--no-hooks --try-decrypt= --quiet ${_notmuch_shared_options}"
     compopt -o nospace
     COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
     ;;
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 6acfa112..c255f980 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,6 +43,18 @@ Supported options for **new** include
     ``--quiet``
         Do not print progress or results.
 
+    ``--try-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
+        considering the security of your index.
+
+        See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 EXIT STATUS
 ===========
 
diff --git a/notmuch-new.c b/notmuch-new.c
index faeb8f0a..cffcd8bc 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -49,6 +49,7 @@ typedef struct {
     size_t new_tags_length;
     const char **new_ignore;
     size_t new_ignore_length;
+    notmuch_indexopts_t *indexopts;
 
     int total_files;
     int processed_files;
@@ -267,7 +268,7 @@ add_file (notmuch_database_t *notmuch, const char *filename,
     if (status)
  goto DONE;
 
-    status = notmuch_database_index_file (notmuch, filename, NULL, &message);
+    status = notmuch_database_index_file (notmuch, filename, state->indexopts, &message);
     switch (status) {
     /* Success. */
     case NOTMUCH_STATUS_SUCCESS:
@@ -949,6 +950,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
     unsigned int i;
     notmuch_bool_t timer_is_active = FALSE;
     notmuch_bool_t no_hooks = FALSE;
+    int try_decrypt = -1;
     notmuch_bool_t quiet = FALSE, verbose = FALSE;
     notmuch_status_t status;
 
@@ -957,6 +959,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
  { NOTMUCH_OPT_BOOLEAN,  &verbose, "verbose", 'v', 0 },
  { NOTMUCH_OPT_BOOLEAN,  &add_files_state.debug, "debug", 'd', 0 },
  { NOTMUCH_OPT_BOOLEAN,  &no_hooks, "no-hooks", 'n', 0 },
+ { NOTMUCH_OPT_BOOLEAN,  &try_decrypt, "try-decrypt", 0, 0 },
  { NOTMUCH_OPT_INHERIT, (void *) &notmuch_shared_options, NULL, 0, 0 },
  { 0, 0, 0, 0, 0 }
     };
@@ -1074,6 +1077,30 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
     if (notmuch == NULL)
  return EXIT_FAILURE;
 
+    add_files_state.indexopts = notmuch_database_get_default_indexopts (notmuch);
+    if (!add_files_state.indexopts) {
+ fprintf (stderr, "Error: could not create index options.\n");
+ return EXIT_FAILURE;
+    }
+    if (try_decrypt == TRUE || try_decrypt == FALSE) {
+ status = notmuch_indexopts_set_try_decrypt (add_files_state.indexopts, try_decrypt);
+ if (status != NOTMUCH_STATUS_SUCCESS) {
+    fprintf (stderr, "Error: Failed to set try_decrypt to %s. (%s)\n",
+     try_decrypt ? "True" : "False", notmuch_status_to_string (status));
+    notmuch_indexopts_destroy (add_files_state.indexopts);
+    return EXIT_FAILURE;
+ }
+    }
+#if (GMIME_MAJOR_VERSION < 3)
+    if (notmuch_indexopts_get_try_decrypt (add_files_state.indexopts)) {
+ 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"
+     "\tbut ignoring (use $PATH instead)\n", gpg_path);
+    }
+#endif
+
+    
     /* Set up our handler for SIGINT. We do this after having
      * potentially done a database upgrade we this interrupt handler
      * won't support. */
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
new file mode 100755
index 00000000..7bbd81f6
--- /dev/null
+++ b/test/T357-index-decryption.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+# TODO: test index-decryption-failed
+
+test_description='indexing decrypted mail'
+. ./test-lib.sh || exit 1
+
+##################################################
+
+add_gnupg_home
+# get key fingerprint
+FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | grep '^fpr:' | cut -d: -f10)
+
+# create a test encrypted message
+test_begin_subtest 'emacs delivery of encrypted message'
+test_expect_success \
+'emacs_fcc_message \
+    "test encrypted message for cleartext index 001" \
+    "This is a test encrypted message with a wumpus.\n" \
+    "(mml-secure-message-encrypt)"'
+
+test_begin_subtest "search for unindexed cleartext"
+output=$(notmuch search wumpus)
+expected=''
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# 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 \
+    "test encrypted message for cleartext index 002" \
+    "This is a test encrypted message with a wumpus.\n" \
+    "(mml-secure-message-encrypt)"'
+
+test_begin_subtest "emacs delivery of encrypted message, indexed cleartext"
+output=$(notmuch search wumpus)
+expected='thread:0000000000000002   2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (encrypted inbox)'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+# and the same search, but by property ($expected is untouched):
+test_begin_subtest "emacs search by property for one message"
+output=$(notmuch search property:index-decryption=success)
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index b8427d97..6a47354f 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -338,8 +338,17 @@ emacs_deliver_message ()
 # Accepts arbitrary extra emacs/elisp functions to modify the 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
+# at the head of the argument list, they are sent directly to "notmuch
+# new" after message delivery
 emacs_fcc_message ()
 {
+    local nmn_args=''
+    while [[ "$1" =~ ^-- ]]; do
+        nmn_args="$nmn_args $1"
+        shift
+    done
     local subject="$1"
     local body="$2"
     shift 2
@@ -358,7 +367,7 @@ emacs_fcc_message ()
    (insert \"${body}\")
    $@
    (notmuch-mua-send-and-exit))" || return 1
-    notmuch new >/dev/null
+    notmuch new $nmn_args >/dev/null
 }
 
 # Add an existing, fixed corpus of email to the database.
--
2.14.1

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