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);
signature.asc
Description: PGP signature
