]> granicus.if.org Git - neomutt/commitdiff
Add protected header received email support
authorKevin McCarthy <kevin@8t8.us>
Tue, 11 Dec 2018 22:11:30 +0000 (14:11 -0800)
committerRichard Russon <rich@flatcap.org>
Mon, 7 Jan 2019 15:09:41 +0000 (15:09 +0000)
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 <rich@flatcap.org>
commands.c
globals.h
handler.c
init.h
ncrypt/crypt.c
ncrypt/crypt_gpgme.c
ncrypt/ncrypt.h
ncrypt/pgp.c
ncrypt/smime.c

index 912df7a6d2e934dc3640147479fb3b21c2ab7e27..94c5e00fee2a44edfe401b72f12800031e8af3a2 100644 (file)
@@ -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)
index 6338f8b5c137e2b31b90c55c4c495ec4a0b39ccd..7ba9f9bd701a495aa2e1afdb3dba62fbc8fe2bd4 100644 (file)
--- 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
index ae056c5db13809125d9a6e54fe4a1ac6da4609f8..7caee9071bc301d812c8e7ec66edb81ea61ed70a 100644 (file)
--- 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 a3bf036ba7f164cc519dbde3c07f512ba79078be..f3b361dcafc7dca1c46b231c068e88657c82e6b8 100644 (file)
--- 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
index 2fae8d839f16b4a6b430062e146cb1981fdb78f1..02b0c23d759a48f62158652265aff80dc797e3ed 100644 (file)
@@ -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
index b105c5fcbfc2baa3205499f90c5537f339d81ff0..e42abe878e47e5a84601687e392921ce84356377 100644 (file)
@@ -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.
      */
index 3d5bc38fa3912ea5dee58923c5a1b4082635d4d3..9657a9e635f59b03d7a2e902f3e412ff634ab826 100644 (file)
@@ -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 */
index 36e22bfaf83eb6bdf787979eaed2f642d3ee0b26..2db68ecf1efe3a6b21fe0dcbceb7cb43b2c10344 100644 (file)
@@ -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.
index 144797ef7f5f2dc243df85564572686d20e28ae8..5ab158a6b0322aec007d0a8252dc8e45d2269874 100644 (file)
@@ -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)
   {