23cca7385ef43a1692b09b1312fc5807c915868f
[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                     print_utf8_buffer3 (stream, dn->value, strlen (dn->value),
508                                          "/");
509                   else
510                     es_write_sanitized (stream, dn->value, strlen (dn->value),
511                                         "/", NULL);
512                 }
513               else
514                 {
515                   fprintf (fp, "/%s=", dn->key);
516                   if (translate)
517                     print_sanitized_utf8_string (fp, dn->value, '/');
518                   else
519                     print_sanitized_string (fp, dn->value, '/');
520                 }
521             }
522           dn->done = 1;
523           if (dn > first_dn && dn[-1].multivalued)
524             {
525               dn--;
526               goto next;
527             }
528         }
529     }
530 }
531
532 /* Print all parts of a DN in a "standard" sequence.  We first print
533    all the known parts, followed by the uncommon ones */
534 static void
535 print_dn_parts (FILE *fp, estream_t stream,
536                 struct dn_array_s *dn, int translate)
537 {
538   const char *stdpart[] = {
539     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
540   };
541   int i;
542
543   for (i=0; stdpart[i]; i++)
544       print_dn_part (fp, stream, dn, stdpart[i], translate);
545
546   /* Now print the rest without any specific ordering */
547   for (; dn->key; dn++)
548     print_dn_part (fp, stream, dn, dn->key, translate);
549 }
550
551
552 /* Print the S-Expression in BUF, which has a valid length of BUFLEN,
553    as a human readable string in one line to FP. */
554 static void
555 pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
556 {
557   size_t len;
558   gcry_sexp_t sexp;
559   char *result, *p;
560
561   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
562     {
563       fputs (_("[Error - invalid encoding]"), fp);
564       return;
565     }
566   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
567   assert (len);
568   result = xtrymalloc (len);
569   if (!result)
570     {
571       fputs (_("[Error - out of core]"), fp);
572       gcry_sexp_release (sexp);
573       return;
574     }
575   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
576   assert (len);
577   for (p = result; len; len--, p++)
578     {
579       if (*p == '\n')
580         {
581           if (len > 1) /* Avoid printing the trailing LF. */
582             fputs ("\\n", fp);
583         }
584       else if (*p == '\r')
585         fputs ("\\r", fp);
586       else if (*p == '\v')
587         fputs ("\\v", fp);
588       else if (*p == '\t')
589         fputs ("\\t", fp);
590       else
591         putc (*p, fp);
592     }
593   xfree (result);
594   gcry_sexp_release (sexp);
595 }
596
597 /* Print the S-Expression in BUF to extended STREAM, which has a valid
598    length of BUFLEN, as a human readable string in one line to FP. */
599 static void
600 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
601 {
602   size_t len;
603   gcry_sexp_t sexp;
604   char *result, *p;
605
606   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
607     {
608       es_fputs (_("[Error - invalid encoding]"), fp);
609       return;
610     }
611   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
612   assert (len);
613   result = xtrymalloc (len);
614   if (!result)
615     {
616       es_fputs (_("[Error - out of core]"), fp);
617       gcry_sexp_release (sexp);
618       return;
619     }
620   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
621   assert (len);
622   for (p = result; len; len--, p++)
623     {
624       if (*p == '\n')
625         {
626           if (len > 1) /* Avoid printing the trailing LF. */
627             es_fputs ("\\n", fp);
628         }
629       else if (*p == '\r')
630         es_fputs ("\\r", fp);
631       else if (*p == '\v')
632         es_fputs ("\\v", fp);
633       else if (*p == '\t')
634         es_fputs ("\\t", fp);
635       else
636         es_putc (*p, fp);
637     }
638   xfree (result);
639   gcry_sexp_release (sexp);
640 }
641
642
643
644
645 void
646 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
647 {
648   const unsigned char *s = (const unsigned char *)name;
649   int i;
650
651   if (!s)
652     {
653       fputs (_("[Error - No name]"), fp);
654     }
655   else if (*s == '<')
656     {
657       const char *s2 = strchr ( (char*)s+1, '>');
658       if (s2)
659         {
660           if (translate)
661             print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
662           else
663             print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
664         }
665     }
666   else if (*s == '(')
667     {
668       pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
669     }
670   else if (!((*s >= '0' && *s < '9')
671              || (*s >= 'A' && *s <= 'Z')
672              || (*s >= 'a' && *s <= 'z')))
673     fputs (_("[Error - invalid encoding]"), fp);
674   else
675     {
676       struct dn_array_s *dn = parse_dn (s);
677       if (!dn)
678         fputs (_("[Error - invalid DN]"), fp);
679       else
680         {
681           print_dn_parts (fp, NULL, dn, translate);
682           for (i=0; dn[i].key; i++)
683             {
684               xfree (dn[i].key);
685               xfree (dn[i].value);
686             }
687           xfree (dn);
688         }
689     }
690 }
691
692
693 void
694 gpgsm_print_name (FILE *fp, const char *name)
695 {
696   gpgsm_print_name2 (fp, name, 1);
697 }
698
699
700 /* This is a variant of gpgsm_print_name sending it output to an estream. */
701 void
702 gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
703 {
704   const unsigned char *s = (const unsigned char *)name;
705   int i;
706
707   if (!s)
708     {
709       es_fputs (_("[Error - No name]"), fp);
710     }
711   else if (*s == '<')
712     {
713       const char *s2 = strchr ( (char*)s+1, '>');
714
715       if (s2)
716         {
717           if (translate)
718             print_utf8_buffer (fp, s + 1, s2 - (char*)s - 1);
719           else
720             es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
721         }
722     }
723   else if (*s == '(')
724     {
725       pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
726     }
727   else if (!((*s >= '0' && *s < '9')
728              || (*s >= 'A' && *s <= 'Z')
729              || (*s >= 'a' && *s <= 'z')))
730     es_fputs (_("[Error - invalid encoding]"), fp);
731   else
732     {
733       struct dn_array_s *dn = parse_dn (s);
734
735       if (!dn)
736         es_fputs (_("[Error - invalid DN]"), fp);
737       else
738         {
739           print_dn_parts (NULL, fp, dn, translate);
740           for (i=0; dn[i].key; i++)
741             {
742               xfree (dn[i].key);
743               xfree (dn[i].value);
744             }
745           xfree (dn);
746         }
747     }
748 }
749
750
751 void
752 gpgsm_es_print_name (estream_t fp, const char *name)
753 {
754   gpgsm_es_print_name2 (fp, name, 1);
755 }
756
757
758 /* A cookie structure used for the memory stream. */
759 struct format_name_cookie
760 {
761   char *buffer;         /* Malloced buffer with the data to deliver. */
762   size_t size;          /* Allocated size of this buffer. */
763   size_t len;           /* strlen (buffer). */
764   int error;            /* system error code if any. */
765 };
766
767 /* The writer function for the memory stream. */
768 static ssize_t
769 format_name_writer (void *cookie, const void *buffer, size_t size)
770 {
771   struct format_name_cookie *c = cookie;
772   char *p;
773
774   if (!c->buffer)
775     {
776       p = xtrymalloc (size + 1 + 1);
777       if (p)
778         {
779           c->size = size + 1;
780           c->buffer = p;
781           c->len = 0;
782         }
783     }
784   else if (c->len + size < c->len)
785     {
786       p = NULL;
787       gpg_err_set_errno (ENOMEM);
788     }
789   else if (c->size < c->len + size)
790     {
791       p = xtryrealloc (c->buffer, c->len + size + 1);
792       if (p)
793         {
794           c->size = c->len + size;
795           c->buffer = p;
796         }
797     }
798   else
799     p = c->buffer;
800   if (!p)
801     {
802       c->error = errno;
803       xfree (c->buffer);
804       c->buffer = NULL;
805       gpg_err_set_errno (c->error);
806       return -1;
807     }
808   memcpy (p + c->len, buffer, size);
809   c->len += size;
810   p[c->len] = 0; /* Terminate string. */
811
812   return (ssize_t)size;
813 }
814
815
816 /* Format NAME which is expected to be in rfc2253 format into a better
817    human readable format. Caller must free the returned string.  NULL
818    is returned in case of an error.  With TRANSLATE set to true the
819    name will be translated to the native encoding.  Note that NAME is
820    internally always UTF-8 encoded. */
821 char *
822 gpgsm_format_name2 (const char *name, int translate)
823 {
824   estream_t fp;
825   struct format_name_cookie cookie;
826   es_cookie_io_functions_t io = { NULL };
827
828   memset (&cookie, 0, sizeof cookie);
829
830   io.func_write = format_name_writer;
831   fp = es_fopencookie (&cookie, "w", io);
832   if (!fp)
833     {
834       int save_errno = errno;
835       log_error ("error creating memory stream: %s\n", strerror (save_errno));
836       gpg_err_set_errno (save_errno);
837       return NULL;
838     }
839   gpgsm_es_print_name2 (fp, name, translate);
840   es_fclose (fp);
841   if (cookie.error || !cookie.buffer)
842     {
843       xfree (cookie.buffer);
844       gpg_err_set_errno (cookie.error);
845       return NULL;
846     }
847   return cookie.buffer;
848 }
849
850
851 char *
852 gpgsm_format_name (const char *name)
853 {
854   return gpgsm_format_name2 (name, 1);
855 }
856
857
858 /* Return fingerprint and a percent escaped name in a human readable
859    format suitable for status messages like GOODSIG.  May return NULL
860    on error (out of core). */
861 char *
862 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
863 {
864   char *fpr, *name, *p;
865   char *buffer;
866
867   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
868   if (!fpr)
869     return NULL;
870
871   name = ksba_cert_get_subject (cert, 0);
872   if (!name)
873     {
874       xfree (fpr);
875       return NULL;
876     }
877
878   p = gpgsm_format_name2 (name, 0);
879   ksba_free (name);
880   name = p;
881   if (!name)
882     {
883       xfree (fpr);
884       return NULL;
885     }
886
887   buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
888   if (buffer)
889     {
890       const char *s;
891
892       p = stpcpy (stpcpy (buffer, fpr), " ");
893       for (s = name; *s; s++)
894         {
895           if (*s < ' ')
896             {
897               sprintf (p, "%%%02X", *(const unsigned char*)s);
898               p += 3;
899             }
900           else
901             *p++ = *s;
902         }
903       *p = 0;
904     }
905   xfree (fpr);
906   xfree (name);
907   return buffer;
908 }
909
910
911 /* Create a key description for the CERT, this may be passed to the
912    pinentry.  The caller must free the returned string.  NULL may be
913    returned on error. */
914 char *
915 gpgsm_format_keydesc (ksba_cert_t cert)
916 {
917   char *name, *subject, *buffer;
918   ksba_isotime_t t;
919   char created[20];
920   char expires[20];
921   char *sn;
922   ksba_sexp_t sexp;
923   char *orig_codeset;
924
925   name = ksba_cert_get_subject (cert, 0);
926   subject = name? gpgsm_format_name2 (name, 0) : NULL;
927   ksba_free (name); name = NULL;
928
929   sexp = ksba_cert_get_serial (cert);
930   sn = sexp? gpgsm_format_serial (sexp) : NULL;
931   ksba_free (sexp);
932
933   ksba_cert_get_validity (cert, 0, t);
934   if (*t)
935     sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
936   else
937     *created = 0;
938   ksba_cert_get_validity (cert, 1, t);
939   if (*t)
940     sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
941   else
942     *expires = 0;
943
944   orig_codeset = i18n_switchto_utf8 ();
945
946   name = xtryasprintf (_("Please enter the passphrase to unlock the"
947                          " secret key for the X.509 certificate:\n"
948                          "\"%s\"\n"
949                          "S/N %s, ID 0x%08lX,\n"
950                          "created %s, expires %s.\n" ),
951                        subject? subject:"?",
952                        sn? sn: "?",
953                        gpgsm_get_short_fingerprint (cert, NULL),
954                        created, expires);
955
956   i18n_switchback (orig_codeset);
957
958   if (!name)
959     {
960       xfree (subject);
961       xfree (sn);
962       return NULL;
963     }
964
965   xfree (subject);
966   xfree (sn);
967
968   buffer = percent_plus_escape (name);
969   xfree (name);
970   return buffer;
971 }