diff options
Diffstat (limited to 'src/pluto/asn1.c')
-rw-r--r-- | src/pluto/asn1.c | 806 |
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; -} |