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