summaryrefslogtreecommitdiff
path: root/src/pluto/asn1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/asn1.c')
-rw-r--r--src/pluto/asn1.c806
1 files changed, 0 insertions, 806 deletions
diff --git a/src/pluto/asn1.c b/src/pluto/asn1.c
deleted file mode 100644
index 6f3695874..000000000
--- a/src/pluto/asn1.c
+++ /dev/null
@@ -1,806 +0,0 @@
-/* Simple ASN.1 parser
- * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * RCSID $Id: asn1.c 5041 2009-03-27 08:58:48Z andreas $
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <freeswan.h>
-
-#include "constants.h"
-#include "defs.h"
-#include "mp_defs.h"
-#include "asn1.h"
-#include <asn1/oid.h>
-#include "log.h"
-
-/* some common prefabricated ASN.1 constants */
-
-static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 };
-static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 };
-static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 };
-
-const chunk_t ASN1_INTEGER_0 = strchunk(ASN1_INTEGER_0_str);
-const chunk_t ASN1_INTEGER_1 = strchunk(ASN1_INTEGER_1_str);
-const chunk_t ASN1_INTEGER_2 = strchunk(ASN1_INTEGER_2_str);
-
-/* some popular algorithmIdentifiers */
-
-static u_char ASN1_md5_id_str[] = {
- 0x30, 0x0C,
- 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05,
- 0x05, 0x00
-};
-
-static u_char ASN1_sha1_id_str[] = {
- 0x30, 0x09,
- 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A,
- 0x05, 0x00
-};
-
-static u_char ASN1_md5WithRSA_id_str[] = {
- 0x30, 0x0D,
- 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
- 0x05, 0x00
-};
-
-static u_char ASN1_sha1WithRSA_id_str[] = {
- 0x30, 0x0D,
- 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
- 0x05, 0x00
-};
-
-static u_char ASN1_rsaEncryption_id_str[] = {
- 0x30, 0x0D,
- 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
- 0x05, 0x00
-};
-
-const chunk_t ASN1_md5_id = strchunk(ASN1_md5_id_str);
-const chunk_t ASN1_sha1_id = strchunk(ASN1_sha1_id_str);
-const chunk_t ASN1_rsaEncryption_id = strchunk(ASN1_rsaEncryption_id_str);
-const chunk_t ASN1_md5WithRSA_id = strchunk(ASN1_md5WithRSA_id_str);
-const chunk_t ASN1_sha1WithRSA_id = strchunk(ASN1_sha1WithRSA_id_str);
-
-/* ASN.1 definition of an algorithmIdentifier */
-
-static const asn1Object_t algorithmIdentifierObjects[] = {
- { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
- { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
- { 1, "parameters", ASN1_EOC, ASN1_OPT |
- ASN1_RAW }, /* 2 */
- { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */
-};
-
-#define ALGORITHM_ID_ALG 1
-#define ALGORITHM_ID_PARAMETERS 2
-#define ALGORITHM_ID_ROOF 4
-
-/*
- * return the ASN.1 encoded algorithm identifier
- */
-chunk_t
-asn1_algorithmIdentifier(int oid)
-{
- switch (oid)
- {
- case OID_RSA_ENCRYPTION:
- return ASN1_rsaEncryption_id;
- case OID_MD5_WITH_RSA:
- return ASN1_md5WithRSA_id;
- case OID_SHA1_WITH_RSA:
- return ASN1_sha1WithRSA_id;
- case OID_MD5:
- return ASN1_md5_id;
- case OID_SHA1:
- return ASN1_sha1_id;
- default:
- return empty_chunk;
- }
-}
-
-/* If the oid is listed in the oid_names table then the corresponding
- * position in the oid_names table is returned otherwise -1 is returned
- */
-int
-known_oid(chunk_t object)
-{
- int oid = 0;
-
- while (object.len)
- {
- if (oid_names[oid].octet == *object.ptr)
- {
- if (--object.len == 0 || oid_names[oid].down == 0)
- {
- return oid; /* found terminal symbol */
- }
- else
- {
- object.ptr++; oid++; /* advance to next hex octet */
- }
- }
- else
- {
- if (oid_names[oid].next)
- oid = oid_names[oid].next;
- else
- return OID_UNKNOWN;
- }
- }
- return -1;
-}
-
-/*
- * Decodes the length in bytes of an ASN.1 object
- */
-u_int
-asn1_length(chunk_t *blob)
-{
- u_char n;
- size_t len;
-
- /* advance from tag field on to length field */
- blob->ptr++;
- blob->len--;
-
- /* read first octet of length field */
- n = *blob->ptr++;
- blob->len--;
-
- if ((n & 0x80) == 0) /* single length octet */
- return n;
-
- /* composite length, determine number of length octets */
- n &= 0x7f;
-
- if (n > blob->len)
- {
- DBG(DBG_PARSING,
- DBG_log("number of length octets is larger than ASN.1 object")
- )
- return ASN1_INVALID_LENGTH;
- }
-
- if (n > sizeof(len))
- {
- DBG(DBG_PARSING,
- DBG_log("number of length octets is larger than limit of %d octets"
- , (int)sizeof(len))
- )
- return ASN1_INVALID_LENGTH;
- }
-
- len = 0;
-
- while (n-- > 0)
- {
- len = 256*len + *blob->ptr++;
- blob->len--;
- }
- if (len > blob->len)
- {
- DBG(DBG_PARSING,
- DBG_log("length is larger than remaining blob size")
- )
- return ASN1_INVALID_LENGTH;
- }
- return len;
-}
-
-/*
- * codes ASN.1 lengths up to a size of 16'777'215 bytes
- */
-void
-code_asn1_length(size_t length, chunk_t *code)
-{
- if (length < 128)
- {
- code->ptr[0] = length;
- code->len = 1;
- }
- else if (length < 256)
- {
- code->ptr[0] = 0x81;
- code->ptr[1] = (u_char) length;
- code->len = 2;
- }
- else if (length < 65536)
- {
- code->ptr[0] = 0x82;
- code->ptr[1] = length >> 8;
- code->ptr[2] = length & 0x00ff;
- code->len = 3;
- }
- else
- {
- code->ptr[0] = 0x83;
- code->ptr[1] = length >> 16;
- code->ptr[2] = (length >> 8) & 0x00ff;
- code->ptr[3] = length & 0x0000ff;
- code->len = 4;
- }
-}
-
-/*
- * build an empty asn.1 object with tag and length fields already filled in
- */
-u_char*
-build_asn1_object(chunk_t *object, asn1_t type, size_t datalen)
-{
- u_char length_buf[4];
- chunk_t length = { length_buf, 0 };
- u_char *pos;
-
- /* code the asn.1 length field */
- code_asn1_length(datalen, &length);
-
- /* allocate memory for the asn.1 TLV object */
- object->len = 1 + length.len + datalen;
- object->ptr = alloc_bytes(object->len, "asn1 object");
-
- /* set position pointer at the start of the object */
- pos = object->ptr;
-
- /* copy the asn.1 tag field and advance the pointer */
- *pos++ = type;
-
- /* copy the asn.1 length field and advance the pointer */
- chunkcpy(pos, length);
-
- return pos;
-}
-
-/*
- * build a simple ASN.1 object
- */
-chunk_t
-asn1_simple_object(asn1_t tag, chunk_t content)
-{
- chunk_t object;
-
- u_char *pos = build_asn1_object(&object, tag, content.len);
- chunkcpy(pos, content);
-
- return object;
-}
-
-/* Build an ASN.1 object from a variable number of individual chunks.
- * Depending on the mode, chunks either are moved ('m') or copied ('c').
- */
-chunk_t
-asn1_wrap(asn1_t type, const char *mode, ...)
-{
- chunk_t construct;
- va_list chunks;
- u_char *pos;
- int i;
- int count = strlen(mode);
-
- /* sum up lengths of individual chunks */
- va_start(chunks, mode);
- construct.len = 0;
- for (i = 0; i < count; i++)
- {
- chunk_t ch = va_arg(chunks, chunk_t);
- construct.len += ch.len;
- }
- va_end(chunks);
-
- /* allocate needed memory for construct */
- pos = build_asn1_object(&construct, type, construct.len);
-
- /* copy or move the chunks */
- va_start(chunks, mode);
- for (i = 0; i < count; i++)
- {
- chunk_t ch = va_arg(chunks, chunk_t);
-
- switch (*mode++)
- {
- case 'm':
- mv_chunk(&pos, ch);
- break;
- case 'c':
- default:
- chunkcpy(pos, ch);
- }
- }
- va_end(chunks);
-
- return construct;
-}
-
-/*
- * convert a MP integer into a DER coded ASN.1 object
- */
-chunk_t
-asn1_integer_from_mpz(const mpz_t value)
-{
- size_t bits = mpz_sizeinbase(value, 2); /* size in bits */
- size_t size = 1 + bits / BITS_PER_BYTE; /* size in bytes */
- chunk_t n = mpz_to_n(value, size);
-
- return asn1_wrap(ASN1_INTEGER, "m", n);
-}
-
-/*
- * determines if a character string is of type ASN.1 printableString
- */
-bool
-is_printablestring(chunk_t str)
-{
- const char printablestring_charset[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
- u_int i;
-
- for (i = 0; i < str.len; i++)
- {
- if (strchr(printablestring_charset, str.ptr[i]) == NULL)
- return FALSE;
- }
- return TRUE;
-}
-
-#define TIME_MAX 0x7fffffff
-
-/*
- * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
- */
-time_t
-asn1totime(const chunk_t *utctime, asn1_t type)
-{
- struct tm t;
- time_t tc, tz_offset;
- u_char *eot = NULL;
-
- if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
- {
- tz_offset = 0; /* Zulu time with a zero time zone offset */
- }
- else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
- {
- int tz_hour, tz_min;
-
- if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
- {
- return 0; /* error in positive timezone offset format */
- }
- tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
- }
- else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
- {
- int tz_hour, tz_min;
-
- if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
- {
- return 0; /* error in negative timezone offset format */
- }
- tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
- }
- else
- {
- return 0; /* error in time format */
- }
-
- /* parse ASN.1 time string */
- {
- const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
- "%4d%2d%2d%2d%2d";
-
- if (sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
- &t.tm_hour, &t.tm_min) != 5)
- {
- return 0; /* error in time st [yy]yymmddhhmm time format */
- }
- }
-
- /* is there a seconds field? */
- if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
- {
- if (sscanf(eot-2, "%2d", &t.tm_sec) != 1)
- {
- return 0; /* error in ss seconds field format */
- }
- }
- else
- {
- t.tm_sec = 0;
- }
-
- /* representation of year */
- if (t.tm_year >= 1900)
- {
- t.tm_year -= 1900;
- }
- else if (t.tm_year >= 100)
- {
- return 0;
- }
- else if (t.tm_year < 50)
- {
- t.tm_year += 100;
- }
-
- /* representation of month 0..11*/
- t.tm_mon--;
-
- /* set daylight saving time to off */
- t.tm_isdst = 0;
-
- /* convert to time_t */
- tc = mktime(&t);
-
- /* if no conversion overflow occurred, compensate timezone */
- return (tc == -1) ? TIME_MAX : (tc - timezone - tz_offset);
-}
-
-/*
- * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
- */
-chunk_t
-timetoasn1(const time_t *time, asn1_t type)
-{
- int offset;
- const char *format;
- char buf[TIMETOA_BUF];
- chunk_t formatted_time;
- struct tm *t = gmtime(time);
-
- if (type == ASN1_GENERALIZEDTIME)
- {
- format = "%04d%02d%02d%02d%02d%02dZ";
- offset = 1900;
- }
- else /* ASN1_UTCTIME */
- {
- format = "%02d%02d%02d%02d%02d%02dZ";
- offset = (t->tm_year < 100)? 0 : -100;
- }
- sprintf(buf, format, t->tm_year + offset, t->tm_mon + 1, t->tm_mday
- , t->tm_hour, t->tm_min, t->tm_sec);
- formatted_time.ptr = buf;
- formatted_time.len = strlen(buf);
- return asn1_simple_object(type, formatted_time);
-}
-
-
-/*
- * Initializes the internal context of the ASN.1 parser
- */
-void
-asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0,
- bool implicit, u_int cond)
-{
- ctx->blobs[0] = blob;
- ctx->level0 = level0;
- ctx->implicit = implicit;
- ctx->cond = cond;
- memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr));
-}
-
-/*
- * print the value of an ASN.1 simple object
- */
-static void
-debug_asn1_simple_object(chunk_t object, asn1_t type, u_int cond)
-{
- int oid;
-
- switch (type)
- {
- case ASN1_OID:
- oid = known_oid(object);
- if (oid != OID_UNKNOWN)
- {
- DBG(DBG_PARSING,
- DBG_log(" '%s'",oid_names[oid].name);
- )
- return;
- }
- break;
- case ASN1_UTF8STRING:
- case ASN1_IA5STRING:
- case ASN1_PRINTABLESTRING:
- case ASN1_T61STRING:
- case ASN1_VISIBLESTRING:
- DBG(DBG_PARSING,
- DBG_log(" '%.*s'", (int)object.len, object.ptr);
- )
- return;
- case ASN1_UTCTIME:
- case ASN1_GENERALIZEDTIME:
- DBG(DBG_PARSING,
- time_t time = asn1totime(&object, type);
- DBG_log(" '%s'", timetoa(&time, TRUE));
- )
- return;
- default:
- break;
- }
- DBG(cond,
- DBG_dump_chunk("", object);
- )
-}
-
-/*
- * Parses and extracts the next ASN.1 object
- */
-bool
-extract_object(asn1Object_t const *objects,
- u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx)
-{
- asn1Object_t obj = objects[*objectID];
- chunk_t *blob;
- chunk_t *blob1;
- u_char *start_ptr;
-
- *object = empty_chunk;
-
- if (obj.flags & ASN1_END) /* end of loop or option found */
- {
- if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0)
- {
- *objectID = ctx->loopAddr[obj.level]; /* another iteration */
- obj = objects[*objectID];
- }
- else
- {
- ctx->loopAddr[obj.level] = 0; /* exit loop or option*/
- return TRUE;
- }
- }
-
- *level = ctx->level0 + obj.level;
- blob = ctx->blobs + obj.level;
- blob1 = blob + 1;
- start_ptr = blob->ptr;
-
- /* handle ASN.1 defaults values */
-
- if ((obj.flags & ASN1_DEF)
- && (blob->len == 0 || *start_ptr != obj.type) )
- {
- /* field is missing */
- DBG(DBG_PARSING,
- DBG_log("L%d - %s:", *level, obj.name);
- )
- if (obj.type & ASN1_CONSTRUCTED)
- {
- (*objectID)++ ; /* skip context-specific tag */
- }
- return TRUE;
- }
-
- /* handle ASN.1 options */
-
- if ((obj.flags & ASN1_OPT)
- && (blob->len == 0 || *start_ptr != obj.type))
- {
- /* advance to end of missing option field */
- do
- (*objectID)++;
- while (!((objects[*objectID].flags & ASN1_END)
- && (objects[*objectID].level == obj.level)));
- return TRUE;
- }
-
- /* an ASN.1 object must possess at least a tag and length field */
-
- if (blob->len < 2)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: ASN.1 object smaller than 2 octets",
- *level, obj.name);
- )
- return FALSE;
- }
-
- blob1->len = asn1_length(blob);
-
- if (blob1->len == ASN1_INVALID_LENGTH)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: length of ASN.1 object invalid or too large",
- *level, obj.name);
- )
- return FALSE;
- }
-
- blob1->ptr = blob->ptr;
- blob->ptr += blob1->len;
- blob->len -= blob1->len;
-
- /* return raw ASN.1 object without prior type checking */
-
- if (obj.flags & ASN1_RAW)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s:", *level, obj.name);
- )
- object->ptr = start_ptr;
- object->len = (size_t)(blob->ptr - start_ptr);
- return TRUE;
- }
-
- if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0))
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
- *level, obj.name, obj.type, *start_ptr);
- DBG_dump("", start_ptr, (u_int)(blob->ptr - start_ptr));
- )
- return FALSE;
- }
-
- DBG(DBG_PARSING,
- DBG_log("L%d - %s:", ctx->level0+obj.level, obj.name);
- )
-
- /* In case of "SEQUENCE OF" or "SET OF" start a loop */
-
- if (obj.flags & ASN1_LOOP)
- {
- if (blob1->len > 0)
- {
- /* at least one item, start the loop */
- ctx->loopAddr[obj.level] = *objectID + 1;
- }
- else
- {
- /* no items, advance directly to end of loop */
- do
- (*objectID)++;
- while (!((objects[*objectID].flags & ASN1_END)
- && (objects[*objectID].level == obj.level)));
- return TRUE;
- }
- }
-
- if (obj.flags & ASN1_OBJ)
- {
- object->ptr = start_ptr;
- object->len = (size_t)(blob->ptr - start_ptr);
- DBG(ctx->cond,
- DBG_dump_chunk("", *object);
- )
- }
- else if (obj.flags & ASN1_BODY)
- {
- *object = *blob1;
- debug_asn1_simple_object(*object, obj.type, ctx->cond);
- }
- return TRUE;
-}
-
-/*
- * parse an ASN.1 simple type
- */
-bool
-parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level
-, const char* name)
-{
- size_t len;
-
- /* an ASN.1 object must possess at least a tag and length field */
- if (object->len < 2)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: ASN.1 object smaller than 2 octets",
- level, name);
- )
- return FALSE;
- }
-
- if (*object->ptr != type)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
- level, name, type, *object->ptr);
- )
- return FALSE;
- }
-
- len = asn1_length(object);
-
- if (len == ASN1_INVALID_LENGTH || object->len < len)
- {
- DBG(DBG_PARSING,
- DBG_log("L%d - %s: length of ASN.1 object invalid or too large",
- level, name);
- )
- return FALSE;
- }
-
- DBG(DBG_PARSING,
- DBG_log("L%d - %s:", level, name);
- )
- debug_asn1_simple_object(*object, type, DBG_RAW);
- return TRUE;
-}
-
-/*
- * extracts an algorithmIdentifier
- */
-int
-parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
-{
- asn1_ctx_t ctx;
- chunk_t object;
- u_int level;
- int alg = OID_UNKNOWN;
- int objectID = 0;
-
- asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
-
- while (objectID < ALGORITHM_ID_ROOF)
- {
- if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx))
- return alg;
-
- switch (objectID)
- {
- case ALGORITHM_ID_ALG:
- alg = known_oid(object);
- break;
- case ALGORITHM_ID_PARAMETERS:
- if (parameters != NULL)
- *parameters = object;
- break;
- default:
- break;
- }
- objectID++;
- }
- return alg;
- }
-
-/*
- * tests if a blob contains a valid ASN.1 set or sequence
- */
-bool
-is_asn1(chunk_t blob)
-{
- u_int len;
- u_char tag = *blob.ptr;
-
- if (tag != ASN1_SEQUENCE && tag != ASN1_SET)
- {
- DBG(DBG_PARSING,
- DBG_log(" file content is not binary ASN.1");
- )
- return FALSE;
- }
-
- len = asn1_length(&blob);
-
- /* exact match */
- if (len == blob.len)
- {
- return TRUE;
- }
-
- /* some websites append a surplus newline character to the blob */
- if (len + 1 == blob.len && *(blob.ptr + len) == '\n')
- {
- return TRUE;
- }
-
- DBG(DBG_PARSING,
- DBG_log(" file size does not match ASN.1 coded length");
- )
- return FALSE;
-}