1 /* certdump.c - Dump a certificate for debugging
2 * Copyright (C) 2001, 2004, 2007 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 3 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, see <https://www.gnu.org/licenses/>.
31 #ifdef HAVE_LANGINFO_CODESET
51 /* Print the first element of an S-Expression. */
53 gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
55 const char *p = (const char *)sn;
60 es_fputs (_("none"), fp);
62 es_fputs ("[Internal error - not an S-expression]", fp);
66 n = strtoul (p, &endp, 10);
69 es_fputs ("[Internal Error - invalid S-expression]", fp);
71 es_write_hexstring (fp, p, n, 0, NULL);
76 /* Dump the serial number or any other simple S-expression. */
78 gpgsm_dump_serial (ksba_const_sexp_t sn)
80 const char *p = (const char *)sn;
87 log_printf ("ERROR - not an S-expression");
91 n = strtoul (p, &endp, 10);
94 log_printf ("ERROR - invalid S-expression");
97 for (p++; n; n--, p++)
98 log_printf ("%02X", *(const unsigned char *)p);
105 gpgsm_format_serial (ksba_const_sexp_t sn)
107 const char *p = (const char *)sn;
117 BUG (); /* Not a valid S-expression. */
120 n = strtoul (p, &endp, 10);
123 BUG (); /* Not a valid S-expression. */
126 buffer = xtrymalloc (n*2+1);
129 for (i=0; n; n--, p++, i+=2)
130 sprintf (buffer+i, "%02X", *(unsigned char *)p);
140 gpgsm_print_time (estream_t fp, ksba_isotime_t t)
143 es_fputs (_("none"), fp);
145 es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
146 t, t+4, t+6, t+9, t+11, t+13);
151 gpgsm_dump_string (const char *string)
155 log_printf ("[error]");
158 const unsigned char *s;
160 for (s=(const unsigned char*)string; *s; s++)
162 if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
165 if (!*s && *string != '[')
166 log_printf ("%s", string);
170 log_printhex (NULL, string, strlen (string));
177 /* This simple dump function is mainly used for debugging purposes. */
179 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
186 log_debug ("BEGIN Certificate '%s':\n", text? text:"");
189 sexp = ksba_cert_get_serial (cert);
190 log_debug (" serial: ");
191 gpgsm_dump_serial (sexp);
195 ksba_cert_get_validity (cert, 0, t);
196 log_debug (" notBefore: ");
199 ksba_cert_get_validity (cert, 1, t);
200 log_debug (" notAfter: ");
204 dn = ksba_cert_get_issuer (cert, 0);
205 log_debug (" issuer: ");
206 gpgsm_dump_string (dn);
210 dn = ksba_cert_get_subject (cert, 0);
211 log_debug (" subject: ");
212 gpgsm_dump_string (dn);
216 log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
218 p = gpgsm_get_fingerprint_string (cert, 0);
219 log_debug (" SHA1 Fingerprint: %s\n", p);
222 log_debug ("END Certificate\n");
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
231 gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
237 p1 = gpgsm_format_serial (sn);
239 p = xtrystrdup ("[invalid SN]");
242 p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
246 strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
252 p = xtrystrdup ("[invalid SN/issuer]");
257 /* Log the certificate's name in "#SN/ISSUERDN" format along with
260 gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
262 log_info ("%s", text? text:"certificate" );
268 p = ksba_cert_get_issuer (cert, 0);
269 sn = ksba_cert_get_serial (cert);
273 gpgsm_dump_serial (sn);
275 gpgsm_dump_string (p);
278 log_printf (" [invalid]");
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)
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" },
304 {"NameDistinguisher", "0.2.262.1.10.7.20"},
305 {"ADDR", "2.5.4.16" },
308 {"PostalCode", "2.5.4.17" },
309 {"Pseudo", "2.5.4.65" },
310 {"SerialNumber", "2.5.4.5" },
313 const unsigned char *s, *s1;
318 /* Parse attributeType */
319 for (s = string+1; *s && *s != '='; s++)
322 return NULL; /* error */
325 return NULL; /* empty key */
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);
332 memcpy (p, string, n);
334 trim_trailing_spaces (p);
338 for (i=0; label_map[i].label; i++ )
339 if ( !strcmp (p, label_map[i].oid) )
341 strcpy (p, label_map[i].label);
350 for (s=string; hexdigitp (s); s++)
354 return NULL; /* Empty or odd number of digits. */
356 array->value = p = xtrymalloc (n+1);
359 for (s1=string; n; s1 += 2, n--, p++)
361 *(unsigned char *)p = xtoi_2 (s1);
363 *p = 0x01; /* Better print a wrong value than truncating
369 { /* regular v3 quoted string */
370 for (n=0, s=string; *s; s++)
375 if (*s == ',' || *s == '=' || *s == '+'
376 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
377 || *s == '\\' || *s == '\"' || *s == ' ')
379 else if (hexdigitp (s) && hexdigitp (s+1))
385 return NULL; /* invalid escape sequence */
388 return NULL; /* invalid encoding */
389 else if (*s == ',' || *s == '=' || *s == '+'
390 || *s == '<' || *s == '>' || *s == ';' )
396 array->value = p = xtrymalloc (n+1);
399 for (s=string; n; s++, n--)
406 *(unsigned char *)p++ = xtoi_2 (s);
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)
427 struct dn_array_s *array;
428 size_t arrayidx, arraysize;
431 arraysize = 7; /* C,ST,L,O,OU,CN,email */
433 array = xtrymalloc ((arraysize+1) * sizeof *array);
438 while (*string == ' ')
442 if (arrayidx >= arraysize)
444 struct dn_array_s *a2;
447 a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
452 array[arrayidx].key = NULL;
453 array[arrayidx].value = NULL;
454 string = parse_dn_part (array+arrayidx, string);
457 while (*string == ' ')
459 array[arrayidx].multivalued = (*string == '+');
460 array[arrayidx].done = 0;
462 if (*string && *string != ',' && *string != ';' && *string != '+')
463 goto failure; /* invalid delimiter */
467 array[arrayidx].key = NULL;
468 array[arrayidx].value = NULL;
472 for (i=0; i < arrayidx; i++)
474 xfree (array[i].key);
475 xfree (array[i].value);
482 /* Print a DN part to STREAM. */
484 print_dn_part (estream_t stream,
485 struct dn_array_s *dn, const char *key, int translate)
487 struct dn_array_s *first_dn = dn;
489 for (; dn->key; dn++)
491 if (!dn->done && !strcmp (dn->key, key))
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
498 while (dn->multivalued && dn[1].key)
501 if (!dn->done && dn->value && *dn->value)
503 es_fprintf (stream, "/%s=", dn->key);
505 print_utf8_buffer3 (stream, dn->value, strlen (dn->value),
508 es_write_sanitized (stream, dn->value, strlen (dn->value),
512 if (dn > first_dn && dn[-1].multivalued)
521 /* Print all parts of a DN in a "standard" sequence. We first print
522 all the known parts, followed by the uncommon ones */
524 print_dn_parts (estream_t stream,
525 struct dn_array_s *dn, int translate)
527 const char *stdpart[] = {
528 "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
532 for (i=0; stdpart[i]; i++)
533 print_dn_part (stream, dn, stdpart[i], translate);
535 /* Now print the rest without any specific ordering */
536 for (; dn->key; dn++)
537 print_dn_part (stream, dn, dn->key, translate);
541 /* Print the S-Expression in BUF to extended STREAM, which has a valid
542 length of BUFLEN, as a human readable string in one line to FP. */
544 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
550 if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
552 es_fputs (_("[Error - invalid encoding]"), fp);
555 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
557 result = xtrymalloc (len);
560 es_fputs (_("[Error - out of core]"), fp);
561 gcry_sexp_release (sexp);
564 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
566 for (p = result; len; len--, p++)
570 if (len > 1) /* Avoid printing the trailing LF. */
571 es_fputs ("\\n", fp);
574 es_fputs ("\\r", fp);
576 es_fputs ("\\v", fp);
578 es_fputs ("\\t", fp);
583 gcry_sexp_release (sexp);
587 /* This is a variant of gpgsm_print_name sending it output to an estream. */
589 gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
591 const unsigned char *s = (const unsigned char *)name;
596 es_fputs (_("[Error - No name]"), fp);
600 const char *s2 = strchr ( (char*)s+1, '>');
605 print_utf8_buffer (fp, s + 1, s2 - (char*)s - 1);
607 es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
612 pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
614 else if (!((*s >= '0' && *s < '9')
615 || (*s >= 'A' && *s <= 'Z')
616 || (*s >= 'a' && *s <= 'z')))
617 es_fputs (_("[Error - invalid encoding]"), fp);
620 struct dn_array_s *dn = parse_dn (s);
623 es_fputs (_("[Error - invalid DN]"), fp);
626 print_dn_parts (fp, dn, translate);
627 for (i=0; dn[i].key; i++)
639 gpgsm_es_print_name (estream_t fp, const char *name)
641 gpgsm_es_print_name2 (fp, name, 1);
645 /* A cookie structure used for the memory stream. */
646 struct format_name_cookie
648 char *buffer; /* Malloced buffer with the data to deliver. */
649 size_t size; /* Allocated size of this buffer. */
650 size_t len; /* strlen (buffer). */
651 int error; /* system error code if any. */
654 /* The writer function for the memory stream. */
656 format_name_writer (void *cookie, const void *buffer, size_t size)
658 struct format_name_cookie *c = cookie;
663 p = xtrymalloc (size + 1 + 1);
671 else if (c->len + size < c->len)
674 gpg_err_set_errno (ENOMEM);
676 else if (c->size < c->len + size)
678 p = xtryrealloc (c->buffer, c->len + size + 1);
681 c->size = c->len + size;
692 gpg_err_set_errno (c->error);
695 memcpy (p + c->len, buffer, size);
697 p[c->len] = 0; /* Terminate string. */
699 return (gpgrt_ssize_t)size;
703 /* Format NAME which is expected to be in rfc2253 format into a better
704 human readable format. Caller must free the returned string. NULL
705 is returned in case of an error. With TRANSLATE set to true the
706 name will be translated to the native encoding. Note that NAME is
707 internally always UTF-8 encoded. */
709 gpgsm_format_name2 (const char *name, int translate)
712 struct format_name_cookie cookie;
713 es_cookie_io_functions_t io = { NULL };
715 memset (&cookie, 0, sizeof cookie);
717 io.func_write = format_name_writer;
718 fp = es_fopencookie (&cookie, "w", io);
721 int save_errno = errno;
722 log_error ("error creating memory stream: %s\n", strerror (save_errno));
723 gpg_err_set_errno (save_errno);
726 gpgsm_es_print_name2 (fp, name, translate);
728 if (cookie.error || !cookie.buffer)
730 xfree (cookie.buffer);
731 gpg_err_set_errno (cookie.error);
734 return cookie.buffer;
739 gpgsm_format_name (const char *name)
741 return gpgsm_format_name2 (name, 1);
745 /* Return fingerprint and a percent escaped name in a human readable
746 format suitable for status messages like GOODSIG. May return NULL
747 on error (out of core). */
749 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
751 char *fpr, *name, *p;
754 fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
758 name = ksba_cert_get_subject (cert, 0);
765 p = gpgsm_format_name2 (name, 0);
774 buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
779 p = stpcpy (stpcpy (buffer, fpr), " ");
780 for (s = name; *s; s++)
784 sprintf (p, "%%%02X", *(const unsigned char*)s);
798 /* Create a key description for the CERT, this may be passed to the
799 pinentry. The caller must free the returned string. NULL may be
800 returned on error. */
802 gpgsm_format_keydesc (ksba_cert_t cert)
804 char *name, *subject, *buffer;
812 name = ksba_cert_get_subject (cert, 0);
813 subject = name? gpgsm_format_name2 (name, 0) : NULL;
814 ksba_free (name); name = NULL;
816 sexp = ksba_cert_get_serial (cert);
817 sn = sexp? gpgsm_format_serial (sexp) : NULL;
820 ksba_cert_get_validity (cert, 0, t);
822 sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
825 ksba_cert_get_validity (cert, 1, t);
827 sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
831 orig_codeset = i18n_switchto_utf8 ();
833 name = xtryasprintf (_("Please enter the passphrase to unlock the"
834 " secret key for the X.509 certificate:\n"
836 "S/N %s, ID 0x%08lX,\n"
837 "created %s, expires %s.\n" ),
838 subject? subject:"?",
840 gpgsm_get_short_fingerprint (cert, NULL),
843 i18n_switchback (orig_codeset);
855 buffer = percent_plus_escape (name);