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