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