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