91539017fa06031b833e3be490c391feab5fb310
[gnupg.git] / sm / certdump.c
1 /* certdump.c - Dump a certificate for debugging
2  * Copyright (C) 2001, 2004, 2007 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h> 
26 #include <time.h>
27 #include <assert.h>
28 #ifdef HAVE_LOCALE_H
29 #include <locale.h>
30 #endif
31 #ifdef HAVE_LANGINFO_CODESET
32 #include <langinfo.h>
33 #endif
34
35 #include "gpgsm.h"
36 #include <gcrypt.h>
37 #include <ksba.h>
38
39 #include "keydb.h"
40 #include "i18n.h"
41
42 #ifdef HAVE_FOPENCOOKIE
43 typedef ssize_t my_funopen_hook_ret_t;
44 typedef size_t  my_funopen_hook_size_t;
45 #else
46 typedef int     my_funopen_hook_ret_t;
47 typedef int     my_funopen_hook_size_t;
48 #endif
49
50
51 struct dn_array_s {
52   char *key;
53   char *value;
54   int   multivalued;
55   int   done;
56 };
57
58
59 /* Print the first element of an S-Expression. */
60 void
61 gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
62 {
63   const char *p = (const char *)sn;
64   unsigned long n;
65   char *endp;
66
67   if (!p)
68     es_fputs (_("none"), fp);
69   else if (*p != '(')
70     es_fputs ("[Internal error - not an S-expression]", fp);
71   else
72     {
73       p++;
74       n = strtoul (p, &endp, 10);
75       p = endp;
76       if (*p++ != ':')
77         es_fputs ("[Internal Error - invalid S-expression]", fp);
78       else
79         es_write_hexstring (fp, p, n, 0, NULL);
80     }
81 }
82
83
84 /* Dump the serial number or any other simple S-expression. */
85 void
86 gpgsm_dump_serial (ksba_const_sexp_t sn)
87 {
88   const char *p = (const char *)sn;
89   unsigned long n;
90   char *endp;
91
92   if (!p)
93     log_printf ("none");
94   else if (*p != '(')
95     log_printf ("ERROR - not an S-expression");
96   else
97     {
98       p++;
99       n = strtoul (p, &endp, 10);
100       p = endp;
101       if (*p!=':')
102         log_printf ("ERROR - invalid S-expression");
103       else
104         {
105           for (p++; n; n--, p++)
106             log_printf ("%02X", *(const unsigned char *)p);
107         }
108     }
109 }
110
111
112 char *
113 gpgsm_format_serial (ksba_const_sexp_t sn)
114 {
115   const char *p = (const char *)sn;
116   unsigned long n;
117   char *endp;
118   char *buffer;
119   int i;
120
121   if (!p)
122     return NULL;
123
124   if (*p != '(')
125     BUG (); /* Not a valid S-expression. */
126
127   p++;
128   n = strtoul (p, &endp, 10);
129   p = endp;
130   if (*p!=':')
131     BUG (); /* Not a valid S-expression. */
132   p++;
133
134   buffer = xtrymalloc (n*2+1);
135   if (buffer)
136     {
137       for (i=0; n; n--, p++, i+=2)
138         sprintf (buffer+i, "%02X", *(unsigned char *)p);
139       buffer[i] = 0;
140     }
141   return buffer;
142 }
143
144
145
146
147 void
148 gpgsm_print_time (estream_t fp, ksba_isotime_t t)
149 {
150   if (!t || !*t)
151     es_fputs (_("none"), fp);
152   else
153     es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
154                 t, t+4, t+6, t+9, t+11, t+13);
155 }
156
157
158 void
159 gpgsm_dump_time (ksba_isotime_t t)
160 {
161   if (!t || !*t)
162     log_printf (_("[none]"));
163   else
164     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
165                 t, t+4, t+6, t+9, t+11, t+13);
166 }
167
168
169
170
171 void
172 gpgsm_dump_string (const char *string)
173 {
174
175   if (!string)
176     log_printf ("[error]");
177   else
178     {
179       const unsigned char *s;
180
181       for (s=(const unsigned char*)string; *s; s++)
182         {
183           if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
184             break;
185         }
186       if (!*s && *string != '[')
187         log_printf ("%s", string);
188       else
189         {
190           log_printf ( "[ ");
191           log_printhex (NULL, string, strlen (string));
192           log_printf ( " ]");
193         }
194     }
195 }
196
197
198 /* This simple dump function is mainly used for debugging purposes. */
199 void 
200 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
201 {
202   ksba_sexp_t sexp;
203   char *p;
204   char *dn;
205   ksba_isotime_t t;
206
207   log_debug ("BEGIN Certificate `%s':\n", text? text:"");
208   if (cert)
209     {
210       sexp = ksba_cert_get_serial (cert);
211       log_debug ("     serial: ");
212       gpgsm_dump_serial (sexp);
213       ksba_free (sexp);
214       log_printf ("\n");
215
216       ksba_cert_get_validity (cert, 0, t);
217       log_debug ("  notBefore: ");
218       gpgsm_dump_time (t);
219       log_printf ("\n");
220       ksba_cert_get_validity (cert, 1, t);
221       log_debug ("   notAfter: ");
222       gpgsm_dump_time (t);
223       log_printf ("\n");
224
225       dn = ksba_cert_get_issuer (cert, 0);
226       log_debug ("     issuer: ");
227       gpgsm_dump_string (dn);
228       ksba_free (dn);
229       log_printf ("\n");
230     
231       dn = ksba_cert_get_subject (cert, 0);
232       log_debug ("    subject: ");
233       gpgsm_dump_string (dn);
234       ksba_free (dn);
235       log_printf ("\n");
236
237       log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));
238
239       p = gpgsm_get_fingerprint_string (cert, 0);
240       log_debug ("  SHA1 Fingerprint: %s\n", p);
241       xfree (p);
242     }
243   log_debug ("END Certificate\n");
244 }
245
246
247 /* Return a new string holding the format serial number and issuer
248    ("#SN/issuer").  No filtering on invalid characters is done.
249    Caller must release the string.  On memory failure NULL is
250    returned.  */
251 char *
252 gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
253 {
254   char *p, *p1;
255
256   if (sn && issuer)
257     {
258       p1 = gpgsm_format_serial (sn);
259       if (!p1)
260         p = xtrystrdup ("[invalid SN]");
261       else
262         {
263           p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
264           if (p)
265             {
266               *p = '#';
267               strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
268             }
269           xfree (p1);
270         }
271     }
272   else
273     p = xtrystrdup ("[invalid SN/issuer]");
274   return p;
275 }
276
277
278 /* Log the certificate's name in "#SN/ISSUERDN" format along with
279    TEXT. */
280 void 
281 gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
282 {
283   log_info ("%s", text? text:"certificate" );
284   if (cert)
285     {
286       ksba_sexp_t sn;
287       char *p;
288
289       p = ksba_cert_get_issuer (cert, 0);
290       sn = ksba_cert_get_serial (cert);
291       if (p && sn)
292         {
293           log_printf (" #");
294           gpgsm_dump_serial (sn);
295           log_printf ("/");
296           gpgsm_dump_string (p);
297         }
298       else
299         log_printf (" [invalid]");
300       ksba_free (sn);
301       xfree (p);
302     }
303   log_printf ("\n");
304 }
305
306
307
308
309
310 \f
311 /* helper for the rfc2253 string parser */
312 static const unsigned char *
313 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
314 {
315   static struct {
316     const char *label;
317     const char *oid;
318   } label_map[] = {
319     /* Warning: When adding new labels, make sure that the buffer
320        below we be allocated large enough. */
321     {"EMail",        "1.2.840.113549.1.9.1" },
322     {"T",            "2.5.4.12" },
323     {"GN",           "2.5.4.42" },
324     {"SN",           "2.5.4.4" },
325     {"NameDistinguisher", "0.2.262.1.10.7.20"}, 
326     {"ADDR",         "2.5.4.16" },
327     {"BC",           "2.5.4.15" },
328     {"D",            "2.5.4.13" },
329     {"PostalCode",   "2.5.4.17" },
330     {"Pseudo",       "2.5.4.65" },
331     {"SerialNumber", "2.5.4.5" },
332     {NULL, NULL}
333   };
334   const unsigned char *s, *s1;
335   size_t n;
336   char *p;
337   int i;
338
339   /* Parse attributeType */
340   for (s = string+1; *s && *s != '='; s++)
341     ;
342   if (!*s)
343     return NULL; /* error */
344   n = s - string;
345   if (!n)
346     return NULL; /* empty key */
347
348   /* We need to allocate a few bytes more due to the possible mapping
349      from the shorter OID to the longer label. */
350   array->key = p = xtrymalloc (n+10);
351   if (!array->key)
352     return NULL;
353   memcpy (p, string, n); 
354   p[n] = 0;
355   trim_trailing_spaces (p);
356
357   if (digitp (p))
358     {
359       for (i=0; label_map[i].label; i++ )
360         if ( !strcmp (p, label_map[i].oid) )
361           {
362             strcpy (p, label_map[i].label);
363             break;
364           }
365     }
366   string = s + 1;
367
368   if (*string == '#')
369     { /* hexstring */
370       string++;
371       for (s=string; hexdigitp (s); s++)
372         s++;
373       n = s - string;
374       if (!n || (n & 1))
375         return NULL; /* Empty or odd number of digits. */
376       n /= 2;
377       array->value = p = xtrymalloc (n+1);
378       if (!p)
379         return NULL;
380       for (s1=string; n; s1 += 2, n--, p++)
381         {
382           *(unsigned char *)p = xtoi_2 (s1);
383           if (!*p)
384             *p = 0x01; /* Better print a wrong value than truncating
385                           the string. */
386         }
387       *p = 0;
388    }
389   else
390     { /* regular v3 quoted string */
391       for (n=0, s=string; *s; s++)
392         {
393           if (*s == '\\')
394             { /* pair */
395               s++;
396               if (*s == ',' || *s == '=' || *s == '+'
397                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
398                   || *s == '\\' || *s == '\"' || *s == ' ')
399                 n++;
400               else if (hexdigitp (s) && hexdigitp (s+1))
401                 {
402                   s++;
403                   n++;
404                 }
405               else
406                 return NULL; /* invalid escape sequence */
407             }
408           else if (*s == '\"')
409             return NULL; /* invalid encoding */
410           else if (*s == ',' || *s == '=' || *s == '+'
411                    || *s == '<' || *s == '>' || *s == ';' )
412             break; 
413           else
414             n++;
415         }
416
417       array->value = p = xtrymalloc (n+1);
418       if (!p)
419         return NULL;
420       for (s=string; n; s++, n--)
421         {
422           if (*s == '\\')
423             { 
424               s++;
425               if (hexdigitp (s))
426                 {
427                   *(unsigned char *)p++ = xtoi_2 (s);
428                   s++;
429                 }
430               else
431                 *p++ = *s;
432             }
433           else
434             *p++ = *s;
435         }
436       *p = 0;
437     }
438   return s;
439 }
440
441
442 /* Parse a DN and return an array-ized one.  This is not a validating
443    parser and it does not support any old-stylish syntax; KSBA is
444    expected to return only rfc2253 compatible strings. */
445 static struct dn_array_s *
446 parse_dn (const unsigned char *string)
447 {
448   struct dn_array_s *array;
449   size_t arrayidx, arraysize;
450   int i;
451
452   arraysize = 7; /* C,ST,L,O,OU,CN,email */
453   arrayidx = 0;
454   array = xtrymalloc ((arraysize+1) * sizeof *array);
455   if (!array)
456     return NULL;
457   while (*string)
458     {
459       while (*string == ' ')
460         string++;
461       if (!*string)
462         break; /* ready */
463       if (arrayidx >= arraysize)
464         { 
465           struct dn_array_s *a2;
466
467           arraysize += 5;
468           a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
469           if (!a2)
470             goto failure;
471           array = a2;
472         }
473       array[arrayidx].key = NULL;
474       array[arrayidx].value = NULL;
475       string = parse_dn_part (array+arrayidx, string);
476       if (!string)
477         goto failure;
478       while (*string == ' ')
479         string++;
480       array[arrayidx].multivalued = (*string == '+');
481       array[arrayidx].done = 0;
482       arrayidx++;
483       if (*string && *string != ',' && *string != ';' && *string != '+')
484         goto failure; /* invalid delimiter */
485       if (*string)
486         string++;
487     }
488   array[arrayidx].key = NULL;
489   array[arrayidx].value = NULL;
490   return array;
491
492  failure:
493   for (i=0; i < arrayidx; i++)
494     {
495       xfree (array[i].key);
496       xfree (array[i].value);
497     }
498   xfree (array);
499   return NULL;
500 }
501
502
503 /* Print a DN part to STREAM or if STREAM is NULL to FP. */
504 static void
505 print_dn_part (FILE *fp, estream_t stream,
506                struct dn_array_s *dn, const char *key, int translate)
507 {
508   struct dn_array_s *first_dn = dn;
509
510   for (; dn->key; dn++)
511     {
512       if (!dn->done && !strcmp (dn->key, key))
513         {
514           /* Forward to the last multi-valued RDN, so that we can
515              print them all in reverse in the correct order.  Note
516              that this overrides the the standard sequence but that
517              seems to a reasonable thing to do with multi-valued
518              RDNs. */
519           while (dn->multivalued && dn[1].key)
520             dn++;
521         next:
522           if (!dn->done && dn->value && *dn->value)
523             {
524               if (stream)
525                 {
526                   es_fprintf (stream, "/%s=", dn->key);
527                   if (translate)
528                     es_write_sanitized_utf8_buffer (stream, dn->value,
529                                                     strlen (dn->value),
530                                                     "/", NULL);
531                   else
532                     es_write_sanitized (stream, dn->value, strlen (dn->value),
533                                         "/", NULL);
534                 }
535               else
536                 {
537                   fprintf (fp, "/%s=", dn->key);
538                   if (translate)
539                     print_sanitized_utf8_string (fp, dn->value, '/');
540                   else
541                     print_sanitized_string (fp, dn->value, '/');
542                 }
543             }
544           dn->done = 1;
545           if (dn > first_dn && dn[-1].multivalued)
546             {
547               dn--;
548               goto next;
549             }
550         }
551     }
552 }
553
554 /* Print all parts of a DN in a "standard" sequence.  We first print
555    all the known parts, followed by the uncommon ones */
556 static void
557 print_dn_parts (FILE *fp, estream_t stream,
558                 struct dn_array_s *dn, int translate)
559 {
560   const char *stdpart[] = {
561     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL 
562   };
563   int i;
564   
565   for (i=0; stdpart[i]; i++)
566       print_dn_part (fp, stream, dn, stdpart[i], translate);
567
568   /* Now print the rest without any specific ordering */
569   for (; dn->key; dn++)
570     print_dn_part (fp, stream, dn, dn->key, translate);
571 }
572
573
574 /* Print the S-Expression in BUF, which has a valid length of BUFLEN,
575    as a human readable string in one line to FP. */
576 static void
577 pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
578 {
579   size_t len;
580   gcry_sexp_t sexp;
581   char *result, *p;
582
583   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
584     {
585       fputs (_("[Error - invalid encoding]"), fp);
586       return;
587     }
588   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
589   assert (len);
590   result = xtrymalloc (len);
591   if (!result)
592     {
593       fputs (_("[Error - out of core]"), fp);
594       gcry_sexp_release (sexp);
595       return;
596     }
597   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
598   assert (len);
599   for (p = result; len; len--, p++)
600     {
601       if (*p == '\n')
602         {
603           if (len > 1) /* Avoid printing the trailing LF. */
604             fputs ("\\n", fp);
605         }
606       else if (*p == '\r')
607         fputs ("\\r", fp);
608       else if (*p == '\v')
609         fputs ("\\v", fp);
610       else if (*p == '\t')
611         fputs ("\\t", fp);
612       else
613         putc (*p, fp);
614     }
615   xfree (result);
616   gcry_sexp_release (sexp);
617 }
618
619 /* Print the S-Expression in BUF to extended STREAM, which has a valid
620    length of BUFLEN, as a human readable string in one line to FP. */
621 static void
622 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
623 {
624   size_t len;
625   gcry_sexp_t sexp;
626   char *result, *p;
627
628   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
629     {
630       es_fputs (_("[Error - invalid encoding]"), fp);
631       return;
632     }
633   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
634   assert (len);
635   result = xtrymalloc (len);
636   if (!result)
637     {
638       es_fputs (_("[Error - out of core]"), fp);
639       gcry_sexp_release (sexp);
640       return;
641     }
642   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
643   assert (len);
644   for (p = result; len; len--, p++)
645     {
646       if (*p == '\n')
647         {
648           if (len > 1) /* Avoid printing the trailing LF. */
649             es_fputs ("\\n", fp);
650         }
651       else if (*p == '\r')
652         es_fputs ("\\r", fp);
653       else if (*p == '\v')
654         es_fputs ("\\v", fp);
655       else if (*p == '\t')
656         es_fputs ("\\t", fp);
657       else
658         es_putc (*p, fp);
659     }
660   xfree (result);
661   gcry_sexp_release (sexp);
662 }
663
664
665
666
667 void
668 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
669 {
670   const unsigned char *s = (const unsigned char *)name;
671   int i;
672
673   if (!s)
674     {
675       fputs (_("[Error - No name]"), fp);
676     }
677   else if (*s == '<')
678     {
679       const char *s2 = strchr ( (char*)s+1, '>');
680       if (s2)
681         {
682           if (translate)
683             print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
684           else
685             print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
686         }
687     }
688   else if (*s == '(')
689     {
690       pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
691     }
692   else if (!((*s >= '0' && *s < '9')
693              || (*s >= 'A' && *s <= 'Z')
694              || (*s >= 'a' && *s <= 'z')))
695     fputs (_("[Error - invalid encoding]"), fp);
696   else
697     {
698       struct dn_array_s *dn = parse_dn (s);
699       if (!dn)
700         fputs (_("[Error - invalid DN]"), fp);
701       else 
702         {
703           print_dn_parts (fp, NULL, dn, translate);          
704           for (i=0; dn[i].key; i++)
705             {
706               xfree (dn[i].key);
707               xfree (dn[i].value);
708             }
709           xfree (dn);
710         }
711     }
712 }
713
714
715 void
716 gpgsm_print_name (FILE *fp, const char *name)
717 {
718   gpgsm_print_name2 (fp, name, 1);
719 }
720
721
722 /* This is avariant of gpgsm_print_name sending it output to an estream. */
723 void
724 gpgsm_es_print_name (estream_t fp, const char *name)
725 {
726   const unsigned char *s = (const unsigned char *)name;
727   int i;
728
729   if (!s)
730     {
731       es_fputs (_("[Error - No name]"), fp);
732     }
733   else if (*s == '<')
734     {
735       const char *s2 = strchr ( (char*)s+1, '>');
736
737       if (s2)
738         es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
739                                         NULL, NULL);
740     }
741   else if (*s == '(')
742     {
743       pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
744     }
745   else if (!((*s >= '0' && *s < '9')
746              || (*s >= 'A' && *s <= 'Z')
747              || (*s >= 'a' && *s <= 'z')))
748     es_fputs (_("[Error - invalid encoding]"), fp);
749   else
750     {
751       struct dn_array_s *dn = parse_dn (s);
752
753       if (!dn)
754         es_fputs (_("[Error - invalid DN]"), fp);
755       else 
756         {
757           print_dn_parts (NULL, fp, dn, 1);          
758           for (i=0; dn[i].key; i++)
759             {
760               xfree (dn[i].key);
761               xfree (dn[i].value);
762             }
763           xfree (dn);
764         }
765     }
766 }
767
768
769
770
771 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
772 /* A cookie structure used for the memory stream. */
773 struct format_name_cookie 
774 {
775   char *buffer;         /* Malloced buffer with the data to deliver. */
776   size_t size;          /* Allocated size of this buffer. */
777   size_t len;           /* strlen (buffer). */
778   int error;            /* system error code if any. */
779 };
780
781 /* The writer function for the memory stream. */
782 static my_funopen_hook_ret_t
783 format_name_writer (void *cookie, const char *buffer,
784                     my_funopen_hook_size_t size)
785 {
786   struct format_name_cookie *c = cookie;
787   char *p;
788
789   if (c->buffer)
790     p = xtryrealloc (c->buffer, c->size + size + 1);
791   else
792     p = xtrymalloc (size + 1);
793   if (!p)
794     {
795       c->error = errno;
796       xfree (c->buffer);
797       errno = c->error;
798       return (my_funopen_hook_ret_t)(-1);
799     }
800   c->buffer = p;
801   memcpy (p + c->len, buffer, size);
802   c->len += size;
803   p[c->len] = 0; /* Terminate string. */ 
804
805   return (my_funopen_hook_ret_t)size;
806 }
807 #endif /*HAVE_FOPENCOOKIE || HAVE_FUNOPEN*/
808
809
810 /* Format NAME which is expected to be in rfc2253 format into a better
811    human readable format. Caller must free the returned string.  NULL
812    is returned in case of an error.  With TRANSLATE set to true the
813    name will be translated to the native encoding.  Note that NAME is
814    internally always UTF-8 encoded. */
815 char *
816 gpgsm_format_name2 (const char *name, int translate)
817 {
818 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
819   FILE *fp;
820   struct format_name_cookie cookie;
821
822   memset (&cookie, 0, sizeof cookie);
823
824 #ifdef HAVE_FOPENCOOKIE
825   {
826     cookie_io_functions_t io = { NULL };
827     io.write = format_name_writer;
828     
829     fp = fopencookie (&cookie, "w", io);
830   }
831 #else /*!HAVE_FOPENCOOKIE*/
832   {
833     fp = funopen (&cookie, NULL, format_name_writer, NULL, NULL);
834   }
835 #endif /*!HAVE_FOPENCOOKIE*/
836   if (!fp)
837     {
838       int save_errno = errno;
839       log_error ("error creating memory stream: %s\n", strerror (errno));
840       errno = save_errno;
841       return NULL;
842     }
843   gpgsm_print_name2 (fp, name, translate);
844   fclose (fp);
845   if (cookie.error || !cookie.buffer)
846     {
847       xfree (cookie.buffer);
848       errno = cookie.error;
849       return NULL;
850     }
851   return cookie.buffer;
852 #else /* No fun - use the name verbatim. */
853   return xtrystrdup (name);
854 #endif /* No fun. */
855 }
856
857 char *
858 gpgsm_format_name (const char *name)
859 {
860   return gpgsm_format_name2 (name, 1);
861 }
862
863
864 /* Return fingerprint and a percent escaped name in a human readable
865    format suitable for status messages like GOODSIG.  May return NULL
866    on error (out of core). */
867 char *
868 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
869 {
870   char *fpr, *name, *p;
871   char *buffer;
872
873   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
874   if (!fpr)
875     return NULL;
876   
877   name = ksba_cert_get_subject (cert, 0);
878   if (!name)
879     {
880       xfree (fpr);
881       return NULL;
882     }
883
884   p = gpgsm_format_name2 (name, 0);
885   ksba_free (name);
886   name = p;
887   if (!name)
888     {
889       xfree (fpr);
890       return NULL;
891     }
892
893   buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
894   if (buffer)
895     {
896       const char *s;
897
898       p = stpcpy (stpcpy (buffer, fpr), " ");
899       for (s = name; *s; s++)
900         {
901           if (*s < ' ')
902             {
903               sprintf (p, "%%%02X", *(const unsigned char*)s);
904               p += 3;
905             }
906           else
907             *p++ = *s;
908         }
909       *p = 0;
910     }
911   xfree (fpr);
912   xfree (name);
913   return buffer;
914 }
915
916
917 /* Create a key description for the CERT, this may be passed to the
918    pinentry.  The caller must free the returned string. NULL may be
919    returned on error. */
920 char *
921 gpgsm_format_keydesc (ksba_cert_t cert)
922 {
923   int rc;
924   char *name, *subject, *buffer, *p;
925   const char *s;
926   ksba_isotime_t t;
927   char created[20];
928   char expires[20];
929   char *sn;
930   ksba_sexp_t sexp;
931   char *orig_codeset;
932
933   name = ksba_cert_get_subject (cert, 0);
934   subject = name? gpgsm_format_name2 (name, 0) : NULL;
935   ksba_free (name); name = NULL;
936
937   sexp = ksba_cert_get_serial (cert);
938   sn = sexp? gpgsm_format_serial (sexp) : NULL;
939   ksba_free (sexp);
940
941   ksba_cert_get_validity (cert, 0, t);
942   if (*t)
943     sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
944   else
945     *created = 0;
946   ksba_cert_get_validity (cert, 1, t);
947   if (*t)
948     sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
949   else
950     *expires = 0;
951
952   orig_codeset = i18n_switchto_utf8 ();
953
954   rc = asprintf (&name,
955                  _("Please enter the passphrase to unlock the"
956                    " secret key for the X.509 certificate:\n"
957                    "\"%s\"\n"
958                    "S/N %s, ID 0x%08lX,\n"
959                    "created %s, expires %s.\n" ),
960                  subject? subject:"?",
961                  sn? sn: "?",
962                  gpgsm_get_short_fingerprint (cert),
963                  created, expires);
964
965   i18n_switchback (orig_codeset);
966
967   if (rc < 0)
968     {
969       int save_errno = errno;
970       xfree (subject);
971       xfree (sn);
972       errno = save_errno;
973       return NULL;
974     }
975   
976   xfree (subject);
977   xfree (sn);
978
979   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
980   for (s=name; *s; s++)
981     {
982       if (*s < ' ' || *s == '+')
983         {
984           sprintf (p, "%%%02X", *(unsigned char *)s);
985           p += 3;
986         }
987       else if (*s == ' ')
988         *p++ = '+';
989       else
990         *p++ = *s;
991     }
992   *p = 0;
993   free (name); 
994
995   return buffer;
996 }
997