]> granicus.if.org Git - mutt/commitdiff
Add protected header received email support.
authorKevin McCarthy <kevin@8t8.us>
Tue, 11 Dec 2018 22:11:30 +0000 (14:11 -0800)
committerKevin McCarthy <kevin@8t8.us>
Mon, 17 Dec 2018 21:30:53 +0000 (13:30 -0800)
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.

commands.c
crypt-gpgme.c
crypt.c
handler.c
init.h
mutt.h
mutt_crypt.h
pgp.c
smime.c

index cd65a0d2185f800db2b7a88c467219c750b9c490..bd09a485da1b0bdecb8cecd7e12e8beebcd9269d 100644 (file)
@@ -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)
index 7f824f3881c0ee108cd9834a048664ea746a73c5..d1cd407e43bd66d984cc40177324174a2a338dea 100644 (file)
@@ -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 d589907530ce607bc859d7326f48abf9c551143c..203a6c94326c1c49bb28cf72957c7081ea857238 100644 (file)
--- 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);
     }
index 7ce53f957d5ea02953edc8bc0fb03a62c8f7252b..ebcd384f9b689d26daab2324565a8c8775659339 100644 (file)
--- 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 8fc6690dbceb5e7b6c7f6dab4b4c7e3a8992aa0f..f3656b5cfb2b50674f3086ab3843b2197cf1c008 100644 (file)
--- 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 11eb51636c56f414ea25e14a08a406f9b4a9c56d..fba214914827ef33d6addc84d926d2fb97457b02 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -532,6 +532,7 @@ enum
   OPTCRYPTAUTOSMIME,
   OPTCRYPTCONFIRMHOOK,
   OPTCRYPTOPPORTUNISTICENCRYPT,
+  OPTCRYPTPROTHDRSREAD,
   OPTCRYPTREPLYENCRYPT,
   OPTCRYPTREPLYSIGN,
   OPTCRYPTREPLYSIGNENCRYPTED,
index ab81858fd21f799dffc19917c2913c2b1d660142..1fa3f3590d147bc5217e84500f5453d6b2681a89 100644 (file)
@@ -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 d61415fc1f4c4cf60320b0c537a35945a88b100c..f3ee034f752800738ac141aa0738c4c318d2c344 100644 (file)
--- 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 e4c8743a56a42adb24fcc50f51e491bdff4add5b..01d4326f6be5f1c8933db7e97e1e621628190a02 100644 (file)
--- 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)
   {