pgppubring.c 18.2 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1997-2003 Thomas Roessler <roessler@does-not-exist.org>
Thomas Roessler's avatar
Thomas Roessler committed
3
 * 
4 5 6 7 8
 *     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.
Thomas Roessler's avatar
Thomas Roessler committed
9
 * 
10 11 12 13 14
 *     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.
Thomas Roessler's avatar
Thomas Roessler committed
15
 * 
16 17
 *     You should have received a copy of the GNU General Public
 *     License along with this program; if not, write to the Free
18
 *     Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 *     Boston, MA  02110-1301, USA.
20
 */
Thomas Roessler's avatar
Thomas Roessler committed
21

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 * This is a "simple" PGP key ring dumper.
 * 
 * The output format is supposed to be compatible to the one GnuPG
 * emits and Mutt expects.
 * 
 * Note that the code of this program could be considerably less
 * complex, but most of it was taken from mutt's second generation
 * key ring parser.
 * 
 * You can actually use this to put together some fairly general
 * PGP key management applications.
 *
 */

37 38 39
#if HAVE_CONFIG_H
# include "config.h"
#endif
Thomas Roessler's avatar
Thomas Roessler committed
40 41 42 43 44 45

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
46 47
#ifdef HAVE_GETOPT_H
# include <getopt.h>
48
#endif
Michael Elkins's avatar
Michael Elkins committed
49
#include <errno.h>
50

51 52
extern char *optarg;
extern int optind;
53

54
#include "sha1.h"
55
#include "md5.h"
56 57
#include "lib.h"
#include "pgplib.h"
58 59
#include "pgppacket.h"

60
#define MD5_DIGEST_LENGTH  16
Thomas Roessler's avatar
Thomas Roessler committed
61

62 63 64 65
#ifdef HAVE_FGETPOS
#define FGETPOS(fp,pos) fgetpos((fp),&(pos))
#define FSETPOS(fp,pos) fsetpos((fp),&(pos))
#else
66 67
#define FGETPOS(fp,pos) pos=ftello((fp));
#define FSETPOS(fp,pos) fseeko((fp),(pos),SEEK_SET)
68 69
#endif

Thomas Roessler's avatar
Thomas Roessler committed
70

71
static short dump_signatures = 0;
72
static short dump_fingerprints = 0;
73

Thomas Roessler's avatar
Thomas Roessler committed
74

75
static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints);
76
static void pgpring_dump_keyblock (pgp_key_t p);
77 78 79 80 81

int main (int argc, char * const argv[])
{
  int c;
  
82 83 84 85 86 87 88
  short version = 2;
  short secring = 0;
  
  const char *_kring = NULL;
  char *env_pgppath, *env_home;

  char pgppath[_POSIX_PATH_MAX];
89 90
  char kring[_POSIX_PATH_MAX];

91
  while ((c = getopt (argc, argv, "f25sk:S")) != EOF)
92 93 94
  {
    switch (c)
    {
95 96 97 98 99 100
      case 'S':
      {
	dump_signatures = 1;
	break;
      }

101 102 103 104 105 106
      case 'f':
      {
	dump_fingerprints = 1;
	break;
      }

107 108 109 110 111 112 113 114
      case 'k':
      {
	_kring = optarg;
	break;
      }
      
      case '2': case '5':
      {
Thomas Roessler's avatar
Thomas Roessler committed
115
	version = c - '0';
116 117 118 119 120 121 122 123 124 125 126
	break;
      }
      
      case 's':
      {
	secring = 1;
	break;
      }
    
      default:
      {
127
	fprintf (stderr, "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
128 129 130 131 132 133 134 135
		 argv[0]);
	exit (1);
      }
    }
  }

  if (_kring)
    strfcpy (kring, _kring, sizeof (kring));
136 137
  else
  {
138 139 140 141
    if ((env_pgppath = getenv ("PGPPATH")))
      strfcpy (pgppath, env_pgppath, sizeof (pgppath));
    else if ((env_home = getenv ("HOME")))
      snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home);
142 143
    else
    {
144
      fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
145 146
      exit (1);
    }
147 148 149 150 151
    
    if (secring)
      snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath, version == 2 ? "pgp" : "skr");
    else
      snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr");
152 153
  }
  
154
  pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind);
155 156 157 158 159
    
  return 0;
}


160
/* The actual key ring parser */
161 162 163
static void pgp_make_pgp2_fingerprint (unsigned char *buff,
                                       unsigned char *digest)
{
164
  struct md5_ctx ctx;
165 166
  unsigned int size = 0;

167
  md5_init_ctx (&ctx);
168 169 170 171 172

  size = (buff[0] << 8) + buff[1];
  size = ((size + 7) / 8);
  buff = &buff[2];

173 174
  md5_process_bytes (buff, size, &ctx);

175 176 177 178 179 180
  buff = &buff[size];

  size = (buff[0] << 8) + buff[1];
  size = ((size + 7) / 8);
  buff = &buff[2];

181
  md5_process_bytes (buff, size, &ctx);
182

183
  md5_finish_ctx (&ctx, digest);
184 185
} /* pgp_make_pgp2_fingerprint() */

186
static pgp_key_t pgp_parse_pgp2_key (unsigned char *buff, size_t l)
187
{
188
  pgp_key_t p;
Thomas Roessler's avatar
Thomas Roessler committed
189
  unsigned char alg;
190
  unsigned char digest[MD5_DIGEST_LENGTH];
Thomas Roessler's avatar
Thomas Roessler committed
191 192
  size_t expl;
  unsigned long id;
193
  time_t gen_time = 0;
Thomas Roessler's avatar
Thomas Roessler committed
194 195 196 197
  unsigned short exp_days = 0;
  size_t j;
  int i, k;
  unsigned char scratch[LONG_STRING];
198 199

  if (l < 12)
Thomas Roessler's avatar
Thomas Roessler committed
200
    return NULL;
201

202
  p = pgp_new_keyinfo();
203 204

  for (i = 0, j = 2; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
205
    gen_time = (gen_time << 8) + buff[j++];
206

207 208
  p->gen_time = gen_time;

209
  for (i = 0; i < 2; i++)
Thomas Roessler's avatar
Thomas Roessler committed
210
    exp_days = (exp_days << 8) + buff[j++];
211 212

  if (exp_days && time (NULL) > gen_time + exp_days * 24 * 3600)
Thomas Roessler's avatar
Thomas Roessler committed
213
    p->flags |= KEYFLAG_EXPIRED;
214

Thomas Roessler's avatar
Thomas Roessler committed
215
  alg = buff[j++];
216

217
  p->numalg = alg;
218 219 220
  p->algorithm = pgp_pkalgbytype (alg);
  p->flags |= pgp_get_abilities (alg);

221 222 223 224 225 226 227 228 229 230
  if (dump_fingerprints)
  {
    /* j now points to the key material, which we need for the fingerprint */
    p->fp_len = MD5_DIGEST_LENGTH;
    pgp_make_pgp2_fingerprint (&buff[j], digest);
    memcpy (p->fingerprint, digest, MD5_DIGEST_LENGTH);
  }
  else	/* just to be usre */
    memset (p->fingerprint, 0, MD5_DIGEST_LENGTH);
    
Thomas Roessler's avatar
Thomas Roessler committed
231
  expl = 0;
232
  for (i = 0; i < 2; i++)
Thomas Roessler's avatar
Thomas Roessler committed
233
    expl = (expl << 8) + buff[j++];
234

Thomas Roessler's avatar
Thomas Roessler committed
235
  p->keylen = expl;
236 237 238

  expl = (expl + 7) / 8;
  if (expl < 4)
Thomas Roessler's avatar
Thomas Roessler committed
239 240
    goto bailout;

241

Thomas Roessler's avatar
Thomas Roessler committed
242
  j += expl - 8;
243 244

  for (k = 0; k < 2; k++)
Thomas Roessler's avatar
Thomas Roessler committed
245
  {
246
    for (id = 0, i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
247
      id = (id << 8) + buff[j++];
248 249 250

    snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8,
	      "%08lX", id);
Thomas Roessler's avatar
Thomas Roessler committed
251
  }
252 253 254

  p->keyid = safe_strdup ((char *) scratch);

Thomas Roessler's avatar
Thomas Roessler committed
255
  return p;
256 257 258

bailout:

259
  FREE (&p);
Thomas Roessler's avatar
Thomas Roessler committed
260 261 262
  return NULL;
}

263 264
static void pgp_make_pgp3_fingerprint (unsigned char *buff, size_t l,
				       unsigned char *digest)
Thomas Roessler's avatar
Thomas Roessler committed
265 266
{
  unsigned char dummy;
267
  SHA1_CTX context;
Thomas Roessler's avatar
Thomas Roessler committed
268

269 270
  SHA1_Init (&context);

Thomas Roessler's avatar
Thomas Roessler committed
271 272
  dummy = buff[0] & 0x3f;

273
  if (dummy == PT_SUBSECKEY || dummy == PT_SUBKEY || dummy == PT_SECKEY)
Thomas Roessler's avatar
Thomas Roessler committed
274 275 276
    dummy = PT_PUBKEY;

  dummy = (dummy << 2) | 0x81;
277
  SHA1_Update (&context, &dummy, 1);
Thomas Roessler's avatar
Thomas Roessler committed
278
  dummy = ((l - 1) >> 8) & 0xff;
279 280 281 282 283
  SHA1_Update (&context, &dummy, 1);
  dummy = (l - 1) & 0xff;
  SHA1_Update (&context, &dummy, 1);
  SHA1_Update (&context, buff + 1, l - 1);
  SHA1_Final (digest, &context);
Thomas Roessler's avatar
Thomas Roessler committed
284 285 286

}

287 288
static void skip_bignum (unsigned char *buff, size_t l, size_t j,
			 size_t * toff, size_t n)
Thomas Roessler's avatar
Thomas Roessler committed
289 290
{
  size_t len;
291

Thomas Roessler's avatar
Thomas Roessler committed
292 293
  do
  {
294
    len = (buff[j] << 8) + buff[j + 1];
Thomas Roessler's avatar
Thomas Roessler committed
295
    j += (len + 7) / 8 + 2;
296 297 298 299 300
  }
  while (j <= l && --n > 0);

  if (toff)
    *toff = j;
Thomas Roessler's avatar
Thomas Roessler committed
301 302
}

303

304
static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
Thomas Roessler's avatar
Thomas Roessler committed
305
{
306
  pgp_key_t p;
Thomas Roessler's avatar
Thomas Roessler committed
307 308 309
  unsigned char alg;
  unsigned char digest[SHA_DIGEST_LENGTH];
  unsigned char scratch[LONG_STRING];
310
  time_t gen_time = 0;
Thomas Roessler's avatar
Thomas Roessler committed
311 312 313 314
  unsigned long id;
  int i, k;
  short len;
  size_t j;
315 316

  p = pgp_new_keyinfo ();
Thomas Roessler's avatar
Thomas Roessler committed
317
  j = 2;
318 319

  for (i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
320 321 322
    gen_time = (gen_time << 8) + buff[j++];

  p->gen_time = gen_time;
323

Thomas Roessler's avatar
Thomas Roessler committed
324
  alg = buff[j++];
325

326
  p->numalg = alg;
327 328
  p->algorithm = pgp_pkalgbytype (alg);
  p->flags |= pgp_get_abilities (alg);
Thomas Roessler's avatar
Thomas Roessler committed
329 330

  if (alg == 17)
331 332 333 334 335
    skip_bignum (buff, l, j, &j, 3);
  else if (alg == 16 || alg == 20)
    skip_bignum (buff, l, j, &j, 2);

  len = (buff[j] << 8) + buff[j + 1];
Thomas Roessler's avatar
Thomas Roessler committed
336 337
  p->keylen = len;

338

339 340
  if (alg >= 1 && alg <= 3)
    skip_bignum (buff, l, j, &j, 2);
341
  else if (alg == 17 || alg == 16 || alg == 20)
342 343 344
    skip_bignum (buff, l, j, &j, 1);

  pgp_make_pgp3_fingerprint (buff, j, digest);
345 346
  p->fp_len = SHA_DIGEST_LENGTH;
  
347
  for (k = 0; k < 2; k++)
Thomas Roessler's avatar
Thomas Roessler committed
348
  {
349 350
    for (id = 0, i = SHA_DIGEST_LENGTH - 8 + k * 4;
	 i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
351
      id = (id << 8) + digest[i];
352 353

    snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX", id);
Thomas Roessler's avatar
Thomas Roessler committed
354
  }
355 356 357

  p->keyid = safe_strdup ((char *) scratch);

Thomas Roessler's avatar
Thomas Roessler committed
358 359 360
  return p;
}

361
static pgp_key_t pgp_parse_keyinfo (unsigned char *buff, size_t l)
Thomas Roessler's avatar
Thomas Roessler committed
362
{
363
  if (!buff || l < 2)
Thomas Roessler's avatar
Thomas Roessler committed
364
    return NULL;
365

366
  switch (buff[1])
Thomas Roessler's avatar
Thomas Roessler committed
367
  {
368 369 370 371 372 373 374
  case 2:
  case 3:
    return pgp_parse_pgp2_key (buff, l);
  case 4:
    return pgp_parse_pgp3_key (buff, l);
  default:
    return NULL;
Thomas Roessler's avatar
Thomas Roessler committed
375 376 377
  }
}

378 379
static int pgp_parse_pgp2_sig (unsigned char *buff, size_t l,
                               pgp_key_t p, pgp_sig_t *s)
Thomas Roessler's avatar
Thomas Roessler committed
380 381
{
  unsigned char sigtype;
382
  time_t sig_gen_time;
383 384
  unsigned long signerid1;
  unsigned long signerid2;
Thomas Roessler's avatar
Thomas Roessler committed
385 386
  size_t j;
  int i;
387 388

  if (l < 22)
Thomas Roessler's avatar
Thomas Roessler committed
389
    return -1;
390

Thomas Roessler's avatar
Thomas Roessler committed
391 392
  j = 3;
  sigtype = buff[j++];
393

Thomas Roessler's avatar
Thomas Roessler committed
394
  sig_gen_time = 0;
395
  for (i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
396
    sig_gen_time = (sig_gen_time << 8) + buff[j++];
397

398 399 400 401
  signerid1 = signerid2 = 0;
  for (i = 0; i < 4; i++)
    signerid1 = (signerid1 << 8) + buff[j++];

402
  for (i = 0; i < 4; i++)
403
    signerid2 = (signerid2 << 8) + buff[j++];
404

405
  
406
  if (sigtype == 0x20 || sigtype == 0x28)
Thomas Roessler's avatar
Thomas Roessler committed
407
    p->flags |= KEYFLAG_REVOKED;
408

409 410 411 412 413 414 415
  if (s)
  {
    s->sigtype = sigtype;
    s->sid1    = signerid1;
    s->sid2    = signerid2;
  }
  
Thomas Roessler's avatar
Thomas Roessler committed
416 417 418
  return 0;
}

419 420
static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l,
                               pgp_key_t p, pgp_sig_t *s)
Thomas Roessler's avatar
Thomas Roessler committed
421 422 423
{
  unsigned char sigtype;
  unsigned char skt;
424
  time_t sig_gen_time = -1;
Thomas Roessler's avatar
Thomas Roessler committed
425 426
  long validity = -1;
  long key_validity = -1;
427 428
  unsigned long signerid1 = 0;
  unsigned long signerid2 = 0;
Thomas Roessler's avatar
Thomas Roessler committed
429 430 431 432
  size_t ml;
  size_t j;
  int i;
  short ii;
433 434 435
  short have_critical_spks = 0;

  if (l < 7)
Thomas Roessler's avatar
Thomas Roessler committed
436
    return -1;
437

Thomas Roessler's avatar
Thomas Roessler committed
438
  j = 2;
439

Thomas Roessler's avatar
Thomas Roessler committed
440
  sigtype = buff[j++];
441
  j += 2; /* pkalg, hashalg */
442 443

  for (ii = 0; ii < 2; ii++)
Thomas Roessler's avatar
Thomas Roessler committed
444 445 446
  {
    size_t skl;
    size_t nextone;
447 448

    ml = (buff[j] << 8) + buff[j + 1];
Thomas Roessler's avatar
Thomas Roessler committed
449
    j += 2;
450 451 452 453

    if (j + ml > l)
      break;

Thomas Roessler's avatar
Thomas Roessler committed
454
    nextone = j;
455
    while (ml)
Thomas Roessler's avatar
Thomas Roessler committed
456 457 458
    {
      j = nextone;
      skl = buff[j++];
459 460 461 462
      if (!--ml)
	break;

      if (skl >= 192)
Thomas Roessler's avatar
Thomas Roessler committed
463 464
      {
	skl = (skl - 192) * 256 + buff[j++] + 192;
465 466
	if (!--ml)
	  break;
Thomas Roessler's avatar
Thomas Roessler committed
467
      }
468 469

      if ((int) ml - (int) skl < 0)
Thomas Roessler's avatar
Thomas Roessler committed
470
	break;
Thomas Roessler's avatar
Thomas Roessler committed
471
      ml -= skl;
472

Thomas Roessler's avatar
Thomas Roessler committed
473 474
      nextone = j + skl;
      skt = buff[j++];
475 476

      switch (skt & 0x7f)
Thomas Roessler's avatar
Thomas Roessler committed
477
      {
478
	case 2:			/* creation time */
Thomas Roessler's avatar
Thomas Roessler committed
479
	{
480
	  if (skl < 4)
Thomas Roessler's avatar
Thomas Roessler committed
481 482
	    break;
	  sig_gen_time = 0;
483
	  for (i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
484
	    sig_gen_time = (sig_gen_time << 8) + buff[j++];
485

Thomas Roessler's avatar
Thomas Roessler committed
486 487
	  break;
	}
488
	case 3:			/* expiration time */
Thomas Roessler's avatar
Thomas Roessler committed
489
	{
490
	  if (skl < 4)
Thomas Roessler's avatar
Thomas Roessler committed
491 492
	    break;
	  validity = 0;
493
	  for (i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
494 495 496
	    validity = (validity << 8) + buff[j++];
	  break;
	}
497
	case 9:			/* key expiration time */
Thomas Roessler's avatar
Thomas Roessler committed
498
	{
499
	  if (skl < 4)
Thomas Roessler's avatar
Thomas Roessler committed
500 501
	    break;
	  key_validity = 0;
502
	  for (i = 0; i < 4; i++)
Thomas Roessler's avatar
Thomas Roessler committed
503 504 505
	    key_validity = (key_validity << 8) + buff[j++];
	  break;
	}
506
	case 16:			/* issuer key ID */
Thomas Roessler's avatar
Thomas Roessler committed
507
	{
508
	  if (skl < 8)
Thomas Roessler's avatar
Thomas Roessler committed
509
	    break;
510
	  signerid2 = signerid1 = 0;
511 512
	  for (i = 0; i < 4; i++)
	    signerid1 = (signerid1 << 8) + buff[j++];
513
	  for (i = 0; i < 4; i++)
514 515
	    signerid2 = (signerid2 << 8) + buff[j++];
	  
Thomas Roessler's avatar
Thomas Roessler committed
516 517
	  break;
	}
518 519 520 521 522 523 524 525 526 527 528 529 530
	case 10:			/* CMR key */
	break;
	case 4:				/* exportable */
	case 5:				/* trust */
	case 6:				/* regexp */
	case 7:				/* revocable */
	case 11:			/* Pref. symm. alg. */
	case 12:			/* revocation key */
	case 20:			/* notation data */
	case 21:			/* pref. hash */
	case 22:			/* pref. comp.alg. */
	case 23:			/* key server prefs. */
	case 24:			/* pref. key server */
Thomas Roessler's avatar
Thomas Roessler committed
531 532
	default:
	{
533
	  if (skt & 0x80)
Thomas Roessler's avatar
Thomas Roessler committed
534 535 536 537 538 539
	    have_critical_spks = 1;
	}
      }
    }
    j = nextone;
  }
540 541

  if (sigtype == 0x20 || sigtype == 0x28)
Thomas Roessler's avatar
Thomas Roessler committed
542
    p->flags |= KEYFLAG_REVOKED;
543
  if (key_validity != -1 && time (NULL) > p->gen_time + key_validity)
Thomas Roessler's avatar
Thomas Roessler committed
544
    p->flags |= KEYFLAG_EXPIRED;
545
  if (have_critical_spks)
Thomas Roessler's avatar
Thomas Roessler committed
546 547
    p->flags |= KEYFLAG_CRITICAL;

548 549 550 551 552 553 554 555
  if (s)
  {
    s->sigtype = sigtype;
    s->sid1    = signerid1;
    s->sid2    = signerid2;
  }

  
Thomas Roessler's avatar
Thomas Roessler committed
556
  return 0;
557

Thomas Roessler's avatar
Thomas Roessler committed
558 559
}

560

561 562
static int pgp_parse_sig (unsigned char *buff, size_t l,
                          pgp_key_t p, pgp_sig_t *sig)
Thomas Roessler's avatar
Thomas Roessler committed
563
{
564
  if (!buff || l < 2 || !p)
Thomas Roessler's avatar
Thomas Roessler committed
565
    return -1;
566 567

  switch (buff[1])
Thomas Roessler's avatar
Thomas Roessler committed
568
  {
569 570
  case 2:
  case 3:
571
    return pgp_parse_pgp2_sig (buff, l, p, sig);      
572
  case 4:
573
    return pgp_parse_pgp3_sig (buff, l, p, sig);
574
  default:
Thomas Roessler's avatar
Thomas Roessler committed
575 576 577 578
    return -1;
  }
}

579 580
/* parse one key block, including all subkeys. */

581
static pgp_key_t pgp_parse_keyblock (FILE * fp)
Thomas Roessler's avatar
Thomas Roessler committed
582 583 584 585 586
{
  unsigned char *buff;
  unsigned char pt = 0;
  unsigned char last_pt;
  size_t l;
587 588
  short err = 0;

589
#ifdef HAVE_FGETPOS
590
  fpos_t pos;
591
#else
592
  LOFF_T pos;
593 594
#endif

595 596 597
  pgp_key_t root = NULL;
  pgp_key_t *last = &root;
  pgp_key_t p = NULL;
598 599
  pgp_uid_t *uid = NULL;
  pgp_uid_t **addr = NULL;
600
  pgp_sig_t **lsig = NULL;
601 602

  FGETPOS(fp,pos);
Thomas Roessler's avatar
Thomas Roessler committed
603
  
604
  while (!err && (buff = pgp_read_packet (fp, &l)) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
605 606 607 608
  {
    last_pt = pt;
    pt = buff[0] & 0x3f;

609 610 611 612
    /* check if we have read the complete key block. */
    
    if ((pt == PT_SECKEY || pt == PT_PUBKEY) && root)
    {
613
      FSETPOS(fp, pos);
614 615 616 617
      return root;
    }
    
    switch (pt)
Thomas Roessler's avatar
Thomas Roessler committed
618 619 620 621 622 623
    {
      case PT_SECKEY:
      case PT_PUBKEY:
      case PT_SUBKEY:
      case PT_SUBSECKEY:
      {
624
	if (!(*last = p = pgp_parse_keyinfo (buff, l)))
625
	{
626 627
	  err = 1;
	  break;
628 629
	}

630
	last = &p->next;
Thomas Roessler's avatar
Thomas Roessler committed
631
	addr = &p->address;
632
	lsig = &p->sigs;
633 634
	
	if (pt == PT_SUBKEY || pt == PT_SUBSECKEY)
Thomas Roessler's avatar
Thomas Roessler committed
635 636
	{
	  p->flags |= KEYFLAG_SUBKEY;
637
	  if (p != root)
638 639 640 641 642
	  {
	    p->parent  = root;
	    p->address = pgp_copy_uids (root->address, p);
	    while (*addr) addr = &(*addr)->next;
	  }
Thomas Roessler's avatar
Thomas Roessler committed
643
	}
644 645 646 647
	
	if (pt == PT_SECKEY || pt == PT_SUBSECKEY)
	  p->flags |= KEYFLAG_SECRET;

Thomas Roessler's avatar
Thomas Roessler committed
648 649
	break;
      }
650

Thomas Roessler's avatar
Thomas Roessler committed
651 652
      case PT_SIG:
      {
653 654 655 656 657 658 659 660
	if (lsig)
	{
	  pgp_sig_t *signature = safe_calloc (sizeof (pgp_sig_t), 1);
	  *lsig = signature;
	  lsig = &signature->next;
	  
	  pgp_parse_sig (buff, l, p, signature);
	}
Thomas Roessler's avatar
Thomas Roessler committed
661 662
	break;
      }
663

Thomas Roessler's avatar
Thomas Roessler committed
664 665
      case PT_TRUST:
      {
666 667
	if (p && (last_pt == PT_SECKEY || last_pt == PT_PUBKEY ||
		  last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY))
Thomas Roessler's avatar
Thomas Roessler committed
668
	{
669
	  if (buff[1] & 0x20)
670
	  {
Thomas Roessler's avatar
Thomas Roessler committed
671
	    p->flags |= KEYFLAG_DISABLED;
672
	  }
Thomas Roessler's avatar
Thomas Roessler committed
673
	}
674
	else if (last_pt == PT_NAME && uid)
675
	{
Thomas Roessler's avatar
Thomas Roessler committed
676
	  uid->trust = buff[1];
677
	}
Thomas Roessler's avatar
Thomas Roessler committed
678 679 680 681 682
	break;
      }
      case PT_NAME:
      {
	char *chr;
683

684 685 686 687 688 689 690 691 692

	if (!addr)
	  break;

	chr = safe_malloc (l);
	memcpy (chr, buff + 1, l - 1);
	chr[l - 1] = '\0';


693
	*addr = uid = safe_calloc (1, sizeof (pgp_uid_t)); /* XXX */
Thomas Roessler's avatar
Thomas Roessler committed
694
	uid->addr = chr;
695
	uid->parent = p;
Thomas Roessler's avatar
Thomas Roessler committed
696
	uid->trust = 0;
697
	addr = &uid->next;
698 699
	lsig = &uid->sigs;
	
Thomas Roessler's avatar
Thomas Roessler committed
700 701 702
	/* the following tags are generated by
	 * pgp 2.6.3in.
	 */
703 704

	if (strstr (chr, "ENCR"))
Thomas Roessler's avatar
Thomas Roessler committed
705
	  p->flags |= KEYFLAG_PREFER_ENCRYPTION;
706
	if (strstr (chr, "SIGN"))
Thomas Roessler's avatar
Thomas Roessler committed
707
	  p->flags |= KEYFLAG_PREFER_SIGNING;
708

Thomas Roessler's avatar
Thomas Roessler committed
709 710 711
	break;
      }
    }
712

713
    FGETPOS(fp,pos);
714
  }
715 716 717

  if (err)
    pgp_free_key (&root);
718
  
719
  return root;  
Thomas Roessler's avatar
Thomas Roessler committed
720 721
}

722
static int pgpring_string_matches_hint (const char *s, const char *hints[], int nhints)
Thomas Roessler's avatar
Thomas Roessler committed
723
{
724 725 726
  int i;

  if (!hints || !nhints)
727
    return 1;
Thomas Roessler's avatar
Thomas Roessler committed
728

729
  for (i = 0; i < nhints; i++)
730
  {
731
    if (mutt_stristr (s, hints[i]) != NULL)
732 733 734 735
      return 1;
  }

  return 0;
Thomas Roessler's avatar
Thomas Roessler committed
736
}
Thomas Roessler's avatar
Thomas Roessler committed
737

738 739
/* 
 * Go through the key ring file and look for keys with
740 741 742
 * matching IDs.
 */

743
static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints)
Thomas Roessler's avatar
Thomas Roessler committed
744
{
745
  FILE *rfp;
746
#ifdef HAVE_FGETPOS
747
  fpos_t pos, keypos;
748
#else
749
  LOFF_T pos, keypos;
750
#endif
751 752 753 754 755 756

  unsigned char *buff = NULL;
  unsigned char pt = 0;
  size_t l = 0;

  short err = 0;
757
  
758 759
  if ((rfp = fopen (ringfile, "r")) == NULL)
  {
Michael Elkins's avatar
Michael Elkins committed
760 761 762 763
    char *error_buf;
    size_t error_buf_len;

    error_buf_len = sizeof ("fopen: ") - 1 + strlen (ringfile) + 1;
764
    error_buf = safe_malloc (error_buf_len);
Michael Elkins's avatar
Michael Elkins committed
765 766
    snprintf (error_buf, error_buf_len, "fopen: %s", ringfile);
    perror (error_buf);
767
    FREE (&error_buf);
768
    return;
769 770
  }

771 772
  FGETPOS(rfp,pos);
  FGETPOS(rfp,keypos);
773 774

  while (!err && (buff = pgp_read_packet (rfp, &l)) != NULL)
Thomas Roessler's avatar
Thomas Roessler committed
775
  {
776 777 778 779 780 781 782 783 784 785
    pt = buff[0] & 0x3f;
    
    if (l < 1)
      continue;
    
    if ((pt == PT_SECKEY) || (pt == PT_PUBKEY))
    {
      keypos = pos;
    }
    else if (pt == PT_NAME)
Thomas Roessler's avatar
Thomas Roessler committed
786
    {
787
      char *tmp = safe_malloc (l);
Thomas Roessler's avatar
Thomas Roessler committed
788

789 790 791
      memcpy (tmp, buff + 1, l - 1);
      tmp[l - 1] = '\0';

792 793 794
      /* mutt_decode_utf8_string (tmp, chs); */

      if (pgpring_string_matches_hint (tmp, hints, nhints))
795
      {
796
	pgp_key_t p;
797

798
	FSETPOS(rfp, keypos);
799 800 801

	/* Not bailing out here would lead us into an endless loop. */

802
	if ((p = pgp_parse_keyblock (rfp)) == NULL)
803
	  err = 1;
804 805 806
	
	pgpring_dump_keyblock (p);
	pgp_free_key (&p);
807 808
      }

809
      FREE (&tmp);
Thomas Roessler's avatar
Thomas Roessler committed
810
    }
811

812
    FGETPOS(rfp,pos);
Thomas Roessler's avatar
Thomas Roessler committed
813
  }
814

815
  safe_fclose (&rfp);
816

Thomas Roessler's avatar
Thomas Roessler committed
817
}
818 819 820 821 822 823 824 825

static void print_userid (const char *id)
{
  for (; id && *id; id++)
  {
    if (*id >= ' ' && *id <= 'z' && *id != ':')
      putchar (*id);
    else
Thomas Roessler's avatar
Thomas Roessler committed
826
      printf ("\\x%02x", (*id) & 0xff);
827 828 829
  }
}

830
static void print_fingerprint (pgp_key_t p) 
831 832 833 834 835 836 837 838 839 840 841
{
  int i = 0;

  printf ("fpr:::::::::");
  for (i = 0; i < p->fp_len; i++)
    printf ("%02X", p->fingerprint[i]);
  printf (":\n");

} /* print_fingerprint() */


Thomas Roessler's avatar
Thomas Roessler committed
842
static void pgpring_dump_signatures (pgp_sig_t *sig)
843 844 845 846 847 848 849 850 851 852 853 854 855 856
{
  for (; sig; sig = sig->next)
  {
    if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
	sig->sigtype == 0x12 || sig->sigtype == 0x13)
      printf ("sig::::%08lX%08lX::::::%X:\n",
	      sig->sid1, sig->sid2, sig->sigtype);
    else if (sig->sigtype == 0x20)
      printf ("rev::::%08lX%08lX::::::%X:\n",
	      sig->sid1, sig->sid2, sig->sigtype);
  }
}


857 858 859 860 861 862 863 864 865 866 867
static char gnupg_trustletter (int t)
{
  switch (t)
  {
    case 1: return 'n';
    case 2: return 'm';
    case 3: return 'f';
  }
  return 'q';
}

868
static void pgpring_dump_keyblock (pgp_key_t p)
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
{
  pgp_uid_t *uid;
  short first;
  struct tm *tp;
  time_t t;
  
  for (; p; p = p->next)
  {
    first = 1;

    if (p->flags & KEYFLAG_SECRET)
    {
      if (p->flags & KEYFLAG_SUBKEY)
	printf ("ssb:");
      else
	printf ("sec:");
    }
    else 
    {
      if (p->flags & KEYFLAG_SUBKEY)
	printf ("sub:");
      else
	printf ("pub:");
    }
    
    if (p->flags & KEYFLAG_REVOKED)
      putchar ('r');
    if (p->flags & KEYFLAG_EXPIRED)
      putchar ('e');
898 899
    if (p->flags & KEYFLAG_DISABLED)
      putchar ('d');
900

901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
    for (uid = p->address; uid; uid = uid->next, first = 0)
    {
      if (!first)
      {
	printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
	print_userid (uid->addr);
	printf (":\n");
      }
      else
      {
	if (p->flags & KEYFLAG_SECRET)
	  putchar ('u');
	else
	  putchar (gnupg_trustletter (uid->trust));

	t = p->gen_time;
	tp = gmtime (&t);

	printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg, p->keyid,
		1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
	
	print_userid (uid->addr);
923
	printf ("::");
924 925 926 927 928

	if(pgp_canencrypt(p->numalg))
	  putchar ('e');
	if(pgp_cansign(p->numalg))
	  putchar ('s');
929 930
	if (p->flags & KEYFLAG_DISABLED)
	  putchar ('D');
931
	printf (":\n");
932

933 934
	if (dump_fingerprints) 
          print_fingerprint (p);
935
      }
936 937 938
      
      if (dump_signatures)
      {
Thomas Roessler's avatar
Thomas Roessler committed
939 940
	if (first) pgpring_dump_signatures (p->sigs);
	pgpring_dump_signatures (uid->sigs);
941
      }
942 943 944
    }
  }
}
945 946 947 948 949 950 951 952

/*
 * The mutt_gettext () defined in gettext.c requires iconv,
 * so we do without charset conversion here.
 */

char *mutt_gettext (const char *message)
{
953
  return (char *)message;
954
}