2005-04-11 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / sm / certdump.c
blobcdf4edcc1db6d788153ba521858584595f47e272
1 /* certdump.c - Dump a certificate for debugging
2 * Copyright (C) 2001, 2004 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <assert.h>
29 #ifdef HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32 #ifdef HAVE_LANGINFO_CODESET
33 #include <langinfo.h>
34 #endif
36 #include "gpgsm.h"
37 #include <gcrypt.h>
38 #include <ksba.h>
40 #include "keydb.h"
41 #include "i18n.h"
43 struct dn_array_s {
44 char *key;
45 char *value;
46 int multivalued;
47 int done;
51 /* print the first element of an S-Expression */
52 void
53 gpgsm_print_serial (FILE *fp, ksba_const_sexp_t p)
55 unsigned long n;
56 char *endp;
58 if (!p)
59 fputs (_("none"), fp);
60 else if (*p != '(')
61 fputs ("[Internal error - not an S-expression]", fp);
62 else
64 p++;
65 n = strtoul (p, &endp, 10);
66 p = endp;
67 if (*p!=':')
68 fputs ("[Internal Error - invalid S-expression]", fp);
69 else
71 for (p++; n; n--, p++)
72 fprintf (fp, "%02X", *p);
78 void
79 gpgsm_dump_serial (ksba_const_sexp_t p)
81 unsigned long n;
82 char *endp;
84 if (!p)
85 log_printf ("none");
86 else if (*p != '(')
87 log_printf ("ERROR - not an S-expression");
88 else
90 p++;
91 n = strtoul (p, &endp, 10);
92 p = endp;
93 if (*p!=':')
94 log_printf ("ERROR - invalid S-expression");
95 else
97 for (p++; n; n--, p++)
98 log_printf ("%02X", *p);
104 char *
105 gpgsm_format_serial (ksba_const_sexp_t p)
107 unsigned long n;
108 char *endp;
109 char *buffer;
110 int i;
112 if (!p)
113 return NULL;
115 if (*p != '(')
116 BUG (); /* Not a valid S-expression. */
118 p++;
119 n = strtoul (p, &endp, 10);
120 p = endp;
121 if (*p!=':')
122 BUG (); /* Not a valid S-expression. */
123 p++;
125 buffer = xtrymalloc (n*2+1);
126 if (buffer)
128 for (i=0; n; n--, p++, i+=2)
129 sprintf (buffer+i, "%02X", *(unsigned char *)p);
130 buffer[i] = 0;
132 return buffer;
138 void
139 gpgsm_print_time (FILE *fp, ksba_isotime_t t)
141 if (!t || !*t)
142 fputs (_("none"), fp);
143 else
144 fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13);
147 void
148 gpgsm_dump_time (ksba_isotime_t t)
150 if (!t || !*t)
151 log_printf (_("[none]"));
152 else
153 log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
154 t, t+4, t+6, t+9, t+11, t+13);
160 void
161 gpgsm_dump_string (const char *string)
164 if (!string)
165 log_printf ("[error]");
166 else
168 const unsigned char *s;
170 for (s=string; *s; s++)
172 if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
173 break;
175 if (!*s && *string != '[')
176 log_printf ("%s", string);
177 else
179 log_printf ( "[ ");
180 log_printhex (NULL, string, strlen (string));
181 log_printf ( " ]");
187 /* This simple dump function is mainly used for debugging purposes. */
188 void
189 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
191 ksba_sexp_t sexp;
192 unsigned char *p;
193 char *dn;
194 ksba_isotime_t t;
196 log_debug ("BEGIN Certificate `%s':\n", text? text:"");
197 if (cert)
199 sexp = ksba_cert_get_serial (cert);
200 log_debug (" serial: ");
201 gpgsm_dump_serial (sexp);
202 ksba_free (sexp);
203 log_printf ("\n");
205 ksba_cert_get_validity (cert, 0, t);
206 log_debug (" notBefore: ");
207 gpgsm_dump_time (t);
208 log_printf ("\n");
209 ksba_cert_get_validity (cert, 1, t);
210 log_debug (" notAfter: ");
211 gpgsm_dump_time (t);
212 log_printf ("\n");
214 dn = ksba_cert_get_issuer (cert, 0);
215 log_debug (" issuer: ");
216 gpgsm_dump_string (dn);
217 ksba_free (dn);
218 log_printf ("\n");
220 dn = ksba_cert_get_subject (cert, 0);
221 log_debug (" subject: ");
222 gpgsm_dump_string (dn);
223 ksba_free (dn);
224 log_printf ("\n");
226 log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
228 p = gpgsm_get_fingerprint_string (cert, 0);
229 log_debug (" SHA1 Fingerprint: %s\n", p);
230 xfree (p);
232 log_debug ("END Certificate\n");
237 /* helper for the rfc2253 string parser */
238 static const unsigned char *
239 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
241 static struct {
242 const char *label;
243 const char *oid;
244 } label_map[] = {
245 /* Warning: When adding new labels, make sure that the buffer
246 below we be allocated large enough. */
247 {"EMail", "1.2.840.113549.1.9.1" },
248 {"T", "2.5.4.12" },
249 {"GN", "2.5.4.42" },
250 {"SN", "2.5.4.4" },
251 {"NameDistinguisher", "0.2.262.1.10.7.20"},
252 {"ADDR", "2.5.4.16" },
253 {"BC", "2.5.4.15" },
254 {"D", "2.5.4.13" },
255 {"PostalCode", "2.5.4.17" },
256 {"Pseudo", "2.5.4.65" },
257 {"SerialNumber", "2.5.4.5" },
258 {NULL, NULL}
260 const unsigned char *s, *s1;
261 size_t n;
262 unsigned char *p;
263 int i;
265 /* Parse attributeType */
266 for (s = string+1; *s && *s != '='; s++)
268 if (!*s)
269 return NULL; /* error */
270 n = s - string;
271 if (!n)
272 return NULL; /* empty key */
274 /* We need to allocate a few bytes more due to the possible mapping
275 from the shorter OID to the longer label. */
276 array->key = p = xtrymalloc (n+10);
277 if (!array->key)
278 return NULL;
279 memcpy (p, string, n);
280 p[n] = 0;
281 trim_trailing_spaces (p);
283 if (digitp (p))
285 for (i=0; label_map[i].label; i++ )
286 if ( !strcmp (p, label_map[i].oid) )
288 strcpy (p, label_map[i].label);
289 break;
292 string = s + 1;
294 if (*string == '#')
295 { /* hexstring */
296 string++;
297 for (s=string; hexdigitp (s); s++)
298 s++;
299 n = s - string;
300 if (!n || (n & 1))
301 return NULL; /* Empty or odd number of digits. */
302 n /= 2;
303 array->value = p = xtrymalloc (n+1);
304 if (!p)
305 return NULL;
306 for (s1=string; n; s1 += 2, n--, p++)
308 *p = xtoi_2 (s1);
309 if (!*p)
310 *p = 0x01; /* Better print a wrong value than truncating
311 the string. */
313 *p = 0;
315 else
316 { /* regular v3 quoted string */
317 for (n=0, s=string; *s; s++)
319 if (*s == '\\')
320 { /* pair */
321 s++;
322 if (*s == ',' || *s == '=' || *s == '+'
323 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
324 || *s == '\\' || *s == '\"' || *s == ' ')
325 n++;
326 else if (hexdigitp (s) && hexdigitp (s+1))
328 s++;
329 n++;
331 else
332 return NULL; /* invalid escape sequence */
334 else if (*s == '\"')
335 return NULL; /* invalid encoding */
336 else if (*s == ',' || *s == '=' || *s == '+'
337 || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
338 break;
339 else
340 n++;
343 array->value = p = xtrymalloc (n+1);
344 if (!p)
345 return NULL;
346 for (s=string; n; s++, n--)
348 if (*s == '\\')
350 s++;
351 if (hexdigitp (s))
353 *p++ = xtoi_2 (s);
354 s++;
356 else
357 *p++ = *s;
359 else
360 *p++ = *s;
362 *p = 0;
364 return s;
368 /* Parse a DN and return an array-ized one. This is not a validating
369 parser and it does not support any old-stylish syntax; KSBA is
370 expected to return only rfc2253 compatible strings. */
371 static struct dn_array_s *
372 parse_dn (const unsigned char *string)
374 struct dn_array_s *array;
375 size_t arrayidx, arraysize;
376 int i;
378 arraysize = 7; /* C,ST,L,O,OU,CN,email */
379 arrayidx = 0;
380 array = xtrymalloc ((arraysize+1) * sizeof *array);
381 if (!array)
382 return NULL;
383 while (*string)
385 while (*string == ' ')
386 string++;
387 if (!*string)
388 break; /* ready */
389 if (arrayidx >= arraysize)
391 struct dn_array_s *a2;
393 arraysize += 5;
394 a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
395 if (!a2)
396 goto failure;
397 array = a2;
399 array[arrayidx].key = NULL;
400 array[arrayidx].value = NULL;
401 string = parse_dn_part (array+arrayidx, string);
402 if (!string)
403 goto failure;
404 while (*string == ' ')
405 string++;
406 array[arrayidx].multivalued = (*string == '+');
407 array[arrayidx].done = 0;
408 arrayidx++;
409 if (*string && *string != ',' && *string != ';' && *string != '+')
410 goto failure; /* invalid delimiter */
411 if (*string)
412 string++;
414 array[arrayidx].key = NULL;
415 array[arrayidx].value = NULL;
416 return array;
418 failure:
419 for (i=0; i < arrayidx; i++)
421 xfree (array[i].key);
422 xfree (array[i].value);
424 xfree (array);
425 return NULL;
429 static void
430 print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key, int translate)
432 struct dn_array_s *first_dn = dn;
434 for (; dn->key; dn++)
436 if (!dn->done && !strcmp (dn->key, key))
438 /* Forward to the last multi-valued RDN, so that we can
439 print them all in reverse in the correct order. Note
440 that this overrides the the standard sequence but that
441 seems to a reasonable thing to do with multi-valued
442 RDNs. */
443 while (dn->multivalued && dn[1].key)
444 dn++;
445 next:
446 if (!dn->done && dn->value && *dn->value)
448 fprintf (fp, "/%s=", dn->key);
449 if (translate)
450 print_sanitized_utf8_string (fp, dn->value, '/');
451 else
452 print_sanitized_string (fp, dn->value, '/');
454 dn->done = 1;
455 if (dn > first_dn && dn[-1].multivalued)
457 dn--;
458 goto next;
464 /* Print all parts of a DN in a "standard" sequence. We first print
465 all the known parts, followed by the uncommon ones */
466 static void
467 print_dn_parts (FILE *fp, struct dn_array_s *dn, int translate)
469 const char *stdpart[] = {
470 "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
472 int i;
474 for (i=0; stdpart[i]; i++)
475 print_dn_part (fp, dn, stdpart[i], translate);
477 /* Now print the rest without any specific ordering */
478 for (; dn->key; dn++)
479 print_dn_part (fp, dn, dn->key, translate);
484 void
485 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
487 const unsigned char *s;
488 int i;
490 s = name;
491 if (!s)
493 fputs (_("[Error - No name]"), fp);
495 else if (*s == '<')
497 const unsigned char *s2 = strchr (s+1, '>');
498 if (s2)
500 if (translate)
501 print_sanitized_utf8_buffer (fp, s + 1, s2 - s - 1, 0);
502 else
503 print_sanitized_buffer (fp, s + 1, s2 - s - 1, 0);
506 else if (*s == '(')
507 fputs (_("[Error - unknown encoding]"), fp);
508 else if (!((*s >= '0' && *s < '9')
509 || (*s >= 'A' && *s <= 'Z')
510 || (*s >= 'a' && *s <= 'z')))
511 fputs (_("[Error - invalid encoding]"), fp);
512 else
514 struct dn_array_s *dn = parse_dn (s);
515 if (!dn)
516 fputs (_("[Error - invalid DN]"), fp);
517 else
519 print_dn_parts (fp, dn, translate);
520 for (i=0; dn[i].key; i++)
522 xfree (dn[i].key);
523 xfree (dn[i].value);
525 xfree (dn);
531 void
532 gpgsm_print_name (FILE *fp, const char *name)
534 gpgsm_print_name2 (fp, name, 1);
538 /* A cookie structure used for the memory stream. */
539 struct format_name_cookie
541 char *buffer; /* Malloced buffer with the data to deliver. */
542 size_t size; /* Allocated size of this buffer. */
543 size_t len; /* strlen (buffer). */
544 int error; /* system error code if any. */
547 /* The writer function for the memory stream. */
548 static int
549 format_name_writer (void *cookie, const char *buffer, size_t size)
551 struct format_name_cookie *c = cookie;
552 char *p;
554 if (c->buffer)
555 p = xtryrealloc (c->buffer, c->size + size + 1);
556 else
557 p = xtrymalloc (size + 1);
558 if (!p)
560 c->error = errno;
561 xfree (c->buffer);
562 errno = c->error;
563 return -1;
565 c->buffer = p;
566 memcpy (p + c->len, buffer, size);
567 c->len += size;
568 p[c->len] = 0; /* Terminate string. */
570 return size;
573 /* Format NAME which is expected to be in rfc2253 format into a better
574 human readable format. Caller must free the returned string. NULL
575 is returned in case of an error. With TRANSLATE set to true the
576 name will be translated to the native encodig. Note that NAME is
577 internally always UTF-8 encoded. */
578 char *
579 gpgsm_format_name2 (const char *name, int translate)
581 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
582 FILE *fp;
583 struct format_name_cookie cookie;
585 memset (&cookie, 0, sizeof cookie);
587 #ifdef HAVE_FOPENCOOKIE
589 cookie_io_functions_t io = { NULL };
590 io.write = format_name_writer;
592 fp = fopencookie (&cookie, "w", io);
594 #else /*!HAVE_FOPENCOOKIE*/
596 fp = funopen (&cookie, NULL, format_name_writer, NULL, NULL);
598 #endif /*!HAVE_FOPENCOOKIE*/
599 if (!fp)
601 int save_errno = errno;
602 log_error ("error creating memory stream: %s\n", strerror (errno));
603 errno = save_errno;
604 return NULL;
606 gpgsm_print_name2 (fp, name, translate);
607 fclose (fp);
608 if (cookie.error || !cookie.buffer)
610 xfree (cookie.buffer);
611 errno = cookie.error;
612 return NULL;
614 return cookie.buffer;
615 #else /* No fun - use the name verbatim. */
616 return xtrystrdup (name);
617 #endif /* No fun. */
620 char *
621 gpgsm_format_name (const char *name)
623 return gpgsm_format_name2 (name, 1);
627 /* Create a key description for the CERT, this may be passed to the
628 pinentry. The caller must free the returned string. NULL may be
629 returned on error. */
630 char *
631 gpgsm_format_keydesc (ksba_cert_t cert)
633 int rc;
634 char *name, *subject, *buffer, *p;
635 const char *s;
636 ksba_isotime_t t;
637 char created[20];
638 char *sn;
639 ksba_sexp_t sexp;
640 char *orig_codeset = NULL;
642 name = ksba_cert_get_subject (cert, 0);
643 subject = name? gpgsm_format_name2 (name, 0) : NULL;
644 ksba_free (name); name = NULL;
646 sexp = ksba_cert_get_serial (cert);
647 sn = sexp? gpgsm_format_serial (sexp) : NULL;
648 ksba_free (sexp);
650 ksba_cert_get_validity (cert, 0, t);
651 if (t && *t)
652 sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
653 else
654 *created = 0;
657 #ifdef ENABLE_NLS
658 /* The Assuan agent protol requires us to transmit utf-8 strings */
659 orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
660 #ifdef HAVE_LANGINFO_CODESET
661 if (!orig_codeset)
662 orig_codeset = nl_langinfo (CODESET);
663 #endif
664 if (orig_codeset)
665 { /* We only switch when we are able to restore the codeset later.
666 Note that bind_textdomain_codeset does only return on memory
667 errors but not if a codeset is not available. Thus we don't
668 bother printing a diagnostic here. */
669 orig_codeset = xstrdup (orig_codeset);
670 if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
671 orig_codeset = NULL;
673 #endif
676 rc = asprintf (&name,
677 _("Please enter the passphrase to unlock the"
678 " secret key for:\n"
679 "\"%s\"\n"
680 "S/N %s, ID %08lX, created %s" ),
681 subject? subject:"?",
682 sn? sn: "?",
683 gpgsm_get_short_fingerprint (cert),
684 created);
686 #ifdef ENABLE_NLS
687 if (orig_codeset)
688 bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
689 #endif
690 xfree (orig_codeset);
692 if (rc < 0)
694 int save_errno = errno;
695 xfree (subject);
696 xfree (sn);
697 errno = save_errno;
698 return NULL;
701 xfree (subject);
702 xfree (sn);
704 buffer = p = xtrymalloc (strlen (name) * 3 + 1);
705 for (s=name; *s; s++)
707 if (*s < ' ' || *s == '+')
709 sprintf (p, "%%%02X", *(unsigned char *)s);
710 p += 3;
712 else if (*s == ' ')
713 *p++ = '+';
714 else
715 *p++ = *s;
717 *p = 0;
718 free (name);
720 return buffer;