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.

-- 
Kevin J. McCarthy
GPG Fingerprint: 8975 A9B3 3AA3 7910 385C  5308 ADEF 7684 8031 6BDA
http://www.8t8.us/configs/gpg-key-transition-statement.txt
# HG changeset patch
# User Kevin McCarthy <[email protected]>
# Date 1455302105 28800
#      Fri Feb 12 10:35:05 2016 -0800
# Node ID cab09cbe5d1b5ad2b5a7acd3c512dfc45a6cad6e
# Parent  b55c6a64a07b70cacaf551e379f950031d963bd8
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.

diff --git a/contrib/gpg.rc b/contrib/gpg.rc
--- a/contrib/gpg.rc
+++ b/contrib/gpg.rc
@@ -77,9 +77,10 @@
 
 # set pgp_good_sign="^gpgv?: Good signature from "
 
 # OK, here's a version which uses gnupg's message catalog:
 # set pgp_good_sign="`gettext -d gnupg -s 'Good signature from "' | tr -d '"'`"
 
 # This version uses --status-fd messages
 set pgp_good_sign="^\\[GNUPG:\\] GOODSIG"
+set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
 
diff --git a/crypt-gpgme.c b/crypt-gpgme.c
--- a/crypt-gpgme.c
+++ b/crypt-gpgme.c
@@ -2634,16 +2634,17 @@
         }
 
       mutt_free_body (&tattach);
       mutt_message _("PGP message successfully decrypted.");
     }
   else
     {
       mutt_error _("Could not decrypt PGP message");
+      mutt_sleep (2);
       rc = -1;
     }
 
   safe_fclose (&fpout);
   mutt_unlink(tempfile);
   dprint (2, (debugfile, "Leaving pgp_encrypted handler\n"));
 
   return rc;
diff --git a/globals.h b/globals.h
--- a/globals.h
+++ b/globals.h
@@ -225,16 +225,17 @@
 
 WHERE int CurrentMenu;
 
 WHERE ALIAS *Aliases INITVAL (0);
 WHERE LIST *UserHeader INITVAL (0);
 
 /*-- formerly in pgp.h --*/
 WHERE REGEXP PgpGoodSign;
+WHERE REGEXP PgpDecryptionOkay;
 WHERE char *PgpSignAs;
 WHERE short PgpTimeout;
 WHERE char *PgpEntryFormat;
 WHERE char *PgpClearSignCommand;
 WHERE char *PgpDecodeCommand;
 WHERE char *PgpVerifyCommand;
 WHERE char *PgpDecryptCommand;
 WHERE char *PgpSignCommand;
diff --git a/init.h b/init.h
--- a/init.h
+++ b/init.h
@@ -1791,16 +1791,27 @@
   /*
   ** .pp
   ** This command is used to decrypt a PGP encrypted message.
   ** .pp
   ** This is a format string, see the $$pgp_decode_command command for
   ** 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
   ** This command is used to encrypt a body part without signing it.
   ** .pp
   ** This is a format string, see the $$pgp_decode_command command for
   ** possible \fCprintf(3)\fP-like sequences.
   ** (PGP only)
diff --git a/pgp.c b/pgp.c
--- a/pgp.c
+++ b/pgp.c
@@ -219,16 +219,55 @@
     dprint (2, (debugfile, "pgp_copy_checksig: No pattern.\n"));
     mutt_copy_stream (fpin, fpout);
     rv = 1;
   }
 
   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.
  * 
  * XXX - charset handling: We assume that it is safe to do
  * character set decoding first, dash decoding second here, while
  * we do it the other way around in the main handler.
  * 
@@ -900,20 +939,28 @@
     if (len > 1 && buf[len - 2] == '\r')
       strcpy (buf + len - 2, "\n");    /* __STRCPY_CHECKED__ */
     fputs (buf, fpout);
   }
 
   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;
     else
       p->goodsig = 0;
     state_attach_puts (_("[-- End of PGP output --]\n\n"), s);
   }
   safe_fclose (&pgperr);
@@ -1075,16 +1122,17 @@
 
     mutt_free_body (&tattach);
     /* clear 'Invoking...' message, since there's no error */
     mutt_message _("PGP message successfully decrypted.");
   }
   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;
   }
 
   safe_fclose (&fpout);
   mutt_unlink(tempfile);
 

Attachment: signature.asc
Description: PGP signature

Reply via email to