* [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted @ 2017-09-12 23:01 Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 02/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor ` (8 more replies) 0 siblings, 9 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 02/10] crypto: Move crypto.c into libutil 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 03/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor ` (7 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; - notmuch_crypto_cleanup (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.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 (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.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 <jrollins@finestructure.net> + * Daniel Kahn Gillmor <dkg@fifthhorseman.net> */ -#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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 03/10] crypto: make shared crypto code behave library-like 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 02/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 04/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor ` (6 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 04/10] tests: prepare for more crypto tests (using add_gnupg_home) 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 02/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 03/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 05/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor ` (5 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 05/10] index: implement notmuch_indexopts_t with try_decrypt 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (2 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 04/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 06/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor ` (4 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 06/10] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (3 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 05/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 07/10] Define new config option index.try_decrypt Daniel Kahn Gillmor ` (3 subsequent siblings) 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 07/10] Define new config option index.try_decrypt 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (4 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 06/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:29 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 08/10] add --try-decrypt=(true|false) to notmuch new Daniel Kahn Gillmor ` (2 subsequent siblings) 8 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH 07/10] Define new config option index.try_decrypt 2017-09-12 23:01 ` [PATCH 07/10] Define new config option index.try_decrypt Daniel Kahn Gillmor @ 2017-09-12 23:29 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:29 UTC (permalink / raw) To: Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 731 bytes --] 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 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH 08/10] add --try-decrypt=(true|false) to notmuch new 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (5 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 07/10] Define new config option index.try_decrypt Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 09/10] add --try-decrypt=(true|false) to notmuch insert Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex Daniel Kahn Gillmor 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 09/10] add --try-decrypt=(true|false) to notmuch insert 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (6 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 08/10] add --try-decrypt=(true|false) to notmuch new Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex Daniel Kahn Gillmor 8 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor ` (7 preceding siblings ...) 2017-09-12 23:01 ` [PATCH 09/10] add --try-decrypt=(true|false) to notmuch insert Daniel Kahn Gillmor @ 2017-09-12 23:01 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor 8 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-12 23:01 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* cleartext-indexing 2017-09-12 23:01 ` [PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 01/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor ` (10 more replies) 0 siblings, 11 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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:cover.1505329740.git.jani@nikula.org 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 ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 01/10] crypto: Move crypto.c into libutil 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 15:23 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 02/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor ` (9 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; - notmuch_crypto_cleanup (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.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 (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.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 <jrollins@finestructure.net> + * Daniel Kahn Gillmor <dkg@fifthhorseman.net> */ -#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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 01/10] crypto: Move crypto.c into libutil 2017-09-15 5:53 ` [PATCH v2 01/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor @ 2017-09-23 15:23 ` Jani Nikula 0 siblings, 0 replies; 67+ messages in thread From: Jani Nikula @ 2017-09-23 15:23 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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. You know, this would be considerably easier to review if this were split to separate patches: - prefixing notmuch_crypto_t and friends with _ - dropping notmuch_crypto_context_t in favour of using GMimeCryptoContext directly - moving the stuff to util - changing the notmuch_crypto_cleanup() return type I think the patch is fine, but I'd have much more confidence in each individual patch if this were split up than I have in everything together. BR, Jani. > --- > 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, ¶ms, format, reply_all) != 0) > return EXIT_FAILURE; > > - notmuch_crypto_cleanup (¶ms.crypto); > + _notmuch_crypto_cleanup (¶ms.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 (¶ms.crypto); > + _notmuch_crypto_cleanup (¶ms.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 <jrollins@finestructure.net> > + * Daniel Kahn Gillmor <dkg@fifthhorseman.net> > */ > > -#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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 02/10] crypto: make shared crypto code behave library-like 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 01/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 15:36 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor ` (8 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 02/10] crypto: make shared crypto code behave library-like 2017-09-15 5:53 ` [PATCH v2 02/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor @ 2017-09-23 15:36 ` Jani Nikula 2017-10-10 3:33 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: Jani Nikula @ 2017-09-23 15:36 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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)"); For NULL protocol this will print "((NULL))". > if (!cryptoctx) > return NULL; I guess this will work because we initialize cryptoctx to NULL, but if we return the status, I think we should trust status == success means cryptoctx is fine, and otherwise we shouldn't touch or look at cryptoctx. Other than that, LGTM. BR, Jani. > } > 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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v2 02/10] crypto: make shared crypto code behave library-like 2017-09-23 15:36 ` Jani Nikula @ 2017-10-10 3:33 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 3:33 UTC (permalink / raw) To: Jani Nikula, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 904 bytes --] On Sat 2017-09-23 18:36:18 +0300, Jani Nikula wrote: >> if (!cryptoctx) >> return NULL; > > I guess this will work because we initialize cryptoctx to NULL, but if > we return the status, I think we should trust status == success means > cryptoctx is fine, and otherwise we shouldn't touch or look at > cryptoctx. this function (_mime_node_create) is *not* returning the status -- it's returning the mime node. a non-successful status might or might not mean that the cryptoctx is set up. so i could move this check within the "if (status)" if you prefer, but if we're going to do the check here, i don't see why we wouldn't just do it regardless of status. at any rate, this is all within the gmime 2.6 variant, and we'll be able to clean it out once we move to gmime 3.0. so i've left this part as-is, while having addressed the rest of your feedback. thanks for the review! --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 01/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 02/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 15:38 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor ` (7 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) 2017-09-15 5:53 ` [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor @ 2017-09-23 15:38 ` Jani Nikula 0 siblings, 0 replies; 67+ messages in thread From: Jani Nikula @ 2017-09-23 15:38 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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 The above lines get dropped. Rebase fail? BR, Jani. > - 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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (2 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 16:10 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor ` (6 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt 2017-09-15 5:53 ` [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-09-23 16:10 ` Jani Nikula 2017-10-10 3:45 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: Jani Nikula @ 2017-09-23 16:10 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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); I wonder about the lifetime of indexopts. Should default indexopts be part of the db object, so that your caller above doesn't have to alloc/destroy it for every file? Our library interface has a leaky abstraction of the talloc hierarchical refcounting. We don't talk about it in any of the docs, some of it is implied, most of it is completely surprising if the library interface user assumes a traditional C memory allocation model without refcounting. > +} > + > +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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt 2017-09-23 16:10 ` Jani Nikula @ 2017-10-10 3:45 ` Daniel Kahn Gillmor 2017-10-14 12:40 ` Jani Nikula 0 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 3:45 UTC (permalink / raw) To: Jani Nikula, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 2168 bytes --] On Sat 2017-09-23 19:10:18 +0300, Jani Nikula wrote: >> --- 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); > > I wonder about the lifetime of indexopts. Should default indexopts be > part of the db object, so that your caller above doesn't have to > alloc/destroy it for every file? The caller doesn't have to alloc/destroy it for every file, they can alloc it once and pass it in for every file. I'd rather not have the indexopts be part of the db object itself explicitly, because i can imagine a longer-running program wanting to create two indexopts objects, configuring them differently, and re-using one or the other without wanting to modify the database itself. > Our library interface has a leaky abstraction of the talloc hierarchical > refcounting. We don't talk about it in any of the docs, some of it is > implied, most of it is completely surprising if the library interface > user assumes a traditional C memory allocation model without > refcounting. right, probably the most surprising thing would be if the user got a default indexopts from one database object, and then tried to apply it to another database object, after having deleted the first database object. However, we *do* talk about it in the docs now, so i think we've given fair warning: ------------- /** * get the current default indexing options for a given database. * * This object will survive until the database itself is destroyed, * but the caller may also release it earlier with * notmuch_indexopts_destroy. * * This object represents a set of options on how a message can be * added to the index. At the moment it is a featureless stub. * * @since libnotmuch 5.1 (notmuch 0.26) */ notmuch_indexopts_t * notmuch_database_get_default_indexopts (notmuch_database_t *db); ------------- --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt 2017-10-10 3:45 ` Daniel Kahn Gillmor @ 2017-10-14 12:40 ` Jani Nikula 0 siblings, 0 replies; 67+ messages in thread From: Jani Nikula @ 2017-10-14 12:40 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Mon, 09 Oct 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > On Sat 2017-09-23 19:10:18 +0300, Jani Nikula wrote: >>> --- 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); >> >> I wonder about the lifetime of indexopts. Should default indexopts be >> part of the db object, so that your caller above doesn't have to >> alloc/destroy it for every file? > > The caller doesn't have to alloc/destroy it for every file, they can > alloc it once and pass it in for every file. My point was, if the caller doesn't do it, it'll get alloced and destroyed per file. BR, Jani. > > I'd rather not have the indexopts be part of the db object itself > explicitly, because i can imagine a longer-running program wanting to > create two indexopts objects, configuring them differently, and re-using > one or the other without wanting to modify the database itself. > >> Our library interface has a leaky abstraction of the talloc hierarchical >> refcounting. We don't talk about it in any of the docs, some of it is >> implied, most of it is completely surprising if the library interface >> user assumes a traditional C memory allocation model without >> refcounting. > > right, probably the most surprising thing would be if the user got a > default indexopts from one database object, and then tried to apply it > to another database object, after having deleted the first database > object. > > However, we *do* talk about it in the docs now, so i think we've given > fair warning: > > ------------- > /** > * get the current default indexing options for a given database. > * > * This object will survive until the database itself is destroyed, > * but the caller may also release it earlier with > * notmuch_indexopts_destroy. > * > * This object represents a set of options on how a message can be > * added to the index. At the moment it is a featureless stub. > * > * @since libnotmuch 5.1 (notmuch 0.26) > */ > notmuch_indexopts_t * > notmuch_database_get_default_indexopts (notmuch_database_t *db); > ------------- > > --dkg ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (3 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 16:05 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 06/10] config: indexing defaults will be stored in the database Daniel Kahn Gillmor ` (5 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-09-15 5:53 ` [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor @ 2017-09-23 16:05 ` Jani Nikula 2017-10-10 4:27 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: Jani Nikula @ 2017-09-23 16:05 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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 I don't think adding this within ifdefs servers a useful purpose. It just makes the code harder to read all over the place. Please just pass it in unconditionally. > + 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 I'd like this #if block to be abstracted to separate functions for gmime 2 vs. 3. Adding conditional compilation within functions is ugly and hard to track for both branches. > + /* 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); Speaking of which, where is crypto_ctx declared for gmime 3? > + 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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-09-23 16:05 ` Jani Nikula @ 2017-10-10 4:27 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 4:27 UTC (permalink / raw) To: Jani Nikula, Notmuch Mail On Sat 2017-09-23 19:05:40 +0300, Jani Nikula wrote: > I'd like this #if block to be abstracted to separate functions for gmime > 2 vs. 3. Adding conditional compilation within functions is ugly and > hard to track for both branches. That abstraction has already happened -- it's in util/crypto.c; there's simply a chunk of code that gmime 2.6 needs that gmime 3.0 does not. :/ i welcome suggestions for how to rewrite this if you really think it's a problem, but i hope the latest revision of this series (which groups all changes into a single, cleaned-up #if block) will be acceptable. --dkg ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 06/10] config: indexing defaults will be stored in the database. 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (4 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 07/10] config: define new option index.try_decrypt Daniel Kahn Gillmor ` (4 subsequent siblings) 10 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v2 07/10] config: define new option index.try_decrypt 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (5 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 06/10] config: indexing defaults will be stored in the database Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 16:17 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor ` (3 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 07/10] config: define new option index.try_decrypt 2017-09-15 5:53 ` [PATCH v2 07/10] config: define new option index.try_decrypt Daniel Kahn Gillmor @ 2017-09-23 16:17 ` Jani Nikula 0 siblings, 0 replies; 67+ messages in thread From: Jani Nikula @ 2017-09-23 16:17 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> 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. > --- > 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))) { I like the style of always separating assigment and conditional. I wonder if this function would look cleaner by doing early returns every step of the way instead of nested ifs. if (!ret) return ret; err = notmuch_database_get_config(); if (err) return ret; and so on. > + 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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (6 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 07/10] config: define new option index.try_decrypt Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-23 16:46 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 09/10] cli/insert: " Daniel Kahn Gillmor ` (2 subsequent siblings) 10 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) 2017-09-15 5:53 ` [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor @ 2017-09-23 16:46 ` Jani Nikula 0 siblings, 0 replies; 67+ messages in thread From: Jani Nikula @ 2017-09-23 16:46 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Fri, 15 Sep 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > 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 *) ¬much_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) { As discussed in the argument parsing thread, I'm not really fond of these implicit "tristate booleans". It's surprising to look at a boolean option and check if it's either true or false... But looks like we need to involve other folks here, and figure out what we want to do wrt stdbools and my --no-arg idea for --arg=false, etc. > + 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 > + Seems like there's quite a bit of duplication with the other commands here. BR, Jani. > + > /* 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 > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v2 09/10] cli/insert: add --try-decrypt=(true|false) 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (7 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 10/10] cli/reindex: " Daniel Kahn Gillmor 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor 10 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v2 10/10] cli/reindex: add --try-decrypt=(true|false) 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (8 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 09/10] cli/insert: " Daniel Kahn Gillmor @ 2017-09-15 5:53 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor 10 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-09-15 5:53 UTC (permalink / raw) To: Notmuch Mail 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 *) ¬much_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 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* cleartext indexing, round 3 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor ` (9 preceding siblings ...) 2017-09-15 5:53 ` [PATCH v2 10/10] cli/reindex: " Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 01/15] crypto: rename notmuch_crypto_t to _notmuch_crypto_t Daniel Kahn Gillmor ` (16 more replies) 10 siblings, 17 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail What follows is the third round of the latest revision of cleartext indexing patches. This series is rebased against current master, with the improved option-handling code, and takes into consideration the review that Jani did on round 2 of this series. I welcome additional review and feedback. Many of these patches may be familiar the third time around and will hopefully be uncontroversial. Please don't hesitate to review and ack the easy ones :) I've also pushed this series to the "cleartext-indexing" branch at https://gitlab.com/dkg/notmuch for those who prefer a direct git pull. it is currently git commit 6f7f6847141db2f031b29c68d966fa13c3be2da5. Regards, --dkg ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 01/15] crypto: rename notmuch_crypto_t to _notmuch_crypto_t 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 02/15] crypto: drop pretense of notmuch_crypto_context_t Daniel Kahn Gillmor ` (15 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail The notmuch_crypto_t struct isn't used externally, and we have no plans to explicitly export it. Prefix its name (and associated functions) with _ to make that intent clear. --- crypto.c | 12 ++++++------ mime-node.c | 6 +++--- notmuch-client.h | 12 ++++++------ notmuch-reply.c | 2 +- notmuch-show.c | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crypto.c b/crypto.c index 9c557d6e..9ae303d4 100644 --- a/crypto.c +++ b/crypto.c @@ -22,7 +22,7 @@ #if (GMIME_MAJOR_VERSION < 3) /* Create a GPG context (GMime 2.6) */ static notmuch_crypto_context_t * -create_gpg_context (notmuch_crypto_t *crypto) +create_gpg_context (_notmuch_crypto_t *crypto) { notmuch_crypto_context_t *gpgctx; @@ -45,7 +45,7 @@ create_gpg_context (notmuch_crypto_t *crypto) /* Create a PKCS7 context (GMime 2.6) */ static notmuch_crypto_context_t * -create_pkcs7_context (notmuch_crypto_t *crypto) +create_pkcs7_context (_notmuch_crypto_t *crypto) { notmuch_crypto_context_t *pkcs7ctx; @@ -67,7 +67,7 @@ create_pkcs7_context (notmuch_crypto_t *crypto) } static const struct { const char *protocol; - notmuch_crypto_context_t *(*get_context) (notmuch_crypto_t *crypto); + notmuch_crypto_context_t *(*get_context) (_notmuch_crypto_t *crypto); } protocols[] = { { .protocol = "application/pgp-signature", @@ -90,7 +90,7 @@ 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) +_notmuch_crypto_get_context (_notmuch_crypto_t *crypto, const char *protocol) { notmuch_crypto_context_t *cryptoctx = NULL; size_t i; @@ -118,7 +118,7 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol) } int -notmuch_crypto_cleanup (notmuch_crypto_t *crypto) +_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto) { if (crypto->gpgctx) { g_object_unref (crypto->gpgctx); @@ -133,7 +133,7 @@ notmuch_crypto_cleanup (notmuch_crypto_t *crypto) return 0; } #else -int notmuch_crypto_cleanup (unused(notmuch_crypto_t *crypto)) +int _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto)) { return 0; } diff --git a/mime-node.c b/mime-node.c index 8b767d78..56187746 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; @@ -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_context (node->ctx->crypto, protocol); if (!cryptoctx) return NULL; } diff --git a/notmuch-client.h b/notmuch-client.h index 0365baae..224912c5 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -72,7 +72,7 @@ typedef struct notmuch_show_format { const struct notmuch_show_params *params); } notmuch_show_format_t; -typedef struct notmuch_crypto { +typedef struct _notmuch_crypto { bool verify; bool decrypt; #if (GMIME_MAJOR_VERSION < 3) @@ -80,14 +80,14 @@ typedef struct notmuch_crypto { notmuch_crypto_context_t* pkcs7ctx; const char *gpgpath; #endif -} notmuch_crypto_t; +} _notmuch_crypto_t; typedef struct notmuch_show_params { bool entire_thread; bool omit_excluded; bool output_body; int part; - notmuch_crypto_t crypto; + _notmuch_crypto_t crypto; bool include_html; GMimeStream *out_stream; } notmuch_show_params_t; @@ -183,11 +183,11 @@ 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); +_notmuch_crypto_get_context (_notmuch_crypto_t *crypto, const char *protocol); #endif int -notmuch_crypto_cleanup (notmuch_crypto_t *crypto); +_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto); int notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]); @@ -449,7 +449,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 3e5a1443..2c7cc4eb 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, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; - notmuch_crypto_cleanup (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.crypto); notmuch_query_destroy (query); notmuch_database_destroy (notmuch); diff --git a/notmuch-show.c b/notmuch-show.c index 1cfc7465..7afd3947 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1234,7 +1234,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 (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.crypto); notmuch_query_destroy (query); notmuch_database_destroy (notmuch); -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 02/15] crypto: drop pretense of notmuch_crypto_context_t 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 01/15] crypto: rename notmuch_crypto_t to _notmuch_crypto_t Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 03/15] crypto: _notmuch_crypto_cleanup should return void Daniel Kahn Gillmor ` (14 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail notmuch_crypto_context_t was introduced (i think) as some sort of abstraction layer to make notmuch somewhat independent of GMime. But it isn't even useful for GMime 3.0 or later -- we can drop the pretense that it's some sort of abstraction in this case, and just call it what it is, GMimeCryptoContext, which is useful for building against older versions of GMime. This also renames _notmuch_crypto_get_context() to _notmuch_crypto_get_gmime_context(). --- crypto.c | 16 ++++++++-------- mime-node.c | 8 ++++---- notmuch-client.h | 9 ++++----- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/crypto.c b/crypto.c index 9ae303d4..fc7c5d1d 100644 --- a/crypto.c +++ b/crypto.c @@ -21,10 +21,10 @@ #include "notmuch-client.h" #if (GMIME_MAJOR_VERSION < 3) /* Create a GPG context (GMime 2.6) */ -static notmuch_crypto_context_t * +static GMimeCryptoContext * create_gpg_context (_notmuch_crypto_t *crypto) { - notmuch_crypto_context_t *gpgctx; + GMimeCryptoContext *gpgctx; if (crypto->gpgctx) return crypto->gpgctx; @@ -44,10 +44,10 @@ create_gpg_context (_notmuch_crypto_t *crypto) } /* Create a PKCS7 context (GMime 2.6) */ -static notmuch_crypto_context_t * +static GMimeCryptoContext * create_pkcs7_context (_notmuch_crypto_t *crypto) { - notmuch_crypto_context_t *pkcs7ctx; + GMimeCryptoContext *pkcs7ctx; if (crypto->pkcs7ctx) return crypto->pkcs7ctx; @@ -67,7 +67,7 @@ create_pkcs7_context (_notmuch_crypto_t *crypto) } 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 +89,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) { diff --git a/mime-node.c b/mime-node.c index 56187746..02e3df48 100644 --- a/mime-node.c +++ b/mime-node.c @@ -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 224912c5..62acc70e 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -32,7 +32,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> @@ -76,8 +75,8 @@ typedef struct _notmuch_crypto { bool verify; bool decrypt; #if (GMIME_MAJOR_VERSION < 3) - notmuch_crypto_context_t* gpgctx; - notmuch_crypto_context_t* pkcs7ctx; + GMimeCryptoContext* gpgctx; + GMimeCryptoContext* pkcs7ctx; const char *gpgpath; #endif } _notmuch_crypto_t; @@ -182,8 +181,8 @@ 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); +GMimeCryptoContext * +_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol); #endif int -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 03/15] crypto: _notmuch_crypto_cleanup should return void 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 01/15] crypto: rename notmuch_crypto_t to _notmuch_crypto_t Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 02/15] crypto: drop pretense of notmuch_crypto_context_t Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 04/15] crypto: move into libutil Daniel Kahn Gillmor ` (13 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail There's no chance that _notmuch_crypto_cleanup() will ever return anything other than 0, and no one ever checks its return value anyway. So make it return void instead of int. --- crypto.c | 7 ++----- notmuch-client.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/crypto.c b/crypto.c index fc7c5d1d..4c1b7eec 100644 --- a/crypto.c +++ b/crypto.c @@ -117,7 +117,7 @@ _notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protoc return NULL; } -int +void _notmuch_crypto_cleanup (_notmuch_crypto_t *crypto) { if (crypto->gpgctx) { @@ -129,12 +129,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/notmuch-client.h b/notmuch-client.h index 62acc70e..bdcfd893 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -185,7 +185,7 @@ GMimeCryptoContext * _notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char *protocol); #endif -int +void _notmuch_crypto_cleanup (_notmuch_crypto_t *crypto); int -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 04/15] crypto: move into libutil 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (2 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 03/15] crypto: _notmuch_crypto_cleanup should return void Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-12 10:54 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 05/15] gmime-extra: remove duplicate GMimeAddressType typedef Daniel Kahn Gillmor ` (12 subsequent siblings) 16 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail This prepares us for using the crypto object in both the library and the client. --- Makefile.local | 1 - notmuch-client.h | 22 +--------------------- util/Makefile.local | 2 +- crypto.c => util/crypto.c | 6 +++++- util/crypto.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 35 insertions(+), 24 deletions(-) rename crypto.c => util/crypto.c (97%) 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/notmuch-client.h b/notmuch-client.h index bdcfd893..d17cdf01 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -32,9 +32,6 @@ #include "gmime-extra.h" -/* 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 +51,7 @@ #include <ctype.h> #include "talloc-extra.h" +#include "crypto.h" #define unused(x) x __attribute__ ((unused)) @@ -71,16 +69,6 @@ typedef struct notmuch_show_format { const struct notmuch_show_params *params); } notmuch_show_format_t; -typedef struct _notmuch_crypto { - bool verify; - bool decrypt; -#if (GMIME_MAJOR_VERSION < 3) - GMimeCryptoContext* gpgctx; - GMimeCryptoContext* pkcs7ctx; - const char *gpgpath; -#endif -} _notmuch_crypto_t; - typedef struct notmuch_show_params { bool entire_thread; bool omit_excluded; @@ -180,14 +168,6 @@ typedef struct _notmuch_config notmuch_config_t; void notmuch_exit_if_unsupported_format (void); -#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); - int notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]); 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 97% rename from crypto.c rename to util/crypto.c index 4c1b7eec..39954ca0 100644 --- a/crypto.c +++ b/util/crypto.c @@ -18,7 +18,11 @@ * Authors: Jameson Rollins <jrollins@finestructure.net> */ -#include "notmuch-client.h" +#include "crypto.h" +#include "lib/notmuch-private.h" + +#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) + #if (GMIME_MAJOR_VERSION < 3) /* Create a GPG context (GMime 2.6) */ static GMimeCryptoContext * diff --git a/util/crypto.h b/util/crypto.h new file mode 100644 index 00000000..80628dc5 --- /dev/null +++ b/util/crypto.h @@ -0,0 +1,28 @@ +#ifndef _CRYPTO_H +#define _CRYPTO_H + +#include <stdbool.h> +#if (GMIME_MAJOR_VERSION < 3) +#include "gmime-extra.h" +#endif + +typedef struct _notmuch_crypto { + bool verify; + bool 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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v3 04/15] crypto: move into libutil 2017-10-10 5:49 ` [PATCH v3 04/15] crypto: move into libutil Daniel Kahn Gillmor @ 2017-10-12 10:54 ` David Bremner 2017-10-12 14:07 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: David Bremner @ 2017-10-12 10:54 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > This prepares us for using the crypto object in both the library and > the client. I think we could be more precise about names here, to help outsiders get up to speed on the code. libutil was renamed to libnotmuch_util, and "the library" should probably be "libnotmuch". > diff --git a/crypto.c b/util/crypto.c > similarity index 97% > rename from crypto.c > rename to util/crypto.c > index 4c1b7eec..39954ca0 100644 > --- a/crypto.c > +++ b/util/crypto.c > @@ -18,7 +18,11 @@ > * Authors: Jameson Rollins <jrollins@finestructure.net> > */ > > -#include "notmuch-client.h" > +#include "crypto.h" > +#include "lib/notmuch-private.h" > + This seems like a kind of layering violation. What's in notmuch-private.h that needs to be exported to things outside of lib/ ? ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 04/15] crypto: move into libutil 2017-10-12 10:54 ` David Bremner @ 2017-10-12 14:07 ` Daniel Kahn Gillmor 2017-10-12 21:07 ` David Bremner 0 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-12 14:07 UTC (permalink / raw) To: David Bremner, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 1555 bytes --] Hi Bremner-- Thanks for the review! On Thu 2017-10-12 07:54:33 -0300, David Bremner wrote: > Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > >> This prepares us for using the crypto object in both the library and >> the client. > > I think we could be more precise about names here, to help outsiders get > up to speed on the code. libutil was renamed to libnotmuch_util, and > "the library" should probably be "libnotmuch". Are you asking for improved comment text in the git commit, or for something else? I'm happy to provide improved comment text, if that's what you're asking for. >> diff --git a/crypto.c b/util/crypto.c >> similarity index 97% >> rename from crypto.c >> rename to util/crypto.c >> index 4c1b7eec..39954ca0 100644 >> --- a/crypto.c >> +++ b/util/crypto.c >> @@ -18,7 +18,11 @@ >> * Authors: Jameson Rollins <jrollins@finestructure.net> >> */ >> >> -#include "notmuch-client.h" >> +#include "crypto.h" >> +#include "lib/notmuch-private.h" >> + > > This seems like a kind of layering violation. What's in > notmuch-private.h that needs to be exported to things outside of lib/ ? when building against gmime-3.0, this #include supplies: #define unused(x) x __attribute__ ((unused)) When building against gmime-2.6, it provides: #include <strings.h> I could replace it with these two things explicitly (and i could put them inside the GMIME_MAJOR_VERSION tests) if that would be preferable to #including lib/notmuch-private.h. Any preference? --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 04/15] crypto: move into libutil 2017-10-12 14:07 ` Daniel Kahn Gillmor @ 2017-10-12 21:07 ` David Bremner 0 siblings, 0 replies; 67+ messages in thread From: David Bremner @ 2017-10-12 21:07 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > Hi Bremner-- > > Thanks for the review! > > On Thu 2017-10-12 07:54:33 -0300, David Bremner wrote: >> Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: >> >>> This prepares us for using the crypto object in both the library and >>> the client. >> >> I think we could be more precise about names here, to help outsiders get >> up to speed on the code. libutil was renamed to libnotmuch_util, and >> "the library" should probably be "libnotmuch". > > Are you asking for improved comment text in the git commit, or for > something else? I'm happy to provide improved comment text, if that's > what you're asking for. > yes. >>> diff --git a/crypto.c b/util/crypto.c >>> similarity index 97% >>> rename from crypto.c >>> rename to util/crypto.c >>> index 4c1b7eec..39954ca0 100644 >>> --- a/crypto.c >>> +++ b/util/crypto.c >>> @@ -18,7 +18,11 @@ >>> * Authors: Jameson Rollins <jrollins@finestructure.net> >>> */ >>> >>> -#include "notmuch-client.h" >>> +#include "crypto.h" >>> +#include "lib/notmuch-private.h" >>> + >> >> This seems like a kind of layering violation. What's in >> notmuch-private.h that needs to be exported to things outside of lib/ ? > > when building against gmime-3.0, this #include supplies: > > #define unused(x) x __attribute__ ((unused)) > > When building against gmime-2.6, it provides: > > #include <strings.h> > > > I could replace it with these two things explicitly (and i could put > them inside the GMIME_MAJOR_VERSION tests) if that would be preferable > to #including lib/notmuch-private.h. Any preference? I guess the most correct thing would be to have a header file in util/ which has the 'unused' definition in it and is included where it is needed. Or failing that to repeat the definition as we already do. d ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 05/15] gmime-extra: remove duplicate GMimeAddressType typedef 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (3 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 04/15] crypto: move into libutil Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 06/15] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor ` (11 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail "typedef GMimeAddressType GMimeRecipientType" is already present further down in the compatibility wrapper (with other typedefs). We don't need it twice. --- util/gmime-extra.h | 1 - 1 file changed, 1 deletion(-) diff --git a/util/gmime-extra.h b/util/gmime-extra.h index de275bc1..e060bcc2 100644 --- a/util/gmime-extra.h +++ b/util/gmime-extra.h @@ -18,7 +18,6 @@ GMimeStream *g_mime_stream_stdout_new(void); #define g_mime_certificate_get_fpr16(cert) g_mime_certificate_get_key_id (cert) #define g_mime_certificate_get_uid(cert) g_mime_certificate_get_name (cert); #else /* GMime >= 3.0 */ -typedef GMimeAddressType GMimeRecipientType; #define GMIME_ENABLE_RFC_2047_WORKAROUNDS 0xdeadbeef #define g_mime_certificate_get_uid(cert) g_mime_certificate_get_key_id (cert); -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 06/15] crypto: make shared crypto code behave library-like 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (4 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 05/15] gmime-extra: remove duplicate GMimeAddressType typedef Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 07/15] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor ` (10 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 92 ++++++++++++++++++++++++++++----------------------------- util/crypto.h | 7 +++-- 5 files changed, 79 insertions(+), 50 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 35c66939..02444e09 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 e8e0cc12..669e01b1 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 02e3df48..07620f43 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 39954ca0..908ba290 100644 --- a/util/crypto.c +++ b/util/crypto.c @@ -24,85 +24,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; + if (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 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 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". @@ -110,15 +111,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 80628dc5..1ff0297d 100644 --- a/util/crypto.h +++ b/util/crypto.h @@ -4,6 +4,7 @@ #include <stdbool.h> #if (GMIME_MAJOR_VERSION < 3) #include "gmime-extra.h" +#include "notmuch.h" #endif typedef struct _notmuch_crypto { @@ -18,8 +19,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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 07/15] tests: prepare for more crypto tests (using add_gnupg_home) 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (5 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 06/15] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor ` (9 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail Move add_gnupg_home to test-lib.sh to prepare it for reuse. --- test/T350-crypto.sh | 17 ----------------- test/test-lib.sh | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh index 53bf4113..401a22c9 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..84051bc9 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -93,6 +93,23 @@ unset GREP_OPTIONS # For emacsclient unset ALTERNATE_EDITOR +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 +} + # Each test should start with something like this, after copyright notices: # # test_description='Description of this test... -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 08/15] index: implement notmuch_indexopts_t with try_decrypt 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (6 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 07/15] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-11 0:29 ` avoid double typedef Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt Daniel Kahn Gillmor ` (8 subsequent siblings) 16 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 20 ++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/add-message.cc b/lib/add-message.cc index bce10a0f..bdbbeed7 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; bool 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..cc1d6422 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, + bool try_decrypt) +{ + if (!indexopts) + return NOTMUCH_STATUS_NULL_POINTER; + indexopts->crypto.decrypt = try_decrypt; + return NOTMUCH_STATUS_SUCCESS; +} + +bool +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 e86f4582..d6008bd0 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -52,6 +52,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 @@ -633,6 +634,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 669e01b1..0b2e8305 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -42,6 +42,7 @@ NOTMUCH_BEGIN_DECLS #include <time.h> +#include <stdbool.h> #pragma GCC visibility push(default) @@ -2214,6 +2215,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, + bool try_decrypt); + +/** + * Return whether to decrypt encrypted parts while indexing. + * see notmuch_indexopts_set_try_decrypt. + */ +bool +notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts); + /** * Destroy a notmuch_indexopts_t object. * -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* avoid double typedef 2017-10-10 5:49 ` [PATCH v3 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-10-11 0:29 ` Daniel Kahn Gillmor 2017-10-11 0:30 ` [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor 2017-10-11 6:22 ` avoid double typedef Tomi Ollila 0 siblings, 2 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-11 0:29 UTC (permalink / raw) To: Notmuch Mail On IRC, Domo pointed out that older gcc complains when a typedef gets repeated. So i'm updating patch 8 in this series to avoid double typedefs. The rest of the series should be unchanged, so i'm avoiding re-flooding the lst with them, but i'm happy to send along a full round of v4 if folks would prefer that. the "cleartext-index" branch of https://gitlab.com/dkg/notmuch has been updated to include this fix, and is now at 9be9d7a6bf083968458ad2cecf4af7f0d99f1cb3 Regards, --dkg ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt 2017-10-11 0:29 ` avoid double typedef Daniel Kahn Gillmor @ 2017-10-11 0:30 ` Daniel Kahn Gillmor 2017-10-12 11:18 ` David Bremner 2017-10-11 6:22 ` avoid double typedef Tomi Ollila 1 sibling, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-11 0:30 UTC (permalink / raw) To: Notmuch Mail 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 | 20 ++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/add-message.cc b/lib/add-message.cc index bce10a0f..bdbbeed7 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; bool 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..cc1d6422 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, + bool try_decrypt) +{ + if (!indexopts) + return NOTMUCH_STATUS_NULL_POINTER; + indexopts->crypto.decrypt = try_decrypt; + return NOTMUCH_STATUS_SUCCESS; +} + +bool +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 e86f4582..da6a8c38 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -52,6 +52,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 @@ -633,6 +634,12 @@ _notmuch_thread_create (void *ctx, notmuch_exclude_t omit_exclude, notmuch_sort_t sort); +/* param.c */ + +struct _notmuch_indexopts { + _notmuch_crypto_t crypto; +}; + NOTMUCH_END_DECLS #ifdef __cplusplus diff --git a/lib/notmuch.h b/lib/notmuch.h index 669e01b1..0b2e8305 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -42,6 +42,7 @@ NOTMUCH_BEGIN_DECLS #include <time.h> +#include <stdbool.h> #pragma GCC visibility push(default) @@ -2214,6 +2215,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, + bool try_decrypt); + +/** + * Return whether to decrypt encrypted parts while indexing. + * see notmuch_indexopts_set_try_decrypt. + */ +bool +notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts); + /** * Destroy a notmuch_indexopts_t object. * -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt 2017-10-11 0:30 ` [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-10-12 11:18 ` David Bremner 2017-10-12 14:30 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: David Bremner @ 2017-10-12 11:18 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > > + if (!indexopts) > + indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch); > + I think I'd like parens here indexopts = (def_indexopts = notmuch_database_get_default_indexopts (notmuch)); or even split into two assignments. Others might disagree though. ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt 2017-10-12 11:18 ` David Bremner @ 2017-10-12 14:30 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-12 14:30 UTC (permalink / raw) To: David Bremner, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 785 bytes --] On Thu 2017-10-12 08:18:15 -0300, David Bremner wrote: > Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > >> >> + if (!indexopts) >> + indexopts = def_indexopts = notmuch_database_get_default_indexopts (notmuch); >> + > > I think I'd like parens here > > indexopts = (def_indexopts = notmuch_database_get_default_indexopts (notmuch)); > > or even split into two assignments. Others might disagree though. I've gone with the simpler variant of splitting into two assignments. My updated branch is still cleartext-index on https://gitlab.com/dkg/notmuch, with commit ID 77e8c5079e854d1934e47637c15f06bbc14a4819. Shall i push this to the mailing list as v5 of this series, or are there more reviews coming that i should try to integrate? --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: avoid double typedef 2017-10-11 0:29 ` avoid double typedef Daniel Kahn Gillmor 2017-10-11 0:30 ` [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-10-11 6:22 ` Tomi Ollila 1 sibling, 0 replies; 67+ messages in thread From: Tomi Ollila @ 2017-10-11 6:22 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail On Tue, Oct 10 2017, Daniel Kahn Gillmor wrote: > On IRC, Domo pointed out that older gcc complains when a typedef gets > repeated. So i'm updating patch 8 in this series to avoid double > typedefs. The rest of the series should be unchanged, so i'm avoiding > re-flooding the lst with them, but i'm happy to send along a full > round of v4 if folks would prefer that. This version fixed the (c99 related ;) gcc compilation error on that particular test system (actually this system where this email is sent). The me the series looks good (based on ~30 min code look-through session) and the tests pass (as good as w/o these changes). Tomi > > the "cleartext-index" branch of https://gitlab.com/dkg/notmuch has > been updated to include this fix, and is now at > 9be9d7a6bf083968458ad2cecf4af7f0d99f1cb3 > > Regards, > > --dkg > > _______________________________________________ > notmuch mailing list > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (7 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-14 14:02 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor ` (7 subsequent siblings) 16 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail In practice, we're going to see this function invoked differently depending on which gmime we build against. The compatibility layer forces our code into the lowest-common-denominator -- unable to make use of new features even when built against a newer version. Dropping the compatibility layer paves the way for clearer use of features from GMime 3.0 in future commits. --- mime-node.c | 4 ++++ util/gmime-extra.h | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mime-node.c b/mime-node.c index 07620f43..c3d5cb9b 100644 --- a/mime-node.c +++ b/mime-node.c @@ -200,7 +200,11 @@ node_decrypt_and_verify (mime_node_t *node, GMimeObject *part, node->decrypt_attempted = true; node->decrypted_child = g_mime_multipart_encrypted_decrypt +#if (GMIME_MAJOR_VERSION < 3) (encrypteddata, cryptoctx, &decrypt_result, &err); +#else + (encrypteddata, GMIME_DECRYPT_NONE, NULL, &decrypt_result, &err); +#endif if (! node->decrypted_child) { fprintf (stderr, "Failed to decrypt part: %s\n", err ? err->message : "no error explanation given"); diff --git a/util/gmime-extra.h b/util/gmime-extra.h index e060bcc2..40bf1454 100644 --- a/util/gmime-extra.h +++ b/util/gmime-extra.h @@ -29,7 +29,6 @@ GMimeStream *g_mime_stream_stdout_new(void); #define g_mime_init(flags) g_mime_init() #define g_mime_message_add_recipient(m,t,n,a) g_mime_message_add_mailbox (m,t,n,a) #define g_mime_message_set_subject(m,s) g_mime_message_set_subject(m,s,NULL) -#define g_mime_multipart_encrypted_decrypt(mpe,ctx,out,err) g_mime_multipart_encrypted_decrypt(mpe, GMIME_DECRYPT_NONE, NULL, out, err) #define g_mime_multipart_signed_verify(mps,ctx,err) g_mime_multipart_signed_verify(mps, GMIME_ENCRYPT_NONE, err) #define g_mime_object_write_to_stream(o,s) g_mime_object_write_to_stream (o,NULL,s) #define g_mime_object_set_header(o,h,v) g_mime_object_set_header (o,h,v,NULL) -- 2.14.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt 2017-10-10 5:49 ` [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt Daniel Kahn Gillmor @ 2017-10-14 14:02 ` David Bremner 0 siblings, 0 replies; 67+ messages in thread From: David Bremner @ 2017-10-14 14:02 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > In practice, we're going to see this function invoked differently > depending on which gmime we build against. The compatibility layer > forces our code into the lowest-common-denominator -- unable to make > use of new features even when built against a newer version. > > Dropping the compatibility layer paves the way for clearer use of > features from GMime 3.0 in future commits. pushed patches 7 and 9. d ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (8 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-13 1:08 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 11/15] config: indexing defaults will be stored in the database Daniel Kahn Gillmor ` (6 subsequent siblings) 16 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 91 ++++++++++++++++++++++++++++++++++++++++++++++----- lib/message.cc | 14 ++++++-- lib/notmuch-private.h | 1 + 4 files changed, 96 insertions(+), 12 deletions(-) diff --git a/lib/add-message.cc b/lib/add-message.cc index bdbbeed7..abd50057 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 e5ae2ba7..4b8b2344 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -364,9 +364,15 @@ _index_content_type (notmuch_message_t *message, GMimeObject *part) } } +static void +_index_encrypted_mime_part (notmuch_message_t *message, notmuch_indexopts_t *indexopts, + GMimeContentType *content_type, + 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; @@ -385,6 +391,7 @@ _index_mime_part (notmuch_message_t *message, } _index_content_type (message, part); + content_type = g_mime_object_get_content_type (part); if (GMIME_IS_MULTIPART (part)) { GMimeMultipart *multipart = GMIME_MULTIPART (part); @@ -409,17 +416,21 @@ _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, + content_type, + GMIME_MULTIPART_ENCRYPTED (part)); + } else { + 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 +441,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; } @@ -464,7 +475,6 @@ _index_mime_part (notmuch_message_t *message, filter = g_mime_stream_filter_new (stream); - content_type = g_mime_object_get_content_type (part); discard_non_term_filter = notmuch_filter_discard_non_term_new (content_type); g_mime_stream_filter_add (GMIME_STREAM_FILTER (filter), @@ -502,8 +512,71 @@ _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, + g_mime_3_unused(GMimeContentType *content_type), + GMimeMultipartEncrypted *encrypted_data) +{ + notmuch_status_t status; + 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) + { + GMimeCryptoContext* crypto_ctx = NULL; + const char *protocol = NULL; + 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; + } + clear = g_mime_multipart_encrypted_decrypt(encrypted_data, crypto_ctx, + NULL, &err); + } +#else + clear = g_mime_multipart_encrypted_decrypt(encrypted_data, GMIME_DECRYPT_NONE, NULL, + NULL, &err); +#endif + 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 +604,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 4ab0ed26..0102580b 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 d6008bd0..1f4e411f 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -448,6 +448,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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-10-10 5:49 ` [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor @ 2017-10-13 1:08 ` David Bremner 2017-10-13 14:35 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: David Bremner @ 2017-10-13 1:08 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > + 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; > + } The second _notmuch_database_log will override the first here. You can use _notmuch_database_log_append if you don't want to clear the existing log (e.g. at least for the second _log here). > + const char *autoproperties[] = { "index-decryption" }; I'm always a bit nervous when I see the same string hard coded into two different places. What about using some prefix naming scheme like "index.auto.decryption" with the idea that all properties with the prefix "index.auto." could be blown away. If we settle on a prefix based naming scheme now, we could do the minor tweak to the properties API later so that it's only a single call. ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-10-13 1:08 ` David Bremner @ 2017-10-13 14:35 ` Daniel Kahn Gillmor 2017-10-13 15:19 ` David Bremner 2017-10-14 11:15 ` David Bremner 0 siblings, 2 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-13 14:35 UTC (permalink / raw) To: David Bremner, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 1902 bytes --] On Thu 2017-10-12 22:08:26 -0300, David Bremner wrote: > The second _notmuch_database_log will override the first > here. You can use _notmuch_database_log_append if you don't want to > clear the existing log (e.g. at least for the second _log > here). good catch, thanks! I'll include that in my next revision. It's already fixed in my gitlab branch. >> + const char *autoproperties[] = { "index-decryption" }; > > I'm always a bit nervous when I see the same string hard coded into two > different places. What about using some prefix naming scheme like > "index.auto.decryption" with the idea that all properties with the > prefix "index.auto." could be blown away. If we settle on a prefix based > naming scheme now, we could do the minor tweak to the properties API > later so that it's only a single call. Hm, i see what you're saying, but i'm a bit worried that stuffing this "property of a property" into the name can cause two different forms of confusion. first confusion: "index.auto.decryption" has at least two different meanings at first blush. For example, it might mean "this message was automatically decrypted", which isn't necessarily the case. second confusion: Even if we used a less ambiguous term like "flush-on-reindex", it would seem weird to need to say "notmuch search property:index.flush-on-reindex.decryption=failure", since the property we're interested in is whether there was a failure in decrypting during index time, not whether we expect the thing to be flushed on reindex. We could maybe avoid both of these confusions by dropping "auto" altogether and defining the following rule: * properties prefixed with "index." are set during indexing and will be cleared (and possibly re-set) upon reindexing wdyt? If you're ok with that, i could try to update the series to work that way (and include documentation along those lines). --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-10-13 14:35 ` Daniel Kahn Gillmor @ 2017-10-13 15:19 ` David Bremner 2017-10-14 11:15 ` David Bremner 1 sibling, 0 replies; 67+ messages in thread From: David Bremner @ 2017-10-13 15:19 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > > * properties prefixed with "index." are set during indexing and will be > cleared (and possibly re-set) upon reindexing > Fine. I think the model is that "applications" should take ownership of ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set. 2017-10-13 14:35 ` Daniel Kahn Gillmor 2017-10-13 15:19 ` David Bremner @ 2017-10-14 11:15 ` David Bremner 1 sibling, 0 replies; 67+ messages in thread From: David Bremner @ 2017-10-14 11:15 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > > "index.auto.decryption" has at least two different meanings at first > blush. For example, it might mean "this message was automatically > decrypted", which isn't necessarily the case. I think in either case you need to document what the properties mean. I don't think "index-decryption" by itself is unambiguous either. > second confusion: > > Even if we used a less ambiguous term like "flush-on-reindex", it would > seem weird to need to say "notmuch search > property:index.flush-on-reindex.decryption=failure", Yes, you seem to envision interactive searching for these properities. I'll have to take your word for it. I tend to think about properties as more like APIs than as end user facing. > We could maybe avoid both of these confusions by dropping "auto" > altogether and defining the following rule: > > * properties prefixed with "index." are set during indexing and will be > cleared (and possibly re-set) upon reindexing > > wdyt? > > If you're ok with that, i could try to update the series to work that > way (and include documentation along those lines). I don't object to this. ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 11/15] config: indexing defaults will be stored in the database. 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (9 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-14 18:08 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 12/15] config: define new option index.try_decrypt Daniel Kahn Gillmor ` (5 subsequent siblings) 16 siblings, 1 reply; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 8fb59f96..e82f7dd7 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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: [PATCH v3 11/15] config: indexing defaults will be stored in the database. 2017-10-10 5:49 ` [PATCH v3 11/15] config: indexing defaults will be stored in the database Daniel Kahn Gillmor @ 2017-10-14 18:08 ` David Bremner 2017-10-15 6:28 ` Daniel Kahn Gillmor 0 siblings, 1 reply; 67+ messages in thread From: David Bremner @ 2017-10-14 18:08 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > 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; I wonder if we should sanity check the value of 'item' more here. With 'query.', it makes sense to get or set anything, since it's just the name of a stored query. With 'index.', presumably only certain parameters make sense. As a motivating example, consider someone who sets $ notmuch config index.try_decrypt true then changes their mind $ notmuch config index.try-decrypt false They _think_ they are safe, but notmuch is silently going to continue decrypting their mail ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PATCH v3 11/15] config: indexing defaults will be stored in the database. 2017-10-14 18:08 ` David Bremner @ 2017-10-15 6:28 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-15 6:28 UTC (permalink / raw) To: David Bremner, Notmuch Mail On Sat 2017-10-14 15:08:51 -0300, David Bremner wrote: > Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > >> 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; > > I wonder if we should sanity check the value of 'item' more here. With > 'query.', it makes sense to get or set anything, since it's just the > name of a stored query. With 'index.', presumably only certain > parameters make sense. As a motivating example, consider someone who > sets > > $ notmuch config index.try_decrypt true > > then changes their mind > > $ notmuch config index.try-decrypt false > > They _think_ they are safe, but notmuch is silently going to continue > decrypting their mail Do you have any suggestions about how we should do this check? Some sort of registry of known options? If we have such a registry, how would an addon or a frontend use notmuch config without having to modify that registry? --dkg ^ permalink raw reply [flat|nested] 67+ messages in thread
* [PATCH v3 12/15] config: define new option index.try_decrypt 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (10 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 11/15] config: indexing defaults will be stored in the database Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 13/15] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor ` (4 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 18 +++++++++++++++++- 2 files changed, 29 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 cc1d6422..987d8952 100644 --- a/lib/indexopts.c +++ b/lib/indexopts.c @@ -23,7 +23,23 @@ 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) + return ret; + + char * try_decrypt; + notmuch_status_t err = notmuch_database_get_config (db, "index.try_decrypt", &try_decrypt); + if (err) + return ret; + + 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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 13/15] cli/new: add --try-decrypt=(true|false) 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (11 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 12/15] config: define new option index.try_decrypt Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 14/15] cli/insert: " Daniel Kahn Gillmor ` (3 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 0f50457e..93046602 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: @@ -956,6 +957,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) bool timer_is_active = false; bool no_hooks = false; bool quiet = false, verbose = false; + bool try_decrypt_set = false, try_decrypt = false; notmuch_status_t status; notmuch_opt_desc_t options[] = { @@ -963,6 +965,8 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) { .opt_bool = &verbose, .name = "verbose" }, { .opt_bool = &add_files_state.debug, .name = "debug" }, { .opt_bool = &no_hooks, .name = "no-hooks" }, + { .opt_bool = &try_decrypt, .name = "try-decrypt", + .present = &try_decrypt_set }, { .opt_inherit = notmuch_shared_options }, { } }; @@ -1080,6 +1084,29 @@ 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_set) { + 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 84051bc9..9c336662 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -340,8 +340,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 @@ -360,7 +369,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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 14/15] cli/insert: add --try-decrypt=(true|false) 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (12 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 13/15] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 15/15] cli/reindex: " Daniel Kahn Gillmor ` (2 subsequent siblings) 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 49 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 32be7419..79f9cb7d 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, - bool synchronize_flags, bool keep) + bool synchronize_flags, bool 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,21 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) bool create_folder = false; bool keep = false; bool no_hooks = false; + bool try_decrypt = false, try_decrypt_set = false; bool synchronize_flags; char *maildir; char *newpath; int opt_index; unsigned int i; + notmuch_indexopts_t *indexopts; notmuch_opt_desc_t options[] = { { .opt_string = &folder, .name = "folder" }, { .opt_bool = &create_folder, .name = "create-folder" }, { .opt_bool = &keep, .name = "keep" }, { .opt_bool = &no_hooks, .name = "no-hooks" }, + { .opt_bool = &try_decrypt, .name = "try-decrypt", + .present = &try_decrypt_set }, { .opt_inherit = notmuch_shared_options }, { } }; @@ -545,9 +550,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_set) { + 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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* [PATCH v3 15/15] cli/reindex: add --try-decrypt=(true|false) 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (13 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 14/15] cli/insert: " Daniel Kahn Gillmor @ 2017-10-10 5:49 ` Daniel Kahn Gillmor 2017-10-10 15:50 ` cleartext indexing, round 3 Jameson Graef Rollins 2017-10-13 1:28 ` David Bremner 16 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 5:49 UTC (permalink / raw) To: Notmuch Mail 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 | 24 ++++++++++++++ test/T357-index-decryption.sh | 65 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 72a75a94..7aae4297 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 57ff5904..f4ad31c5 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; + bool try_decrypt = false, try_decrypt_set = false; + notmuch_status_t status; /* Set up our handler for SIGINT */ memset (&action, 0, sizeof (struct sigaction)); @@ -100,6 +102,8 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_opt_desc_t options[] = { { .opt_inherit = notmuch_shared_options }, + { .opt_bool = &try_decrypt, .name = "try-decrypt", + .present = &try_decrypt_set }, { } }; @@ -115,6 +119,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_set) { + 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.2 ^ permalink raw reply related [flat|nested] 67+ messages in thread
* Re: cleartext indexing, round 3 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (14 preceding siblings ...) 2017-10-10 5:49 ` [PATCH v3 15/15] cli/reindex: " Daniel Kahn Gillmor @ 2017-10-10 15:50 ` Jameson Graef Rollins 2017-10-10 16:47 ` Daniel Kahn Gillmor 2017-10-13 1:28 ` David Bremner 16 siblings, 1 reply; 67+ messages in thread From: Jameson Graef Rollins @ 2017-10-10 15:50 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 422 bytes --] On Tue, Oct 10 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: > I've also pushed this series to the "cleartext-indexing" branch at > https://gitlab.com/dkg/notmuch for those who prefer a direct git pull. > it is currently git commit 6f7f6847141db2f031b29c68d966fa13c3be2da5. Hey, Daniel, I think you mean the branch named "cleartext-index". That's the branch in your repo that corresponds to that hash. jamie. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: cleartext indexing, round 3 2017-10-10 15:50 ` cleartext indexing, round 3 Jameson Graef Rollins @ 2017-10-10 16:47 ` Daniel Kahn Gillmor 0 siblings, 0 replies; 67+ messages in thread From: Daniel Kahn Gillmor @ 2017-10-10 16:47 UTC (permalink / raw) To: Jameson Graef Rollins, Notmuch Mail [-- Attachment #1: Type: text/plain, Size: 627 bytes --] On Tue 2017-10-10 08:50:17 -0700, Jameson Graef Rollins wrote: > On Tue, Oct 10 2017, Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote: >> I've also pushed this series to the "cleartext-indexing" branch at >> https://gitlab.com/dkg/notmuch for those who prefer a direct git pull. >> it is currently git commit 6f7f6847141db2f031b29c68d966fa13c3be2da5. > > Hey, Daniel, I think you mean the branch named "cleartext-index". > That's the branch in your repo that corresponds to that hash. yep, that's the correct name for the branch in question. sorry for the accidental extra gerunding in my e-mail message. --dkg [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: cleartext indexing, round 3 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor ` (15 preceding siblings ...) 2017-10-10 15:50 ` cleartext indexing, round 3 Jameson Graef Rollins @ 2017-10-13 1:28 ` David Bremner 16 siblings, 0 replies; 67+ messages in thread From: David Bremner @ 2017-10-13 1:28 UTC (permalink / raw) To: Daniel Kahn Gillmor, Notmuch Mail Daniel Kahn Gillmor <dkg@fifthhorseman.net> writes: > What follows is the third round of the latest revision of cleartext > indexing patches. pushed patches 1,2,3, and 5 to master. d ^ permalink raw reply [flat|nested] 67+ messages in thread
end of thread, other threads:[~2017-10-15 6:48 UTC | newest] Thread overview: 67+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-09-12 23:01 [PATCH 01/10] reorganize indexing of multipart/signed and multipart/encrypted Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 02/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 03/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 04/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 05/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 06/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 07/10] Define new config option index.try_decrypt Daniel Kahn Gillmor 2017-09-12 23:29 ` Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 08/10] add --try-decrypt=(true|false) to notmuch new Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 09/10] add --try-decrypt=(true|false) to notmuch insert Daniel Kahn Gillmor 2017-09-12 23:01 ` [PATCH 10/10] add --try-decrypt=(true|false) to notmuch reindex Daniel Kahn Gillmor 2017-09-15 5:53 ` cleartext-indexing Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 01/10] crypto: Move crypto.c into libutil Daniel Kahn Gillmor 2017-09-23 15:23 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 02/10] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor 2017-09-23 15:36 ` Jani Nikula 2017-10-10 3:33 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 03/10] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor 2017-09-23 15:38 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor 2017-09-23 16:10 ` Jani Nikula 2017-10-10 3:45 ` Daniel Kahn Gillmor 2017-10-14 12:40 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 05/10] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor 2017-09-23 16:05 ` Jani Nikula 2017-10-10 4:27 ` Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 06/10] config: indexing defaults will be stored in the database Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 07/10] config: define new option index.try_decrypt Daniel Kahn Gillmor 2017-09-23 16:17 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 08/10] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor 2017-09-23 16:46 ` Jani Nikula 2017-09-15 5:53 ` [PATCH v2 09/10] cli/insert: " Daniel Kahn Gillmor 2017-09-15 5:53 ` [PATCH v2 10/10] cli/reindex: " Daniel Kahn Gillmor 2017-10-10 5:49 ` cleartext indexing, round 3 Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 01/15] crypto: rename notmuch_crypto_t to _notmuch_crypto_t Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 02/15] crypto: drop pretense of notmuch_crypto_context_t Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 03/15] crypto: _notmuch_crypto_cleanup should return void Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 04/15] crypto: move into libutil Daniel Kahn Gillmor 2017-10-12 10:54 ` David Bremner 2017-10-12 14:07 ` Daniel Kahn Gillmor 2017-10-12 21:07 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 05/15] gmime-extra: remove duplicate GMimeAddressType typedef Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 06/15] crypto: make shared crypto code behave library-like Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 07/15] tests: prepare for more crypto tests (using add_gnupg_home) Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor 2017-10-11 0:29 ` avoid double typedef Daniel Kahn Gillmor 2017-10-11 0:30 ` [PATCH v4 08/15] index: implement notmuch_indexopts_t with try_decrypt Daniel Kahn Gillmor 2017-10-12 11:18 ` David Bremner 2017-10-12 14:30 ` Daniel Kahn Gillmor 2017-10-11 6:22 ` avoid double typedef Tomi Ollila 2017-10-10 5:49 ` [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt Daniel Kahn Gillmor 2017-10-14 14:02 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set Daniel Kahn Gillmor 2017-10-13 1:08 ` David Bremner 2017-10-13 14:35 ` Daniel Kahn Gillmor 2017-10-13 15:19 ` David Bremner 2017-10-14 11:15 ` David Bremner 2017-10-10 5:49 ` [PATCH v3 11/15] config: indexing defaults will be stored in the database Daniel Kahn Gillmor 2017-10-14 18:08 ` David Bremner 2017-10-15 6:28 ` Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 12/15] config: define new option index.try_decrypt Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 13/15] cli/new: add --try-decrypt=(true|false) Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 14/15] cli/insert: " Daniel Kahn Gillmor 2017-10-10 5:49 ` [PATCH v3 15/15] cli/reindex: " Daniel Kahn Gillmor 2017-10-10 15:50 ` cleartext indexing, round 3 Jameson Graef Rollins 2017-10-10 16:47 ` Daniel Kahn Gillmor 2017-10-13 1:28 ` David Bremner
Code repositories for project(s) associated with this public inbox https://yhetil.org/notmuch.git/ This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).