(parse_dn_part): Pretty print the nameDistinguisher OID.
[gnupg.git] / sm / certdump.c
1 /* certdump.c - Dump a certificate for debugging
2  *      Copyright (C) 2001 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
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
30 #include "gpgsm.h"
31 #include <gcrypt.h>
32 #include <ksba.h>
33
34 #include "keydb.h"
35 #include "i18n.h"
36
37 struct dn_array_s {
38   char *key;
39   char *value;
40   int   multivalued;
41   int   done;
42 };
43
44
45 /* print the first element of an S-Expression */
46 void
47 gpgsm_print_serial (FILE *fp, ksba_const_sexp_t p)
48 {
49   unsigned long n;
50   char *endp;
51
52   if (!p)
53     fputs (_("none"), fp);
54   else if (*p != '(')
55     fputs ("[Internal error - not an S-expression]", fp);
56   else
57     {
58       p++;
59       n = strtoul (p, &endp, 10);
60       p = endp;
61       if (*p!=':')
62         fputs ("[Internal Error - invalid S-expression]", fp);
63       else
64         {
65           for (p++; n; n--, p++)
66             fprintf (fp, "%02X", *p);
67         }
68     }
69 }
70
71
72 void
73 gpgsm_dump_serial (ksba_const_sexp_t p)
74 {
75   unsigned long n;
76   char *endp;
77
78   if (!p)
79     log_printf ("none");
80   else if (*p != '(')
81     log_printf ("ERROR - not an S-expression");
82   else
83     {
84       p++;
85       n = strtoul (p, &endp, 10);
86       p = endp;
87       if (*p!=':')
88         log_printf ("ERROR - invalid S-expression");
89       else
90         {
91           for (p++; n; n--, p++)
92             log_printf ("%02X", *p);
93         }
94     }
95 }
96
97 void
98 gpgsm_print_time (FILE *fp, ksba_isotime_t t)
99 {
100   if (!t || !*t)
101     fputs (_("none"), fp);
102   else
103     fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13);
104 }
105
106 void
107 gpgsm_dump_time (ksba_isotime_t t)
108 {
109   if (!t || !*t)
110     log_printf (_("[none]"));
111   else
112     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
113                 t, t+4, t+6, t+9, t+11, t+13);
114 }
115
116
117
118
119 void
120 gpgsm_dump_string (const char *string)
121 {
122
123   if (!string)
124     log_printf ("[error]");
125   else
126     {
127       const unsigned char *s;
128
129       for (s=string; *s; s++)
130         {
131           if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
132             break;
133         }
134       if (!*s && *string != '[')
135         log_printf ("%s", string);
136       else
137         {
138           log_printf ( "[ ");
139           log_printhex (NULL, string, strlen (string));
140           log_printf ( " ]");
141         }
142     }
143 }
144
145
146 void 
147 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
148 {
149   ksba_sexp_t sexp;
150   unsigned char *p;
151   char *dn;
152   ksba_isotime_t t;
153
154   log_debug ("BEGIN Certificate `%s':\n", text? text:"");
155   if (cert)
156     {
157       sexp = ksba_cert_get_serial (cert);
158       log_debug ("     serial: ");
159       gpgsm_dump_serial (sexp);
160       ksba_free (sexp);
161       log_printf ("\n");
162
163       ksba_cert_get_validity (cert, 0, t);
164       log_debug ("  notBefore: ");
165       gpgsm_dump_time (t);
166       log_printf ("\n");
167       ksba_cert_get_validity (cert, 1, t);
168       log_debug ("   notAfter: ");
169       gpgsm_dump_time (t);
170       log_printf ("\n");
171
172       dn = ksba_cert_get_issuer (cert, 0);
173       log_debug ("     issuer: ");
174       gpgsm_dump_string (dn);
175       ksba_free (dn);
176       log_printf ("\n");
177     
178       dn = ksba_cert_get_subject (cert, 0);
179       log_debug ("    subject: ");
180       gpgsm_dump_string (dn);
181       ksba_free (dn);
182       log_printf ("\n");
183
184       log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));
185
186       p = gpgsm_get_fingerprint_string (cert, 0);
187       log_debug ("  SHA1 Fingerprint: %s\n", p);
188       xfree (p);
189     }
190   log_debug ("END Certificate\n");
191 }
192
193
194 \f
195 /* helper for the rfc2253 string parser */
196 static const unsigned char *
197 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
198 {
199   const unsigned char *s, *s1;
200   size_t n;
201   unsigned char *p;
202
203   /* parse attributeType */
204   for (s = string+1; *s && *s != '='; s++)
205     ;
206   if (!*s)
207     return NULL; /* error */
208   n = s - string;
209   if (!n)
210     return NULL; /* empty key */
211   array->key = p = xtrymalloc (n+1);
212   if (!array->key)
213     return NULL;
214   memcpy (p, string, n); 
215   p[n] = 0;
216   trim_trailing_spaces (p);
217   if ( !strcmp (p, "1.2.840.113549.1.9.1") )
218     strcpy (p,     "EMail");
219   else if ( !strcmp (p, "0.2.262.1.10.7.20") )
220     strcpy (p,          "NameDistinguisher");
221
222   string = s + 1;
223
224   if (*string == '#')
225     { /* hexstring */
226       string++;
227       for (s=string; hexdigitp (s); s++)
228         s++;
229       n = s - string;
230       if (!n || (n & 1))
231         return NULL; /* empty or odd number of digits */
232       n /= 2;
233       array->value = p = xtrymalloc (n+1);
234       if (!p)
235         return NULL;
236       for (s1=string; n; s1 += 2, n--)
237         *p++ = xtoi_2 (s1);
238       *p = 0;
239    }
240   else
241     { /* regular v3 quoted string */
242       for (n=0, s=string; *s; s++)
243         {
244           if (*s == '\\')
245             { /* pair */
246               s++;
247               if (*s == ',' || *s == '=' || *s == '+'
248                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
249                   || *s == '\\' || *s == '\"' || *s == ' ')
250                 n++;
251               else if (hexdigitp (s) && hexdigitp (s+1))
252                 {
253                   s++;
254                   n++;
255                 }
256               else
257                 return NULL; /* invalid escape sequence */
258             }
259           else if (*s == '\"')
260             return NULL; /* invalid encoding */
261           else if (*s == ',' || *s == '=' || *s == '+'
262                    || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
263             break; 
264           else
265             n++;
266         }
267
268       array->value = p = xtrymalloc (n+1);
269       if (!p)
270         return NULL;
271       for (s=string; n; s++, n--)
272         {
273           if (*s == '\\')
274             { 
275               s++;
276               if (hexdigitp (s))
277                 {
278                   *p++ = xtoi_2 (s);
279                   s++;
280                 }
281               else
282                 *p++ = *s;
283             }
284           else
285             *p++ = *s;
286         }
287       *p = 0;
288     }
289   return s;
290 }
291
292
293 /* Parse a DN and return an array-ized one.  This is not a validating
294    parser and it does not support any old-stylish syntax; KSBA is
295    expected to return only rfc2253 compatible strings. */
296 static struct dn_array_s *
297 parse_dn (const unsigned char *string)
298 {
299   struct dn_array_s *array;
300   size_t arrayidx, arraysize;
301   int i;
302
303   arraysize = 7; /* C,ST,L,O,OU,CN,email */
304   arrayidx = 0;
305   array = xtrymalloc ((arraysize+1) * sizeof *array);
306   if (!array)
307     return NULL;
308   while (*string)
309     {
310       while (*string == ' ')
311         string++;
312       if (!*string)
313         break; /* ready */
314       if (arrayidx >= arraysize)
315         { 
316           struct dn_array_s *a2;
317
318           arraysize += 5;
319           a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
320           if (!a2)
321             goto failure;
322           array = a2;
323         }
324       array[arrayidx].key = NULL;
325       array[arrayidx].value = NULL;
326       string = parse_dn_part (array+arrayidx, string);
327       if (!string)
328         goto failure;
329       while (*string == ' ')
330         string++;
331       array[arrayidx].multivalued = (*string == '+');
332       array[arrayidx].done = 0;
333       arrayidx++;
334       if (*string && *string != ',' && *string != ';' && *string != '+')
335         goto failure; /* invalid delimiter */
336       if (*string)
337         string++;
338     }
339   array[arrayidx].key = NULL;
340   array[arrayidx].value = NULL;
341   return array;
342
343  failure:
344   for (i=0; i < arrayidx; i++)
345     {
346       xfree (array[i].key);
347       xfree (array[i].value);
348     }
349   xfree (array);
350   return NULL;
351 }
352
353
354 static void
355 print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key)
356 {
357   struct dn_array_s *first_dn = dn;
358
359   for (; dn->key; dn++)
360     {
361       if (!dn->done && !strcmp (dn->key, key))
362         {
363           /* Forward to the last multi-valued RDN, so that we can
364              print them all in reverse in the correct order.  Note
365              that this overrides the the standard sequence but that
366              seems to a reasonable thing to do with multi-valued
367              RDNs. */
368           while (dn->multivalued && dn[1].key)
369             dn++;
370         next:
371           if (!dn->done && dn->value && *dn->value)
372             {
373               fprintf (fp, "/%s=", dn->key);
374               print_sanitized_utf8_string (fp, dn->value, '/');
375             }
376           dn->done = 1;
377           if (dn > first_dn && dn[-1].multivalued)
378             {
379               dn--;
380               goto next;
381             }
382         }
383     }
384 }
385
386 /* Print all parts of a DN in a "standard" sequence.  We first print
387    all the known parts, followed by the uncommon ones */
388 static void
389 print_dn_parts (FILE *fp, struct dn_array_s *dn)
390 {
391   const char *stdpart[] = {
392     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL 
393   };
394   int i;
395   
396   for (i=0; stdpart[i]; i++)
397       print_dn_part (fp, dn, stdpart[i]);
398
399   /* Now print the rest without any specific ordering */
400   for (; dn->key; dn++)
401     print_dn_part (fp, dn, dn->key);
402 }
403
404
405
406 void
407 gpgsm_print_name (FILE *fp, const char *name)
408 {
409   const unsigned char *s;
410   int i;
411
412   s = name;
413   if (!s)
414     {
415       fputs (_("[Error - No name]"), fp);
416     }
417   else if (*s == '<')
418     {
419       const unsigned char *s2 = strchr (s+1, '>');
420       if (s2)
421         print_sanitized_utf8_buffer (fp, s + 1, s2 - s - 1, 0);
422     }
423   else if (*s == '(')
424     fputs (_("[Error - unknown encoding]"), fp);
425   else if (!((*s >= '0' && *s < '9')
426              || (*s >= 'A' && *s <= 'Z')
427              || (*s >= 'a' && *s <= 'z')))
428     fputs (_("[Error - invalid encoding]"), fp);
429   else
430     {
431       struct dn_array_s *dn = parse_dn (s);
432       if (!dn)
433         fputs (_("[Error - invalid DN]"), fp);
434       else 
435         {
436           print_dn_parts (fp, dn);          
437           for (i=0; dn[i].key; i++)
438             {
439               xfree (dn[i].key);
440               xfree (dn[i].value);
441             }
442           xfree (dn);
443         }
444     }
445 }
446
447
448