/** 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
/* 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)
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
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;
}
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;
}
** 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
#include "mailbox.h"
#include "mutt_curses.h"
#include "mutt_parse.h"
+#include "mutt_window.h"
#include "muttlib.h"
#include "ncrypt.h"
#include "options.h"
}
}
+/**
+ * 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
*/
/* 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
"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;
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.
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)
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;
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.
*/
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 */
{
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.
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);
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);
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)
{