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