From d502889132413a260e1bec5902269b1ea31d64cb Mon Sep 17 00:00:00 2001 From: jsing Date: Thu, 19 May 2022 19:45:18 +0000 Subject: [PATCH] Reorder functions within file. Order functions by use, moving public API to the bottom and utility functions to the top. This makes the code more logical/readable, plus we can remove all except one of the static function prototypes. No functional change. --- lib/libcrypto/asn1/tasn_dec.c | 1760 ++++++++++++++++----------------- 1 file changed, 872 insertions(+), 888 deletions(-) diff --git a/lib/libcrypto/asn1/tasn_dec.c b/lib/libcrypto/asn1/tasn_dec.c index 79cbd4c75bc..7583019aaab 100644 --- a/lib/libcrypto/asn1/tasn_dec.c +++ b/lib/libcrypto/asn1/tasn_dec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tasn_dec.c,v 1.71 2022/05/19 19:31:39 jsing Exp $ */ +/* $OpenBSD: tasn_dec.c,v 1.72 2022/05/19 19:45:18 jsing Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project 2000. */ @@ -69,541 +69,559 @@ #include "asn1_locl.h" #include "bytestring.h" -/* Constructed types with a recursive definition (such as can be found in PKCS7) +/* + * Constructed types with a recursive definition (such as can be found in PKCS7) * could eventually exceed the stack given malicious input with excessive * recursion. Therefore we limit the stack depth. */ #define ASN1_MAX_CONSTRUCTED_NEST 30 -static int asn1_check_eoc(CBS *cbs); -static int asn1_find_end(CBS *cbs, size_t length, char indefinite); - -static int asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag, - int expected_class, int depth); +#ifndef ASN1_MAX_STRING_NEST +/* + * This determines how many levels of recursion are permitted in ASN.1 string + * types. If it is not limited stack overflows can occur. If set to zero no + * recursion is allowed at all. Although zero should be adequate examples exist + * that require a value of 1. So 5 should be more than enough. + */ +#define ASN1_MAX_STRING_NEST 5 +#endif static int asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, char optional, int depth); -static int asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs, - const ASN1_TEMPLATE *tt, char optional, int depth); -static int asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *CBS, - const ASN1_ITEM *it, int tag_number, int tag_class, char optional); -static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, - const ASN1_ITEM *it, int tag_number, int tag_class, char optional); -static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, - const ASN1_ITEM *it); - -static int asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag, - uint8_t *out_class, char *out_indefinite, char *out_constructed, - int expected_tag, int expected_class, char optional); - -ASN1_VALUE * -ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen, - const ASN1_ITEM *it) -{ - ASN1_VALUE *ptmpval = NULL; - - if (pval == NULL) - pval = &ptmpval; - if (ASN1_item_ex_d2i(pval, in, inlen, it, -1, 0, 0, 0) <= 0) - return NULL; - return *pval; -} - -int -ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, - const ASN1_TEMPLATE *tt) +static int +asn1_check_eoc(CBS *cbs) { - CBS cbs; - int ret; + uint16_t eoc; - if (len < 0) + if (!CBS_peek_u16(cbs, &eoc)) + return 0; + if (eoc != 0) return 0; - CBS_init(&cbs, *in, len); - if ((ret = asn1_template_ex_d2i(pval, &cbs, tt, 0, 0)) == 1) - *in = CBS_data(&cbs); - - return ret; + return CBS_skip(cbs, 2); } static int -asn1_item_ex_d2i_choice(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional, int depth) +asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag, uint8_t *out_class, + char *out_indefinite, char *out_constructed, int expected_tag, + int expected_class, char optional) { - const ASN1_TEMPLATE *tt, *errtt = NULL; - const ASN1_AUX *aux; - ASN1_aux_cb *asn1_cb = NULL; - ASN1_VALUE *achoice = NULL; - ASN1_VALUE **pchptr; - int i, ret; - - if ((aux = it->funcs) != NULL) - asn1_cb = aux->asn1_cb; + int constructed, indefinite; + uint32_t tag_number; + uint8_t tag_class; + size_t length; - if (it->itype != ASN1_ITYPE_CHOICE) - goto err; + if (out_len != NULL) + *out_len = 0; + if (out_tag != NULL) + *out_tag = 0; + if (out_class != NULL) + *out_class = 0; + if (out_indefinite != NULL) + *out_indefinite = 0; + if (out_constructed != NULL) + *out_constructed = 0; - /* - * It never makes sense for CHOICE types to have implicit tagging, so - * if tag_number != -1, then this looks like an error in the template. - */ - if (tag_number != -1) { - ASN1error(ASN1_R_BAD_TEMPLATE); - goto err; + if (!asn1_get_identifier_cbs(cbs, 0, &tag_class, &constructed, + &tag_number)) { + ASN1error(ASN1_R_BAD_OBJECT_HEADER); + return 0; } + if (expected_tag >= 0) { + if (expected_tag != tag_number || + expected_class != tag_class << 6) { + /* Indicate missing type if this is OPTIONAL. */ + if (optional) + return -1; - if (*pval != NULL) { - ASN1_item_ex_free(pval, it); - *pval = NULL; + ASN1error(ASN1_R_WRONG_TAG); + return 0; + } } - - if (!ASN1_item_ex_new(&achoice, it)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - goto err; + if (!asn1_get_length_cbs(cbs, 0, &indefinite, &length)) { + ASN1error(ASN1_R_BAD_OBJECT_HEADER); + return 0; } - if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &achoice, it, NULL)) { - ASN1error(ASN1_R_AUX_ERROR); - goto err; + /* Indefinite length can only be used with constructed encoding. */ + if (indefinite && !constructed) { + ASN1error(ASN1_R_BAD_OBJECT_HEADER); + return 0; } - /* Try each possible CHOICE in turn. */ - for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) { - pchptr = asn1_get_field_ptr(&achoice, tt); - - /* Mark field as OPTIONAL so its absence can be identified. */ - ret = asn1_template_ex_d2i(pchptr, cbs, tt, 1, depth); - if (ret == -1) - continue; - if (ret != 1) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - errtt = tt; - goto err; - } - - /* We've successfully decoded an ASN.1 object. */ - asn1_set_choice_selector(&achoice, i, it); - break; + if (!indefinite && CBS_len(cbs) < length) { + ASN1error(ASN1_R_TOO_LONG); + return 0; } - /* Did we fall off the end without reading anything? */ - if (i == it->tcount) { - if (optional) { - ASN1_item_ex_free(&achoice, it); - return -1; - } - ASN1error(ASN1_R_NO_MATCHING_CHOICE_TYPE); - goto err; + if (tag_number > INT_MAX) { + ASN1error(ASN1_R_TOO_LONG); + return 0; } - if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &achoice, it, NULL)) { - ASN1error(ASN1_R_AUX_ERROR); - goto err; - } + if (indefinite) + length = CBS_len(cbs); - *pval = achoice; - achoice = NULL; + if (out_len != NULL) + *out_len = length; + if (out_tag != NULL) + *out_tag = tag_number; + if (out_class != NULL) + *out_class = tag_class << 6; + if (out_indefinite != NULL && indefinite) + *out_indefinite = 1 << 0; + if (out_constructed != NULL && constructed) + *out_constructed = 1 << 5; return 1; - - err: - ASN1_item_ex_free(&achoice, it); - - if (errtt) - ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name, - it->sname); - else - ERR_asprintf_error_data("Type=%s", it->sname); - - return 0; } +/* Collect the contents from a constructed ASN.1 object. */ static int -asn1_item_ex_d2i_sequence(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional, int depth) +asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag, + int expected_class, int depth) { - CBS cbs_seq, cbs_seq_content, cbs_object; - char constructed, indefinite, optional_field; - const ASN1_TEMPLATE *errtt = NULL; - const ASN1_TEMPLATE *seqtt, *tt; - ASN1_aux_cb *asn1_cb = NULL; - const ASN1_AUX *aux; - ASN1_VALUE *aseq = NULL; - ASN1_VALUE **pseqval; - int eoc_needed, i; + char constructed; size_t length; - int ret = 0; - - CBS_init(&cbs_seq, CBS_data(cbs), CBS_len(cbs)); + CBS content; + int need_eoc; - if ((aux = it->funcs) != NULL) - asn1_cb = aux->asn1_cb; + if (depth > ASN1_MAX_STRING_NEST) { + ASN1error(ASN1_R_NESTED_ASN1_STRING); + return 0; + } - if (it->itype != ASN1_ITYPE_NDEF_SEQUENCE && - it->itype != ASN1_ITYPE_SEQUENCE) - goto err; + need_eoc = indefinite; - if (*pval != NULL) { - ASN1_item_ex_free(pval, it); - *pval = NULL; - } + while (CBS_len(cbs) > 0) { + if (asn1_check_eoc(cbs)) { + if (!need_eoc) { + ASN1error(ASN1_R_UNEXPECTED_EOC); + return 0; + } + return 1; + } + if (!asn1_check_tag(cbs, &length, NULL, NULL, &indefinite, + &constructed, expected_tag, expected_class, 0)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; + } - /* If no IMPLICIT tagging use UNIVERSAL/SEQUENCE. */ - if (tag_number == -1) { - tag_class = V_ASN1_UNIVERSAL; - tag_number = V_ASN1_SEQUENCE; - } + if (constructed) { + if (!asn1_collect(cbb, cbs, indefinite, expected_tag, + expected_class, depth + 1)) + return 0; + continue; + } - /* Read ASN.1 SEQUENCE header. */ - ret = asn1_check_tag(&cbs_seq, &length, NULL, NULL, &indefinite, - &constructed, tag_number, tag_class, optional); - if (ret == -1) - return -1; - if (ret != 1) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - goto err; + if (!CBS_get_bytes(cbs, &content, length)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; + } + if (!CBB_add_bytes(cbb, CBS_data(&content), CBS_len(&content))) + return 0; } - if (!constructed) { - ASN1error(ASN1_R_SEQUENCE_NOT_CONSTRUCTED); - goto err; + if (need_eoc) { + ASN1error(ASN1_R_MISSING_EOC); + return 0; } - if (indefinite) { - eoc_needed = 1; - CBS_init(&cbs_seq_content, CBS_data(&cbs_seq), CBS_len(&cbs_seq)); - } else { - eoc_needed = 0; - if (!CBS_get_bytes(&cbs_seq, &cbs_seq_content, length)) - goto err; - } + return 1; +} - if (!ASN1_item_ex_new(&aseq, it)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - goto err; - } - - if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &aseq, it, NULL)) { - ASN1error(ASN1_R_AUX_ERROR); - goto err; - } +/* Find the end of an ASN.1 object. */ +static int +asn1_find_end(CBS *cbs, size_t length, char indefinite) +{ + size_t eoc_count; - for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) { - if (asn1_check_eoc(&cbs_seq_content)) { - if (!indefinite) { - ASN1error(ASN1_R_UNEXPECTED_EOC); - goto err; - } - eoc_needed = 0; - break; + if (!indefinite) { + if (!CBS_skip(cbs, length)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; } - if (CBS_len(&cbs_seq_content) == 0) - break; - - if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL) - goto err; - - pseqval = asn1_get_field_ptr(&aseq, seqtt); + return 1; + } - /* - * This was originally implemented to "increase efficiency", - * however it currently needs to remain since it papers over - * the use of ASN.1 ANY with OPTIONAL in SEQUENCEs (which - * asn1_d2i_ex_primitive() currently rejects). - */ - optional_field = (seqtt->flags & ASN1_TFLG_OPTIONAL) != 0; - if (i == it->tcount - 1) - optional_field = 0; + eoc_count = 1; - ret = asn1_template_ex_d2i(pseqval, &cbs_seq_content, - seqtt, optional_field, depth); - if (ret == -1) { - /* Absent OPTIONAL component. */ - ASN1_template_free(pseqval, seqtt); + while (CBS_len(cbs) > 0) { + if (asn1_check_eoc(cbs)) { + if (--eoc_count == 0) + break; continue; } - if (ret != 1) { - errtt = seqtt; - goto err; + if (!asn1_check_tag(cbs, &length, NULL, NULL, + &indefinite, NULL, -1, 0, 0)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; + } + if (indefinite) { + eoc_count++; + continue; } + if (!CBS_skip(cbs, length)) + return 0; } - if (eoc_needed && !asn1_check_eoc(&cbs_seq_content)) { + if (eoc_count > 0) { ASN1error(ASN1_R_MISSING_EOC); - goto err; + return 0; } - if (indefinite) { - if (!CBS_skip(&cbs_seq, CBS_offset(&cbs_seq_content))) + return 1; +} + +static int +asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) +{ + ASN1_STRING *stmp; + ASN1_INTEGER **tint; + ASN1_BOOLEAN *tbool; + uint8_t u8val; + int ret = 0; + + if (it->funcs != NULL) + return 0; + + if (CBS_len(content) > INT_MAX) + return 0; + + switch (utype) { + case V_ASN1_OBJECT: + if (!c2i_ASN1_OBJECT_cbs((ASN1_OBJECT **)pval, content)) goto err; - } else if (CBS_len(&cbs_seq_content) != 0) { - ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH); - goto err; - } + break; - /* - * There is no more data in the ASN.1 SEQUENCE, however we may not have - * populated all fields - check that any remaining are OPTIONAL. - */ - for (; i < it->tcount; tt++, i++) { - if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL) + case V_ASN1_NULL: + if (CBS_len(content) != 0) { + ASN1error(ASN1_R_NULL_IS_WRONG_LENGTH); goto err; + } + *pval = (ASN1_VALUE *)1; + break; - if ((seqtt->flags & ASN1_TFLG_OPTIONAL) == 0) { - ASN1error(ASN1_R_FIELD_MISSING); - errtt = seqtt; + case V_ASN1_BOOLEAN: + tbool = (ASN1_BOOLEAN *)pval; + if (CBS_len(content) != 1) { + ASN1error(ASN1_R_BOOLEAN_IS_WRONG_LENGTH); goto err; } + if (!CBS_get_u8(content, &u8val)) + goto err; + *tbool = u8val; + break; - /* XXX - this is probably unnecessary with earlier free. */ - pseqval = asn1_get_field_ptr(&aseq, seqtt); - ASN1_template_free(pseqval, seqtt); + case V_ASN1_BIT_STRING: + if (!c2i_ASN1_BIT_STRING_cbs((ASN1_BIT_STRING **)pval, content)) + goto err; + break; + + case V_ASN1_INTEGER: + case V_ASN1_ENUMERATED: + tint = (ASN1_INTEGER **)pval; + if (!c2i_ASN1_INTEGER_cbs(tint, content)) + goto err; + /* Fixup type to match the expected form */ + (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG); + break; + + case V_ASN1_OCTET_STRING: + case V_ASN1_NUMERICSTRING: + case V_ASN1_PRINTABLESTRING: + case V_ASN1_T61STRING: + case V_ASN1_VIDEOTEXSTRING: + case V_ASN1_IA5STRING: + case V_ASN1_UTCTIME: + case V_ASN1_GENERALIZEDTIME: + case V_ASN1_GRAPHICSTRING: + case V_ASN1_VISIBLESTRING: + case V_ASN1_GENERALSTRING: + case V_ASN1_UNIVERSALSTRING: + case V_ASN1_BMPSTRING: + case V_ASN1_UTF8STRING: + case V_ASN1_OTHER: + case V_ASN1_SET: + case V_ASN1_SEQUENCE: + default: + if (utype == V_ASN1_BMPSTRING && (CBS_len(content) & 1)) { + ASN1error(ASN1_R_BMPSTRING_IS_WRONG_LENGTH); + goto err; + } + if (utype == V_ASN1_UNIVERSALSTRING && (CBS_len(content) & 3)) { + ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH); + goto err; + } + /* All based on ASN1_STRING and handled the same way. */ + if (*pval == NULL) { + if ((stmp = ASN1_STRING_type_new(utype)) == NULL) { + ASN1error(ERR_R_MALLOC_FAILURE); + goto err; + } + *pval = (ASN1_VALUE *)stmp; + } else { + stmp = (ASN1_STRING *)*pval; + stmp->type = utype; + } + if (!ASN1_STRING_set(stmp, CBS_data(content), CBS_len(content))) { + ASN1_STRING_free(stmp); + *pval = NULL; + goto err; + } + break; } - if (!CBS_get_bytes(cbs, &cbs_object, CBS_offset(&cbs_seq))) - goto err; + ret = 1; - if (!asn1_enc_save(&aseq, &cbs_object, it)) { - ASN1error(ERR_R_MALLOC_FAILURE); - goto err; + err: + return ret; +} + +static int +asn1_ex_c2i_any(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) +{ + ASN1_TYPE *atype; + + if (it->utype != V_ASN1_ANY || it->funcs != NULL) + return 0; + + if (*pval != NULL) { + ASN1_TYPE_free((ASN1_TYPE *)*pval); + *pval = NULL; } - if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &aseq, it, NULL)) { - ASN1error(ASN1_R_AUX_ERROR); - goto err; + if ((atype = ASN1_TYPE_new()) == NULL) + return 0; + + if (!asn1_ex_c2i_primitive(&atype->value.asn1_value, content, utype, it)) { + ASN1_TYPE_free(atype); + return 0; } + atype->type = utype; - *pval = aseq; - aseq = NULL; + /* Fix up value for ASN.1 NULL. */ + if (atype->type == V_ASN1_NULL) + atype->value.ptr = NULL; + + *pval = (ASN1_VALUE *)atype; return 1; +} - err: - ASN1_item_ex_free(&aseq, it); +static int +asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) +{ + if (CBS_len(content) > INT_MAX) + return 0; - if (errtt != NULL) - ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name, - it->sname); - else - ERR_asprintf_error_data("Type=%s", it->sname); + if (it->funcs != NULL) { + const ASN1_PRIMITIVE_FUNCS *pf = it->funcs; + char free_content = 0; - return 0; + if (pf->prim_c2i == NULL) + return 0; + + return pf->prim_c2i(pval, CBS_data(content), CBS_len(content), + utype, &free_content, it); + } + + if (it->utype == V_ASN1_ANY) + return asn1_ex_c2i_any(pval, content, utype, it); + + return asn1_ex_c2i_primitive(pval, content, utype, it); } /* - * Decode an item, taking care of IMPLICIT tagging, if any. - * If 'opt' set and tag mismatch return -1 to handle OPTIONAL + * Decode ASN.1 content into a primitive type. There are three possible forms - + * a SEQUENCE/SET/OTHER that is stored verbatim (including the ASN.1 tag and + * length octets), constructed objects and non-constructed objects. In the + * first two cases indefinite length is permitted, which we may need to handle. + * When this function is called the *cbs should reference the start of the + * ASN.1 object (i.e. the tag/length header), while *cbs_object should + * reference the start of the object contents (i.e. after the tag/length + * header. Additionally, the *cbs_object offset should be relative to the + * ASN.1 object being parsed. On success the *cbs will point at the octet + * after the object. */ static int -asn1_item_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional, int depth) +asn1_d2i_ex_primitive_content(ASN1_VALUE **pval, CBS *cbs, CBS *cbs_object, + int utype, char constructed, char indefinite, size_t length, + const ASN1_ITEM *it) { - const ASN1_EXTERN_FUNCS *ef = it->funcs; - const unsigned char *p = NULL; - ASN1_TLC ctx = { 0 }; + CBS cbs_content, cbs_initial; + uint8_t *data = NULL; + size_t data_len = 0; + CBB cbb; int ret = 0; - if (pval == NULL) - return 0; + memset(&cbb, 0, sizeof(cbb)); - if (++depth > ASN1_MAX_CONSTRUCTED_NEST) { - ASN1error(ASN1_R_NESTED_TOO_DEEP); + CBS_dup(cbs, &cbs_initial); + CBS_init(&cbs_content, NULL, 0); + + /* XXX - check primitive vs constructed based on utype. */ + + /* SEQUENCE and SET must be constructed. */ + if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) { + ASN1error(ASN1_R_TYPE_NOT_CONSTRUCTED); goto err; } - switch (it->itype) { - case ASN1_ITYPE_PRIMITIVE: - if (it->templates != NULL) { - /* - * Tagging or OPTIONAL is currently illegal on an item - * template because the flags can't get passed down. - * In practice this isn't a problem: we include the - * relevant flags from the item template in the - * template itself. - */ - if (tag_number != -1 || optional) { - ASN1error(ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE); - goto err; - } - return asn1_template_ex_d2i(pval, cbs, - it->templates, optional, depth); - } - return asn1_d2i_ex_primitive(pval, cbs, it, tag_number, - tag_class, optional); - - case ASN1_ITYPE_MSTRING: - return asn1_d2i_ex_mstring(pval, cbs, it, tag_number, tag_class, - optional); + /* SEQUENCE, SET and "OTHER" are left in encoded form. */ + if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET || + utype == V_ASN1_OTHER) { + if (!asn1_find_end(cbs_object, length, indefinite)) + goto err; + if (!CBS_get_bytes(&cbs_initial, &cbs_content, + CBS_offset(cbs_object))) + goto err; + } else if (constructed) { + /* + * Should really check the internal tags are correct but + * some things may get this wrong. The relevant specs + * say that constructed string types should be OCTET STRINGs + * internally irrespective of the type. So instead just check + * for UNIVERSAL class and ignore the tag. + */ + if (!CBB_init(&cbb, 0)) + goto err; + if (!asn1_collect(&cbb, cbs_object, indefinite, -1, + V_ASN1_UNIVERSAL, 0)) + goto err; + if (!CBB_finish(&cbb, &data, &data_len)) + goto err; - case ASN1_ITYPE_EXTERN: - if (CBS_len(cbs) > LONG_MAX) - return 0; - p = CBS_data(cbs); - if ((ret = ef->asn1_ex_d2i(pval, &p, (long)CBS_len(cbs), it, - tag_number, tag_class, optional, &ctx)) == 1) { - if (!CBS_skip(cbs, p - CBS_data(cbs))) - goto err; - } - return ret; + CBS_init(&cbs_content, data, data_len); + } else { + if (!CBS_get_bytes(cbs_object, &cbs_content, length)) + goto err; + } - case ASN1_ITYPE_CHOICE: - return asn1_item_ex_d2i_choice(pval, cbs, it, tag_number, - tag_class, optional, depth); + if (!asn1_ex_c2i(pval, &cbs_content, utype, it)) + goto err; - case ASN1_ITYPE_NDEF_SEQUENCE: - case ASN1_ITYPE_SEQUENCE: - return asn1_item_ex_d2i_sequence(pval, cbs, it, tag_number, - tag_class, optional, depth); + if (!CBS_skip(cbs, CBS_offset(cbs_object))) + goto err; - default: - return 0; - } + ret = 1; err: - ASN1_item_ex_free(pval, it); - - ERR_asprintf_error_data("Type=%s", it->sname); + CBB_cleanup(&cbb); + freezero(data, data_len); - return 0; + return ret; } -int -ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen, - const ASN1_ITEM *it, int tag_number, int tag_class, char optional, - ASN1_TLC *ctx) +static int +asn1_d2i_ex_any(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional) { - CBS cbs; - int ret; + char constructed, indefinite; + unsigned char object_class; + int object_type; + CBS cbs_object; + size_t length; - if (inlen < 0) + CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); + + if (it->utype != V_ASN1_ANY) return 0; - CBS_init(&cbs, *in, inlen); + if (tag_number >= 0) { + ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY); + return 0; + } + if (optional) { + ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY); + return 0; + } - if ((ret = asn1_item_ex_d2i(pval, &cbs, it, tag_number, tag_class, - optional, 0)) == 1) - *in = CBS_data(&cbs); + /* Determine type from ASN.1 tag. */ + if (asn1_check_tag(&cbs_object, &length, &object_type, &object_class, + &indefinite, &constructed, -1, 0, 0) != 1) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; + } + if (object_class != V_ASN1_UNIVERSAL) + object_type = V_ASN1_OTHER; - return ret; + return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, object_type, + constructed, indefinite, length, it); } static int -asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, - char optional, int depth) +asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional) { - CBS cbs_exp, cbs_exp_content; char constructed, indefinite; + unsigned char object_class; + int object_tag; + CBS cbs_object; size_t length; - int ret; - - if (pval == NULL) - return 0; - - /* Check if EXPLICIT tag is expected. */ - if ((tt->flags & ASN1_TFLG_EXPTAG) == 0) - return asn1_template_noexp_d2i(pval, cbs, tt, optional, depth); - - CBS_init(&cbs_exp, CBS_data(cbs), CBS_len(cbs)); - /* Read ASN.1 header for EXPLICIT tagged object. */ - ret = asn1_check_tag(&cbs_exp, &length, NULL, NULL, &indefinite, - &constructed, tt->tag, tt->flags & ASN1_TFLG_TAG_CLASS, optional); - if (ret == -1) - return -1; - if (ret != 1) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } + CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); - if (!constructed) { - ASN1error(ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED); + /* + * It never makes sense for multi-strings to have implicit tagging, so + * if tag_number != -1, then this looks like an error in the template. + */ + if (tag_number != -1) { + ASN1error(ASN1_R_BAD_TEMPLATE); return 0; } - if (indefinite) { - CBS_init(&cbs_exp_content, CBS_data(&cbs_exp), CBS_len(&cbs_exp)); - } else { - if (!CBS_get_bytes(&cbs_exp, &cbs_exp_content, length)) - goto err; - } - - if ((ret = asn1_template_noexp_d2i(pval, &cbs_exp_content, tt, 0, - depth)) != 1) { + if (asn1_check_tag(&cbs_object, &length, &object_tag, &object_class, + &indefinite, &constructed, -1, 0, 1) != 1) { ASN1error(ERR_R_NESTED_ASN1_ERROR); return 0; } - if (indefinite) { - if (!asn1_check_eoc(&cbs_exp_content)) { - ASN1error(ASN1_R_MISSING_EOC); - goto err; - } - if (!CBS_skip(&cbs_exp, CBS_offset(&cbs_exp_content))) - goto err; - } else if (CBS_len(&cbs_exp_content) != 0) { - ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH); - goto err; + /* Class must be UNIVERSAL. */ + if (object_class != V_ASN1_UNIVERSAL) { + if (optional) + return -1; + ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL); + return 0; } - - if (!CBS_skip(cbs, CBS_offset(&cbs_exp))) - goto err; - - return 1; - - err: - ASN1_template_free(pval, tt); - return 0; -} - -static void -asn1_template_stack_of_free(STACK_OF(ASN1_VALUE) *avals, const ASN1_TEMPLATE *tt) { - ASN1_VALUE *aval; - - if (avals == NULL) - return; - - while (sk_ASN1_VALUE_num(avals) > 0) { - aval = sk_ASN1_VALUE_pop(avals); - ASN1_item_ex_free(&aval, tt->item); + /* Check tag matches bit map. */ + if ((ASN1_tag2bit(object_tag) & it->utype) == 0) { + if (optional) + return -1; + ASN1error(ASN1_R_MSTRING_WRONG_TAG); + return 0; } - sk_ASN1_VALUE_free(avals); + + return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, + object_tag, constructed, indefinite, length, it); } static int -asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, - char optional, int depth) +asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional) { - CBS cbs_object, cbs_object_content; - STACK_OF(ASN1_VALUE) *avals = NULL; - ASN1_VALUE *aval = NULL; - int tag_number, tag_class; - int eoc_needed; - char indefinite; + CBS cbs_object; + char constructed, indefinite; + int utype = it->utype; size_t length; int ret; CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); - if (pval == NULL) + if (it->itype == ASN1_ITYPE_MSTRING) return 0; - asn1_template_stack_of_free((STACK_OF(ASN1_VALUE) *)*pval, tt); - *pval = NULL; - - tag_number = tt->tag; - tag_class = tt->flags & ASN1_TFLG_TAG_CLASS; + if (it->utype == V_ASN1_ANY) + return asn1_d2i_ex_any(pval, cbs, it, tag_number, tag_class, optional); - /* Determine the inner tag value for SET OF or SEQUENCE OF. */ - if ((tt->flags & ASN1_TFLG_IMPTAG) == 0) { - tag_number = V_ASN1_SEQUENCE; + if (tag_number == -1) { + tag_number = it->utype; tag_class = V_ASN1_UNIVERSAL; - if ((tt->flags & ASN1_TFLG_SET_OF) != 0) - tag_number = V_ASN1_SET; } ret = asn1_check_tag(&cbs_object, &length, NULL, NULL, &indefinite, - NULL, tag_number, tag_class, optional); + &constructed, tag_number, tag_class, optional); if (ret == -1) return -1; if (ret != 1) { @@ -611,296 +629,399 @@ asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, return 0; } - if (indefinite) { - eoc_needed = 1; - CBS_init(&cbs_object_content, CBS_data(&cbs_object), - CBS_len(&cbs_object)); - } else { - eoc_needed = 0; - if (!CBS_get_bytes(&cbs_object, &cbs_object_content, - length)) - goto err; + return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, utype, + constructed, indefinite, length, it); +} + +static int +asn1_item_ex_d2i_choice(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional, int depth) +{ + const ASN1_TEMPLATE *tt, *errtt = NULL; + const ASN1_AUX *aux; + ASN1_aux_cb *asn1_cb = NULL; + ASN1_VALUE *achoice = NULL; + ASN1_VALUE **pchptr; + int i, ret; + + if ((aux = it->funcs) != NULL) + asn1_cb = aux->asn1_cb; + + if (it->itype != ASN1_ITYPE_CHOICE) + goto err; + + /* + * It never makes sense for CHOICE types to have implicit tagging, so + * if tag_number != -1, then this looks like an error in the template. + */ + if (tag_number != -1) { + ASN1error(ASN1_R_BAD_TEMPLATE); + goto err; } - if ((avals = sk_ASN1_VALUE_new_null()) == NULL) { - ASN1error(ERR_R_MALLOC_FAILURE); + if (*pval != NULL) { + ASN1_item_ex_free(pval, it); + *pval = NULL; + } + + if (!ASN1_item_ex_new(&achoice, it)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); goto err; } - /* Read as many items as possible. */ - while (CBS_len(&cbs_object_content) > 0) { - if (asn1_check_eoc(&cbs_object_content)) { - if (!eoc_needed) { - ASN1error(ASN1_R_UNEXPECTED_EOC); - goto err; - } - eoc_needed = 0; - break; - } - if (!asn1_item_ex_d2i(&aval, &cbs_object_content, tt->item, - -1, 0, 0, depth)) { + if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &achoice, it, NULL)) { + ASN1error(ASN1_R_AUX_ERROR); + goto err; + } + + /* Try each possible CHOICE in turn. */ + for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) { + pchptr = asn1_get_field_ptr(&achoice, tt); + + /* Mark field as OPTIONAL so its absence can be identified. */ + ret = asn1_template_ex_d2i(pchptr, cbs, tt, 1, depth); + if (ret == -1) + continue; + if (ret != 1) { ASN1error(ERR_R_NESTED_ASN1_ERROR); + errtt = tt; goto err; } - if (!sk_ASN1_VALUE_push(avals, aval)) { - ASN1error(ERR_R_MALLOC_FAILURE); - goto err; - } - aval = NULL; - } - if (eoc_needed) { - ASN1error(ASN1_R_MISSING_EOC); - goto err; + + /* We've successfully decoded an ASN.1 object. */ + asn1_set_choice_selector(&achoice, i, it); + break; } - if (indefinite) { - if (!CBS_skip(&cbs_object, CBS_offset(&cbs_object_content))) - goto err; + /* Did we fall off the end without reading anything? */ + if (i == it->tcount) { + if (optional) { + ASN1_item_ex_free(&achoice, it); + return -1; + } + ASN1error(ASN1_R_NO_MATCHING_CHOICE_TYPE); + goto err; } - if (!CBS_skip(cbs, CBS_offset(&cbs_object))) + if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &achoice, it, NULL)) { + ASN1error(ASN1_R_AUX_ERROR); goto err; + } - *pval = (ASN1_VALUE *)avals; - avals = NULL; + *pval = achoice; + achoice = NULL; return 1; err: - asn1_template_stack_of_free(avals, tt); - ASN1_item_ex_free(&aval, tt->item); + ASN1_item_ex_free(&achoice, it); + + if (errtt) + ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name, + it->sname); + else + ERR_asprintf_error_data("Type=%s", it->sname); return 0; } static int -asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, - char optional, int depth) +asn1_item_ex_d2i_sequence(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional, int depth) { - int tag_number, tag_class; - int ret; + CBS cbs_seq, cbs_seq_content, cbs_object; + char constructed, indefinite, optional_field; + const ASN1_TEMPLATE *errtt = NULL; + const ASN1_TEMPLATE *seqtt, *tt; + ASN1_aux_cb *asn1_cb = NULL; + const ASN1_AUX *aux; + ASN1_VALUE *aseq = NULL; + ASN1_VALUE **pseqval; + int eoc_needed, i; + size_t length; + int ret = 0; - if (pval == NULL) - return 0; + CBS_init(&cbs_seq, CBS_data(cbs), CBS_len(cbs)); - if ((tt->flags & ASN1_TFLG_SK_MASK) != 0) - return asn1_template_stack_of_d2i(pval, cbs, tt, optional, depth); + if ((aux = it->funcs) != NULL) + asn1_cb = aux->asn1_cb; - tag_number = -1; - tag_class = V_ASN1_UNIVERSAL; + if (it->itype != ASN1_ITYPE_NDEF_SEQUENCE && + it->itype != ASN1_ITYPE_SEQUENCE) + goto err; - /* See if we need to use IMPLICIT tagging. */ - if ((tt->flags & ASN1_TFLG_IMPTAG) != 0) { - tag_number = tt->tag; - tag_class = tt->flags & ASN1_TFLG_TAG_CLASS; + if (*pval != NULL) { + ASN1_item_ex_free(pval, it); + *pval = NULL; + } + + /* If no IMPLICIT tagging use UNIVERSAL/SEQUENCE. */ + if (tag_number == -1) { + tag_class = V_ASN1_UNIVERSAL; + tag_number = V_ASN1_SEQUENCE; + } + + /* Read ASN.1 SEQUENCE header. */ + ret = asn1_check_tag(&cbs_seq, &length, NULL, NULL, &indefinite, + &constructed, tag_number, tag_class, optional); + if (ret == -1) + return -1; + if (ret != 1) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + goto err; + } + + if (!constructed) { + ASN1error(ASN1_R_SEQUENCE_NOT_CONSTRUCTED); + goto err; + } + + if (indefinite) { + eoc_needed = 1; + CBS_init(&cbs_seq_content, CBS_data(&cbs_seq), CBS_len(&cbs_seq)); + } else { + eoc_needed = 0; + if (!CBS_get_bytes(&cbs_seq, &cbs_seq_content, length)) + goto err; } - ret = asn1_item_ex_d2i(pval, cbs, tt->item, tag_number, tag_class, - optional, depth); - if (ret == -1) - return -1; - if (ret != 1) { + if (!ASN1_item_ex_new(&aseq, it)) { ASN1error(ERR_R_NESTED_ASN1_ERROR); goto err; } - return 1; + if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &aseq, it, NULL)) { + ASN1error(ASN1_R_AUX_ERROR); + goto err; + } - err: - /* XXX - The called function should have freed already. */ - ASN1_template_free(pval, tt); - return 0; -} + for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) { + if (asn1_check_eoc(&cbs_seq_content)) { + if (!indefinite) { + ASN1error(ASN1_R_UNEXPECTED_EOC); + goto err; + } + eoc_needed = 0; + break; + } + if (CBS_len(&cbs_seq_content) == 0) + break; -/* - * Decode ASN.1 content into a primitive type. There are three possible forms - - * a SEQUENCE/SET/OTHER that is stored verbatim (including the ASN.1 tag and - * length octets), constructed objects and non-constructed objects. In the - * first two cases indefinite length is permitted, which we may need to handle. - * When this function is called the *cbs should reference the start of the - * ASN.1 object (i.e. the tag/length header), while *cbs_object should - * reference the start of the object contents (i.e. after the tag/length - * header. Additionally, the *cbs_object offset should be relative to the - * ASN.1 object being parsed. On success the *cbs will point at the octet - * after the object. - */ -static int -asn1_d2i_ex_primitive_content(ASN1_VALUE **pval, CBS *cbs, CBS *cbs_object, - int utype, char constructed, char indefinite, size_t length, - const ASN1_ITEM *it) -{ - CBS cbs_content, cbs_initial; - uint8_t *data = NULL; - size_t data_len = 0; - CBB cbb; - int ret = 0; + if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL) + goto err; - memset(&cbb, 0, sizeof(cbb)); + pseqval = asn1_get_field_ptr(&aseq, seqtt); - CBS_dup(cbs, &cbs_initial); - CBS_init(&cbs_content, NULL, 0); + /* + * This was originally implemented to "increase efficiency", + * however it currently needs to remain since it papers over + * the use of ASN.1 ANY with OPTIONAL in SEQUENCEs (which + * asn1_d2i_ex_primitive() currently rejects). + */ + optional_field = (seqtt->flags & ASN1_TFLG_OPTIONAL) != 0; + if (i == it->tcount - 1) + optional_field = 0; - /* XXX - check primitive vs constructed based on utype. */ + ret = asn1_template_ex_d2i(pseqval, &cbs_seq_content, + seqtt, optional_field, depth); + if (ret == -1) { + /* Absent OPTIONAL component. */ + ASN1_template_free(pseqval, seqtt); + continue; + } + if (ret != 1) { + errtt = seqtt; + goto err; + } + } - /* SEQUENCE and SET must be constructed. */ - if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) { - ASN1error(ASN1_R_TYPE_NOT_CONSTRUCTED); + if (eoc_needed && !asn1_check_eoc(&cbs_seq_content)) { + ASN1error(ASN1_R_MISSING_EOC); goto err; } - /* SEQUENCE, SET and "OTHER" are left in encoded form. */ - if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET || - utype == V_ASN1_OTHER) { - if (!asn1_find_end(cbs_object, length, indefinite)) - goto err; - if (!CBS_get_bytes(&cbs_initial, &cbs_content, - CBS_offset(cbs_object))) - goto err; - } else if (constructed) { - /* - * Should really check the internal tags are correct but - * some things may get this wrong. The relevant specs - * say that constructed string types should be OCTET STRINGs - * internally irrespective of the type. So instead just check - * for UNIVERSAL class and ignore the tag. - */ - if (!CBB_init(&cbb, 0)) - goto err; - if (!asn1_collect(&cbb, cbs_object, indefinite, -1, - V_ASN1_UNIVERSAL, 0)) + if (indefinite) { + if (!CBS_skip(&cbs_seq, CBS_offset(&cbs_seq_content))) goto err; - if (!CBB_finish(&cbb, &data, &data_len)) + } else if (CBS_len(&cbs_seq_content) != 0) { + ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH); + goto err; + } + + /* + * There is no more data in the ASN.1 SEQUENCE, however we may not have + * populated all fields - check that any remaining are OPTIONAL. + */ + for (; i < it->tcount; tt++, i++) { + if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL) goto err; - CBS_init(&cbs_content, data, data_len); - } else { - if (!CBS_get_bytes(cbs_object, &cbs_content, length)) + if ((seqtt->flags & ASN1_TFLG_OPTIONAL) == 0) { + ASN1error(ASN1_R_FIELD_MISSING); + errtt = seqtt; goto err; + } + + /* XXX - this is probably unnecessary with earlier free. */ + pseqval = asn1_get_field_ptr(&aseq, seqtt); + ASN1_template_free(pseqval, seqtt); } - if (!asn1_ex_c2i(pval, &cbs_content, utype, it)) + if (!CBS_get_bytes(cbs, &cbs_object, CBS_offset(&cbs_seq))) goto err; - if (!CBS_skip(cbs, CBS_offset(cbs_object))) + if (!asn1_enc_save(&aseq, &cbs_object, it)) { + ASN1error(ERR_R_MALLOC_FAILURE); goto err; + } - ret = 1; + if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &aseq, it, NULL)) { + ASN1error(ASN1_R_AUX_ERROR); + goto err; + } + + *pval = aseq; + aseq = NULL; + + return 1; err: - CBB_cleanup(&cbb); - freezero(data, data_len); + ASN1_item_ex_free(&aseq, it); - return ret; + if (errtt != NULL) + ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name, + it->sname); + else + ERR_asprintf_error_data("Type=%s", it->sname); + + return 0; } +/* + * Decode an item, taking care of IMPLICIT tagging, if any. + * If 'opt' set and tag mismatch return -1 to handle OPTIONAL + */ static int -asn1_d2i_ex_any(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional) +asn1_item_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, + int tag_number, int tag_class, char optional, int depth) { - char constructed, indefinite; - unsigned char object_class; - int object_type; - CBS cbs_object; - size_t length; - - CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); + const ASN1_EXTERN_FUNCS *ef = it->funcs; + const unsigned char *p = NULL; + ASN1_TLC ctx = { 0 }; + int ret = 0; - if (it->utype != V_ASN1_ANY) + if (pval == NULL) return 0; - if (tag_number >= 0) { - ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY); - return 0; - } - if (optional) { - ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY); - return 0; + if (++depth > ASN1_MAX_CONSTRUCTED_NEST) { + ASN1error(ASN1_R_NESTED_TOO_DEEP); + goto err; } - /* Determine type from ASN.1 tag. */ - if (asn1_check_tag(&cbs_object, &length, &object_type, &object_class, - &indefinite, &constructed, -1, 0, 0) != 1) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } - if (object_class != V_ASN1_UNIVERSAL) - object_type = V_ASN1_OTHER; + switch (it->itype) { + case ASN1_ITYPE_PRIMITIVE: + if (it->templates != NULL) { + /* + * Tagging or OPTIONAL is currently illegal on an item + * template because the flags can't get passed down. + * In practice this isn't a problem: we include the + * relevant flags from the item template in the + * template itself. + */ + if (tag_number != -1 || optional) { + ASN1error(ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE); + goto err; + } + return asn1_template_ex_d2i(pval, cbs, + it->templates, optional, depth); + } + return asn1_d2i_ex_primitive(pval, cbs, it, tag_number, + tag_class, optional); - return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, object_type, - constructed, indefinite, length, it); -} + case ASN1_ITYPE_MSTRING: + return asn1_d2i_ex_mstring(pval, cbs, it, tag_number, tag_class, + optional); -static int -asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional) -{ - char constructed, indefinite; - unsigned char object_class; - int object_tag; - CBS cbs_object; - size_t length; + case ASN1_ITYPE_EXTERN: + if (CBS_len(cbs) > LONG_MAX) + return 0; + p = CBS_data(cbs); + if ((ret = ef->asn1_ex_d2i(pval, &p, (long)CBS_len(cbs), it, + tag_number, tag_class, optional, &ctx)) == 1) { + if (!CBS_skip(cbs, p - CBS_data(cbs))) + goto err; + } + return ret; - CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); + case ASN1_ITYPE_CHOICE: + return asn1_item_ex_d2i_choice(pval, cbs, it, tag_number, + tag_class, optional, depth); - /* - * It never makes sense for multi-strings to have implicit tagging, so - * if tag_number != -1, then this looks like an error in the template. - */ - if (tag_number != -1) { - ASN1error(ASN1_R_BAD_TEMPLATE); - return 0; - } + case ASN1_ITYPE_NDEF_SEQUENCE: + case ASN1_ITYPE_SEQUENCE: + return asn1_item_ex_d2i_sequence(pval, cbs, it, tag_number, + tag_class, optional, depth); - if (asn1_check_tag(&cbs_object, &length, &object_tag, &object_class, - &indefinite, &constructed, -1, 0, 1) != 1) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); + default: return 0; } - /* Class must be UNIVERSAL. */ - if (object_class != V_ASN1_UNIVERSAL) { - if (optional) - return -1; - ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL); - return 0; - } - /* Check tag matches bit map. */ - if ((ASN1_tag2bit(object_tag) & it->utype) == 0) { - if (optional) - return -1; - ASN1error(ASN1_R_MSTRING_WRONG_TAG); - return 0; - } + err: + ASN1_item_ex_free(pval, it); - return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, - object_tag, constructed, indefinite, length, it); + ERR_asprintf_error_data("Type=%s", it->sname); + + return 0; +} + +static void +asn1_template_stack_of_free(STACK_OF(ASN1_VALUE) *avals, const ASN1_TEMPLATE *tt) { + ASN1_VALUE *aval; + + if (avals == NULL) + return; + + while (sk_ASN1_VALUE_num(avals) > 0) { + aval = sk_ASN1_VALUE_pop(avals); + ASN1_item_ex_free(&aval, tt->item); + } + sk_ASN1_VALUE_free(avals); } static int -asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, - int tag_number, int tag_class, char optional) +asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, + char optional, int depth) { - CBS cbs_object; - char constructed, indefinite; - int utype = it->utype; + CBS cbs_object, cbs_object_content; + STACK_OF(ASN1_VALUE) *avals = NULL; + ASN1_VALUE *aval = NULL; + int tag_number, tag_class; + int eoc_needed; + char indefinite; size_t length; int ret; CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); - if (it->itype == ASN1_ITYPE_MSTRING) + if (pval == NULL) return 0; - if (it->utype == V_ASN1_ANY) - return asn1_d2i_ex_any(pval, cbs, it, tag_number, tag_class, optional); + asn1_template_stack_of_free((STACK_OF(ASN1_VALUE) *)*pval, tt); + *pval = NULL; - if (tag_number == -1) { - tag_number = it->utype; + tag_number = tt->tag; + tag_class = tt->flags & ASN1_TFLG_TAG_CLASS; + + /* Determine the inner tag value for SET OF or SEQUENCE OF. */ + if ((tt->flags & ASN1_TFLG_IMPTAG) == 0) { + tag_number = V_ASN1_SEQUENCE; tag_class = V_ASN1_UNIVERSAL; + if ((tt->flags & ASN1_TFLG_SET_OF) != 0) + tag_number = V_ASN1_SET; } ret = asn1_check_tag(&cbs_object, &length, NULL, NULL, &indefinite, - &constructed, tag_number, tag_class, optional); + NULL, tag_number, tag_class, optional); if (ret == -1) return -1; if (ret != 1) { @@ -908,359 +1029,222 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, return 0; } - return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, utype, - constructed, indefinite, length, it); -} - -static int -asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) -{ - ASN1_STRING *stmp; - ASN1_INTEGER **tint; - ASN1_BOOLEAN *tbool; - uint8_t u8val; - int ret = 0; - - if (it->funcs != NULL) - return 0; - - if (CBS_len(content) > INT_MAX) - return 0; - - switch (utype) { - case V_ASN1_OBJECT: - if (!c2i_ASN1_OBJECT_cbs((ASN1_OBJECT **)pval, content)) - goto err; - break; - - case V_ASN1_NULL: - if (CBS_len(content) != 0) { - ASN1error(ASN1_R_NULL_IS_WRONG_LENGTH); - goto err; - } - *pval = (ASN1_VALUE *)1; - break; - - case V_ASN1_BOOLEAN: - tbool = (ASN1_BOOLEAN *)pval; - if (CBS_len(content) != 1) { - ASN1error(ASN1_R_BOOLEAN_IS_WRONG_LENGTH); - goto err; - } - if (!CBS_get_u8(content, &u8val)) - goto err; - *tbool = u8val; - break; - - case V_ASN1_BIT_STRING: - if (!c2i_ASN1_BIT_STRING_cbs((ASN1_BIT_STRING **)pval, content)) + if (indefinite) { + eoc_needed = 1; + CBS_init(&cbs_object_content, CBS_data(&cbs_object), + CBS_len(&cbs_object)); + } else { + eoc_needed = 0; + if (!CBS_get_bytes(&cbs_object, &cbs_object_content, + length)) goto err; - break; + } - case V_ASN1_INTEGER: - case V_ASN1_ENUMERATED: - tint = (ASN1_INTEGER **)pval; - if (!c2i_ASN1_INTEGER_cbs(tint, content)) - goto err; - /* Fixup type to match the expected form */ - (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG); - break; + if ((avals = sk_ASN1_VALUE_new_null()) == NULL) { + ASN1error(ERR_R_MALLOC_FAILURE); + goto err; + } - case V_ASN1_OCTET_STRING: - case V_ASN1_NUMERICSTRING: - case V_ASN1_PRINTABLESTRING: - case V_ASN1_T61STRING: - case V_ASN1_VIDEOTEXSTRING: - case V_ASN1_IA5STRING: - case V_ASN1_UTCTIME: - case V_ASN1_GENERALIZEDTIME: - case V_ASN1_GRAPHICSTRING: - case V_ASN1_VISIBLESTRING: - case V_ASN1_GENERALSTRING: - case V_ASN1_UNIVERSALSTRING: - case V_ASN1_BMPSTRING: - case V_ASN1_UTF8STRING: - case V_ASN1_OTHER: - case V_ASN1_SET: - case V_ASN1_SEQUENCE: - default: - if (utype == V_ASN1_BMPSTRING && (CBS_len(content) & 1)) { - ASN1error(ASN1_R_BMPSTRING_IS_WRONG_LENGTH); - goto err; - } - if (utype == V_ASN1_UNIVERSALSTRING && (CBS_len(content) & 3)) { - ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH); - goto err; - } - /* All based on ASN1_STRING and handled the same way. */ - if (*pval == NULL) { - if ((stmp = ASN1_STRING_type_new(utype)) == NULL) { - ASN1error(ERR_R_MALLOC_FAILURE); + /* Read as many items as possible. */ + while (CBS_len(&cbs_object_content) > 0) { + if (asn1_check_eoc(&cbs_object_content)) { + if (!eoc_needed) { + ASN1error(ASN1_R_UNEXPECTED_EOC); goto err; } - *pval = (ASN1_VALUE *)stmp; - } else { - stmp = (ASN1_STRING *)*pval; - stmp->type = utype; + eoc_needed = 0; + break; } - if (!ASN1_STRING_set(stmp, CBS_data(content), CBS_len(content))) { - ASN1_STRING_free(stmp); - *pval = NULL; + if (!asn1_item_ex_d2i(&aval, &cbs_object_content, tt->item, + -1, 0, 0, depth)) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); goto err; } - break; + if (!sk_ASN1_VALUE_push(avals, aval)) { + ASN1error(ERR_R_MALLOC_FAILURE); + goto err; + } + aval = NULL; } - - ret = 1; - - err: - return ret; -} - -static int -asn1_ex_c2i_any(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) -{ - ASN1_TYPE *atype; - - if (it->utype != V_ASN1_ANY || it->funcs != NULL) - return 0; - - if (*pval != NULL) { - ASN1_TYPE_free((ASN1_TYPE *)*pval); - *pval = NULL; + if (eoc_needed) { + ASN1error(ASN1_R_MISSING_EOC); + goto err; } - if ((atype = ASN1_TYPE_new()) == NULL) - return 0; - - if (!asn1_ex_c2i_primitive(&atype->value.asn1_value, content, utype, it)) { - ASN1_TYPE_free(atype); - return 0; + if (indefinite) { + if (!CBS_skip(&cbs_object, CBS_offset(&cbs_object_content))) + goto err; } - atype->type = utype; - /* Fix up value for ASN.1 NULL. */ - if (atype->type == V_ASN1_NULL) - atype->value.ptr = NULL; + if (!CBS_skip(cbs, CBS_offset(&cbs_object))) + goto err; - *pval = (ASN1_VALUE *)atype; + *pval = (ASN1_VALUE *)avals; + avals = NULL; return 1; -} - -static int -asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) -{ - if (CBS_len(content) > INT_MAX) - return 0; - - if (it->funcs != NULL) { - const ASN1_PRIMITIVE_FUNCS *pf = it->funcs; - char free_content = 0; - - if (pf->prim_c2i == NULL) - return 0; - - return pf->prim_c2i(pval, CBS_data(content), CBS_len(content), - utype, &free_content, it); - } - if (it->utype == V_ASN1_ANY) - return asn1_ex_c2i_any(pval, content, utype, it); + err: + asn1_template_stack_of_free(avals, tt); + ASN1_item_ex_free(&aval, tt->item); - return asn1_ex_c2i_primitive(pval, content, utype, it); + return 0; } -/* Find the end of an ASN.1 object. */ static int -asn1_find_end(CBS *cbs, size_t length, char indefinite) +asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, + char optional, int depth) { - size_t eoc_count; + int tag_number, tag_class; + int ret; - if (!indefinite) { - if (!CBS_skip(cbs, length)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } - return 1; - } + if (pval == NULL) + return 0; - eoc_count = 1; + if ((tt->flags & ASN1_TFLG_SK_MASK) != 0) + return asn1_template_stack_of_d2i(pval, cbs, tt, optional, depth); - while (CBS_len(cbs) > 0) { - if (asn1_check_eoc(cbs)) { - if (--eoc_count == 0) - break; - continue; - } - if (!asn1_check_tag(cbs, &length, NULL, NULL, - &indefinite, NULL, -1, 0, 0)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } - if (indefinite) { - eoc_count++; - continue; - } - if (!CBS_skip(cbs, length)) - return 0; + tag_number = -1; + tag_class = V_ASN1_UNIVERSAL; + + /* See if we need to use IMPLICIT tagging. */ + if ((tt->flags & ASN1_TFLG_IMPTAG) != 0) { + tag_number = tt->tag; + tag_class = tt->flags & ASN1_TFLG_TAG_CLASS; } - if (eoc_count > 0) { - ASN1error(ASN1_R_MISSING_EOC); - return 0; + ret = asn1_item_ex_d2i(pval, cbs, tt->item, tag_number, tag_class, + optional, depth); + if (ret == -1) + return -1; + if (ret != 1) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + goto err; } return 1; -} -#ifndef ASN1_MAX_STRING_NEST -/* This determines how many levels of recursion are permitted in ASN1 - * string types. If it is not limited stack overflows can occur. If set - * to zero no recursion is allowed at all. Although zero should be adequate - * examples exist that require a value of 1. So 5 should be more than enough. - */ -#define ASN1_MAX_STRING_NEST 5 -#endif + err: + /* XXX - The called function should have freed already. */ + ASN1_template_free(pval, tt); + return 0; +} -/* Collect the contents from a constructed ASN.1 object. */ static int -asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag, - int expected_class, int depth) +asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt, + char optional, int depth) { - char constructed; + CBS cbs_exp, cbs_exp_content; + char constructed, indefinite; size_t length; - CBS content; - int need_eoc; + int ret; - if (depth > ASN1_MAX_STRING_NEST) { - ASN1error(ASN1_R_NESTED_ASN1_STRING); + if (pval == NULL) return 0; - } - need_eoc = indefinite; + /* Check if EXPLICIT tag is expected. */ + if ((tt->flags & ASN1_TFLG_EXPTAG) == 0) + return asn1_template_noexp_d2i(pval, cbs, tt, optional, depth); - while (CBS_len(cbs) > 0) { - if (asn1_check_eoc(cbs)) { - if (!need_eoc) { - ASN1error(ASN1_R_UNEXPECTED_EOC); - return 0; - } - return 1; - } - if (!asn1_check_tag(cbs, &length, NULL, NULL, &indefinite, - &constructed, expected_tag, expected_class, 0)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } + CBS_init(&cbs_exp, CBS_data(cbs), CBS_len(cbs)); - if (constructed) { - if (!asn1_collect(cbb, cbs, indefinite, expected_tag, - expected_class, depth + 1)) - return 0; - continue; - } + /* Read ASN.1 header for EXPLICIT tagged object. */ + ret = asn1_check_tag(&cbs_exp, &length, NULL, NULL, &indefinite, + &constructed, tt->tag, tt->flags & ASN1_TFLG_TAG_CLASS, optional); + if (ret == -1) + return -1; + if (ret != 1) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); + return 0; + } - if (!CBS_get_bytes(cbs, &content, length)) { - ASN1error(ERR_R_NESTED_ASN1_ERROR); - return 0; - } - if (!CBB_add_bytes(cbb, CBS_data(&content), CBS_len(&content))) - return 0; + if (!constructed) { + ASN1error(ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED); + return 0; } - if (need_eoc) { - ASN1error(ASN1_R_MISSING_EOC); + if (indefinite) { + CBS_init(&cbs_exp_content, CBS_data(&cbs_exp), CBS_len(&cbs_exp)); + } else { + if (!CBS_get_bytes(&cbs_exp, &cbs_exp_content, length)) + goto err; + } + + if ((ret = asn1_template_noexp_d2i(pval, &cbs_exp_content, tt, 0, + depth)) != 1) { + ASN1error(ERR_R_NESTED_ASN1_ERROR); return 0; } + if (indefinite) { + if (!asn1_check_eoc(&cbs_exp_content)) { + ASN1error(ASN1_R_MISSING_EOC); + goto err; + } + if (!CBS_skip(&cbs_exp, CBS_offset(&cbs_exp_content))) + goto err; + } else if (CBS_len(&cbs_exp_content) != 0) { + ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH); + goto err; + } + + if (!CBS_skip(cbs, CBS_offset(&cbs_exp))) + goto err; + return 1; + + err: + ASN1_template_free(pval, tt); + return 0; } -static int -asn1_check_eoc(CBS *cbs) +ASN1_VALUE * +ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen, + const ASN1_ITEM *it) { - uint16_t eoc; + ASN1_VALUE *ptmpval = NULL; - if (!CBS_peek_u16(cbs, &eoc)) - return 0; - if (eoc != 0) - return 0; + if (pval == NULL) + pval = &ptmpval; + if (ASN1_item_ex_d2i(pval, in, inlen, it, -1, 0, 0, 0) <= 0) + return NULL; - return CBS_skip(cbs, 2); + return *pval; } -static int -asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag, uint8_t *out_class, - char *out_indefinite, char *out_constructed, int expected_tag, - int expected_class, char optional) +int +ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen, + const ASN1_ITEM *it, int tag_number, int tag_class, char optional, + ASN1_TLC *ctx) { - int constructed, indefinite; - uint32_t tag_number; - uint8_t tag_class; - size_t length; - - if (out_len != NULL) - *out_len = 0; - if (out_tag != NULL) - *out_tag = 0; - if (out_class != NULL) - *out_class = 0; - if (out_indefinite != NULL) - *out_indefinite = 0; - if (out_constructed != NULL) - *out_constructed = 0; + CBS cbs; + int ret; - if (!asn1_get_identifier_cbs(cbs, 0, &tag_class, &constructed, - &tag_number)) { - ASN1error(ASN1_R_BAD_OBJECT_HEADER); + if (inlen < 0) return 0; - } - if (expected_tag >= 0) { - if (expected_tag != tag_number || - expected_class != tag_class << 6) { - /* Indicate missing type if this is OPTIONAL. */ - if (optional) - return -1; - ASN1error(ASN1_R_WRONG_TAG); - return 0; - } - } - if (!asn1_get_length_cbs(cbs, 0, &indefinite, &length)) { - ASN1error(ASN1_R_BAD_OBJECT_HEADER); - return 0; - } + CBS_init(&cbs, *in, inlen); - /* Indefinite length can only be used with constructed encoding. */ - if (indefinite && !constructed) { - ASN1error(ASN1_R_BAD_OBJECT_HEADER); - return 0; - } + if ((ret = asn1_item_ex_d2i(pval, &cbs, it, tag_number, tag_class, + optional, 0)) == 1) + *in = CBS_data(&cbs); - if (!indefinite && CBS_len(cbs) < length) { - ASN1error(ASN1_R_TOO_LONG); - return 0; - } + return ret; +} - if (tag_number > INT_MAX) { - ASN1error(ASN1_R_TOO_LONG); - return 0; - } +int +ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, + const ASN1_TEMPLATE *tt) +{ + CBS cbs; + int ret; - if (indefinite) - length = CBS_len(cbs); + if (len < 0) + return 0; - if (out_len != NULL) - *out_len = length; - if (out_tag != NULL) - *out_tag = tag_number; - if (out_class != NULL) - *out_class = tag_class << 6; - if (out_indefinite != NULL && indefinite) - *out_indefinite = 1 << 0; - if (out_constructed != NULL && constructed) - *out_constructed = 1 << 5; + CBS_init(&cbs, *in, len); + if ((ret = asn1_template_ex_d2i(pval, &cbs, tt, 0, 0)) == 1) + *in = CBS_data(&cbs); - return 1; + return ret; } -- 2.20.1