OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / freeswan / pluto / asn1.c
1 /* Simple ASN.1 parser
2  * Copyright (C) 2000-2002 Andreas Steffen, Zuercher Hochschule Winterthur
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  *
14  * RCSID $Id: asn1.c,v 0.1 2002/04/12 00:00:00 as Exp $
15  */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <time.h>
20
21 #include <freeswan.h>
22
23 #include "constants.h"
24 #include "defs.h"
25 #include "asn1.h"
26 #include "log.h"
27 #include "pem.h"
28
29 /* Some well known object identifiers (OIDs) */
30
31 const oid_t oid_names[] = {
32   {0x55,                       27, 1, "X.500" },
33   {  0x04,                     14, 1, "X.509" },
34   {    0x03,                    1, 0, "CN"    }, /* commonName             */
35   {    0x04,                    1, 0, "S"     }, /* surname                */
36   {    0x05,                    1, 0, "SN"    }, /* serialNumber           */
37   {    0x06,                    1, 0, "C"     }, /* countryMame            */
38   {    0x07,                    1, 0, "L"     }, /* localityName           */
39   {    0x08,                    1, 0, "ST"    }, /* stateOrProvinceName    */
40   {    0x0A,                    1, 0, "O"     }, /* organizationName       */
41   {    0x0B,                    1, 0, "OU"    }, /* organizationalUnitName */
42   {    0x0C,                    1, 0, "T"     }, /* personalTitle          */
43   {    0x0D,                    1, 0, "D"     }, /* description            */
44   {    0x29,                    1, 0, "N"     }, /* name                   */
45   {    0x2A,                    1, 0, "G"     }, /* givenName              */
46   {    0x2B,                    0, 0, "I"     }, /* initials               */
47   {  0x1d,                      0, 1, "id-ce"                   },
48   {    0x0E,                    1, 0, "subjectKeyIdentifier"    },
49   {    0x0F,                    1, 0, "keyUsage"                },
50   {    0x10,                    1, 0, "privateKeyUsagePeriod"   },
51   {    0x11,                    1, 0, "subjectAltName"          }, /* 19 */
52   {    0x12,                    1, 0, "issuerAltName"           },
53   {    0x13,                    1, 0, "basicConstraints"        }, /* 21 */
54   {    0x15,                    1, 0, "crlReason"               },
55   {    0x1F,                    1, 0, "crlDistributionPoints"   }, /* 23 */
56   {    0x20,                    1, 0, "certificatePolicies"     },
57   {    0x23,                    1, 0, "authorityKeyIdentifier"  },
58   {    0x25,                    0, 0, "extendedKeyUsage"        },
59   {0x2A,                       28, 1, ""                        },
60   {  0x86,                      0, 1, ""                        },
61   {    0x48,                    0, 1, ""                        },
62   {      0x86,                  0, 1, ""                        },
63   {        0xF7,                0, 1, ""                        },
64   {          0x0D,              0, 1, "RSADSI"                  },
65   {            0x01,           19, 1, "PKCS"                    },
66   {              0x01,          8, 1, "PKCS-1"                  },
67   {                0x01,        1, 0, "rsaEncryption"           }, /* 35 */
68   {                0x02,        1, 0, "md2WithRSAEncryption"    }, /* 36 */
69   {                0x04,        1, 0, "md5WithRSAEncryption"    }, /* 37 */
70   {                0x05,        1, 0, "sha-1WithRSAEncryption"  }, /* 38 */
71   {                0x0B,        1, 0, "sha256WithRSAEncryption" }, /* 39 */
72   {                0x0C,        1, 0, "sha384WithRSAEncryption" }, /* 40 */
73   {                0x0D,        0, 0, "sha512WithRSAEncryption" }, /* 41 */
74   {              0x07,          7, 1, "PKCS-7"                  },
75   {                0x01,        1, 0, "data"                    }, /* 43 */
76   {                0x02,        1, 0, "signedData"              }, /* 44 */
77   {                0x03,        1, 0, "envelopedData"           },
78   {                0x04,        1, 0, "signedAndEnvelopedData"  },
79   {                0x05,        1, 0, "digestedData"            },
80   {                0x06,        0, 0, "encryptedData"           },
81   {              0x09,          0, 1, "PKCS-9"                  },
82   {                0x01,        1, 0, "E"     }, /* emailAddress      50 */
83   {                0x02,        0, 0, "unstructuredName"        },
84   {            0x02,            0, 1, "digestAlgorithm"         },
85   {              0x02,          1, 0, "md2"                     }, /* 53 */
86   {              0x05,          0, 0, "md5"                     }, /* 54 */
87   {0x2B,                       16, 1, ""                        },
88   {  0x06,                     11, 1, "dod"                     },
89   {    0x01,                    0, 1, "internet"                },
90   {      0x04,                  0, 1, "private"                 },
91   {        0x01,                0, 1, "enterprise"              },
92   {          0x89,              0, 1, ""                        },
93   {            0x31,            0, 1, ""                        },
94   {              0x01,          0, 1, ""                        },
95   {                0x01,        0, 1, ""                        },
96   {                  0x02,      0, 1, ""                        },
97   {                    0x02,    0, 1, ""                        },
98   {                      0x4B,  0, 0, "TCGID" }, /* Trust Center Global ID */
99   {  0x0E,                      0, 1, "oiw"                     },
100   {    0x03,                    0, 1, "secsig"                  },
101   {      0x02,                  0, 1, "algorithms"              },
102   {        0x1A,                0, 0, "id-SHA-1"                }, /* 70 */
103   {0x60,                        0, 1, ""                        },
104   {  0x86,                      0, 1, ""                        },
105   {    0x48,                    0, 1, ""                        },
106   {      0x01,                  0, 1, "organization"            },
107   {        0x65,                7, 1, "gov"                     },
108   {          0x03,              0, 1, "csor"                    },
109   {            0x04,            0, 1, "nistalgorithm"           },
110   {              0x02,          0, 1, "hashalgs"                },
111   {                0x01,        1, 0, "id-SHA-256"              },
112   {                0x02,        1, 0, "id-SHA-384"              },
113   {                0x03,        0, 0, "id-SHA-512"              },
114   {        0x86,                0, 1, ""                        },
115   {          0xf8,              0, 1, ""                        },
116   {            0x42,            0, 1, ""                        },
117   {              0x01,          0, 1, ""                        },
118   {                0x01,        1, 0, "nsCertType"              },
119   {                0x03,        1, 0, "nsRevocationUrl"         },
120   {                0x0d,        0, 0, "nsComment"               }
121 };
122
123
124 /*  If the oid is listed in the oid_names table then the corresponding
125  *  position in the oid_names table is returned otherwise -1 is returned
126  */
127 int
128 known_oid(chunk_t object)
129 {
130     int oid = 0;
131
132     while (object.len)
133     {
134         if (oid_names[oid].digit == *object.ptr)
135         {
136             if (--object.len == 0 || oid_names[oid].down == 0)
137             {
138                 return oid;          /* found terminal symbol */
139             }
140             else
141             {
142                 object.ptr++; oid++; /* advance to next hex digit */
143             }
144         }
145         else
146         {
147             if (oid_names[oid].next)
148                 oid += oid_names[oid].next;
149             else
150                 return -1;
151         }
152     }
153     return -1;
154 }
155
156 /*
157  *  Decodes the length in bytes of an ASN.1 object
158  */
159 u_int
160 asn1_length(chunk_t *blob)
161 {
162     u_char n;
163     size_t len;
164
165     /* advance from tag field on to length field */
166     blob->ptr++;
167     blob->len--;
168
169     /* read first octet of length field */
170     n = *blob->ptr++;
171     blob->len--;
172
173     if ((n & 0x80) == 0) { /* single length octet */
174         if (n > blob->len) {
175             DBG(DBG_PARSING,
176                 DBG_log("number of length octets is larger than ASN.1 object")
177             )
178             return ASN1_INVALID_LENGTH;
179         }
180         return n;
181     }
182
183     /* composite length, determine number of length octets */
184     n &= 0x7f;
185
186     if (n > blob->len)
187     {
188         DBG(DBG_PARSING,
189             DBG_log("number of length octets is larger than ASN.1 object")
190         )
191         return ASN1_INVALID_LENGTH;
192     }
193
194     if (n > sizeof(len))
195     {
196         DBG(DBG_PARSING,
197             DBG_log("number of length octets is larger than limit of %d octets"
198                 , (int) sizeof(len))
199         )
200         return ASN1_INVALID_LENGTH;
201     }
202
203     len = 0;
204     
205     while (n-- > 0)
206     {
207         len = 256*len + *blob->ptr++;
208         blob->len--;
209     }
210     if (len > blob->len)
211     {
212         DBG(DBG_PARSING,
213             DBG_log("length is larger than remaining blob size")
214         )
215         return ASN1_INVALID_LENGTH;
216     }
217
218     return len;
219 }
220
221 /*
222  *  determines if a character string is of type ASN.1 printableString
223  */
224 bool
225 is_printablestring(chunk_t str)
226 {
227     const char printablestring_charset[] =
228         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
229     u_int i;
230
231     for (i = 0; i < str.len; i++)
232     {
233         if (strchr(printablestring_charset, str.ptr[i]) == NULL)
234             return FALSE;
235     }
236     return TRUE;
237 }
238
239 /*
240  *  Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
241  */
242 time_t
243 asn1totime(const chunk_t *utctime, asn1_t type)
244 {
245     struct tm t;
246     time_t tz_offset;
247     u_char *eot = NULL;
248
249     if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
250     {
251         tz_offset = 0; /* Zulu time with a zero time zone offset */
252     }
253     else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
254     {
255         int tz_hour, tz_min;
256
257         if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
258         {
259             return 0; /* error in positive timezone offset format */
260         }
261
262         tz_offset = 3600*tz_hour + 60*tz_min;  /* positive time zone offset */
263     }
264     else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
265     {
266         int tz_hour, tz_min;
267
268         if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
269         {
270             return 0; /* error in negative timezone offset format */
271         }
272         tz_offset = -3600*tz_hour - 60*tz_min;  /* negative time zone offset */
273     }
274     else
275     {
276         return 0; /* error in time format */
277     }
278
279     {
280         const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
281                                                      "%4d%2d%2d%2d%2d";
282
283         if (sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
284                                          &t.tm_hour, &t.tm_min) != 5)
285         {
286             return 0; /* error in time st [yy]yymmddhhmm time format */
287         }
288     }
289
290     /* is there a seconds field? */
291     if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
292     {
293         if (sscanf(eot-2, "%2d", &t.tm_sec) != 1)
294         {
295             return 0; /* error in ss seconds field format */
296         }
297     }
298     else
299     {
300         t.tm_sec = 0;
301     }
302
303     /* representation of year */
304     if (t.tm_year >= 1900)
305     {
306         t.tm_year -= 1900;
307     }
308     else if (t.tm_year >= 100)
309     {
310         return 0;
311     }
312     else if (t.tm_year < 50)
313     {
314         t.tm_year += 100;
315     }
316
317     if (t.tm_mon < 1 || t.tm_mon > 12)
318     {
319         return 0; /* error in month format */
320     }
321     /* representation of month 0..11 in struct tm */
322     t.tm_mon--;
323
324     /* set daylight saving time to off */
325     t.tm_isdst = 0;
326
327     /* compensate timezone */
328
329     return mktime(&t) - tz_offset;
330 }
331
332 /*
333  * Initializes the internal context of the ASN.1 parser
334  */
335 void
336 asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0,
337         bool implicit, u_int cond)
338 {
339     ctx->blobs[0] = blob;
340     ctx->level0   = level0;
341     ctx->implicit = implicit;
342     ctx->cond     = cond;
343     memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr));
344 }
345
346 /*
347  * Parses and extracts the next ASN.1 object
348  */
349 bool
350 extract_object(asn1Object_t const *objects,
351         u_int *objectID, chunk_t *object, asn1_ctx_t *ctx)
352 {
353     asn1Object_t obj = objects[*objectID];
354     chunk_t *blob;
355     chunk_t *blob1;
356     u_char *start_ptr;
357
358     *object = empty_chunk;
359
360     if (obj.flags & ASN1_END)  /* end of loop or option found */
361     {
362         if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0)
363         {
364             *objectID = ctx->loopAddr[obj.level]; /* another iteration */
365             obj = objects[*objectID];
366         }
367         else
368         {
369             ctx->loopAddr[obj.level] = 0;         /* exit loop or option*/
370             return TRUE;
371         }
372     }
373
374     blob = ctx->blobs + obj.level;
375     blob1 = blob + 1;
376     start_ptr = blob->ptr;
377
378     /* handle ASN.1 defaults values */
379
380     if ( (obj.flags & ASN1_DEF) && (*start_ptr != obj.type) )
381     {
382         /* field is missing */
383
384         DBG(DBG_PARSING,
385             DBG_log("L%d - %s:", ctx->level0+obj.level, obj.name);
386         )
387         if (obj.type & ASN1_CONSTRUCTED)
388         {
389             (*objectID)++ ;  /* skip context-specific tag */
390         }
391         return TRUE;
392     }
393
394     /* handle ASN.1 options */
395
396     if ( (obj.flags & ASN1_OPT) &&
397          ( blob->len == 0 || *start_ptr != obj.type) )
398     {
399         /* advance to end of missing option field */
400         do
401         {
402             (*objectID)++;
403         }  while (!((objects[*objectID].flags & ASN1_END) &&
404                       (objects[*objectID].level == obj.level )));
405         return TRUE;
406     }
407
408     blob1->len = asn1_length(blob);
409
410     if (blob1->len == ASN1_INVALID_LENGTH)
411     {
412         DBG(DBG_PARSING,
413             DBG_log("L%d - %s:  length of ASN1 object too large",
414                     ctx->level0+obj.level, obj.name);
415         )
416         return FALSE;
417     }
418
419     blob1->ptr = blob->ptr;
420     blob->ptr += blob1->len;
421     blob->len -= blob1->len;
422
423     if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0))
424     {
425         DBG(DBG_PARSING,
426             DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
427                 ctx->level0+obj.level, obj.name, obj.type, *start_ptr);
428             DBG_dump("", start_ptr, (u_int)(blob->ptr - start_ptr));
429         )
430         return FALSE;
431     }
432
433     DBG(DBG_PARSING,
434         DBG_log("L%d - %s:", ctx->level0+obj.level, obj.name);
435     )
436
437     /* In case of "SEQUENCE OF" or "SET OF" start a loop */
438
439     if (obj.flags & ASN1_LOOP) ctx->loopAddr[obj.level] = *objectID + 1;
440
441     if (obj.flags & ASN1_OBJ)
442     {
443         object->ptr = start_ptr;
444         object->len = (u_int)(blob->ptr - start_ptr);
445         DBG(ctx->cond,
446             DBG_dump_chunk("", *object);
447         )
448     }
449     else if (obj.flags & ASN1_BODY)
450     {
451         int oid;
452         *object = *blob1;
453
454         switch (obj.type)
455         {
456         case ASN1_OID:
457             oid = known_oid(*object);
458             if (oid != -1)
459             {
460                 DBG(DBG_PARSING,
461                    DBG_log("  '%s'",oid_names[oid].name);
462                 )
463                 return TRUE;
464             }
465             break;
466         case ASN1_IA5STRING:
467         case ASN1_T61STRING:
468         case ASN1_PRINTABLESTRING:
469         case ASN1_VISIBLESTRING:
470             DBG(DBG_PARSING,
471                 DBG_log("  '%.*s'", (int)object->len, object->ptr);
472             )
473             return TRUE;
474         case ASN1_UTCTIME:
475         case ASN1_GENERALIZEDTIME:
476             DBG(DBG_PARSING,
477                 time_t time = asn1totime(object, obj.type);
478                 DBG_log("  '%s'", timetoa(&time, TRUE));
479             )
480             return TRUE;
481
482         default:
483             break;
484         }
485         DBG(ctx->cond,
486             DBG_dump_chunk("", *object);
487         )
488     }
489     return TRUE;
490 }
491
492 /*
493  *  tests if a blob contains a valid ASN.1 set or sequence
494  */
495 static bool
496 is_asn1(chunk_t blob)
497 {
498     u_int len;
499     u_char tag = *blob.ptr;
500
501     if (tag != ASN1_SEQUENCE && tag != ASN1_SET)
502     {
503         DBG(DBG_PARSING,
504             DBG_log("  file content is not binary ASN.1");
505         )
506         return FALSE;
507     }
508     len = asn1_length(&blob);
509     if (len != blob.len)
510     {
511         DBG(DBG_PARSING,
512             DBG_log("  file size does not match ASN.1 coded length");
513         )
514         return FALSE;
515     }
516     return TRUE;
517 }
518
519 /*  load an ASN.1 coded file with autodetection
520  *  of binary DER and base64 PEM formats
521  */
522 bool
523 load_asn1_file(const char* filename, const char* passphrase,
524                const char* type, chunk_t *blob)
525 {
526     err_t ugh = NULL;
527     FILE *fd;
528
529     fd = fopen(filename, "r");
530     if (fd)
531     {
532         int bytes;
533         fseek(fd, 0, SEEK_END );
534         blob->len = ftell(fd);
535         if (blob->len <= 0)
536         {
537             log("  %s file '%s' is zero length", type, filename);
538             fclose(fd);
539             return FALSE;
540         }
541         rewind(fd);
542         blob->ptr = alloc_bytes(blob->len, type);
543         bytes = fread(blob->ptr, 1, blob->len, fd);
544         fclose(fd);
545 //      log("  loaded %s file '%s' (%d bytes)", type, filename, bytes);
546
547         /* try DER format */
548
549         if (is_asn1(*blob))
550         {
551             DBG(DBG_PARSING,
552                 DBG_log("  file coded in DER format");
553             )
554             return TRUE;
555         }
556
557         /* try PEM format */
558         ugh = pemtobin(blob, passphrase);
559
560         if (ugh == NULL)
561         {
562             if (is_asn1(*blob))
563             {
564                 DBG(DBG_PARSING,
565                     DBG_log("  file coded in PEM format");
566                 )
567                 return TRUE;
568             }
569
570             ugh = "file coded in unknown format, discarded";
571         }
572
573         /* a conversion error has occured */
574         if (!strcmp(ugh, "file coded in unknown format, discarded"))
575                 log("  %s", ugh);
576         pfree(blob->ptr);
577         *blob = empty_chunk;
578     }
579     else
580     {
581         log("  could not open %s file '%s'", type, filename);
582     }
583     return FALSE;
584 }