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