/* * Copyright (C) 2006 Martin Will * Copyright (C) 2000-2017 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * 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 . * * 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. */ #include #include #include #include #include "asn1.h" #include "asn1_parser.h" #define ASN1_MAX_LEVEL 10 typedef struct private_asn1_parser_t private_asn1_parser_t; /** * Private data of an asn1_cxt_t object. */ struct private_asn1_parser_t { /** * Public interface. */ asn1_parser_t public; /** * Syntax definition of ASN.1 object */ asn1Object_t const *objects; /** * Current syntax definition line */ int line; /** * Current stat of the parsing operation */ bool success; /** * Declare object data as private - use debug level 4 to log it */ bool private; /** * Top-most type is implicit - ignore it */ bool implicit; /** * Top-most parsing level - defaults to 0 */ u_int level0; /** * Jump back address for loops for each level */ int loopAddr[ASN1_MAX_LEVEL + 1]; /** * Current parsing pointer for each level */ chunk_t blobs[ASN1_MAX_LEVEL + 2]; /** * Parsing a CHOICE on the current level ? */ bool choice[ASN1_MAX_LEVEL + 2]; }; METHOD(asn1_parser_t, iterate, bool, private_asn1_parser_t *this, int *objectID, chunk_t *object) { chunk_t *blob, *blob1, blob_ori; u_char *start_ptr; u_int level; asn1Object_t obj; *object = chunk_empty; /* Advance to the next object syntax definition line */ obj = this->objects[++(this->line)]; /* Terminate if the end of the object syntax definition has been reached */ if (obj.flags & ASN1_EXIT) { return FALSE; } if (obj.flags & ASN1_END) /* end of loop or choice or option found */ { if (this->loopAddr[obj.level] && this->blobs[obj.level+1].len > 0) { this->line = this->loopAddr[obj.level]; /* another iteration */ obj = this->objects[this->line]; } else { this->loopAddr[obj.level] = 0; /* exit loop */ if (obj.flags & ASN1_CHOICE) /* end of choices */ { if (this->choice[obj.level+1]) { DBG1(DBG_ASN, "L%d - %s: incorrect choice encoding", this->level0 + obj.level, obj.name); this->success = FALSE; goto end; } } if (obj.flags & ASN1_CH) /* end of choice */ { /* parsed a valid choice */ this->choice[obj.level] = FALSE; /* advance to end of choices */ do { this->line++; } while (!((this->objects[this->line].flags & ASN1_END) && (this->objects[this->line].flags & ASN1_CHOICE) && (this->objects[this->line].level == obj.level-1))); this->line--; } goto end; } } level = this->level0 + obj.level; blob = this->blobs + obj.level; blob_ori = *blob; 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 */ DBG2(DBG_ASN, "L%d - %s:", level, obj.name); if (obj.type & ASN1_CONSTRUCTED) { this->line++ ; /* skip context-specific tag */ } goto end; } /* handle ASN.1 options */ if ((obj.flags & ASN1_OPT) && (blob->len == 0 || *start_ptr != obj.type)) { /* advance to end of missing option field */ do { this->line++; } while (!((this->objects[this->line].flags & ASN1_END) && (this->objects[this->line].level == obj.level))); goto end; } /* an ASN.1 object must possess at least a tag and length field */ if (blob->len < 2) { DBG1(DBG_ASN, "L%d - %s: ASN.1 object smaller than 2 octets", level, obj.name); this->success = FALSE; goto end; } blob1->len = asn1_length(blob); if (blob1->len == ASN1_INVALID_LENGTH) { DBG1(DBG_ASN, "L%d - %s: length of ASN.1 object invalid or too large", level, obj.name); this->success = FALSE; goto end; } blob1->ptr = blob->ptr; blob->ptr += blob1->len; blob->len -= blob1->len; /* handle ASN.1 choice without explicit context encoding */ if ((obj.flags & ASN1_CHOICE) && obj.type == ASN1_EOC) { DBG2(DBG_ASN, "L%d - %s:", level, obj.name); this->choice[obj.level+1] = TRUE; *blob1 = blob_ori; goto end; } /* return raw ASN.1 object without prior type checking */ if (obj.flags & ASN1_RAW) { DBG2(DBG_ASN, "L%d - %s:", level, obj.name); object->ptr = start_ptr; object->len = (size_t)(blob->ptr - start_ptr); goto end; } if (*start_ptr != obj.type && !(this->implicit && this->line == 0)) { DBG2(DBG_ASN, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", level, obj.name, obj.type, *start_ptr); DBG3(DBG_ASN, "%b", start_ptr, (u_int)(blob->ptr - start_ptr)); this->success = FALSE; goto end; } DBG2(DBG_ASN, "L%d - %s:", 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 */ this->loopAddr[obj.level] = this->line + 1; } else { /* no items, advance directly to end of loop */ do { this->line++; } while (!((this->objects[this->line].flags & ASN1_END) && (this->objects[this->line].level == obj.level))); goto end; } } /* In case of a "CHOICE" start to scan for exactly one valid choice */ if (obj.flags & ASN1_CHOICE) { if (blob1->len == 0) { DBG1(DBG_ASN, "L%d - %s: contains no choice", level, obj.name); this->success = FALSE; goto end; } this->choice[obj.level+1] = TRUE; } if (obj.flags & ASN1_OBJ) { object->ptr = start_ptr; object->len = (size_t)(blob->ptr - start_ptr); if (this->private) { DBG4(DBG_ASN, "%B", object); } else { DBG3(DBG_ASN, "%B", object); } } else if (obj.flags & ASN1_BODY) { *object = *blob1; asn1_debug_simple_object(*object, obj.type, this->private); } end: *objectID = this->line; return this->success; } METHOD(asn1_parser_t, get_level, u_int, private_asn1_parser_t *this) { return this->level0 + this->objects[this->line].level; } METHOD(asn1_parser_t, set_top_level, void, private_asn1_parser_t *this, u_int level0) { this->level0 = level0; } METHOD(asn1_parser_t, set_flags, void, private_asn1_parser_t *this, bool implicit, bool private) { this->implicit = implicit; this->private = private; } METHOD(asn1_parser_t, success, bool, private_asn1_parser_t *this) { return this->success; } METHOD(asn1_parser_t, destroy, void, private_asn1_parser_t *this) { free(this); } /** * Defined in header. */ asn1_parser_t* asn1_parser_create(asn1Object_t const *objects, chunk_t blob) { private_asn1_parser_t *this; INIT(this, .public = { .iterate = _iterate, .get_level = _get_level, .set_top_level = _set_top_level, .set_flags = _set_flags, .success = _success, .destroy = _destroy, }, .objects = objects, .blobs[0] = blob, .line = -1, .success = TRUE, ); return &this->public; }