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