pgp.c 37.9 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2 3
 * Copyright (C) 1996-7,2000 Michael R. Elkins <me@mutt.org>
 * Copyright (C) 1998-2005 Thomas Roessler <roessler@does-not-exist.org>
4
 * Copyright (C) 2004 g10 Code GmbH
5
 *
Thomas Roessler's avatar
Thomas Roessler committed
6 7 8 9 10 11 12 13 14 15 16 17
 *     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
18
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Thomas Roessler's avatar
Thomas Roessler committed
19 20 21 22 23 24 25 26 27 28
 */ 

/*
 * 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.
 */

29 30 31 32
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
33 34 35 36
#include "mutt.h"
#include "mutt_curses.h"
#include "pgp.h"
#include "mime.h"
37
#include "copy.h"
Thomas Roessler's avatar
Thomas Roessler committed
38 39 40 41 42 43 44 45 46

#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>

Thomas Roessler's avatar
Thomas Roessler committed
47 48 49 50
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

51 52 53 54
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

55 56 57 58
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif

59
#ifdef CRYPT_BACKEND_CLASSIC_PGP
Thomas Roessler's avatar
Thomas Roessler committed
60

61
#include "mutt_crypt.h"
62
#include "mutt_menu.h"
63

Thomas Roessler's avatar
Thomas Roessler committed
64

65
char PgpPass[LONG_STRING];
66
time_t PgpExptime = 0; /* when does the cached passphrase expire? */
Thomas Roessler's avatar
Thomas Roessler committed
67 68 69 70 71 72 73

void pgp_void_passphrase (void)
{
  memset (PgpPass, 0, sizeof (PgpPass));
  PgpExptime = 0;
}

74 75 76 77
int pgp_valid_passphrase (void)
{
  time_t now = time (NULL);

78 79 80 81 82 83
  if (pgp_use_gpg_agent())
    {
      *PgpPass = 0;
      return 1; /* handled by gpg-agent */
    }

84 85 86
  if (now < PgpExptime)
    /* Use cached copy.  */
    return 1;
87 88
  
  pgp_void_passphrase ();
89 90 91 92 93 94 95 96 97 98 99

  if (mutt_get_password (_("Enter PGP passphrase:"), PgpPass, sizeof (PgpPass)) == 0)
    {
      PgpExptime = time (NULL) + PgpTimeout;
      return (1);
    }
  else
    PgpExptime = 0;

  return 0;
}
100

101
void pgp_forget_passphrase (void)
Thomas Roessler's avatar
Thomas Roessler committed
102 103
{
  pgp_void_passphrase ();
104
  mutt_message _("PGP passphrase forgotten.");
Thomas Roessler's avatar
Thomas Roessler committed
105 106
}

107 108
int pgp_use_gpg_agent (void)
{
109 110 111 112 113 114 115 116 117
  char *tty;

  if (!option (OPTUSEGPGAGENT) || !getenv ("GPG_AGENT_INFO"))
    return 0;

  if ((tty = ttyname(0)))
    setenv("GPG_TTY", tty, 0);

  return 1;
118
}
Thomas Roessler's avatar
Thomas Roessler committed
119

120
char *pgp_keyid(pgp_key_t k)
Thomas Roessler's avatar
Thomas Roessler committed
121
{
David Shaw's avatar
David Shaw committed
122
  if((k->flags & KEYFLAG_SUBKEY) && k->parent && option(OPTPGPIGNORESUB))
123
    k = k->parent;
Thomas Roessler's avatar
Thomas Roessler committed
124 125 126 127

  return _pgp_keyid(k);
}

128
char *_pgp_keyid(pgp_key_t k)
Thomas Roessler's avatar
Thomas Roessler committed
129 130 131 132 133 134 135 136 137 138 139 140 141
{
  if(option(OPTPGPLONGIDS))
    return k->keyid;
  else
    return (k->keyid + 8);
}

/* ----------------------------------------------------------------------------
 * Routines for handing PGP input.
 */



142
/* Copy PGP output messages and look for signs of a good signature */
143 144 145 146 147 148 149 150 151 152 153

static int pgp_copy_checksig (FILE *fpin, FILE *fpout)
{
  int rv = -1;

  if (PgpGoodSign.pattern)
  {
    char *line = NULL;
    int lineno = 0;
    size_t linelen;
    
154
    while ((line = mutt_read_line (line, &linelen, fpin, &lineno, 0)) != NULL)
155 156
    {
      if (regexec (PgpGoodSign.rx, line, 0, NULL, 0) == 0)
157 158 159
      {
	dprint (2, (debugfile, "pgp_copy_checksig: \"%s\" matches regexp.\n",
		    line));
160
	rv = 0;
161 162 163 164
      }
      else
	dprint (2, (debugfile, "pgp_copy_checksig: \"%s\" doesn't match regexp.\n",
		    line));
165
      
166 167
      if (strncmp (line, "[GNUPG:] ", 9) == 0)
	continue;
168 169 170
      fputs (line, fpout);
      fputc ('\n', fpout);
    }
171
    FREE (&line);
172 173 174
  }
  else
  {
175
    dprint (2, (debugfile, "pgp_copy_checksig: No pattern.\n"));
176 177 178 179 180 181 182
    mutt_copy_stream (fpin, fpout);
    rv = 1;
  }

  return rv;
}

183 184 185
/* 
 * Copy a clearsigned message, and strip the signature and PGP's
 * dash-escaping.
186 187 188 189 190 191 192 193
 * 
 * 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.) 
194 195
 */

196
static void pgp_copy_clearsigned (FILE *fpin, STATE *s, char *charset)
197 198
{
  char buf[HUGE_STRING];
199 200 201 202 203
  short complete, armor_header;
  
  FGETCONV *fc;
  
  rewind (fpin);
204 205 206 207 208

  /* fromcode comes from the MIME Content-Type charset label. It might
   * be a wrong label, so we want the ability to do corrections via
   * charset-hooks. Therefore we set flags to M_ICONV_HOOK_FROM.
   */
209
  fc = fgetconv_open (fpin, charset, Charset, M_ICONV_HOOK_FROM);
210
  
211 212
  for (complete = 1, armor_header = 1;
       fgetconvs (buf, sizeof (buf), fc) != NULL;
213 214 215 216 217 218 219 220 221 222 223 224 225 226
       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)
    {
227
      char *p = mutt_skip_whitespace (buf);
228
      if (*p == '\0') 
229 230 231 232 233 234 235 236 237 238 239 240 241
	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);
  }
  
242
  fgetconv_close (&fc);
243 244
}

245

Thomas Roessler's avatar
Thomas Roessler committed
246 247
/* Support for the Application/PGP Content Type. */

248
int pgp_application_pgp_handler (BODY *m, STATE *s)
Thomas Roessler's avatar
Thomas Roessler committed
249
{
250
  int could_not_decrypt = 0;
Thomas Roessler's avatar
Thomas Roessler committed
251
  int needpass = -1, pgp_keyblock = 0;
252
  int clearsign = 0, rv, rc;
253
  int c = 1; /* silence GCC warning */
254 255
  long bytes;
  LOFF_T last_pos, offset;
Thomas Roessler's avatar
Thomas Roessler committed
256 257 258
  char buf[HUGE_STRING];
  char outfile[_POSIX_PATH_MAX];
  char tmpfname[_POSIX_PATH_MAX];
259
  FILE *pgpout = NULL, *pgpin = NULL, *pgperr = NULL;
260
  FILE *tmpfp = NULL;
Thomas Roessler's avatar
Thomas Roessler committed
261 262
  pid_t thepid;

263 264
  short maybe_goodsig = 1;
  short have_any_sigs = 0;
265

266
  char *gpgcharset = NULL;
267 268
  char body_charset[STRING];
  mutt_get_body_charset (body_charset, sizeof (body_charset), m);
David Champion's avatar
David Champion committed
269 270 271

  rc = 0;	/* silence false compiler warning if (s->flags & M_DISPLAY) */

272
  fseeko (s->fpin, m->offset, 0);
Thomas Roessler's avatar
Thomas Roessler committed
273 274 275 276
  last_pos = m->offset;
  
  for (bytes = m->length; bytes > 0;)
  {
277
    if (fgets (buf, sizeof (buf), s->fpin) == NULL)
Thomas Roessler's avatar
Thomas Roessler committed
278
      break;
279
    
280
    offset = ftello (s->fpin);
281
    bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf) */
Thomas Roessler's avatar
Thomas Roessler committed
282
    last_pos = offset;
283
    
284
    if (mutt_strncmp ("-----BEGIN PGP ", buf, 15) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
285 286 287
    {
      clearsign = 0;

288
      if (mutt_strcmp ("MESSAGE-----\n", buf + 15) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
289
        needpass = 1;
290
      else if (mutt_strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
291 292 293 294
      {
	clearsign = 1;
        needpass = 0;
      }
295
      else if (!mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15))
Thomas Roessler's avatar
Thomas Roessler committed
296 297
      {
        needpass = 0;
298
        pgp_keyblock = 1;
Thomas Roessler's avatar
Thomas Roessler committed
299 300 301
      } 
      else
      {
302
	/* XXX - we may wish to recode here */
Thomas Roessler's avatar
Thomas Roessler committed
303 304 305 306 307 308
	if (s->prefix)
	  state_puts (s->prefix, s);
	state_puts (buf, s);
	continue;
      }

Thomas Roessler's avatar
Thomas Roessler committed
309
      have_any_sigs = have_any_sigs || (clearsign && (s->flags & M_VERIFY));
310 311

      /* Copy PGP material to temporary file */
312
      mutt_mktemp (tmpfname, sizeof (tmpfname));
313 314 315
      if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL)
      {
	mutt_perror (tmpfname);
316
	return -1;
317
      }
318
      
319 320
      fputs (buf, tmpfp);
      while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
321
      {
322
	offset = ftello (s->fpin);
323 324
	bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf) */
	last_pos = offset;
Thomas Roessler's avatar
Thomas Roessler committed
325
	
326 327 328 329 330 331 332
	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;
333 334 335 336 337 338 339 340 341 342
	/* remember optional Charset: armor header as defined by RfC4880 */
	if (mutt_strncmp ("Charset: ", buf, 9) == 0)
	{
	  size_t l = 0;
	  gpgcharset = safe_strdup (buf + 9);
	  if ((l = mutt_strlen (gpgcharset)) > 0 && gpgcharset[l-1] == '\n')
	    gpgcharset[l-1] = 0;
	  if (mutt_check_charset (gpgcharset, 0) < 0)
	    mutt_str_replace (&gpgcharset, "UTF-8");
	}
343 344 345 346
      }

      /* leave tmpfp open in case we still need it - but flush it! */
      fflush (tmpfp);
347

348 349 350
      /* Invoke PGP if needed */
      if (!clearsign || (s->flags & M_VERIFY))
      {
351
	mutt_mktemp (outfile, sizeof (outfile));
Thomas Roessler's avatar
Thomas Roessler committed
352 353
	if ((pgpout = safe_fopen (outfile, "w+")) == NULL)
	{
354
	  mutt_perror (tmpfname);
355
	  return -1;
Thomas Roessler's avatar
Thomas Roessler committed
356 357
	}
	
358 359 360
	if ((thepid = pgp_invoke_decode (&pgpin, NULL, &pgperr, -1,
					 fileno (pgpout), -1, tmpfname,
					 needpass)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
361
	{
362 363 364 365
	  safe_fclose (&pgpout);
	  maybe_goodsig = 0;
	  pgpin = NULL;
	  pgperr = NULL;
366
	  state_attach_puts (_("[-- Error: unable to create PGP subprocess! --]\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
367
	}
368
	else /* PGP started successfully */
Thomas Roessler's avatar
Thomas Roessler committed
369
	{
370 371
	  if (needpass)
	  {
372
	    if (!pgp_valid_passphrase ()) pgp_void_passphrase();
373 374
            if (pgp_use_gpg_agent())
              *PgpPass = 0;
375
	    fprintf (pgpin, "%s\n", PgpPass);
376 377 378
	  }
	  
	  safe_fclose (&pgpin);
379

380 381
	  if (s->flags & M_DISPLAY)
	  {
382
	    crypt_current_time (s, "PGP");
383 384 385 386
	    rc = pgp_copy_checksig (pgperr, s->fpout);
	  }
	  
	  safe_fclose (&pgperr);
387
	  rv = mutt_wait_filter (thepid);
388
	  
389
	  if (s->flags & M_DISPLAY)
390
	  {
391
	    if (rc == 0) have_any_sigs = 1;
392 393 394 395 396 397
	    /*
	     * 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
	     */
398
	    if (rc == -1 || rv) maybe_goodsig = 0;
399
	    
400 401
	    state_attach_puts (_("[-- End of PGP output --]\n\n"), s);
	  }
402 403
	  if (pgp_use_gpg_agent())
	    mutt_need_hard_redraw ();
404
	}
405
	
406
        /* treat empty result as sign of failure */
407
	/* TODO: maybe on failure mutt should include the original undecoded text. */
408 409 410 411 412 413
	if (pgpout)
	{
	  rewind (pgpout);
	  c = fgetc (pgpout);
	  ungetc (c, pgpout);
	}
414
        if (!clearsign && (!pgpout || c == EOF))
415 416 417 418 419 420
	{
	  could_not_decrypt = 1;
	  pgp_void_passphrase ();
	}
	
	if (could_not_decrypt && !(s->flags & M_DISPLAY))
421
	{
422
          mutt_error _("Could not decrypt PGP message");
423
	  mutt_sleep (1);
424 425
	  rc = -1;
	  goto out;
426
        }
427
      }
428
      
Thomas Roessler's avatar
Thomas Roessler committed
429
      /*
430
       * Now, copy cleartext to the screen.
Thomas Roessler's avatar
Thomas Roessler committed
431 432
       */

433 434 435 436 437 438 439 440 441 442
      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);
      }

443 444 445 446 447 448 449
      if (clearsign)
      {
	rewind (tmpfp);
	if (tmpfp) 
	  pgp_copy_clearsigned (tmpfp, s, body_charset);
      }
      else if (pgpout)
Thomas Roessler's avatar
Thomas Roessler committed
450
      {
Thomas Roessler's avatar
Thomas Roessler committed
451
	FGETCONV *fc;
452
	int c;
453 454 455 456 457
	char *expected_charset = gpgcharset && *gpgcharset ? gpgcharset : "utf-8";

	dprint(4,(debugfile,"pgp: recoding inline from [%s] to [%s]\n",
		  expected_charset, Charset));

Thomas Roessler's avatar
Thomas Roessler committed
458
	rewind (pgpout);
459
	state_set_prefix (s);
460
	fc = fgetconv_open (pgpout, expected_charset, Charset, M_ICONV_HOOK_FROM);
Thomas Roessler's avatar
Thomas Roessler committed
461
	while ((c = fgetconv (fc)) != EOF)
462
	  state_prefix_putc (c, s);
Thomas Roessler's avatar
Thomas Roessler committed
463
	fgetconv_close (&fc);
Thomas Roessler's avatar
Thomas Roessler committed
464 465 466 467
      }

      if (s->flags & M_DISPLAY)
      {
468
	state_putc ('\n', s);
Thomas Roessler's avatar
Thomas Roessler committed
469
	if (needpass)
470
        {
471
	  state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
472 473 474 475
	  if (could_not_decrypt)
	    mutt_error _("Could not decrypt PGP message");
	  else
	    mutt_message _("PGP message successfully decrypted.");
476
        }
Thomas Roessler's avatar
Thomas Roessler committed
477
	else if (pgp_keyblock)
478
	  state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
479
	else
480
	  state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
481 482 483 484
      }
    }
    else
    {
485
      /* A traditional PGP part may mix signed and unsigned content */
486
      /* XXX - we may wish to recode here */
Thomas Roessler's avatar
Thomas Roessler committed
487 488 489 490 491 492
      if (s->prefix)
	state_puts (s->prefix, s);
      state_puts (buf, s);
    }
  }

493 494
  rc = 0;

495
out:
496 497
  m->goodsig = (maybe_goodsig && have_any_sigs);

498 499 500 501 502 503 504 505 506 507
  if (tmpfp)
  {
    safe_fclose (&tmpfp);
    mutt_unlink (tmpfname);
  }
  if (pgpout)
  {
    safe_fclose (&pgpout);
    mutt_unlink (outfile);
  }
508 509 510

  FREE(&gpgcharset);

Thomas Roessler's avatar
Thomas Roessler committed
511 512
  if (needpass == -1)
  {
513
    state_attach_puts (_("[-- Error: could not find beginning of PGP message! --]\n\n"), s);
514
    return -1;
Thomas Roessler's avatar
Thomas Roessler committed
515
  }
516 517
  
  return rc;
Thomas Roessler's avatar
Thomas Roessler committed
518 519
}

520 521 522 523 524 525 526 527
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;
528
  short key = 0;
529 530 531 532 533 534
  
  if (b->type != TYPETEXT)
    return 0;

  if (tagged_only && !b->tagged)
    return 0;
535

536
  mutt_mktemp (tempfile, sizeof (tempfile));
537 538 539 540 541 542 543 544 545 546 547
  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;
  }
548
  
549 550 551 552 553 554 555 556
  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;
557 558
      else if (mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0)
	key = 1;
559 560 561 562 563
    }
  }
  safe_fclose (&tfp);
  unlink (tempfile);

564
  if (!enc && !sgn && !key)
565 566 567 568
    return 0;

  /* fix the content type */
  
569
  mutt_set_parameter ("format", "fixed", &b->parameter);
570 571 572 573 574 575
  if (enc)
    mutt_set_parameter ("x-action", "pgp-encrypted", &b->parameter);
  else if (sgn)
    mutt_set_parameter ("x-action", "pgp-signed", &b->parameter);
  else if (key)
    mutt_set_parameter ("x-action", "pgp-keys", &b->parameter);
576 577 578 579 580 581 582
  
  return 1;
}

int pgp_check_traditional (FILE *fp, BODY *b, int tagged_only)
{
  int rv = 0;
583
  int r;
584 585 586 587 588
  for (; b; b = b->next)
  {
    if (is_multipart (b))
      rv = pgp_check_traditional (fp, b->parts, tagged_only) || rv;
    else if (b->type == TYPETEXT)
589 590 591 592 593 594
    {
      if ((r = mutt_is_application_pgp (b)))
	rv = rv || r;
      else
	rv = pgp_check_traditional_one_body (fp, b, tagged_only) || rv;
    }
595 596 597 598 599
  }

  return rv;
}

600
     
601 602


Thomas Roessler's avatar
Thomas Roessler committed
603

604
int pgp_verify_one (BODY *sigbdy, STATE *s, const char *tempfile)
605 606 607 608
{
  char sigfile[_POSIX_PATH_MAX], pgperrfile[_POSIX_PATH_MAX];
  FILE *fp, *pgpout, *pgperr;
  pid_t thepid;
609
  int badsig = -1;
Thomas Roessler's avatar
Thomas Roessler committed
610 611
  int rv;
  
612 613 614 615 616 617 618
  snprintf (sigfile, sizeof (sigfile), "%s.asc", tempfile);
  
  if(!(fp = safe_fopen (sigfile, "w")))
  {
    mutt_perror(sigfile);
    return -1;
  }
Thomas Roessler's avatar
Thomas Roessler committed
619
	
620
  fseeko (s->fpin, sigbdy->offset, 0);
621
  mutt_copy_bytes (s->fpin, fp, sigbdy->length);
622
  safe_fclose (&fp);
623
  
624
  mutt_mktemp (pgperrfile, sizeof (pgperrfile));
625 626 627 628 629 630 631
  if(!(pgperr = safe_fopen(pgperrfile, "w+")))
  {
    mutt_perror(pgperrfile);
    unlink(sigfile);
    return -1;
  }
  
632
  crypt_current_time (s, "PGP");
633
  
634
  if((thepid = pgp_invoke_verify (NULL, &pgpout, NULL, 
635 636 637
				   -1, -1, fileno(pgperr),
				   tempfile, sigfile)) != -1)
  {
638
    if (pgp_copy_checksig (pgpout, s->fpout) >= 0)
639
      badsig = 0;
640 641 642 643 644
    
    
    safe_fclose (&pgpout);
    fflush (pgperr);
    rewind (pgperr);
645 646 647 648
    
    if (pgp_copy_checksig  (pgperr, s->fpout) >= 0)
      badsig = 0;

Thomas Roessler's avatar
Thomas Roessler committed
649
    if ((rv = mutt_wait_filter (thepid)))
650
      badsig = -1;
Thomas Roessler's avatar
Thomas Roessler committed
651 652
    
     dprint (1, (debugfile, "pgp_verify_one: mutt_wait_filter returned %d.\n", rv));
653
  }
654 655 656

  safe_fclose (&pgperr);

657
  state_attach_puts (_("[-- End of PGP output --]\n\n"), s);
658

659 660
  mutt_unlink (sigfile);
  mutt_unlink (pgperrfile);
Thomas Roessler's avatar
Thomas Roessler committed
661

662 663
  dprint (1, (debugfile, "pgp_verify_one: returning %d.\n", badsig));
  
664
  return badsig;
665
}
Thomas Roessler's avatar
Thomas Roessler committed
666 667 668 669 670 671 672 673


/* Extract pgp public keys from messages or attachments */

void pgp_extract_keys_from_messages (HEADER *h)
{
  int i;
  char tempfname[_POSIX_PATH_MAX];
674
  FILE *fpout;
675 676

  if (h)
Thomas Roessler's avatar
Thomas Roessler committed
677
  {
678
    mutt_parse_mime_message (Context, h);
679
    if(h->security & PGPENCRYPT && !pgp_valid_passphrase ())
Thomas Roessler's avatar
Thomas Roessler committed
680 681 682
      return;
  }

683
  mutt_mktemp (tempfname, sizeof (tempfname));
684
  if (!(fpout = safe_fopen (tempfname, "w")))
Thomas Roessler's avatar
Thomas Roessler committed
685
  {
686
    mutt_perror (tempfname);
Thomas Roessler's avatar
Thomas Roessler committed
687 688 689
    return;
  }

690
  set_option (OPTDONTHANDLEPGPKEYS);
Thomas Roessler's avatar
Thomas Roessler committed
691
  
692
  if (!h)
Thomas Roessler's avatar
Thomas Roessler committed
693
  {
694
    for (i = 0; i < Context->vcount; i++)
Thomas Roessler's avatar
Thomas Roessler committed
695
    {
696
      if (Context->hdrs[Context->v2r[i]]->tagged)
Thomas Roessler's avatar
Thomas Roessler committed
697
      {
698
	mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
699
	if (Context->hdrs[Context->v2r[i]]->security & PGPENCRYPT
Thomas Roessler's avatar
Thomas Roessler committed
700 701
	   && !pgp_valid_passphrase())
	{
702
	  safe_fclose (&fpout);
Thomas Roessler's avatar
Thomas Roessler committed
703 704
	  goto bailout;
	}
705 706
	mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]], 
			   M_CM_DECODE|M_CM_CHARCONV, 0);
Thomas Roessler's avatar
Thomas Roessler committed
707 708 709 710 711
      }
    }
  } 
  else
  {
712
    mutt_parse_mime_message (Context, h);
713
    if (h->security & PGPENCRYPT && !pgp_valid_passphrase())
Thomas Roessler's avatar
Thomas Roessler committed
714
    {
715
      safe_fclose (&fpout);
Thomas Roessler's avatar
Thomas Roessler committed
716 717
      goto bailout;
    }
718
    mutt_copy_message (fpout, Context, h, M_CM_DECODE|M_CM_CHARCONV, 0);
Thomas Roessler's avatar
Thomas Roessler committed
719 720
  }
      
721
  safe_fclose (&fpout);
722
  mutt_endwin (NULL);
723 724
  pgp_invoke_import (tempfname);
  mutt_any_key_to_continue (NULL);
Thomas Roessler's avatar
Thomas Roessler committed
725 726 727

  bailout:
  
728 729
  mutt_unlink (tempfname);
  unset_option (OPTDONTHANDLEPGPKEYS);
Thomas Roessler's avatar
Thomas Roessler committed
730 731 732
  
}

733
static void pgp_extract_keys_from_attachment (FILE *fp, BODY *top)
Thomas Roessler's avatar
Thomas Roessler committed
734 735 736 737 738
{
  STATE s;
  FILE *tempfp;
  char tempfname[_POSIX_PATH_MAX];

739
  mutt_mktemp (tempfname, sizeof (tempfname));
740
  if (!(tempfp = safe_fopen (tempfname, "w")))
Thomas Roessler's avatar
Thomas Roessler committed
741
  {
742
    mutt_perror (tempfname);
Thomas Roessler's avatar
Thomas Roessler committed
743 744 745
    return;
  }

746
  memset (&s, 0, sizeof (STATE));
Thomas Roessler's avatar
Thomas Roessler committed
747 748 749 750
  
  s.fpin = fp;
  s.fpout = tempfp;
  
751
  mutt_body_handler (top, &s);
Thomas Roessler's avatar
Thomas Roessler committed
752

753
  safe_fclose (&tempfp);
Thomas Roessler's avatar
Thomas Roessler committed
754

755 756
  pgp_invoke_import (tempfname);
  mutt_any_key_to_continue (NULL);
Thomas Roessler's avatar
Thomas Roessler committed
757

758
  mutt_unlink (tempfname);
Thomas Roessler's avatar
Thomas Roessler committed
759 760 761 762 763 764
}

void pgp_extract_keys_from_attachment_list (FILE *fp, int tag, BODY *top)
{
  if(!fp)
  {
765
    mutt_error _("Internal error. Inform <roessler@does-not-exist.org>.");
Thomas Roessler's avatar
Thomas Roessler committed
766 767 768
    return;
  }

769
  mutt_endwin (NULL);
Thomas Roessler's avatar
Thomas Roessler committed
770 771 772 773 774
  set_option(OPTDONTHANDLEPGPKEYS);
  
  for(; top; top = top->next)
  {
    if(!tag || top->tagged)
775
      pgp_extract_keys_from_attachment (fp, top);
Thomas Roessler's avatar
Thomas Roessler committed
776 777 778 779 780 781 782 783
    
    if(!tag)
      break;
  }
  
  unset_option(OPTDONTHANDLEPGPKEYS);
}

784
BODY *pgp_decrypt_part (BODY *a, STATE *s, FILE *fpout, BODY *p)
Thomas Roessler's avatar
Thomas Roessler committed
785 786 787 788 789 790 791 792 793
{
  char buf[LONG_STRING];
  FILE *pgpin, *pgpout, *pgperr, *pgptmp;
  struct stat info;
  BODY *tattach;
  int len;
  char pgperrfile[_POSIX_PATH_MAX];
  char pgptmpfile[_POSIX_PATH_MAX];
  pid_t thepid;
794
  int rv;
Thomas Roessler's avatar
Thomas Roessler committed
795
  
796
  mutt_mktemp (pgperrfile, sizeof (pgperrfile));
Thomas Roessler's avatar
Thomas Roessler committed
797 798 799 800 801 802 803
  if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL)
  {
    mutt_perror (pgperrfile);
    return NULL;
  }
  unlink (pgperrfile);

804
  mutt_mktemp (pgptmpfile, sizeof (pgptmpfile));
Thomas Roessler's avatar
Thomas Roessler committed
805 806 807
  if((pgptmp = safe_fopen (pgptmpfile, "w")) == NULL)
  {
    mutt_perror (pgptmpfile);
808
    safe_fclose (&pgperr);
Thomas Roessler's avatar
Thomas Roessler committed
809 810 811 812 813 814 815
    return NULL;
  }

  /* Position the stream at the beginning of the body, and send the data to
   * the temporary file.
   */

816
  fseeko (s->fpin, a->offset, 0);
Thomas Roessler's avatar
Thomas Roessler committed
817
  mutt_copy_bytes (s->fpin, pgptmp, a->length);
818
  safe_fclose (&pgptmp);
Thomas Roessler's avatar
Thomas Roessler committed
819

820
  if ((thepid = pgp_invoke_decrypt (&pgpin, &pgpout, NULL, -1, -1,
Thomas Roessler's avatar
Thomas Roessler committed
821 822
				    fileno (pgperr), pgptmpfile)) == -1)
  {
823
    safe_fclose (&pgperr);
Thomas Roessler's avatar
Thomas Roessler committed
824 825
    unlink (pgptmpfile);
    if (s->flags & M_DISPLAY)
826
      state_attach_puts (_("[-- Error: could not create a PGP subprocess! --]\n\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
827 828 829
    return (NULL);
  }

830 831 832 833 834
  /* send the PGP passphrase to the subprocess.  Never do this if the
     agent is active, because this might lead to a passphrase send as
     the message. */
  if (!pgp_use_gpg_agent())
    fputs (PgpPass, pgpin);
Thomas Roessler's avatar
Thomas Roessler committed
835
  fputc ('\n', pgpin);
836
  safe_fclose (&pgpin);
Thomas Roessler's avatar
Thomas Roessler committed
837 838 839 840 841 842
  
  /* Read the output from PGP, and make sure to change CRLF to LF, otherwise
   * read_mime_header has a hard time parsing the message.
   */
  while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL)
  {
843
    len = mutt_strlen (buf);
Thomas Roessler's avatar
Thomas Roessler committed
844
    if (len > 1 && buf[len - 2] == '\r')
845
      strcpy (buf + len - 2, "\n");	/* __STRCPY_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
846 847 848
    fputs (buf, fpout);
  }

849
  safe_fclose (&pgpout);
850
  rv = mutt_wait_filter (thepid);
Thomas Roessler's avatar
Thomas Roessler committed
851 852 853 854 855 856
  mutt_unlink(pgptmpfile);
  
  if (s->flags & M_DISPLAY)
  {
    fflush (pgperr);
    rewind (pgperr);
857
    if (pgp_copy_checksig (pgperr, s->fpout) == 0 && !rv && p)
858
      p->goodsig = 1;
859 860
    else
      p->goodsig = 0;
861
    state_attach_puts (_("[-- End of PGP output --]\n\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
862
  }
863
  safe_fclose (&pgperr);
Thomas Roessler's avatar
Thomas Roessler committed
864 865 866

  fflush (fpout);
  rewind (fpout);
867 868 869 870

  if (pgp_use_gpg_agent())
    mutt_need_hard_redraw ();

871
  if (fgetc (fpout) == EOF)
872 873 874
  {
    mutt_error _("Decryption failed");
    pgp_void_passphrase ();
875
    return NULL;
876
  }
877 878

  rewind (fpout);
879
  
Thomas Roessler's avatar
Thomas Roessler committed
880 881 882 883 884 885 886 887 888 889
  if ((tattach = mutt_read_mime_header (fpout, 0)) != NULL)
  {
    /*
     * Need to set the length of this body part.
     */
    fstat (fileno (fpout), &info);
    tattach->length = info.st_size - tattach->offset;

    /* See if we need to recurse on this MIME part.  */

890
    mutt_parse_part (fpout, tattach);
Thomas Roessler's avatar
Thomas Roessler committed
891 892 893 894 895
  }

  return (tattach);
}

896 897 898 899
int pgp_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
{
  char tempfile[_POSIX_PATH_MAX];
  STATE s;
900
  BODY *p = b;
901 902 903
  
  if(!mutt_is_multipart_encrypted(b))
    return -1;
904 905 906 907 908

  if(!b->parts || !b->parts->next)
    return -1;
  
  b = b->parts->next;
909
  
910 911
  memset (&s, 0, sizeof (s));
  s.fpin = fpin;
912
  mutt_mktemp (tempfile, sizeof (tempfile));
913 914 915 916 917 918 919
  if ((*fpout = safe_fopen (tempfile, "w+")) == NULL)
  {
    mutt_perror (tempfile);
    return (-1);
  }
  unlink (tempfile);

920
  *cur = pgp_decrypt_part (b, &s, *fpout, p);
921 922

  rewind (*fpout);
Thomas Roessler's avatar
Thomas Roessler committed
923
  
924
  if (!*cur)
Thomas Roessler's avatar
Thomas Roessler committed
925 926
    return -1;
  
927 928 929
  return (0);
}

930
int pgp_encrypted_handler (BODY *a, STATE *s)
Thomas Roessler's avatar
Thomas Roessler committed
931 932 933 934
{
  char tempfile[_POSIX_PATH_MAX];
  FILE *fpout, *fpin;
  BODY *tattach;
935
  BODY *p = a;
936
  int rc = 0;
937
  
Thomas Roessler's avatar
Thomas Roessler committed
938 939
  a = a->parts;
  if (!a || a->type != TYPEAPPLICATION || !a->subtype || 
940
      ascii_strcasecmp ("pgp-encrypted", a->subtype) != 0 ||
Thomas Roessler's avatar
Thomas Roessler committed
941
      !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype ||
942
      ascii_strcasecmp ("octet-stream", a->next->subtype) != 0)
Thomas Roessler's avatar
Thomas Roessler committed
943 944
  {
    if (s->flags & M_DISPLAY)
945
      state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"), s);
946
    return -1;
Thomas Roessler's avatar
Thomas Roessler committed
947 948 949 950 951 952 953
  }

  /*
   * Move forward to the application/pgp-encrypted body.
   */
  a = a->next;

954
  mutt_mktemp (tempfile, sizeof (tempfile));
Thomas Roessler's avatar
Thomas Roessler committed
955 956 957
  if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
  {
    if (s->flags & M_DISPLAY)
958
      state_attach_puts (_("[-- Error: could not create temporary file! --]\n"), s);
959
    return -1;
Thomas Roessler's avatar
Thomas Roessler committed
960 961
  }

962
  if (s->flags & M_DISPLAY) crypt_current_time (s, "PGP");
Thomas Roessler's avatar
Thomas Roessler committed
963

964
  if ((tattach = pgp_decrypt_part (a, s, fpout, p)) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
965 966
  {
    if (s->flags & M_DISPLAY)
967
      state_attach_puts (_("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
Thomas Roessler's avatar
Thomas Roessler committed
968 969 970

    fpin = s->fpin;
    s->fpin = fpout;
971
    rc = mutt_body_handler (tattach, s);
Thomas Roessler's avatar
Thomas Roessler committed
972 973
    s->fpin = fpin;

Thomas Roessler's avatar
Thomas Roessler committed
974 975 976 977 978 979 980 981
    /* 
     * if a multipart/signed is the _only_ sub-part of a
     * multipart/encrypted, cache signature verification
     * status.
     *
     */
    
    if (mutt_is_multipart_signed (tattach) && !tattach->next)
982
      p->goodsig |= tattach->goodsig;
Thomas Roessler's avatar
Thomas Roessler committed
983
    
Thomas Roessler's avatar
Thomas Roessler committed
984
    if (s->flags & M_DISPLAY)
985 986 987 988
    {
      state_puts ("\n", s);
      state_attach_puts (_("[-- End of PGP/MIME encrypted data --]\n"), s);
    }
Thomas Roessler's avatar
Thomas Roessler committed
989 990

    mutt_free_body (&tattach);
991
    /* clear 'Invoking...' message, since there's no error */
992
    mutt_message _("PGP message successfully decrypted.");
Thomas Roessler's avatar
Thomas Roessler committed
993
  }
994
  else
995
  {
996
    mutt_error _("Could not decrypt PGP message");
997 998
    /* void the passphrase, even if it's not necessarily the problem */
    pgp_void_passphrase ();
999
    rc = -1;
1000
  }
Thomas Roessler's avatar
Thomas Roessler committed
1001

1002
  safe_fclose (&fpout);
Thomas Roessler's avatar
Thomas Roessler committed
1003
  mutt_unlink(tempfile);
1004 1005

  return rc;
Thomas Roessler's avatar
Thomas Roessler committed
1006 1007 1008 1009 1010 1011 1012
}

/* ----------------------------------------------------------------------------
 * Routines for sending PGP/MIME messages.
 */


1013
BODY *pgp_sign_message (BODY *a)
Thomas Roessler's avatar
Thomas Roessler committed
1014 1015 1016 1017 1018 1019 1020 1021
{
  BODY *t;
  char buffer[LONG_STRING];
  char sigfile[_POSIX_PATH_MAX], signedfile[_POSIX_PATH_MAX];
  FILE *pgpin, *pgpout, *pgperr, *fp, *sfp;
  int err = 0;
  int empty = 1;
  pid_t thepid;
Thomas Roessler's avatar
Thomas Roessler committed
1022
  
Thomas Roessler's avatar
Thomas Roessler committed
1023 1024
  convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */

1025
  mutt_mktemp (sigfile, sizeof (sigfile));
Thomas Roessler's avatar
Thomas Roessler committed
1026 1027 1028 1029 1030
  if ((fp = safe_fopen (sigfile, "w")) == NULL)
  {
    return (NULL);
  }

1031
  mutt_mktemp (signedfile, sizeof (signedfile));
Thomas Roessler's avatar
Thomas Roessler committed
1032 1033 1034
  if ((sfp = safe_fopen(signedfile, "w")) == NULL)
  {
    mutt_perror(signedfile);
1035
    safe_fclose (&fp);
Thomas Roessler's avatar
Thomas Roessler committed
1036 1037 1038 1039 1040 1041 1042
    unlink(sigfile);
    return NULL;
  }
  
  mutt_write_mime_header (a, sfp);
  fputc ('\n', sfp);
  mutt_write_mime_body (a, sfp);
1043
  safe_fclose (&sfp);
Thomas Roessler's avatar
Thomas Roessler committed
1044
  
1045 1046
  if ((thepid = pgp_invoke_sign (&pgpin, &pgpout, &pgperr,
				 -1, -1, -1, signedfile)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
1047
  {
1048
    mutt_perror _("Can't open PGP subprocess!");
1049
    safe_fclose (&fp);
Thomas Roessler's avatar
Thomas Roessler committed
1050 1051 1052 1053 1054
    unlink(sigfile);
    unlink(signedfile);
    return NULL;
  }
  
1055 1056
  if (!pgp_use_gpg_agent())
     fputs(PgpPass, pgpin);
Thomas Roessler's avatar
Thomas Roessler committed
1057
  fputc('\n', pgpin);
1058
  safe_fclose (&pgpin);
Thomas Roessler's avatar
Thomas Roessler committed
1059 1060 1061 1062 1063 1064 1065
  
  /*
   * Read back the PGP signature.  Also, change MESSAGE=>SIGNATURE as
   * recommended for future releases of PGP.
   */
  while (fgets (buffer, sizeof (buffer) - 1, pgpout) != NULL)
  {
1066
    if (mutt_strcmp ("-----BEGIN PGP MESSAGE-----\n", buffer) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1067
      fputs ("-----BEGIN PGP SIGNATURE-----\n", fp);
1068
    else if (mutt_strcmp("-----END PGP MESSAGE-----\n", buffer) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
      fputs ("-----END PGP SIGNATURE-----\n", fp);
    else
      fputs (buffer, fp);
    empty = 0; /* got some output, so we're ok */
  }

  /* check for errors from PGP */
  err = 0;
  while (fgets (buffer, sizeof (buffer) - 1, pgperr) != NULL)
  {
    err = 1;
    fputs (buffer, stdout);
  }

1083 1084 1085
  if(mutt_wait_filter (thepid) && option(OPTPGPCHECKEXIT))
    empty=1;

1086 1087
  safe_fclose (&pgperr);
  safe_fclose (&pgpout);
Thomas Roessler's avatar
Thomas Roessler committed
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
  unlink (signedfile);
  
  if (fclose (fp) != 0)
  {
    mutt_perror ("fclose");
    unlink (sigfile);
    return (NULL);
  }

  if (err)
    mutt_any_key_to_continue (NULL);
  if (empty)
  {
    unlink (sigfile);
1102 1103
    /* most likely error is a bad passphrase, so automatically forget it */
    pgp_void_passphrase ();
Thomas Roessler's avatar
Thomas Roessler committed
1104 1105 1106 1107 1108 1109 1110
    return (NULL); /* fatal error while signing */
  }

  t = mutt_new_body ();
  t->type = TYPEMULTIPART;
  t->subtype = safe_strdup ("signed");
  t->encoding = ENC7BIT;
1111 1112
  t->use_disp = 0;
  t->disposition = DISPINLINE;
Thomas Roessler's avatar
Thomas Roessler committed
1113

1114
  mutt_generate_boundary (&t->parameter);
1115
  mutt_set_parameter ("protocol", "application/pgp-signature", &t->parameter);
1116
  mutt_set_parameter ("micalg", pgp_micalg (sigfile), &t->parameter);
Thomas Roessler's avatar
Thomas Roessler committed
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126

  t->parts = a;
  a = t;

  t->parts->next = mutt_new_body ();
  t = t->parts->next;
  t->type = TYPEAPPLICATION;
  t->subtype = safe_strdup ("pgp-signature");
  t->filename = safe_strdup (sigfile);
  t->use_disp = 0;
1127
  t->disposition = DISPNONE;
Thomas Roessler's avatar
Thomas Roessler committed
1128 1129 1130 1131 1132 1133
  t->encoding = ENC7BIT;
  t->unlink = 1; /* ok to remove this file after sending. */

  return (a);
}

1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
static short is_numerical_keyid (const char *s)
{
  /* or should we require the "0x"? */
  if (strncmp (s, "0x", 2) == 0)
    s += 2;
  if (strlen (s) % 8)
    return 0;
  while (*s)
    if (strchr ("0123456789ABCDEFabcdef", *s++) == NULL)
      return 0;
  
  return 1;
}

Thomas Roessler's avatar
Thomas Roessler committed
1148 1149 1150 1151 1152