* keylist.c (email_kludge): Reworked.
[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
488 void
489 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
490 {
491   const unsigned char *s = (const unsigned char *)name;
492   int i;
493
494   if (!s)
495     {
496       fputs (_("[Error - No name]"), fp);
497     }
498   else if (*s == '<')
499     {
500       const char *s2 = strchr ( (char*)s+1, '>');
501       if (s2)
502         {
503           if (translate)
504             print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
505           else
506             print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
507         }
508     }
509   else if (*s == '(')
510     fputs (_("[Error - unknown encoding]"), fp);
511   else if (!((*s >= '0' && *s < '9')
512              || (*s >= 'A' && *s <= 'Z')
513              || (*s >= 'a' && *s <= 'z')))
514     fputs (_("[Error - invalid encoding]"), fp);
515   else
516     {
517       struct dn_array_s *dn = parse_dn (s);
518       if (!dn)
519         fputs (_("[Error - invalid DN]"), fp);
520       else 
521         {
522           print_dn_parts (fp, dn, translate);          
523           for (i=0; dn[i].key; i++)
524             {
525               xfree (dn[i].key);
526               xfree (dn[i].value);
527             }
528           xfree (dn);
529         }
530     }
531 }
532
533
534 void
535 gpgsm_print_name (FILE *fp, const char *name)
536 {
537   gpgsm_print_name2 (fp, name, 1);
538 }
539
540
541 /* A cookie structure used for the memory stream. */
542 struct format_name_cookie 
543 {
544   char *buffer;         /* Malloced buffer with the data to deliver. */
545   size_t size;          /* Allocated size of this buffer. */
546   size_t len;           /* strlen (buffer). */
547   int error;            /* system error code if any. */
548 };
549
550 /* The writer function for the memory stream. */
551 static int 
552 format_name_writer (void *cookie, const char *buffer, size_t size)
553 {
554   struct format_name_cookie *c = cookie;
555   char *p;
556
557   if (c->buffer)
558     p = xtryrealloc (c->buffer, c->size + size + 1);
559   else
560     p = xtrymalloc (size + 1);
561   if (!p)
562     {
563       c->error = errno;
564       xfree (c->buffer);
565       errno = c->error;
566       return -1;
567     }
568   c->buffer = p;
569   memcpy (p + c->len, buffer, size);
570   c->len += size;
571   p[c->len] = 0; /* Terminate string. */ 
572
573   return size;
574 }
575
576 /* Format NAME which is expected to be in rfc2253 format into a better
577    human readable format. Caller must free the returned string.  NULL
578    is returned in case of an error.  With TRANSLATE set to true the
579    name will be translated to the native encodig.  Note that NAME is
580    internally always UTF-8 encoded. */
581 char *
582 gpgsm_format_name2 (const char *name, int translate)
583 {
584 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
585   FILE *fp;
586   struct format_name_cookie cookie;
587
588   memset (&cookie, 0, sizeof cookie);
589
590 #ifdef HAVE_FOPENCOOKIE
591   {
592     cookie_io_functions_t io = { NULL };
593     io.write = format_name_writer;
594     
595     fp = fopencookie (&cookie, "w", io);
596   }
597 #else /*!HAVE_FOPENCOOKIE*/
598   {
599     fp = funopen (&cookie, NULL, format_name_writer, NULL, NULL);
600   }
601 #endif /*!HAVE_FOPENCOOKIE*/
602   if (!fp)
603     {
604       int save_errno = errno;
605       log_error ("error creating memory stream: %s\n", strerror (errno));
606       errno = save_errno;
607       return NULL;
608     }
609   gpgsm_print_name2 (fp, name, translate);
610   fclose (fp);
611   if (cookie.error || !cookie.buffer)
612     {
613       xfree (cookie.buffer);
614       errno = cookie.error;
615       return NULL;
616     }
617   return cookie.buffer;
618 #else /* No fun - use the name verbatim. */
619   return xtrystrdup (name);
620 #endif /* No fun. */
621 }
622
623 char *
624 gpgsm_format_name (const char *name)
625 {
626   return gpgsm_format_name2 (name, 1);
627 }
628
629
630 /* Create a key description for the CERT, this may be passed to the
631    pinentry.  The caller must free the returned string. NULL may be
632    returned on error. */
633 char *
634 gpgsm_format_keydesc (ksba_cert_t cert)
635 {
636   int rc;
637   char *name, *subject, *buffer, *p;
638   const char *s;
639   ksba_isotime_t t;
640   char created[20];
641   char *sn;
642   ksba_sexp_t sexp;
643   char *orig_codeset = NULL;
644
645   name = ksba_cert_get_subject (cert, 0);
646   subject = name? gpgsm_format_name2 (name, 0) : NULL;
647   ksba_free (name); name = NULL;
648
649   sexp = ksba_cert_get_serial (cert);
650   sn = sexp? gpgsm_format_serial (sexp) : NULL;
651   ksba_free (sexp);
652
653   ksba_cert_get_validity (cert, 0, t);
654   if (t && *t)
655     sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
656   else
657     *created = 0;
658
659
660 #ifdef ENABLE_NLS
661   /* The Assuan agent protol requires us to transmit utf-8 strings */
662   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
663 #ifdef HAVE_LANGINFO_CODESET
664   if (!orig_codeset)
665     orig_codeset = nl_langinfo (CODESET);
666 #endif
667   if (orig_codeset)
668     { /* We only switch when we are able to restore the codeset later.
669          Note that bind_textdomain_codeset does only return on memory
670          errors but not if a codeset is not available.  Thus we don't
671          bother printing a diagnostic here. */
672       orig_codeset = xstrdup (orig_codeset);
673       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
674         orig_codeset = NULL; 
675     }
676 #endif
677
678
679   rc = asprintf (&name,
680                  _("Please enter the passphrase to unlock the"
681                    " secret key for:\n"
682                    "\"%s\"\n"
683                    "S/N %s, ID %08lX, created %s" ),
684                  subject? subject:"?",
685                  sn? sn: "?",
686                  gpgsm_get_short_fingerprint (cert),
687                  created);
688
689 #ifdef ENABLE_NLS
690   if (orig_codeset)
691     bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
692 #endif
693   xfree (orig_codeset);
694
695   if (rc < 0)
696     {
697       int save_errno = errno;
698       xfree (subject);
699       xfree (sn);
700       errno = save_errno;
701       return NULL;
702     }
703   
704   xfree (subject);
705   xfree (sn);
706
707   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
708   for (s=name; *s; s++)
709     {
710       if (*s < ' ' || *s == '+')
711         {
712           sprintf (p, "%%%02X", *(unsigned char *)s);
713           p += 3;
714         }
715       else if (*s == ' ')
716         *p++ = '+';
717       else
718         *p++ = *s;
719     }
720   *p = 0;
721   free (name); 
722
723   return buffer;
724 }