crypto: index encrypted parts when indexopts try_decrypt is set.

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").

Because of the property name, this will be automatically cleared (and
possibly re-set) during re-indexing.  This means it will subsequently
correspond to the actual semantics of the stored index.
This commit is contained in:
Daniel Kahn Gillmor 2017-10-20 22:25:41 -04:00 committed by David Bremner
parent 0bb05ff693
commit 4dfcc8c9b2
5 changed files with 112 additions and 12 deletions

View file

@ -47,6 +47,32 @@ CONVENTIONS
Any property with a key that starts with "index." will be removed (and
possibly re-set) upon reindexing (see **notmuch-reindex(1)**).
MESSAGE PROPERTIES
==================
The following properties are set by notmuch internally in the course
of its normal activity.
**index.decryption**
If a message contains encrypted content, and notmuch tries to
decrypt that content during indexing, it will add the property
``index.decryption=success`` when the cleartext was successfully
indexed. If notmuch attempts to decrypt any part of a message
during indexing and that decryption attempt fails, it will add the
property ``index.decryption=failure`` to the message.
Note that it's possible for a single message to have both
``index.decryption=success`` and ``index.decryption=failure``.
Consider an encrypted e-mail message that contains another
encrypted e-mail message as an attachment -- if the outer message
can be decrypted, but the attached part cannot, then both
properties will be set on the message as a whole.
If notmuch never tried to decrypt an encrypted message during
indexing (which is the default), then this property will not be
set on that message.
SEE ALSO
========

View file

@ -546,7 +546,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
indexopts = def_indexopts;
}
ret = _notmuch_message_index_file (message, message_file);
ret = _notmuch_message_index_file (message, indexopts, message_file);
if (ret)
goto DONE;

View file

@ -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_append (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_append (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;
}

View file

@ -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;
@ -2042,7 +2042,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;

View file

@ -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 */