/*- * Copyright (c) 2003 Lev Walkin . All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ #include #include #define ADVANCE(num_bytes) do { \ size_t num = num_bytes; \ ptr += num; \ size -= num; \ consumed_myself += num; \ } while(0) #define RETURN(_code) do { \ ber_dec_rval_t rval; \ rval.code = _code; \ rval.consumed = consumed_myself; \ return rval; \ } while(0) /* * The BER decoder of any type. */ ber_dec_rval_t ber_decode(asn1_TYPE_descriptor_t *type_descriptor, void **struct_ptr, void *ptr, size_t size) { /* * Invoke type-specific decoder. */ return type_descriptor->ber_decoder(type_descriptor, struct_ptr, /* Pointer to the destination structure */ ptr, size, /* Buffer and its size */ 0 /* Default tag mode is 0 */ ); } /* * Check the set of >> tags matches the definition. */ ber_dec_rval_t ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx, void *ptr, size_t size, int tag_mode, ber_tlv_len_t *last_length, int *opt_tlv_form) { ssize_t consumed_myself = 0; ssize_t tag_len; ssize_t len_len; ber_tlv_tag_t tlv_tag; ber_tlv_len_t tlv_len; ber_tlv_len_t limit_len = -1; int expect_00_terminators = 0; int tlv_constr = -1; /* If CHOICE, opt_tlv_form is not given */ int tagno; /* * So what does all this tags_impl_skip stuff mean? * Imagine two types, * A ::= [5] IMPLICIT T * B ::= [2] EXPLICIT T * Where T is defined as * T ::= [4] IMPLICIT SEQUENCE { ... } * * Let's say, we are starting to decode type A, given the * following TLV stream: <5> <0>. What does this mean? * It means that the type A contains type T which is, * in turn, empty. * Remember though, that we are still in A. We cannot * just pass control to the type T decoder. Why? Because * the type T decoder expects <4> <0>, not <5> <0>. * So, we must make sure we are going to receive <5> while * still in A, then pass control to the T decoder, indicating * that the tag <4> was implicitly skipped. The decoder of T * hence will be prepared to treat <4> as valid tag, and decode * it appropriately. */ /* * We have a list of tags that must occur in the stream: * {A,B,C} * However, it may be indicated that the type is * implicitly tagged in the caller, so it really boils down to the * {I,B,C} or even {I,C} * This is because the implicit tag at above structure may replace * zero or more (or every) tags which follow it. We don't care * about the precise number, as it is already computed for us * by the ASN.1 compiler and placed into head->tags_impl_skip. * So let's suppose the only tag left after implicit tagging is {I}. * Yet, the table we have is {A,B,C} and head->tags_impl_skip=3. * We need to check at least one tag in the loop, so the loop range * is modified so it will be invoked at least one time. */ tagno = ctx->step /* Continuing where left previously */ + (tag_mode==-1?(head->tags_impl_skip-1):0) + (tag_mode==1?-1:0) ; //assert(head->tags_count >= 1); ?May not be the case for CHOICE! assert(tagno < head->tags_count); /* At least one loop */ for((void)tagno; tagno < head->tags_count; tagno++, ctx->step++) { /* * Fetch and process T from TLV. */ tag_len = ber_fetch_tag(ptr, size, &tlv_tag); ASN_DEBUG("Fetching tag from {%p,%ld} %02X: " "len %ld, tag %s", ptr, (long)size, *(uint8_t *)ptr, (long)tag_len, ber_tlv_tag_string(tlv_tag)); switch(tag_len) { case -1: RETURN(RC_FAIL); case 0: RETURN(RC_WMORE); } tlv_constr = BER_TLV_CONSTRUCTED(ptr); /* * If {I}, don't check anything. * If {I,B,C}, check B and C unless we're at I. */ if(tag_mode != 0 && ctx->step == 0) { /* * We don't expect tag to match here. * It's just because we don't know how the tag * is supposed to look like. */ } else { assert(tagno >= 0); /* Guaranteed by the code above */ if(tlv_tag != head->tags[tagno]) { /* * Unexpected tag. Too bad. */ ASN_DEBUG("Expected: %s, expectation failed", ber_tlv_tag_string(head->tags[tagno])); RETURN(RC_FAIL); } } /* * Attention: if there are more tags expected, * ensure that the current tag is presented * in constructed form (it contains other tags!). * If this one is the last one, check that the tag form * matches the one given in descriptor. */ if(tagno < (head->tags_count - 1)) { if(tlv_constr == 0) { RETURN(RC_FAIL); } } else { if(head->last_tag_form != tlv_constr && head->last_tag_form != -1) { RETURN(RC_FAIL); } } /* * Fetch and process L from TLV. */ len_len = ber_fetch_length(tlv_constr, ptr + tag_len, size - tag_len, &tlv_len); switch(len_len) { case -1: RETURN(RC_FAIL); case 0: RETURN(RC_WMORE); } /* * FIXME * As of today, the chain of tags * must either contain several indefinite length TLVs, * or several definite length ones. * No mixing is allowed. */ if(tlv_len == -1) { /* * Indefinite length. */ if(limit_len == -1) { expect_00_terminators++; } else { ASN_DEBUG("Unexpected indefinite length " "in a chain of definite lengths"); RETURN(RC_FAIL); } ADVANCE(tag_len + len_len); continue; } else { if(expect_00_terminators) { ASN_DEBUG("Unexpected definite length " "in a chain of indefinite lengths"); RETURN(RC_FAIL); } } /* * Check that multiple TLVs specify ever decreasing length, * which is consistent. */ if(limit_len == -1) { limit_len = tlv_len + tag_len + len_len; } else if(limit_len != tlv_len + tag_len + len_len) { /* * Inner TLV specifies length which is inconsistent * with the outer TLV's length value. */ ASN_DEBUG("Outer TLV is %d and inner is %d", limit_len, tlv_len); RETURN(RC_FAIL); } ADVANCE(tag_len + len_len); limit_len -= (tag_len + len_len); if((ssize_t)size > limit_len) { /* * Make sure that we won't consume more bytes * from the large buffer than the inferred limit. */ size = limit_len; } } if(opt_tlv_form) *opt_tlv_form = tlv_constr; if(expect_00_terminators) *last_length = -expect_00_terminators; else *last_length = tlv_len; RETURN(RC_OK); }