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