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