Use m_tempfile and better errors msg
[apps/madmutt.git] / pgppubring.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1997-2001 Thomas Roessler <roessler@does-not-exist.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 /*
11  * This is a "simple" PGP key ring dumper.
12  * 
13  * The output format is supposed to be compatible to the one GnuPG
14  * emits and Mutt expects.
15  * 
16  * Note that the code of this program could be considerably less
17  * complex, but most of it was taken from mutt's second generation
18  * key ring parser.
19  * 
20  * You can actually use this to put together some fairly general
21  * PGP key management applications.
22  *
23  */
24
25 #include <lib-lib/lib-lib.h>
26
27 #include <lib-hash/hash.h>
28
29 #ifdef HAVE_GETOPT_H
30 # include <getopt.h>
31 #else
32 extern char *optarg;
33 extern int optind;
34 #endif
35
36 short Umask;   /* dirty hack because we need Umask in lib.c but don't want globals.h there */
37
38 #include "lib.h"
39 #include <lib-crypt/pgplib.h>
40 #include <lib-crypt/pgppacket.h>
41
42 #define MD5_DIGEST_LENGTH  16
43
44 #ifdef HAVE_FGETPOS
45 #define FGETPOS(fp,pos) fgetpos((fp),&(pos))
46 #define FSETPOS(fp,pos) fsetpos((fp),&(pos))
47 #else
48 #define FGETPOS(fp,pos) pos=ftello((fp));
49 #define FSETPOS(fp,pos) fseeko((fp),(pos),SEEK_SET)
50 #endif
51
52
53 static short dump_signatures = 0;
54 static short dump_fingerprints = 0;
55
56
57 static void pgpring_find_candidates (char *ringfile, const char *hints[],
58                                      int nhints);
59 static void pgpring_dump_keyblock (pgp_key_t p);
60
61 int main (int argc, char *const argv[])
62 {
63   int c;
64
65   short version = 2;
66   short secring = 0;
67
68   const char *_kring = NULL;
69   char *env_pgppath, *env_home;
70
71   char pgppath[_POSIX_PATH_MAX];
72   char kring[_POSIX_PATH_MAX];
73
74   while ((c = getopt (argc, argv, "f25sk:S")) != EOF) {
75     switch (c) {
76     case 'S':
77       {
78         dump_signatures = 1;
79         break;
80       }
81
82     case 'f':
83       {
84         dump_fingerprints = 1;
85         break;
86       }
87
88     case 'k':
89       {
90         _kring = optarg;
91         break;
92       }
93
94     case '2':
95     case '5':
96       {
97         version = c - '0';
98         break;
99       }
100
101     case 's':
102       {
103         secring = 1;
104         break;
105       }
106
107     default:
108       {
109         fprintf (stderr,
110                  "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
111                  argv[0]);
112         exit (1);
113       }
114     }
115   }
116
117   if (_kring)
118     m_strcpy(kring, sizeof(kring), _kring);
119   else {
120     if ((env_pgppath = getenv ("PGPPATH")))
121       m_strcpy(pgppath, sizeof(pgppath), env_pgppath);
122     else if ((env_home = getenv ("HOME")))
123       snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home);
124     else {
125       fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
126       exit (1);
127     }
128
129     if (secring)
130       snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath,
131                 version == 2 ? "pgp" : "skr");
132     else
133       snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath,
134                 version == 2 ? "pgp" : "pkr");
135   }
136
137   pgpring_find_candidates (kring, (const char **) argv + optind,
138                            argc - optind);
139
140   return 0;
141 }
142
143
144 /* The actual key ring parser */
145
146 static void pgp_make_pgp2_fingerprint (unsigned char *buff,
147                                        unsigned char *digest)
148 {
149
150   MD5_CTX context;
151   unsigned int size = 0;
152
153
154   MD5Init (&context);
155
156   size = (buff[0] << 8) + buff[1];
157   size = ((size + 7) / 8);
158   buff = &buff[2];
159
160   MD5Update (&context, buff, size);
161   buff = &buff[size];
162
163   size = (buff[0] << 8) + buff[1];
164   size = ((size + 7) / 8);
165   buff = &buff[2];
166
167   MD5Update (&context, buff, size);
168
169   MD5Final (digest, &context);
170
171 }                               /* pgp_make_pgp2_fingerprint() */
172
173 static pgp_key_t pgp_parse_pgp2_key (unsigned char *buff, size_t l)
174 {
175   pgp_key_t p;
176   unsigned char alg;
177   unsigned char digest[MD5_DIGEST_LENGTH];
178   size_t expl;
179   unsigned long id;
180   time_t gen_time = 0;
181   unsigned short exp_days = 0;
182   size_t j;
183   int i, k;
184   unsigned char scratch[LONG_STRING];
185
186   if (l < 12)
187     return NULL;
188
189   p = pgp_new_keyinfo ();
190
191   for (i = 0, j = 2; i < 4; i++)
192     gen_time = (gen_time << 8) + buff[j++];
193
194   p->gen_time = gen_time;
195
196   for (i = 0; i < 2; i++)
197     exp_days = (exp_days << 8) + buff[j++];
198
199   if (exp_days && time (NULL) > gen_time + exp_days * 24 * 3600)
200     p->flags |= KEYFLAG_EXPIRED;
201
202   alg = buff[j++];
203
204   p->numalg = alg;
205   p->algorithm = pgp_pkalgbytype (alg);
206   p->flags |= pgp_get_abilities (alg);
207
208   if (dump_fingerprints) {
209     /* j now points to the key material, which we need for the fingerprint */
210     p->fp_len = MD5_DIGEST_LENGTH;
211     pgp_make_pgp2_fingerprint (&buff[j], digest);
212     memcpy (p->fingerprint, digest, MD5_DIGEST_LENGTH);
213   }
214   else                          /* just to be usre */
215     p_clear(p->fingerprint, MD5_DIGEST_LENGTH);
216
217   expl = 0;
218   for (i = 0; i < 2; i++)
219     expl = (expl << 8) + buff[j++];
220
221   p->keylen = expl;
222
223   expl = (expl + 7) / 8;
224   if (expl < 4)
225     goto bailout;
226
227
228   j += expl - 8;
229
230   for (k = 0; k < 2; k++) {
231     for (id = 0, i = 0; i < 4; i++)
232       id = (id << 8) + buff[j++];
233
234     snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8,
235               "%08lX", id);
236   }
237
238   p->keyid = m_strdup((char *) scratch);
239
240   return p;
241
242 bailout:
243
244   p_delete(&p);
245   return NULL;
246 }
247
248 static void pgp_make_pgp3_fingerprint (unsigned char *buff, size_t l,
249                                        unsigned char *digest)
250 {
251   unsigned char dummy;
252   SHA1_CTX context;
253
254   SHA1_Init (&context);
255
256   dummy = buff[0] & 0x3f;
257
258   if (dummy == PT_SUBSECKEY || dummy == PT_SUBKEY || dummy == PT_SECKEY)
259     dummy = PT_PUBKEY;
260
261   dummy = (dummy << 2) | 0x81;
262   SHA1_Update (&context, &dummy, 1);
263   dummy = ((l - 1) >> 8) & 0xff;
264   SHA1_Update (&context, &dummy, 1);
265   dummy = (l - 1) & 0xff;
266   SHA1_Update (&context, &dummy, 1);
267   SHA1_Update (&context, buff + 1, l - 1);
268   SHA1_Final (digest, &context);
269
270 }
271
272 static void skip_bignum (unsigned char *buff, size_t l, size_t j,
273                          size_t * toff, size_t n)
274 {
275   size_t len;
276
277   do {
278     len = (buff[j] << 8) + buff[j + 1];
279     j += (len + 7) / 8 + 2;
280   }
281   while (j <= l && --n > 0);
282
283   if (toff)
284     *toff = j;
285 }
286
287
288 static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
289 {
290   pgp_key_t p;
291   unsigned char alg;
292   unsigned char digest[SHA_DIGEST_LENGTH];
293   unsigned char scratch[LONG_STRING];
294   time_t gen_time = 0;
295   unsigned long id;
296   int i, k;
297   short len;
298   size_t j;
299
300   p = pgp_new_keyinfo ();
301   j = 2;
302
303   for (i = 0; i < 4; i++)
304     gen_time = (gen_time << 8) + buff[j++];
305
306   p->gen_time = gen_time;
307
308   alg = buff[j++];
309
310   p->numalg = alg;
311   p->algorithm = pgp_pkalgbytype (alg);
312   p->flags |= pgp_get_abilities (alg);
313
314   if (alg == 17)
315     skip_bignum (buff, l, j, &j, 3);
316   else if (alg == 16 || alg == 20)
317     skip_bignum (buff, l, j, &j, 2);
318
319   len = (buff[j] << 8) + buff[j + 1];
320   p->keylen = len;
321
322
323   if (alg >= 1 && alg <= 3)
324     skip_bignum (buff, l, j, &j, 2);
325   else if (alg == 17 || alg == 16 || alg == 20)
326     skip_bignum (buff, l, j, &j, 1);
327
328   pgp_make_pgp3_fingerprint (buff, j, digest);
329   p->fp_len = SHA_DIGEST_LENGTH;
330
331   for (k = 0; k < 2; k++) {
332     for (id = 0, i = SHA_DIGEST_LENGTH - 8 + k * 4;
333          i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++)
334       id = (id << 8) + digest[i];
335
336     snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX",
337               id);
338   }
339
340   p->keyid = m_strdup((char *) scratch);
341
342   return p;
343 }
344
345 static pgp_key_t pgp_parse_keyinfo (unsigned char *buff, size_t l)
346 {
347   if (!buff || l < 2)
348     return NULL;
349
350   switch (buff[1]) {
351   case 2:
352   case 3:
353     return pgp_parse_pgp2_key (buff, l);
354   case 4:
355     return pgp_parse_pgp3_key (buff, l);
356   default:
357     return NULL;
358   }
359 }
360
361 static int pgp_parse_pgp2_sig (unsigned char *buff, size_t l,
362                                pgp_key_t p, pgp_sig_t * s)
363 {
364   unsigned char sigtype;
365   time_t sig_gen_time;
366   unsigned long signerid1;
367   unsigned long signerid2;
368   size_t j;
369   int i;
370
371   if (l < 22)
372     return -1;
373
374   j = 3;
375   sigtype = buff[j++];
376
377   sig_gen_time = 0;
378   for (i = 0; i < 4; i++)
379     sig_gen_time = (sig_gen_time << 8) + buff[j++];
380
381   signerid1 = signerid2 = 0;
382   for (i = 0; i < 4; i++)
383     signerid1 = (signerid1 << 8) + buff[j++];
384
385   for (i = 0; i < 4; i++)
386     signerid2 = (signerid2 << 8) + buff[j++];
387
388
389   if (sigtype == 0x20 || sigtype == 0x28)
390     p->flags |= KEYFLAG_REVOKED;
391
392   if (s) {
393     s->sigtype = sigtype;
394     s->sid1 = signerid1;
395     s->sid2 = signerid2;
396   }
397
398   return 0;
399 }
400
401 static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l,
402                                pgp_key_t p, pgp_sig_t * s)
403 {
404   unsigned char sigtype;
405   unsigned char pkalg;
406   unsigned char hashalg;
407   unsigned char skt;
408   time_t sig_gen_time = -1;
409   long validity = -1;
410   long key_validity = -1;
411   unsigned long signerid1 = 0;
412   unsigned long signerid2 = 0;
413   size_t ml;
414   size_t j;
415   int i;
416   short ii;
417   short have_critical_spks = 0;
418
419   if (l < 7)
420     return -1;
421
422   j = 2;
423
424   sigtype = buff[j++];
425   pkalg = buff[j++];
426   hashalg = buff[j++];
427
428   for (ii = 0; ii < 2; ii++) {
429     size_t skl;
430     size_t nextone;
431
432     ml = (buff[j] << 8) + buff[j + 1];
433     j += 2;
434
435     if (j + ml > l)
436       break;
437
438     nextone = j;
439     while (ml) {
440       j = nextone;
441       skl = buff[j++];
442       if (!--ml)
443         break;
444
445       if (skl >= 192) {
446         skl = (skl - 192) * 256 + buff[j++] + 192;
447         if (!--ml)
448           break;
449       }
450
451       if ((int) ml - (int) skl < 0)
452         break;
453       ml -= skl;
454
455       nextone = j + skl;
456       skt = buff[j++];
457
458       switch (skt & 0x7f) {
459       case 2:                  /* creation time */
460         {
461           if (skl < 4)
462             break;
463           sig_gen_time = 0;
464           for (i = 0; i < 4; i++)
465             sig_gen_time = (sig_gen_time << 8) + buff[j++];
466
467           break;
468         }
469       case 3:                  /* expiration time */
470         {
471           if (skl < 4)
472             break;
473           validity = 0;
474           for (i = 0; i < 4; i++)
475             validity = (validity << 8) + buff[j++];
476           break;
477         }
478       case 9:                  /* key expiration time */
479         {
480           if (skl < 4)
481             break;
482           key_validity = 0;
483           for (i = 0; i < 4; i++)
484             key_validity = (key_validity << 8) + buff[j++];
485           break;
486         }
487       case 16:                 /* issuer key ID */
488         {
489           if (skl < 8)
490             break;
491           signerid2 = signerid1 = 0;
492           for (i = 0; i < 4; i++)
493             signerid1 = (signerid1 << 8) + buff[j++];
494           for (i = 0; i < 4; i++)
495             signerid2 = (signerid2 << 8) + buff[j++];
496
497           break;
498         }
499       case 10:                 /* CMR key */
500         break;
501       case 4:                  /* exportable */
502       case 5:                  /* trust */
503       case 6:                  /* regexp */
504       case 7:                  /* revocable */
505       case 11:                 /* Pref. symm. alg. */
506       case 12:                 /* revocation key */
507       case 20:                 /* notation data */
508       case 21:                 /* pref. hash */
509       case 22:                 /* pref. comp.alg. */
510       case 23:                 /* key server prefs. */
511       case 24:                 /* pref. key server */
512       default:
513         {
514           if (skt & 0x80)
515             have_critical_spks = 1;
516         }
517       }
518     }
519     j = nextone;
520   }
521
522   if (sigtype == 0x20 || sigtype == 0x28)
523     p->flags |= KEYFLAG_REVOKED;
524   if (key_validity != -1 && time (NULL) > p->gen_time + key_validity)
525     p->flags |= KEYFLAG_EXPIRED;
526   if (have_critical_spks)
527     p->flags |= KEYFLAG_CRITICAL;
528
529   if (s) {
530     s->sigtype = sigtype;
531     s->sid1 = signerid1;
532     s->sid2 = signerid2;
533   }
534
535
536   return 0;
537
538 }
539
540
541 static int pgp_parse_sig (unsigned char *buff, size_t l,
542                           pgp_key_t p, pgp_sig_t * sig)
543 {
544   if (!buff || l < 2 || !p)
545     return -1;
546
547   switch (buff[1]) {
548   case 2:
549   case 3:
550     return pgp_parse_pgp2_sig (buff, l, p, sig);
551   case 4:
552     return pgp_parse_pgp3_sig (buff, l, p, sig);
553   default:
554     return -1;
555   }
556 }
557
558 /* parse one key block, including all subkeys. */
559
560 static pgp_key_t pgp_parse_keyblock (FILE * fp)
561 {
562   unsigned char *buff;
563   unsigned char pt = 0;
564   unsigned char last_pt;
565   size_t l;
566   short err = 0;
567
568 #ifdef HAVE_FGETPOS
569   fpos_t pos;
570 #else
571   off_t pos;
572 #endif
573
574   pgp_key_t root = NULL;
575   pgp_key_t *last = &root;
576   pgp_key_t p = NULL;
577   pgp_uid_t *uid = NULL;
578   pgp_uid_t **addr = NULL;
579   pgp_sig_t **lsig = NULL;
580
581   FGETPOS (fp, pos);
582
583   while (!err && (buff = pgp_read_packet (fp, &l)) != NULL) {
584     last_pt = pt;
585     pt = buff[0] & 0x3f;
586
587     /* check if we have read the complete key block. */
588
589     if ((pt == PT_SECKEY || pt == PT_PUBKEY) && root) {
590       FSETPOS (fp, pos);
591       return root;
592     }
593
594     switch (pt) {
595     case PT_SECKEY:
596     case PT_PUBKEY:
597     case PT_SUBKEY:
598     case PT_SUBSECKEY:
599       {
600         if (!(*last = p = pgp_parse_keyinfo (buff, l))) {
601           err = 1;
602           break;
603         }
604
605         last = &p->next;
606         addr = &p->address;
607         lsig = &p->sigs;
608
609         if (pt == PT_SUBKEY || pt == PT_SUBSECKEY) {
610           p->flags |= KEYFLAG_SUBKEY;
611           if (p != root) {
612             p->parent = root;
613             p->address = pgp_copy_uids (root->address, p);
614             while (*addr)
615               addr = &(*addr)->next;
616           }
617         }
618
619         if (pt == PT_SECKEY || pt == PT_SUBSECKEY)
620           p->flags |= KEYFLAG_SECRET;
621
622         break;
623       }
624
625     case PT_SIG:
626       {
627         if (lsig) {
628           pgp_sig_t *signature = p_new(pgp_sig_t, 1);
629
630           *lsig = signature;
631           lsig = &signature->next;
632
633           pgp_parse_sig (buff, l, p, signature);
634         }
635         break;
636       }
637
638     case PT_TRUST:
639       {
640         if (p && (last_pt == PT_SECKEY || last_pt == PT_PUBKEY ||
641                   last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY)) {
642           if (buff[1] & 0x20) {
643             p->flags |= KEYFLAG_DISABLED;
644           }
645         }
646         else if (last_pt == PT_NAME && uid) {
647           uid->trust = buff[1];
648         }
649         break;
650       }
651     case PT_NAME:
652       {
653         char *chr;
654
655
656         if (!addr)
657           break;
658
659         chr = p_dupstr(buff + 1, l - 1);
660
661         *addr = uid = p_new(pgp_uid_t, 1);      /* XXX */
662         uid->addr = chr;
663         uid->parent = p;
664         uid->trust = 0;
665         addr = &uid->next;
666         lsig = &uid->sigs;
667
668         /* the following tags are generated by
669          * pgp 2.6.3in.
670          */
671
672         if (strstr (chr, "ENCR"))
673           p->flags |= KEYFLAG_PREFER_ENCRYPTION;
674         if (strstr (chr, "SIGN"))
675           p->flags |= KEYFLAG_PREFER_SIGNING;
676
677         break;
678       }
679     }
680
681     FGETPOS (fp, pos);
682   }
683
684   if (err)
685     pgp_free_key (&root);
686
687   return root;
688 }
689
690 static int pgpring_string_matches_hint (const char *s, const char *hints[],
691                                         int nhints)
692 {
693   int i;
694
695   if (!hints || !nhints)
696     return 1;
697
698   for (i = 0; i < nhints; i++) {
699     if (m_stristr(s, hints[i]) != NULL)
700       return 1;
701   }
702
703   return 0;
704 }
705
706 /* 
707  * Go through the key ring file and look for keys with
708  * matching IDs.
709  */
710
711 static void pgpring_find_candidates (char *ringfile, const char *hints[],
712                                      int nhints)
713 {
714   FILE *rfp;
715
716 #ifdef HAVE_FGETPOS
717   fpos_t pos, keypos;
718 #else
719   off_t pos, keypos;
720 #endif
721
722   unsigned char *buff = NULL;
723   unsigned char pt = 0;
724   size_t l = 0;
725
726   short err = 0;
727
728   if ((rfp = fopen (ringfile, "r")) == NULL) {
729     char *error_buf;
730     size_t error_buf_len;
731
732     error_buf_len = sizeof ("fopen: ") - 1 + m_strlen(ringfile) + 1;
733     error_buf = p_new(char, error_buf_len);
734     snprintf (error_buf, error_buf_len, "fopen: %s", ringfile);
735     perror (error_buf);
736     p_delete(&error_buf);
737     return;
738   }
739
740   FGETPOS (rfp, pos);
741   FGETPOS (rfp, keypos);
742
743   while (!err && (buff = pgp_read_packet (rfp, &l)) != NULL) {
744     pt = buff[0] & 0x3f;
745
746     if (l < 1)
747       continue;
748
749     if ((pt == PT_SECKEY) || (pt == PT_PUBKEY)) {
750       keypos = pos;
751     }
752     else if (pt == PT_NAME) {
753       char *tmp = p_dupstr(buff + 1, l - 1);
754
755       /* mutt_decode_utf8_string (tmp, chs); */
756
757       if (pgpring_string_matches_hint (tmp, hints, nhints)) {
758         pgp_key_t p;
759
760         FSETPOS (rfp, keypos);
761
762         /* Not bailing out here would lead us into an endless loop. */
763
764         if ((p = pgp_parse_keyblock (rfp)) == NULL)
765           err = 1;
766
767         pgpring_dump_keyblock (p);
768         pgp_free_key (&p);
769       }
770
771       p_delete(&tmp);
772     }
773
774     FGETPOS (rfp, pos);
775   }
776   m_fclose(&rfp);
777 }
778
779 static void print_userid (const char *id)
780 {
781   for (; id && *id; id++) {
782     if (*id >= ' ' && *id <= 'z' && *id != ':')
783       putchar (*id);
784     else
785       printf ("\\x%02x", (*id) & 0xff);
786   }
787 }
788
789 static void print_fingerprint (pgp_key_t p)
790 {
791   int i = 0;
792
793   printf ("fpr:::::::::");
794   for (i = 0; i < p->fp_len; i++)
795     printf ("%02X", p->fingerprint[i]);
796   printf (":\n");
797
798 }                               /* print_fingerprint() */
799
800
801 static void pgpring_dump_signatures (pgp_sig_t * sig)
802 {
803   for (; sig; sig = sig->next) {
804     if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
805         sig->sigtype == 0x12 || sig->sigtype == 0x13)
806       printf ("sig::::%08lX%08lX::::::%X:\n",
807               sig->sid1, sig->sid2, sig->sigtype);
808     else if (sig->sigtype == 0x20)
809       printf ("rev::::%08lX%08lX::::::%X:\n",
810               sig->sid1, sig->sid2, sig->sigtype);
811   }
812 }
813
814
815 static char gnupg_trustletter (int t)
816 {
817   switch (t) {
818   case 1:
819     return 'n';
820   case 2:
821     return 'm';
822   case 3:
823     return 'f';
824   }
825   return 'q';
826 }
827
828 static void pgpring_dump_keyblock (pgp_key_t p)
829 {
830   pgp_uid_t *uid;
831   short first;
832   struct tm *tp;
833   time_t t;
834
835   for (; p; p = p->next) {
836     first = 1;
837
838     if (p->flags & KEYFLAG_SECRET) {
839       if (p->flags & KEYFLAG_SUBKEY)
840         printf ("ssb:");
841       else
842         printf ("sec:");
843     }
844     else {
845       if (p->flags & KEYFLAG_SUBKEY)
846         printf ("sub:");
847       else
848         printf ("pub:");
849     }
850
851     if (p->flags & KEYFLAG_REVOKED)
852       putchar ('r');
853     if (p->flags & KEYFLAG_EXPIRED)
854       putchar ('e');
855     if (p->flags & KEYFLAG_DISABLED)
856       putchar ('d');
857
858     for (uid = p->address; uid; uid = uid->next, first = 0) {
859       if (!first) {
860         printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
861         print_userid (uid->addr);
862         printf (":\n");
863       }
864       else {
865         if (p->flags & KEYFLAG_SECRET)
866           putchar ('u');
867         else
868           putchar (gnupg_trustletter (uid->trust));
869
870         t = p->gen_time;
871         tp = gmtime (&t);
872
873         printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg,
874                 p->keyid, 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
875
876         print_userid (uid->addr);
877         printf ("::");
878
879         if (pgp_canencrypt (p->numalg))
880           putchar ('e');
881         if (pgp_cansign (p->numalg))
882           putchar ('s');
883         if (p->flags & KEYFLAG_DISABLED)
884           putchar ('D');
885         printf (":\n");
886
887         if (dump_fingerprints)
888           print_fingerprint (p);
889       }
890
891       if (dump_signatures) {
892         if (first)
893           pgpring_dump_signatures (p->sigs);
894         pgpring_dump_signatures (uid->sigs);
895       }
896     }
897   }
898 }