From: Kevin McCarthy Date: Tue, 11 Dec 2018 22:11:30 +0000 (-0800) Subject: Add protected header received email support. X-Git-Tag: mutt-1-12-rel~180 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cac6705aa51f281f6eba22136f6ce51490c62dff;p=mutt 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. --- diff --git a/commands.c b/commands.c index cd65a0d2..bd09a485 100644 --- a/commands.c +++ b/commands.c @@ -56,6 +56,74 @@ static const char *ExtPagerProgress = "all"; /* The folder the user last saved to. Used by ci_save_message() */ static char LastSaveFolder[_POSIX_PATH_MAX] = ""; +static void update_protected_headers (HEADER *cur) +{ + ENVELOPE *prot_headers = NULL; + regmatch_t pmatch[1]; + + if (!option (OPTCRYPTPROTHDRSREAD)) + 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_strcmp (cur->env->subject, prot_headers->subject)) + { + if (Context->subj_hash && cur->env->real_subj) + hash_delete (Context->subj_hash, cur->env->real_subj, cur, NULL); + + mutt_str_replace (&cur->env->subject, prot_headers->subject); + FREE (&cur->env->disp_subj); + if (regexec (ReplyRegexp.rx, 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->subj_hash) + hash_insert (Context->subj_hash, cur->env->real_subj, cur); + + mx_save_to_header_cache (Context, cur); + } +} + int mutt_display_message (HEADER *cur) { char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING]; @@ -165,16 +233,18 @@ int mutt_display_message (HEADER *cur) safe_fclose (&fpfilterout); /* XXX - check result? */ - if (WithCrypto) { /* update crypto information for this message */ cur->security &= ~(GOODSIGN|BADSIGN); cur->security |= crypt_query (cur->content); - + /* 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/crypt-gpgme.c b/crypt-gpgme.c index 7f824f38..d1cd407e 100644 --- a/crypt-gpgme.c +++ b/crypt-gpgme.c @@ -2901,11 +2901,23 @@ int pgp_gpgme_encrypted_handler (BODY *a, STATE *s) tattach->goodsig = is_signed > 0; if (s->flags & MUTT_DISPLAY) + { state_attach_puts (is_signed? _("[-- The following data is PGP/MIME signed and 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_free_envelope (&a->mime_headers); + a->mime_headers = tattach->mime_headers; + tattach->mime_headers = NULL; + { FILE *savefp = s->fpin; s->fpin = fpout; @@ -2913,6 +2925,18 @@ int pgp_gpgme_encrypted_handler (BODY *a, 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_free_envelope (&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 @@ -2958,6 +2982,10 @@ int smime_gpgme_application_handler (BODY *a, STATE *s) dprint (2, (debugfile, "Entering smime_encrypted handler\n")); + /* clear out any mime headers before the handler, so they can't be + * spoofed. */ + mutt_free_envelope (&a->mime_headers); + a->warnsig = 0; mutt_mktemp (tempfile, sizeof (tempfile)); if (!(fpout = safe_fopen (tempfile, "w+"))) @@ -2974,11 +3002,23 @@ int smime_gpgme_application_handler (BODY *a, STATE *s) tattach->goodsig = is_signed > 0; if (s->flags & MUTT_DISPLAY) + { state_attach_puts (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_free_envelope (&a->mime_headers); + a->mime_headers = tattach->mime_headers; + tattach->mime_headers = NULL; + { FILE *savefp = s->fpin; s->fpin = fpout; @@ -2986,6 +3026,18 @@ int smime_gpgme_application_handler (BODY *a, 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_free_envelope (&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 diff --git a/crypt.c b/crypt.c index d5899075..203a6c94 100644 --- a/crypt.c +++ b/crypt.c @@ -908,6 +908,22 @@ static void crypt_fetch_signatures (BODY ***signatures, BODY *a, int *n) } } +int mutt_protected_headers_handler (BODY *a, STATE *s) +{ + if (option (OPTCRYPTPROTHDRSREAD) && 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; +} /* * This routine verifies a "multipart/signed" body. @@ -1022,6 +1038,7 @@ int mutt_signed_handler (BODY *a, 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); } diff --git a/handler.c b/handler.c index 7ce53f95..ebcd384f 100644 --- a/handler.c +++ b/handler.c @@ -1695,9 +1695,22 @@ static int valid_pgp_encrypted_handler (BODY *b, STATE *s) BODY *octetstream; octetstream = b->parts->next; + + /* clear out any mime headers before the handler, so they can't be + * spoofed. */ + mutt_free_envelope (&b->mime_headers); + mutt_free_envelope (&octetstream->mime_headers); + 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; } @@ -1707,10 +1720,23 @@ static int malformed_pgp_encrypted_handler (BODY *b, STATE *s) BODY *octetstream; octetstream = b->parts->next->next; + + /* clear out any mime headers before the handler, so they can't be + * spoofed. */ + mutt_free_envelope (&b->mime_headers); + mutt_free_envelope (&octetstream->mime_headers); + /* exchange encodes the octet-stream, so re-run it through the decoder */ rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0); 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 8fc6690d..f3656b5c 100644 --- a/init.h +++ b/init.h @@ -586,6 +586,19 @@ struct option_t MuttVars[] = { ** If $$crypt_autoencrypt or $$crypt_replyencrypt enable encryption for ** a message, this option will be disabled for that message. It can ** be manually re-enabled in the pgp or smime menus. + ** (Crypto only) + */ + { "crypt_protected_headers_read", DT_BOOL, R_NONE, OPTCRYPTPROTHDRSREAD, 1 }, + /* + ** .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) */ { "pgp_replyencrypt", DT_SYN, R_NONE, UL "crypt_replyencrypt", 1 }, diff --git a/mutt.h b/mutt.h index 11eb5163..fba21491 100644 --- a/mutt.h +++ b/mutt.h @@ -532,6 +532,7 @@ enum OPTCRYPTAUTOSMIME, OPTCRYPTCONFIRMHOOK, OPTCRYPTOPPORTUNISTICENCRYPT, + OPTCRYPTPROTHDRSREAD, OPTCRYPTREPLYENCRYPT, OPTCRYPTREPLYSIGN, OPTCRYPTREPLYSIGNENCRYPTED, diff --git a/mutt_crypt.h b/mutt_crypt.h index ab81858f..1fa3f359 100644 --- a/mutt_crypt.h +++ b/mutt_crypt.h @@ -122,6 +122,8 @@ int mutt_is_application_pgp (BODY *); int mutt_is_application_smime (BODY *); +int mutt_protected_headers_handler (BODY *, STATE *); + int mutt_signed_handler (BODY *, STATE *); int mutt_parse_crypt_hdr (const char *, int, int); diff --git a/pgp.c b/pgp.c index d61415fc..f3ee034f 100644 --- a/pgp.c +++ b/pgp.c @@ -1132,13 +1132,37 @@ int pgp_encrypted_handler (BODY *a, STATE *s) if ((tattach = pgp_decrypt_part (a, s, fpout, a)) != NULL) { if (s->flags & MUTT_DISPLAY) + { 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_free_envelope (&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_free_envelope (&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 diff --git a/smime.c b/smime.c index e4c8743a..01d4326f 100644 --- a/smime.c +++ b/smime.c @@ -1970,6 +1970,20 @@ static BODY *smime_handle_entity (BODY *m, STATE *s, FILE *outFile) 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_free_envelope (&m->mime_headers); + m->mime_headers = p->mime_headers; + p->mime_headers = NULL; + if (s->fpout) { rewind (fpout); @@ -1979,6 +1993,18 @@ static BODY *smime_handle_entity (BODY *m, STATE *s, FILE *outFile) 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 & SMIMESIGN) && + mutt_is_multipart_signed (p) && + p->parts && + p->parts->mime_headers) + { + mutt_free_envelope (&m->mime_headers); + m->mime_headers = p->parts->mime_headers; + p->parts->mime_headers = NULL; + } } safe_fclose (&smimeout); @@ -2100,6 +2126,10 @@ int smime_application_smime_handler (BODY *m, STATE *s) int rv = -1; BODY *tattach; + /* clear out any mime headers before the handler, so they can't be + * spoofed. */ + mutt_free_envelope (&m->mime_headers); + tattach = smime_handle_entity (m, s, NULL); if (tattach) {