/* 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];
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)
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;
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
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+")))
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;
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
}
}
+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.
/* Now display the signed body */
state_attach_puts (_("[-- The following data is signed --]\n\n"), s);
+ mutt_protected_headers_handler (a, s);
FREE (&signatures);
}
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;
}
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;
}
** 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 },
OPTCRYPTAUTOSMIME,
OPTCRYPTCONFIRMHOOK,
OPTCRYPTOPPORTUNISTICENCRYPT,
+ OPTCRYPTPROTHDRSREAD,
OPTCRYPTREPLYENCRYPT,
OPTCRYPTREPLYSIGN,
OPTCRYPTREPLYSIGNENCRYPTED,
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);
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
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);
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);
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)
{