]> granicus.if.org Git - neomutt/commitdiff
Add $pgp_decryption_okay to verify multipart/encrypted are actually encrypted. (close...
authorKevin McCarthy <kevin@8t8.us>
Tue, 16 Feb 2016 03:44:23 +0000 (19:44 -0800)
committerKevin McCarthy <kevin@8t8.us>
Tue, 16 Feb 2016 03:44:23 +0000 (19:44 -0800)
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.

contrib/gpg.rc
crypt-gpgme.c
globals.h
init.h
pgp.c

index c713b6d5024b91700a903974211acba25f4f3fd3..2ef0ae2cf4ef1531f9162a4a7a22e524f3427fdf 100644 (file)
@@ -83,3 +83,6 @@ set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --w
 # 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"
+
index c7187024e7166545b8ba3996b55cee3ec2e69cd1..1728bdc7d9d9b6120b528c1c547e579e991dd398 100644 (file)
@@ -2639,6 +2639,7 @@ int pgp_gpgme_encrypted_handler (BODY *a, STATE *s)
   else
     {
       mutt_error _("Could not decrypt PGP message");
+      mutt_sleep (2);
       rc = -1;
     }
 
index 850750ff3bb83884c842faaa56fdd2822b51a9f6..6b614c6c5e12376f48535438baf0e7a4c3ec3a4d 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -230,6 +230,7 @@ WHERE LIST *UserHeader INITVAL (0);
 
 /*-- formerly in pgp.h --*/
 WHERE REGEXP PgpGoodSign;
+WHERE REGEXP PgpDecryptionOkay;
 WHERE char *PgpSignAs;
 WHERE short PgpTimeout;
 WHERE char *PgpEntryFormat;
diff --git a/init.h b/init.h
index 6cc1a0cbfd181c660d49bb13d84dd4293583a75f..065944f381f4bb91fa708acb0efbd0adc48f70f4 100644 (file)
--- a/init.h
+++ b/init.h
@@ -1796,6 +1796,17 @@ struct option_t MuttVars[] = {
   ** 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 --git a/pgp.c b/pgp.c
index 650c5d53884e851e43b522a79ffe038aa1a3de00..633c9f16fe1cb85c6053513f0f9abd6a5b9bda43 100644 (file)
--- a/pgp.c
+++ b/pgp.c
@@ -224,6 +224,45 @@ static int pgp_copy_checksig (FILE *fpin, FILE *fpout)
   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 @@ BODY *pgp_decrypt_part (BODY *a, STATE *s, FILE *fpout, BODY *p)
   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 @@ int pgp_encrypted_handler (BODY *a, STATE *s)
   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;