]> granicus.if.org Git - mutt/commitdiff
Add $pgp_check_gpg_decrypt_status_fd.
authorKevin McCarthy <kevin@8t8.us>
Mon, 4 Jun 2018 22:40:57 +0000 (15:40 -0700)
committerKevin McCarthy <kevin@8t8.us>
Mon, 4 Jun 2018 22:40:57 +0000 (15:40 -0700)
If set (the default) mutt performs more thorough checking of the
$pgp_decrypt_command status output for GnuPG result codes.

Ticket #39 revealed that GnuPG (currently) does not protect against
messages that have been manipulated to contain an empty encryption
packet followed by a plaintext packet.

A huge thanks to Marcus Brinkmann for researching this issue, taking
the time to report it to us (and the GnuPG team), and taking even more
time to clarify exactly what needed to be checked for.   

contrib/gpg.rc
contrib/pgp2.rc
contrib/pgp5.rc
contrib/pgp6.rc
init.h
mutt.h
pgp.c

index 452eed2b8e599ad659227cd4b99a93ad0fa57e4d..7adcbdb24916f375052119c95056633b8b343670 100644 (file)
@@ -106,5 +106,7 @@ set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --w
 set pgp_good_sign="^\\[GNUPG:\\] GOODSIG"
 
 # pattern to verify a decryption occurred
-set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
+# This is now deprecated by pgp_check_gpg_decrypt_status_fd:
+# set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
+set pgp_check_gpg_decrypt_status_fd
 
index bd66cf10e80bac480101056379a30793f2003a05..212804534188e636e718eb897188443ab2e7a775 100644 (file)
@@ -20,6 +20,9 @@ set pgp_verify_command="pgp +language=mutt +verbose=0 +batchmode -t %s %f"
 # decrypt a pgp/mime attachment
 set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp +language=mutt +verbose=0 +batchmode -f"
 
+# don't check for GnuPG decryption status codes
+unset pgp_check_gpg_decrypt_status_fd
+
 # create a pgp/mime signed attachment
 set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp +language=mutt +verbose=0 +batchmode -abfst %?a? -u %a?"
 
index d2e578fb79519395e51108d8b36700d15eb24279..61d1c2c9b108f3f8bef34b6c558e3c735cfac85e 100644 (file)
@@ -17,6 +17,9 @@ set pgp_good_sign = "Good signature"
 # decrypt a pgp/mime attachment
 set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgpv +language=mutt +verbose=0 +batchmode --OutputInformationFD=2 -f"
 
+# don't check for GnuPG decryption status codes
+unset pgp_check_gpg_decrypt_status_fd
+
 # create a pgp/mime signed attachment
 set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgps +language=mutt +verbose=0 +batchmode -abft %?a? -u %a?"
 
index a317d4bec5de0980b7ec0e96eca9710185872d21..a5fca65460415e099dbbf16a5e1db0e2cdaad3ab 100644 (file)
@@ -14,6 +14,9 @@ set pgp_verify_command="pgp6 +compatible   +verbose=0 +batchmode -t %s %f"
 # decrypt a pgp/mime attachment
 set pgp_decrypt_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible   +verbose=0 +batchmode -f"
 
+# don't check for GnuPG decryption status codes
+unset pgp_check_gpg_decrypt_status_fd
+
 # create a pgp/mime signed attachment
 set pgp_sign_command="PGPPASSFD=0; export PGPPASSFD; cat - %f | pgp6 +compatible   +verbose=0 +batchmode -abfst %?a? -u %a?"
 
diff --git a/init.h b/init.h
index 6c443945d28c93744a647cce39dc15dedda85f80..44d7fabd26fcbc005a05fcd1137f97c4593d3b70 100644 (file)
--- a/init.h
+++ b/init.h
@@ -1941,6 +1941,20 @@ struct option_t MuttVars[] = {
   ** subprocess failed.
   ** (PGP only)
   */
+  { "pgp_check_gpg_decrypt_status_fd", DT_BOOL, R_NONE, OPTPGPCHECKGPGDECRYPTSTATUSFD, 1 },
+  /*
+  ** .pp
+  ** If \fIset\fP, mutt will check the status file descriptor output
+  ** of $$pgp_decrypt_command for GnuPG status codes indicating
+  ** successful decryption.  This will check for the presence of
+  ** DECRYPTION_OKAY, absence of DECRYPTION_FAILED, and that all
+  ** PLAINTEXT occurs between the BEGIN_DECRYPTION and END_DECRYPTION
+  ** status codes.
+  ** .pp
+  ** If \fIunset\fP, mutt will instead match the status fd output
+  ** against $$pgp_decryption_okay.
+  ** (PGP only)
+  */
   { "pgp_clearsign_command",   DT_STR, R_NONE, UL &PgpClearSignCommand, 0 },
   /*
   ** .pp
@@ -1994,6 +2008,9 @@ struct option_t MuttVars[] = {
   ** protect against a spoofed encrypted message, with multipart/encrypted
   ** headers but containing a block that is not actually encrypted.
   ** (e.g. simply signed and ascii armored text).
+  ** .pp
+  ** Note that if $$pgp_check_gpg_decrypt_status_fd is set, this variable
+  ** is ignored.
   ** (PGP only)
   */
   { "pgp_self_encrypt_as",     DT_SYN,  R_NONE, UL "pgp_default_key", 0 },
diff --git a/mutt.h b/mutt.h
index 663beb4ff031ce01cd91663b09417afe19208253..7f49c116af0252d2183b2340075b2b65e1733f98 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -529,6 +529,7 @@ enum
   OPTSDEFAULTDECRYPTKEY,
   OPTPGPIGNORESUB,
   OPTPGPCHECKEXIT,
+  OPTPGPCHECKGPGDECRYPTSTATUSFD,
   OPTPGPLONGIDS,
   OPTPGPAUTODEC,
 #if 0
diff --git a/pgp.c b/pgp.c
index ea307997692053991685f36753be51b01ca7d696..c9a4c91f5a06547b3d024af3d40d3d2faf988684 100644 (file)
--- a/pgp.c
+++ b/pgp.c
@@ -231,7 +231,7 @@ static int pgp_copy_checksig (FILE *fpin, FILE *fpout)
  * This protects against messages with multipart/encrypted headers
  * but which aren't actually encrypted.  See ticket #3770
  */
-static int pgp_check_decryption_okay (FILE *fpin)
+static int pgp_check_pgp_decryption_okay_regexp (FILE *fpin)
 {
   int rv = -1;
 
@@ -245,26 +245,79 @@ static int pgp_check_decryption_okay (FILE *fpin)
     {
       if (regexec (PgpDecryptionOkay.rx, line, 0, NULL, 0) == 0)
       {
-        dprint (2, (debugfile, "pgp_check_decryption_okay: \"%s\" matches regexp.\n",
+        dprint (2, (debugfile, "pgp_check_pgp_decryption_okay_regexp: \"%s\" matches regexp.\n",
                     line));
         rv = 0;
         break;
       }
       else
-        dprint (2, (debugfile, "pgp_check_decryption_okay: \"%s\" doesn't match regexp.\n",
+        dprint (2, (debugfile, "pgp_check_pgp_decryption_okay_regexp: \"%s\" doesn't match regexp.\n",
                     line));
     }
     FREE (&line);
   }
   else
   {
-    dprint (2, (debugfile, "pgp_check_decryption_okay: No pattern.\n"));
+    dprint (2, (debugfile, "pgp_check_pgp_decryption_okay_regexp: No pattern.\n"));
     rv = 1;
   }
 
   return rv;
 }
 
+/* Checks GnuPGP status fd output for various status codes indicating
+ * an issue.  If $pgp_check_gpg_decrypt_status_fd is unset, it falls
+ * back to the old behavior of just scanning for $pgp_decryption_okay.
+ */
+static int pgp_check_decryption_okay (FILE *fpin)
+{
+  int rv = -1;
+  char *line = NULL, *s;
+  int lineno = 0;
+  size_t linelen;
+  int inside_decrypt = 0;
+
+  if (!option (OPTPGPCHECKGPGDECRYPTSTATUSFD))
+    return pgp_check_pgp_decryption_okay_regexp (fpin);
+
+  while ((line = mutt_read_line (line, &linelen, fpin, &lineno, 0)) != NULL)
+  {
+    if (strncmp (line, "[GNUPG:] ", 9) != 0)
+      continue;
+    s = line + 9;
+    dprint (2, (debugfile, "pgp_check_decryption_okay: checking \"%s\".\n",
+                  line));
+    if (mutt_strncmp (s, "BEGIN_DECRYPTION", 16) == 0)
+      inside_decrypt = 1;
+    else if (mutt_strncmp (s, "END_DECRYPTION", 14) == 0)
+      inside_decrypt = 0;
+    else if (mutt_strncmp (s, "PLAINTEXT", 9) == 0)
+    {
+      if (!inside_decrypt)
+      {
+        dprint (2, (debugfile, "\tPLAINTEXT encountered outside of DECRYPTION.  Failure.\n"));
+        rv = -1;
+        break;
+      }
+    }
+    else if (mutt_strncmp (s, "DECRYPTION_FAILED", 17) == 0)
+    {
+      dprint (2, (debugfile, "\tDECRYPTION_FAILED encountered.  Failure.\n"));
+      rv = -1;
+      break;
+    }
+    else if (mutt_strncmp (s, "DECRYPTION_OKAY", 15) == 0)
+    {
+      /* Don't break out because we still have to check for
+       * PLAINTEXT outside of the decryption boundaries. */
+      dprint (2, (debugfile, "\tDECRYPTION_OKAY encountered.\n"));
+      rv = 0;
+    }
+  }
+  FREE (&line);
+
+  return rv;
+}
 
 /* 
  * Copy a clearsigned message, and strip the signature and PGP's