📄 pgp.c
字号:
/* * Copyright (C) 1996,1997 Michael R. Elkins <me@mutt.org> * Copyright (C) 1998,1999 Thomas Roessler <roessler@does-not-exist.org> * Copyright (C) 2004 g10 Code GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This file contains all of the PGP routines necessary to sign, encrypt, * verify and decrypt PGP messages in either the new PGP/MIME format, or * in the older Application/Pgp format. It also contains some code to * cache the user's passphrase for repeat use when decrypting or signing * a message. */#if HAVE_CONFIG_H# include "config.h"#endif#include "mutt.h"#include "mutt_curses.h"#include "pgp.h"#include "mime.h"#include "copy.h"#include <sys/wait.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/stat.h>#include <errno.h>#include <ctype.h>#ifdef HAVE_LOCALE_H#include <locale.h>#endif#ifdef HAVE_SYS_TIME_H# include <sys/time.h>#endif#ifdef HAVE_SYS_RESOURCE_H# include <sys/resource.h>#endif#ifdef CRYPT_BACKEND_CLASSIC_PGP#include "mutt_crypt.h"#include "mutt_menu.h"char PgpPass[LONG_STRING];time_t PgpExptime = 0; /* when does the cached passphrase expire? */void pgp_void_passphrase (void){ memset (PgpPass, 0, sizeof (PgpPass)); PgpExptime = 0;}int pgp_valid_passphrase (void){ time_t now = time (NULL); if (pgp_use_gpg_agent()) { *PgpPass = 0; return 1; /* handled by gpg-agent */ } if (now < PgpExptime) /* Use cached copy. */ return 1; pgp_void_passphrase (); if (mutt_get_password (_("Enter PGP passphrase:"), PgpPass, sizeof (PgpPass)) == 0) { PgpExptime = time (NULL) + PgpTimeout; return (1); } else PgpExptime = 0; return 0;}void pgp_forget_passphrase (void){ pgp_void_passphrase (); mutt_message _("PGP passphrase forgotten.");}int pgp_use_gpg_agent (void){ char *tty; if (!option (OPTUSEGPGAGENT) || !getenv ("GPG_AGENT_INFO")) return 0; if ((tty = ttyname(0))) setenv("GPG_TTY", tty, 0); return 1;}char *pgp_keyid(pgp_key_t k){ if((k->flags & KEYFLAG_SUBKEY) && k->parent && option(OPTPGPIGNORESUB)) k = k->parent; return _pgp_keyid(k);}char *_pgp_keyid(pgp_key_t k){ if(option(OPTPGPLONGIDS)) return k->keyid; else return (k->keyid + 8);}/* ---------------------------------------------------------------------------- * Routines for handing PGP input. *//* Copy PGP output messages and look for signs of a good signature */static int pgp_copy_checksig (FILE *fpin, FILE *fpout){ int rv = -1; if (PgpGoodSign.pattern) { char *line = NULL; int lineno = 0; size_t linelen; while ((line = mutt_read_line (line, &linelen, fpin, &lineno)) != NULL) { if (regexec (PgpGoodSign.rx, line, 0, NULL, 0) == 0) { dprint (2, (debugfile, "pgp_copy_checksig: \"%s\" matches regexp.\n", line)); rv = 0; } else dprint (2, (debugfile, "pgp_copy_checksig: \"%s\" doesn't match regexp.\n", line)); if (strncmp (line, "[GNUPG:] ", 9) == 0) continue; fputs (line, fpout); fputc ('\n', fpout); } FREE (&line); } else { dprint (2, (debugfile, "pgp_copy_checksig: No pattern.\n")); mutt_copy_stream (fpin, fpout); 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. * * (Note that we aren't worse than Outlook &c in this, and also * note that we can successfully handle anything produced by any * existing versions of mutt.) */static void pgp_copy_clearsigned (FILE *fpin, STATE *s, char *charset){ char buf[HUGE_STRING]; short complete, armor_header; FGETCONV *fc; rewind (fpin); fc = fgetconv_open (fpin, charset, Charset, M_ICONV_HOOK_FROM); for (complete = 1, armor_header = 1; fgetconvs (buf, sizeof (buf), fc) != NULL; complete = strchr (buf, '\n') != NULL) { if (!complete) { if (!armor_header) state_puts (buf, s); continue; } if (mutt_strcmp (buf, "-----BEGIN PGP SIGNATURE-----\n") == 0) break; if (armor_header) { char *p = mutt_skip_whitespace (buf); if (*p == '\0') armor_header = 0; continue; } if (s->prefix) state_puts (s->prefix, s); if (buf[0] == '-' && buf[1] == ' ') state_puts (buf + 2, s); else state_puts (buf, s); } fgetconv_close (&fc);}/* Support for the Application/PGP Content Type. */int pgp_application_pgp_handler (BODY *m, STATE *s){ int could_not_decrypt = 0; int needpass = -1, pgp_keyblock = 0; int clearsign = 0, rv, rc; int c = 1; /* silence GCC warning */ long start_pos = 0; long bytes; LOFF_T last_pos, offset; char buf[HUGE_STRING]; char outfile[_POSIX_PATH_MAX]; char tmpfname[_POSIX_PATH_MAX]; FILE *pgpout = NULL, *pgpin = NULL, *pgperr = NULL; FILE *tmpfp = NULL; pid_t thepid; short maybe_goodsig = 1; short have_any_sigs = 0; char body_charset[STRING]; mutt_get_body_charset (body_charset, sizeof (body_charset), m); rc = 0; /* silence false compiler warning if (s->flags & M_DISPLAY) */ fseeko (s->fpin, m->offset, 0); last_pos = m->offset; for (bytes = m->length; bytes > 0;) { if (fgets (buf, sizeof (buf), s->fpin) == NULL) break; offset = ftello (s->fpin); bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf) */ last_pos = offset; if (mutt_strncmp ("-----BEGIN PGP ", buf, 15) == 0) { clearsign = 0; start_pos = last_pos; if (mutt_strcmp ("MESSAGE-----\n", buf + 15) == 0) needpass = 1; else if (mutt_strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0) { clearsign = 1; needpass = 0; } else if (!option (OPTDONTHANDLEPGPKEYS) && mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0) { needpass = 0; pgp_keyblock =1; } else { /* XXX - we may wish to recode here */ if (s->prefix) state_puts (s->prefix, s); state_puts (buf, s); continue; } have_any_sigs = have_any_sigs || (clearsign && (s->flags & M_VERIFY)); /* Copy PGP material to temporary file */ mutt_mktemp (tmpfname); if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) { mutt_perror (tmpfname); return -1; } fputs (buf, tmpfp); while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) { offset = ftello (s->fpin); bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf) */ last_pos = offset; fputs (buf, tmpfp); if ((needpass && mutt_strcmp ("-----END PGP MESSAGE-----\n", buf) == 0) || (!needpass && (mutt_strcmp ("-----END PGP SIGNATURE-----\n", buf) == 0 || mutt_strcmp ("-----END PGP PUBLIC KEY BLOCK-----\n",buf) == 0))) break; } /* leave tmpfp open in case we still need it - but flush it! */ fflush (tmpfp); /* Invoke PGP if needed */ if (!clearsign || (s->flags & M_VERIFY)) { mutt_mktemp (outfile); if ((pgpout = safe_fopen (outfile, "w+")) == NULL) { mutt_perror (tmpfname); return -1; } if ((thepid = pgp_invoke_decode (&pgpin, NULL, &pgperr, -1, fileno (pgpout), -1, tmpfname, needpass)) == -1) { safe_fclose (&pgpout); maybe_goodsig = 0; pgpin = NULL; pgperr = NULL; state_attach_puts (_("[-- Error: unable to create PGP subprocess! --]\n"), s); } else /* PGP started successfully */ { if (needpass) { if (!pgp_valid_passphrase ()) pgp_void_passphrase(); if (pgp_use_gpg_agent()) *PgpPass = 0; fprintf (pgpin, "%s\n", PgpPass); } safe_fclose (&pgpin); if (s->flags & M_DISPLAY) { crypt_current_time (s, "PGP"); rc = pgp_copy_checksig (pgperr, s->fpout); } safe_fclose (&pgperr); rv = mutt_wait_filter (thepid); if (s->flags & M_DISPLAY) { if (rc == 0) have_any_sigs = 1; /* * Sig is bad if * gpg_good_sign-pattern did not match || pgp_decode_command returned not 0 * Sig _is_ correct if * gpg_good_sign="" && pgp_decode_command returned 0 */ if (rc == -1 || rv) maybe_goodsig = 0; state_attach_puts (_("[-- End of PGP output --]\n\n"), s); } } /* treat empty result as sign of failure */ /* TODO: maybe on failure mutt should include the original undecoded text. */ if (pgpout) { rewind (pgpout); c = fgetc (pgpout); ungetc (c, pgpout); } if (!clearsign && (!pgpout || c == EOF)) { could_not_decrypt = 1; pgp_void_passphrase (); } if (could_not_decrypt && !(s->flags & M_DISPLAY)) { mutt_error _("Could not decrypt PGP message"); mutt_sleep (1); rc = -1; goto out; } } /* * Now, copy cleartext to the screen. NOTE - we expect that PGP * outputs utf-8 cleartext. This may not always be true, but it * seems to be a reasonable guess. */ if(s->flags & M_DISPLAY) { if (needpass) state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s); else if (pgp_keyblock) state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s); else state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s); } if (clearsign) { rewind (tmpfp); if (tmpfp) pgp_copy_clearsigned (tmpfp, s, body_charset); } else if (pgpout) { FGETCONV *fc; int c; rewind (pgpout); state_set_prefix (s); fc = fgetconv_open (pgpout, "utf-8", Charset, 0); while ((c = fgetconv (fc)) != EOF) state_prefix_putc (c, s); fgetconv_close (&fc); } if (s->flags & M_DISPLAY) { state_putc ('\n', s); if (needpass) { state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s); if (could_not_decrypt) mutt_error _("Could not decrypt PGP message"); else mutt_message _("PGP message successfully decrypted."); } else if (pgp_keyblock) state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s); else state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s); } } else { /* XXX - we may wish to recode here */ if (s->prefix) state_puts (s->prefix, s); state_puts (buf, s); } } rc = 0;out: m->goodsig = (maybe_goodsig && have_any_sigs); if (tmpfp) { safe_fclose (&tmpfp); mutt_unlink (tmpfname); } if (pgpout) { safe_fclose (&pgpout); mutt_unlink (outfile); } if (needpass == -1) { state_attach_puts (_("[-- Error: could not find beginning of PGP message! --]\n\n"), s); return -1; } return rc;}static int pgp_check_traditional_one_body (FILE *fp, BODY *b, int tagged_only){ char tempfile[_POSIX_PATH_MAX]; char buf[HUGE_STRING]; FILE *tfp; short sgn = 0; short enc = 0; short key = 0; if (b->type != TYPETEXT) return 0; if (tagged_only && !b->tagged) return 0; mutt_mktemp (tempfile); if (mutt_decode_save_attachment (fp, b, tempfile, 0, 0) != 0) { unlink (tempfile); return 0; } if ((tfp = fopen (tempfile, "r")) == NULL) { unlink (tempfile); return 0; } while (fgets (buf, sizeof (buf), tfp)) { if (mutt_strncmp ("-----BEGIN PGP ", buf, 15) == 0) { if (mutt_strcmp ("MESSAGE-----\n", buf + 15) == 0) enc = 1; else if (mutt_strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0) sgn = 1; else if (mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0) key = 1; } } safe_fclose (&tfp); unlink (tempfile); if (!enc && !sgn && !key)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -