changeset: 6559:909cf6dd067c
user:      Kevin McCarthy <[email protected]>
date:      Mon Feb 15 19:44:23 2016 -0800
link:      http://dev.mutt.org/hg/mutt/rev/909cf6dd067c

Add $pgp_decryption_okay to verify multipart/encrypted are actually encrypted. 
(closes #3770)

In pgp classic mode, if the $pgp_decrypt_command generated output, it
assumed the content was encrypted.  However, gpg will generate output
even if the block is simply signed and armored text.  The problem is
that mutt was then printing mime headers labelling the output as
encrypted text in the ui.

Add a new option, and suggested value of:
  set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
If set, the output from the decrypt command will be scanned for this
regexp to confirm an actual decryption occurred.

Note that gpgme already correctly rejects this form of spoofed message.

diffs (131 lines):

diff -r b55c6a64a07b -r 909cf6dd067c contrib/gpg.rc
--- a/contrib/gpg.rc    Thu Feb 11 15:12:48 2016 -0800
+++ b/contrib/gpg.rc    Mon Feb 15 19:44:23 2016 -0800
@@ -83,3 +83,6 @@
 # This version uses --status-fd messages
 set pgp_good_sign="^\\[GNUPG:\\] GOODSIG"
 
+# pattern to verify a decryption occurred
+set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
+
diff -r b55c6a64a07b -r 909cf6dd067c crypt-gpgme.c
--- a/crypt-gpgme.c     Thu Feb 11 15:12:48 2016 -0800
+++ b/crypt-gpgme.c     Mon Feb 15 19:44:23 2016 -0800
@@ -2639,6 +2639,7 @@
   else
     {
       mutt_error _("Could not decrypt PGP message");
+      mutt_sleep (2);
       rc = -1;
     }
 
diff -r b55c6a64a07b -r 909cf6dd067c globals.h
--- a/globals.h Thu Feb 11 15:12:48 2016 -0800
+++ b/globals.h Mon Feb 15 19:44:23 2016 -0800
@@ -230,6 +230,7 @@
 
 /*-- formerly in pgp.h --*/
 WHERE REGEXP PgpGoodSign;
+WHERE REGEXP PgpDecryptionOkay;
 WHERE char *PgpSignAs;
 WHERE short PgpTimeout;
 WHERE char *PgpEntryFormat;
diff -r b55c6a64a07b -r 909cf6dd067c init.h
--- a/init.h    Thu Feb 11 15:12:48 2016 -0800
+++ b/init.h    Mon Feb 15 19:44:23 2016 -0800
@@ -1796,6 +1796,17 @@
   ** possible \fCprintf(3)\fP-like sequences.
   ** (PGP only)
   */
+  { "pgp_decryption_okay",     DT_RX,  R_NONE, UL &PgpDecryptionOkay, 0 },
+  /*
+  ** .pp
+  ** If you assign text to this variable, then an encrypted PGP
+  ** message is only considered successfully decrypted if the output
+  ** from $$pgp_decrypt_command contains the text.  This is used to
+  ** 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).
+  ** (PGP only)
+  */
   { "pgp_encrypt_only_command", DT_STR, R_NONE, UL &PgpEncryptOnlyCommand, 0},
   /*
   ** .pp
diff -r b55c6a64a07b -r 909cf6dd067c pgp.c
--- a/pgp.c     Thu Feb 11 15:12:48 2016 -0800
+++ b/pgp.c     Mon Feb 15 19:44:23 2016 -0800
@@ -224,6 +224,45 @@
   return rv;
 }
 
+/* Checks PGP output messages to look for the $pgp_decryption_okay message.
+ * 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)
+{
+  int rv = -1;
+
+  if (PgpDecryptionOkay.pattern)
+  {
+    char *line = NULL;
+    int lineno = 0;
+    size_t linelen;
+
+    while ((line = mutt_read_line (line, &linelen, fpin, &lineno, 0)) != NULL)
+    {
+      if (regexec (PgpDecryptionOkay.rx, line, 0, NULL, 0) == 0)
+      {
+        dprint (2, (debugfile, "pgp_check_decryption_okay: \"%s\" matches 
regexp.\n",
+                    line));
+        rv = 0;
+        break;
+      }
+      else
+        dprint (2, (debugfile, "pgp_check_decryption_okay: \"%s\" doesn't 
match regexp.\n",
+                    line));
+    }
+    FREE (&line);
+  }
+  else
+  {
+    dprint (2, (debugfile, "pgp_check_decryption_okay: No pattern.\n"));
+    rv = 1;
+  }
+
+  return rv;
+}
+
+
 /* 
  * Copy a clearsigned message, and strip the signature and PGP's
  * dash-escaping.
@@ -905,10 +944,18 @@
   safe_fclose (&pgpout);
   rv = mutt_wait_filter (thepid);
   mutt_unlink(pgptmpfile);
-  
+
+  fflush (pgperr);
+  rewind (pgperr);
+  if (pgp_check_decryption_okay (pgperr) < 0)
+  {
+    mutt_error _("Decryption failed");
+    pgp_void_passphrase ();
+    return NULL;
+  }
+
   if (s->flags & M_DISPLAY)
   {
-    fflush (pgperr);
     rewind (pgperr);
     if (pgp_copy_checksig (pgperr, s->fpout) == 0 && !rv && p)
       p->goodsig = 1;
@@ -1080,6 +1127,7 @@
   else
   {
     mutt_error _("Could not decrypt PGP message");
+    mutt_sleep (2);
     /* void the passphrase, even if it's not necessarily the problem */
     pgp_void_passphrase ();
     rc = -1;

Reply via email to