From: Kevin McCarthy Date: Tue, 11 Dec 2018 22:11:30 +0000 (-0800) Subject: Add protected header received email support X-Git-Tag: 2019-10-25~396^2~31 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=53c0a3b650f8d9a01cbf0f5ec19c165b86f1731b;p=neomutt Add protected header received email support Add $crypt_protected_headers_read config variable to enable reading and updating the index/header cache. Print protected Subject header in the pager as parts are rendered. Once opened, update the index, subject hash, and header cache. Co-authored-by: Richard Russon --- diff --git a/commands.c b/commands.c index 912df7a6d..94c5e00fe 100644 --- a/commands.c +++ b/commands.c @@ -90,6 +90,74 @@ static const char *ExtPagerProgress = "all"; /** The folder the user last saved to. Used by ci_save_message() */ static char LastSaveFolder[PATH_MAX] = ""; +/** + * update_protected_headers - Get the protected header and update the index + * @param cur Email to update + */ +static void update_protected_headers(struct Email *cur) +{ + struct Envelope *prot_headers = NULL; + regmatch_t pmatch[1]; + + if (!CryptProtectedHeadersRead) + return; + + /* Grab protected headers to update in the index */ + if (cur->security & SIGN) + { + /* Don't update on a bad signature. + * + * This is a simplification. It's possible the headers are in the + * encrypted part of a nested encrypt/signed. But properly handling that + * case would require more complexity in the decryption handlers, which + * I'm not sure is worth it. */ + if (!(cur->security & GOODSIGN)) + return; + + if (mutt_is_multipart_signed(cur->content) && cur->content->parts) + { + prot_headers = cur->content->parts->mime_headers; + } + else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(cur->content)) + { + prot_headers = cur->content->mime_headers; + } + } + if (!prot_headers && (cur->security & ENCRYPT)) + { + if ((WithCrypto & APPLICATION_PGP) && + (mutt_is_valid_multipart_pgp_encrypted(cur->content) || + mutt_is_malformed_multipart_pgp_encrypted(cur->content))) + { + prot_headers = cur->content->mime_headers; + } + else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(cur->content)) + { + prot_headers = cur->content->mime_headers; + } + } + + /* Update protected headers in the index and header cache. */ + if (prot_headers && prot_headers->subject && + mutt_str_strcmp(cur->env->subject, prot_headers->subject)) + { + if (Context->mailbox->subj_hash && cur->env->real_subj) + mutt_hash_delete(Context->mailbox->subj_hash, cur->env->real_subj, cur); + + mutt_str_replace(&cur->env->subject, prot_headers->subject); + FREE(&cur->env->disp_subj); + if (regexec(ReplyRegex->regex, cur->env->subject, 1, pmatch, 0) == 0) + cur->env->real_subj = cur->env->subject + pmatch[0].rm_eo; + else + cur->env->real_subj = cur->env->subject; + + if (Context->mailbox->subj_hash) + mutt_hash_insert(Context->mailbox->subj_hash, cur->env->real_subj, cur); + + mx_save_hcache(Context->mailbox, cur); + } +} + /** * mutt_display_message - Display a message in the pager * @param cur Header of current message @@ -220,6 +288,9 @@ int mutt_display_message(struct Email *cur) /* Remove color cache for this message, in case there are color patterns for both ~g and ~V */ cur->pair = 0; + + /* Grab protected headers and update the header and index */ + update_protected_headers(cur); } if (builtin) diff --git a/globals.h b/globals.h index 6338f8b5c..7ba9f9bd7 100644 --- a/globals.h +++ b/globals.h @@ -271,6 +271,7 @@ WHERE bool CryptUsePka; ///< Config: Use GPGME to use PKA (lo WHERE bool CryptConfirmhook; ///< Config: Prompt the user to confirm keys before use WHERE bool CryptOpportunisticEncrypt; ///< Config: Enable encryption when the recipient's key is available +WHERE bool CryptProtectedHeadersRead; ///< Config: Display protected headers (Memory Hole) in the pager WHERE bool SmimeIsDefault; ///< Config: Use SMIME rather than PGP by default WHERE bool PgpIgnoreSubkeys; ///< Config: Only use the principal PGP key WHERE bool PgpLongIds; ///< Config: Display long PGP key IDs to the user diff --git a/handler.c b/handler.c index ae056c5db..7caee9071 100644 --- a/handler.c +++ b/handler.c @@ -1409,9 +1409,21 @@ static int run_decode_and_handler(struct Body *b, struct State *s, static int valid_pgp_encrypted_handler(struct Body *b, struct State *s) { struct Body *octetstream = b->parts->next; + + /* clear out any mime headers before the handler, so they can't be spoofed. */ + mutt_env_free(&b->mime_headers); + mutt_env_free(&octetstream->mime_headers); + int rc = crypt_pgp_encrypted_handler(octetstream, s); b->goodsig |= octetstream->goodsig; + /* Relocate protected headers onto the multipart/encrypted part */ + if (!rc && octetstream->mime_headers) + { + b->mime_headers = octetstream->mime_headers; + octetstream->mime_headers = NULL; + } + return rc; } @@ -1421,10 +1433,22 @@ static int valid_pgp_encrypted_handler(struct Body *b, struct State *s) static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s) { struct Body *octetstream = b->parts->next->next; + + /* clear out any mime headers before the handler, so they can't be spoofed. */ + mutt_env_free(&b->mime_headers); + mutt_env_free(&octetstream->mime_headers); + /* exchange encodes the octet-stream, so re-run it through the decoder */ int rc = run_decode_and_handler(octetstream, s, crypt_pgp_encrypted_handler, false); b->goodsig |= octetstream->goodsig; + /* Relocate protected headers onto the multipart/encrypted part */ + if (!rc && octetstream->mime_headers) + { + b->mime_headers = octetstream->mime_headers; + octetstream->mime_headers = NULL; + } + return rc; } diff --git a/init.h b/init.h index a3bf036ba..f3b361dca 100644 --- a/init.h +++ b/init.h @@ -660,6 +660,19 @@ struct ConfigDef MuttVars[] = { ** be manually re-enabled in the pgp or smime menus. ** (Crypto only) */ + { "crypt_protected_headers_read", DT_BOOL, R_NONE, &CryptProtectedHeadersRead, true }, + /* + ** .pp + ** When set, Mutt will display protected headers ("Memory Hole") in the pager, + ** and will update the index and header cache with revised headers. + ** + ** Protected headers are stored inside the encrypted or signed part of an + ** an email, to prevent disclosure or tampering. + ** For more information see https://github.com/autocrypt/memoryhole. + ** + ** Currently Mutt only supports the Subject header. + ** (Crypto only) + */ { "crypt_replyencrypt", DT_BOOL, R_NONE, &CryptReplyencrypt, true }, /* ** .pp diff --git a/ncrypt/crypt.c b/ncrypt/crypt.c index 2fae8d839..02b0c23d7 100644 --- a/ncrypt/crypt.c +++ b/ncrypt/crypt.c @@ -52,6 +52,7 @@ #include "mailbox.h" #include "mutt_curses.h" #include "mutt_parse.h" +#include "mutt_window.h" #include "muttlib.h" #include "ncrypt.h" #include "options.h" @@ -1065,6 +1066,25 @@ static void crypt_fetch_signatures(struct Body ***signatures, struct Body *a, in } } +/** + * mutt_protected_headers_handler - Process a protected header - Implements ::handler_t + */ +int mutt_protected_headers_handler(struct Body *a, struct State *s) +{ + if (CryptProtectedHeadersRead && a->mime_headers) + { + if (a->mime_headers->subject) + { + mutt_write_one_header(s->fpout, "Subject", a->mime_headers->subject, + s->prefix, mutt_window_wrap_cols(MuttIndexWindow, Wrap), + (s->flags & MUTT_DISPLAY) ? CH_DISPLAY : 0); + state_puts("\n", s); + } + } + + return 0; +} + /** * mutt_signed_handler - Verify a "multipart/signed" body - Implements ::handler_t */ @@ -1183,6 +1203,8 @@ int mutt_signed_handler(struct Body *a, struct State *s) /* Now display the signed body */ state_attach_puts(_("[-- The following data is signed --]\n\n"), s); + mutt_protected_headers_handler(a, s); + FREE(&signatures); } else diff --git a/ncrypt/crypt_gpgme.c b/ncrypt/crypt_gpgme.c index b105c5fcb..e42abe878 100644 --- a/ncrypt/crypt_gpgme.c +++ b/ncrypt/crypt_gpgme.c @@ -3082,8 +3082,17 @@ int pgp_gpgme_encrypted_handler(struct Body *a, struct State *s) "encrypted --]\n\n") : _("[-- The following data is PGP/MIME encrypted --]\n\n"), s); + mutt_protected_headers_handler(tattach, s); } + /* Store any protected headers in the parent so they can be + * accessed for index updates after the handler recursion is done. + * This is done before the handler to prevent a nested encrypted + * handler from freeing the headers. */ + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->mime_headers; + tattach->mime_headers = NULL; + { FILE *savefp = s->fpin; s->fpin = fpout; @@ -3091,6 +3100,16 @@ int pgp_gpgme_encrypted_handler(struct Body *a, struct State *s) s->fpin = savefp; } + /* Embedded multipart signed protected headers override the + * encrypted headers. We need to do this after the handler so + * they can be printed in the pager. */ + if (mutt_is_multipart_signed(tattach) && tattach->parts && tattach->parts->mime_headers) + { + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->parts->mime_headers; + tattach->parts->mime_headers = NULL; + } + /* if a multipart/signed is the _only_ sub-part of a * multipart/encrypted, cache signature verification * status. @@ -3132,6 +3151,9 @@ int smime_gpgme_application_handler(struct Body *a, struct State *s) mutt_debug(2, "Entering handler\n"); + /* clear out any mime headers before the handler, so they can't be spoofed. */ + mutt_env_free(&a->mime_headers); + a->warnsig = false; FILE *fpout = mutt_file_mkstemp(); if (!fpout) @@ -3157,8 +3179,17 @@ int smime_gpgme_application_handler(struct Body *a, struct State *s) is_signed ? _("[-- The following data is S/MIME signed --]\n\n") : _("[-- The following data is S/MIME encrypted --]\n\n"), s); + mutt_protected_headers_handler(tattach, s); } + /* Store any protected headers in the parent so they can be + * accessed for index updates after the handler recursion is done. + * This is done before the handler to prevent a nested encrypted + * handler from freeing the headers. */ + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->mime_headers; + tattach->mime_headers = NULL; + { FILE *savefp = s->fpin; s->fpin = fpout; @@ -3166,6 +3197,16 @@ int smime_gpgme_application_handler(struct Body *a, struct State *s) s->fpin = savefp; } + /* Embedded multipart signed protected headers override the + * encrypted headers. We need to do this after the handler so + * they can be printed in the pager. */ + if (mutt_is_multipart_signed(tattach) && tattach->parts && tattach->parts->mime_headers) + { + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->parts->mime_headers; + tattach->parts->mime_headers = NULL; + } + /* if a multipart/signed is the _only_ sub-part of a multipart/encrypted, * cache signature verification status. */ diff --git a/ncrypt/ncrypt.h b/ncrypt/ncrypt.h index 3d5bc38fa..9657a9e63 100644 --- a/ncrypt/ncrypt.h +++ b/ncrypt/ncrypt.h @@ -191,6 +191,7 @@ int mutt_is_multipart_encrypted(struct Body *b); int mutt_is_multipart_signed(struct Body *b); int mutt_is_valid_multipart_pgp_encrypted(struct Body *b); int mutt_protect(struct Email *msg, char *keylist); +int mutt_protected_headers_handler(struct Body *m, struct State *s); int mutt_signed_handler(struct Body *a, struct State *s); /* cryptglue.c */ diff --git a/ncrypt/pgp.c b/ncrypt/pgp.c index 36e22bfaf..2db68ecf1 100644 --- a/ncrypt/pgp.c +++ b/ncrypt/pgp.c @@ -1221,13 +1221,32 @@ int pgp_class_encrypted_handler(struct Body *a, struct State *s) { state_attach_puts( _("[-- The following data is PGP/MIME encrypted --]\n\n"), s); + mutt_protected_headers_handler(tattach, s); } + /* Store any protected headers in the parent so they can be + * accessed for index updates after the handler recursion is done. + * This is done before the handler to prevent a nested encrypted + * handler from freeing the headers. */ + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->mime_headers; + tattach->mime_headers = NULL; + fpin = s->fpin; s->fpin = fpout; rc = mutt_body_handler(tattach, s); s->fpin = fpin; + /* Embedded multipart signed protected headers override the + * encrypted headers. We need to do this after the handler so + * they can be printed in the pager. */ + if (mutt_is_multipart_signed(tattach) && tattach->parts && tattach->parts->mime_headers) + { + mutt_env_free(&a->mime_headers); + a->mime_headers = tattach->parts->mime_headers; + tattach->parts->mime_headers = NULL; + } + /* if a multipart/signed is the _only_ sub-part of a * multipart/encrypted, cache signature verification * status. diff --git a/ncrypt/smime.c b/ncrypt/smime.c index 144797ef7..5ab158a6b 100644 --- a/ncrypt/smime.c +++ b/ncrypt/smime.c @@ -2158,6 +2158,18 @@ static struct Body *smime_handle_entity(struct Body *m, struct State *s, FILE *o p->length = info.st_size - p->offset; mutt_parse_part(fpout, p); + + if (s->flags & MUTT_DISPLAY) + mutt_protected_headers_handler(p, s); + + /* Store any protected headers in the parent so they can be + * accessed for index updates after the handler recursion is done. + * This is done before the handler to prevent a nested encrypted + * handler from freeing the headers. */ + mutt_env_free(&m->mime_headers); + m->mime_headers = p->mime_headers; + p->mime_headers = NULL; + if (s->fpout) { rewind(fpout); @@ -2166,6 +2178,17 @@ static struct Body *smime_handle_entity(struct Body *m, struct State *s, FILE *o mutt_body_handler(p, s); s->fpin = tmpfp_buffer; } + + /* Embedded multipart signed protected headers override the + * encrypted headers. We need to do this after the handler so + * they can be printed in the pager. */ + if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts && + p->parts->mime_headers) + { + mutt_env_free(&m->mime_headers); + m->mime_headers = p->parts->mime_headers; + p->parts->mime_headers = NULL; + } } mutt_file_fclose(&smimeout); @@ -2277,6 +2300,10 @@ bail: int smime_class_application_handler(struct Body *m, struct State *s) { int rv = -1; + + /* clear out any mime headers before the handler, so they can't be spoofed. */ + mutt_env_free(&m->mime_headers); + struct Body *tattach = smime_handle_entity(m, s, NULL); if (tattach) {