(main): New commands --dump-keys, --dump-external-keys,
[gnupg.git] / sm / certdump.c
1 /* certdump.c - Dump a certificate for debugging
2  *      Copyright (C) 2001, 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29 #ifdef HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32 #ifdef HAVE_LANGINFO_CODESET
33 #include <langinfo.h>
34 #endif
35
36 #include "gpgsm.h"
37 #include <gcrypt.h>
38 #include <ksba.h>
39
40 #include "keydb.h"
41 #include "i18n.h"
42
43 struct dn_array_s {
44   char *key;
45   char *value;
46   int   multivalued;
47   int   done;
48 };
49
50
51 /* print the first element of an S-Expression */
52 void
53 gpgsm_print_serial (FILE *fp, ksba_const_sexp_t p)
54 {
55   unsigned long n;
56   char *endp;
57
58   if (!p)
59     fputs (_("none"), fp);
60   else if (*p != '(')
61     fputs ("[Internal error - not an S-expression]", fp);
62   else
63     {
64       p++;
65       n = strtoul (p, &endp, 10);
66       p = endp;
67       if (*p!=':')
68         fputs ("[Internal Error - invalid S-expression]", fp);
69       else
70         {
71           for (p++; n; n--, p++)
72             fprintf (fp, "%02X", *p);
73         }
74     }
75 }
76
77
78 void
79 gpgsm_dump_serial (ksba_const_sexp_t p)
80 {
81   unsigned long n;
82   char *endp;
83
84   if (!p)
85     log_printf ("none");
86   else if (*p != '(')
87     log_printf ("ERROR - not an S-expression");
88   else
89     {
90       p++;
91       n = strtoul (p, &endp, 10);
92       p = endp;
93       if (*p!=':')
94         log_printf ("ERROR - invalid S-expression");
95       else
96         {
97           for (p++; n; n--, p++)
98             log_printf ("%02X", *p);
99         }
100     }
101 }
102
103
104 char *
105 gpgsm_format_serial (ksba_const_sexp_t p)
106 {
107   unsigned long n;
108   char *endp;
109   char *buffer;
110   int i;
111
112   if (!p)
113     return NULL;
114
115   if (*p != '(')
116     BUG (); /* Not a valid S-expression. */
117
118   p++;
119   n = strtoul (p, &endp, 10);
120   p = endp;
121   if (*p!=':')
122     BUG (); /* Not a valid S-expression. */
123   p++;
124
125   buffer = xtrymalloc (n*2+1);
126   if (buffer)
127     {
128       for (i=0; n; n--, p++, i+=2)
129         sprintf (buffer+i, "%02X", *(unsigned char *)p);
130       buffer[i] = 0;
131     }
132   return buffer;
133 }
134
135
136
137
138 void
139 gpgsm_print_time (FILE *fp, ksba_isotime_t t)
140 {
141   if (!t || !*t)
142     fputs (_("none"), fp);
143   else
144     fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13);
145 }
146
147 void
148 gpgsm_dump_time (ksba_isotime_t t)
149 {
150   if (!t || !*t)
151     log_printf (_("[none]"));
152   else
153     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
154                 t, t+4, t+6, t+9, t+11, t+13);
155 }
156
157
158
159
160 void
161 gpgsm_dump_string (const char *string)
162 {
163
164   if (!string)
165     log_printf ("[error]");
166   else
167     {
168       const unsigned char *s;
169
170       for (s=string; *s; s++)
171         {
172           if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
173             break;
174         }
175       if (!*s && *string != '[')
176         log_printf ("%s", string);
177       else
178         {
179           log_printf ( "[ ");
180           log_printhex (NULL, string, strlen (string));
181           log_printf ( " ]");
182         }
183     }
184 }
185
186
187 /* This simple dump function is mainly used for debugging purposes. */
188 void 
189 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
190 {
191   ksba_sexp_t sexp;
192   unsigned char *p;
193   char *dn;
194   ksba_isotime_t t;
195
196   log_debug ("BEGIN Certificate `%s':\n", text? text:"");
197   if (cert)
198     {
199       sexp = ksba_cert_get_serial (cert);
200       log_debug ("     serial: ");
201       gpgsm_dump_serial (sexp);
202       ksba_free (sexp);
203       log_printf ("\n");
204
205       ksba_cert_get_validity (cert, 0, t);
206       log_debug ("  notBefore: ");
207       gpgsm_dump_time (t);
208       log_printf ("\n");
209       ksba_cert_get_validity (cert, 1, t);
210       log_debug ("   notAfter: ");
211       gpgsm_dump_time (t);
212       log_printf ("\n");
213
214       dn = ksba_cert_get_issuer (cert, 0);
215       log_debug ("     issuer: ");
216       gpgsm_dump_string (dn);
217       ksba_free (dn);
218       log_printf ("\n");
219     
220       dn = ksba_cert_get_subject (cert, 0);
221       log_debug ("    subject: ");
222       gpgsm_dump_string (dn);
223       ksba_free (dn);
224       log_printf ("\n");
225
226       log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));
227
228       p = gpgsm_get_fingerprint_string (cert, 0);
229       log_debug ("  SHA1 Fingerprint: %s\n", p);
230       xfree (p);
231     }
232   log_debug ("END Certificate\n");
233 }
234
235
236 \f
237 /* helper for the rfc2253 string parser */
238 static const unsigned char *
239 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
240 {
241   static struct {
242     const char *label;
243     const char *oid;
244   } label_map[] = {
245     /* Warning: When adding new labels, make sure that the buffer
246        below we be allocated large enough. */
247     {"EMail",        "1.2.840.113549.1.9.1" },
248     {"T",            "2.5.4.12" },
249     {"GN",           "2.5.4.42" },
250     {"SN",           "2.5.4.4" },
251     {"NameDistinguisher", "0.2.262.1.10.7.20"}, 
252     {"ADDR",         "2.5.4.16" },
253     {"BC",           "2.5.4.15" },
254     {"D",            "2.5.4.13" },
255     {"PostalCode",   "2.5.4.17" },
256     {"Pseudo",       "2.5.4.65" },
257     {"SerialNumber", "2.5.4.5" },
258     {NULL, NULL}
259   };
260   const unsigned char *s, *s1;
261   size_t n;
262   unsigned char *p;
263   int i;
264
265   /* Parse attributeType */
266   for (s = string+1; *s && *s != '='; s++)
267     ;
268   if (!*s)
269     return NULL; /* error */
270   n = s - string;
271   if (!n)
272     return NULL; /* empty key */
273
274   /* We need to allocate a few bytes more due to the possible mapping
275      from the shorter OID to the longer label. */
276   array->key = p = xtrymalloc (n+10);
277   if (!array->key)
278     return NULL;
279   memcpy (p, string, n); 
280   p[n] = 0;
281   trim_trailing_spaces (p);
282
283   if (digitp (p))
284     {
285       for (i=0; label_map[i].label; i++ )
286         if ( !strcmp (p, label_map[i].oid) )
287           {
288             strcpy (p, label_map[i].label);
289             break;
290           }
291     }
292   string = s + 1;
293
294   if (*string == '#')
295     { /* hexstring */
296       string++;
297       for (s=string; hexdigitp (s); s++)
298         s++;
299       n = s - string;
300       if (!n || (n & 1))
301         return NULL; /* Empty or odd number of digits. */
302       n /= 2;
303       array->value = p = xtrymalloc (n+1);
304       if (!p)
305         return NULL;
306       for (s1=string; n; s1 += 2, n--, p++)
307         {
308           *p = xtoi_2 (s1);
309           if (!*p)
310             *p = 0x01; /* Better print a wrong value than truncating
311                           the string. */
312         }
313       *p = 0;
314    }
315   else
316     { /* regular v3 quoted string */
317       for (n=0, s=string; *s; s++)
318         {
319           if (*s == '\\')
320             { /* pair */
321               s++;
322               if (*s == ',' || *s == '=' || *s == '+'
323                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
324                   || *s == '\\' || *s == '\"' || *s == ' ')
325                 n++;
326               else if (hexdigitp (s) && hexdigitp (s+1))
327                 {
328                   s++;
329                   n++;
330                 }
331               else
332                 return NULL; /* invalid escape sequence */
333             }
334           else if (*s == '\"')
335             return NULL; /* invalid encoding */
336           else if (*s == ',' || *s == '=' || *s == '+'
337                    || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
338             break; 
339           else
340             n++;
341         }
342
343       array->value = p = xtrymalloc (n+1);
344       if (!p)
345         return NULL;
346       for (s=string; n; s++, n--)
347         {
348           if (*s == '\\')
349             { 
350               s++;
351               if (hexdigitp (s))
352                 {
353                   *p++ = xtoi_2 (s);
354                   s++;
355                 }
356               else
357                 *p++ = *s;
358             }
359           else
360             *p++ = *s;
361         }
362       *p = 0;
363     }
364   return s;
365 }
366
367
368 /* Parse a DN and return an array-ized one.  This is not a validating
369    parser and it does not support any old-stylish syntax; KSBA is
370    expected to return only rfc2253 compatible strings. */
371 static struct dn_array_s *
372 parse_dn (const unsigned char *string)
373 {
374   struct dn_array_s *array;
375   size_t arrayidx, arraysize;
376   int i;
377
378   arraysize = 7; /* C,ST,L,O,OU,CN,email */
379   arrayidx = 0;
380   array = xtrymalloc ((arraysize+1) * sizeof *array);
381   if (!array)
382     return NULL;
383   while (*string)
384     {
385       while (*string == ' ')
386         string++;
387       if (!*string)
388         break; /* ready */
389       if (arrayidx >= arraysize)
390         { 
391           struct dn_array_s *a2;
392
393           arraysize += 5;
394           a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
395           if (!a2)
396             goto failure;
397           array = a2;
398         }
399       array[arrayidx].key = NULL;
400       array[arrayidx].value = NULL;
401       string = parse_dn_part (array+arrayidx, string);
402       if (!string)
403         goto failure;
404       while (*string == ' ')
405         string++;
406       array[arrayidx].multivalued = (*string == '+');
407       array[arrayidx].done = 0;
408       arrayidx++;
409       if (*string && *string != ',' && *string != ';' && *string != '+')
410         goto failure; /* invalid delimiter */
411       if (*string)
412         string++;
413     }
414   array[arrayidx].key = NULL;
415   array[arrayidx].value = NULL;
416   return array;
417
418  failure:
419   for (i=0; i < arrayidx; i++)
420     {
421       xfree (array[i].key);
422       xfree (array[i].value);
423     }
424   xfree (array);
425   return NULL;
426 }
427
428
429 static void
430 print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key)
431 {
432   struct dn_array_s *first_dn = dn;
433
434   for (; dn->key; dn++)
435     {
436       if (!dn->done && !strcmp (dn->key, key))
437         {
438           /* Forward to the last multi-valued RDN, so that we can
439              print them all in reverse in the correct order.  Note
440              that this overrides the the standard sequence but that
441              seems to a reasonable thing to do with multi-valued
442              RDNs. */
443           while (dn->multivalued && dn[1].key)
444             dn++;
445         next:
446           if (!dn->done && dn->value && *dn->value)
447             {
448               fprintf (fp, "/%s=", dn->key);
449               print_sanitized_utf8_string (fp, dn->value, '/');
450             }
451           dn->done = 1;
452           if (dn > first_dn && dn[-1].multivalued)
453             {
454               dn--;
455               goto next;
456             }
457         }
458     }
459 }
460
461 /* Print all parts of a DN in a "standard" sequence.  We first print
462    all the known parts, followed by the uncommon ones */
463 static void
464 print_dn_parts (FILE *fp, struct dn_array_s *dn)
465 {
466   const char *stdpart[] = {
467     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL 
468   };
469   int i;
470   
471   for (i=0; stdpart[i]; i++)
472       print_dn_part (fp, dn, stdpart[i]);
473
474   /* Now print the rest without any specific ordering */
475   for (; dn->key; dn++)
476     print_dn_part (fp, dn, dn->key);
477 }
478
479
480
481 void
482 gpgsm_print_name (FILE *fp, const char *name)
483 {
484   const unsigned char *s;
485   int i;
486
487   s = name;
488   if (!s)
489     {
490       fputs (_("[Error - No name]"), fp);
491     }
492   else if (*s == '<')
493     {
494       const unsigned char *s2 = strchr (s+1, '>');
495       if (s2)
496         print_sanitized_utf8_buffer (fp, s + 1, s2 - s - 1, 0);
497     }
498   else if (*s == '(')
499     fputs (_("[Error - unknown encoding]"), fp);
500   else if (!((*s >= '0' && *s < '9')
501              || (*s >= 'A' && *s <= 'Z')
502              || (*s >= 'a' && *s <= 'z')))
503     fputs (_("[Error - invalid encoding]"), fp);
504   else
505     {
506       struct dn_array_s *dn = parse_dn (s);
507       if (!dn)
508         fputs (_("[Error - invalid DN]"), fp);
509       else 
510         {
511           print_dn_parts (fp, dn);          
512           for (i=0; dn[i].key; i++)
513             {
514               xfree (dn[i].key);
515               xfree (dn[i].value);
516             }
517           xfree (dn);
518         }
519     }
520 }
521
522
523
524 /* A cookie structure used for the memory stream. */
525 struct format_name_cookie 
526 {
527   char *buffer;         /* Malloced buffer with the data to deliver. */
528   size_t size;          /* Allocated size of this buffer. */
529   size_t len;           /* strlen (buffer). */
530   int error;            /* system error code if any. */
531 };
532
533 /* The writer function for the memory stream. */
534 static int 
535 format_name_writer (void *cookie, const char *buffer, size_t size)
536 {
537   struct format_name_cookie *c = cookie;
538   char *p;
539
540   if (c->buffer)
541     p = xtryrealloc (c->buffer, c->size + size + 1);
542   else
543     p = xtrymalloc (size + 1);
544   if (!p)
545     {
546       c->error = errno;
547       xfree (c->buffer);
548       errno = c->error;
549       return -1;
550     }
551   c->buffer = p;
552   memcpy (p + c->len, buffer, size);
553   c->len += size;
554   p[c->len] = 0; /* Terminate string. */ 
555
556   return size;
557 }
558
559 /* Format NAME which is expected to be in rfc2253 format into a better
560    human readable format. Caller must free the returned string.  NULL
561    is returned in case of an error. */
562 char *
563 gpgsm_format_name (const char *name)
564 {
565 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
566   FILE *fp;
567   struct format_name_cookie cookie;
568
569   memset (&cookie, 0, sizeof cookie);
570
571 #ifdef HAVE_FOPENCOOKIE
572   {
573     cookie_io_functions_t io = { NULL };
574     io.write = format_name_writer;
575     
576     fp = fopencookie (&cookie, "w", io);
577   }
578 #else /*!HAVE_FOPENCOOKIE*/
579   {
580     fp = funopen (&cookie, NULL, format_name_writer, NULL, NULL);
581   }
582 #endif /*!HAVE_FOPENCOOKIE*/
583   if (!fp)
584     {
585       int save_errno = errno;
586       log_error ("error creating memory stream: %s\n", strerror (errno));
587       errno = save_errno;
588       return NULL;
589     }
590   gpgsm_print_name (fp, name);
591   fclose (fp);
592   if (cookie.error || !cookie.buffer)
593     {
594       xfree (cookie.buffer);
595       errno = cookie.error;
596       return NULL;
597     }
598   return cookie.buffer;
599 #else /* No fun - use the name verbatim. */
600   return xtrystrdup (name);
601 #endif /* No fun. */
602 }
603
604
605 /* Create a key description for the CERT, this may be passed to the
606    pinentry.  The caller must free the returned string. NULL may be
607    returned on error. */
608 char *
609 gpgsm_format_keydesc (ksba_cert_t cert)
610 {
611   int rc;
612   char *name, *subject, *buffer, *p;
613   const char *s;
614   ksba_isotime_t t;
615   char created[20];
616   char *sn;
617   ksba_sexp_t sexp;
618   char *orig_codeset = NULL;
619
620   name = ksba_cert_get_subject (cert, 0);
621   subject = name? gpgsm_format_name (name) : NULL;
622   ksba_free (name); name = NULL;
623
624   sexp = ksba_cert_get_serial (cert);
625   sn = sexp? gpgsm_format_serial (sexp) : NULL;
626   ksba_free (sexp);
627
628   ksba_cert_get_validity (cert, 0, t);
629   if (t && *t)
630     sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
631   else
632     *created = 0;
633
634
635 #ifdef ENABLE_NLS
636   /* The Assuan agent protol requires us to transmit utf-8 strings */
637   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
638 #ifdef HAVE_LANGINFO_CODESET
639   if (!orig_codeset)
640     orig_codeset = nl_langinfo (CODESET);
641 #endif
642   if (orig_codeset)
643     { /* We only switch when we are able to restore the codeset later. */
644       orig_codeset = xstrdup (orig_codeset);
645       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
646         orig_codeset = NULL; 
647     }
648 #endif
649
650
651   rc = asprintf (&name,
652                  _("Please enter the passphrase to unlock the"
653                    " secret key for:\n"
654                    "\"%s\"\n"
655                    "S/N %s, ID %08lX, created %s" ),
656                  subject? subject:"?",
657                  sn? sn: "?",
658                  gpgsm_get_short_fingerprint (cert),
659                  created);
660
661 #ifdef ENABLE_NLS
662   if (orig_codeset)
663     bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
664 #endif
665   xfree (orig_codeset);
666
667   if (rc < 0)
668     {
669       int save_errno = errno;
670       xfree (subject);
671       xfree (sn);
672       errno = save_errno;
673       return NULL;
674     }
675   
676   xfree (subject);
677   xfree (sn);
678
679   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
680   for (s=name; *s; s++)
681     {
682       if (*s < ' ' || *s == '+')
683         {
684           sprintf (p, "%%%02X", *(unsigned char *)s);
685           p += 3;
686         }
687       else if (*s == ' ')
688         *p++ = '+';
689       else
690         *p++ = *s;
691     }
692   *p = 0;
693   free (name); 
694
695   return buffer;
696 }