/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Support for DEcoding ASN.1 data based on BER/DER (Basic/Distinguished * Encoding Rules). */ /* #define DEBUG_ASN1D_STATES 1 */ #ifdef DEBUG_ASN1D_STATES #include #define PR_Assert sec_asn1d_Assert #endif #include #include "secasn1.h" #include "secerr.h" typedef enum { beforeIdentifier, duringIdentifier, afterIdentifier, beforeLength, duringLength, afterLength, beforeBitString, duringBitString, duringConstructedString, duringGroup, duringLeaf, duringSaveEncoding, duringSequence, afterConstructedString, afterGroup, afterExplicit, afterImplicit, afterInline, afterPointer, afterSaveEncoding, beforeEndOfContents, duringEndOfContents, afterEndOfContents, beforeChoice, duringChoice, afterChoice, notInUse } sec_asn1d_parse_place; #ifdef DEBUG_ASN1D_STATES static const char *const place_names[] = { "beforeIdentifier", "duringIdentifier", "afterIdentifier", "beforeLength", "duringLength", "afterLength", "beforeBitString", "duringBitString", "duringConstructedString", "duringGroup", "duringLeaf", "duringSaveEncoding", "duringSequence", "afterConstructedString", "afterGroup", "afterExplicit", "afterImplicit", "afterInline", "afterPointer", "afterSaveEncoding", "beforeEndOfContents", "duringEndOfContents", "afterEndOfContents", "beforeChoice", "duringChoice", "afterChoice", "notInUse" }; static const char *const class_names[] = { "UNIVERSAL", "APPLICATION", "CONTEXT_SPECIFIC", "PRIVATE" }; static const char *const method_names[] = { "PRIMITIVE", "CONSTRUCTED" }; static const char *const type_names[] = { "END_OF_CONTENTS", "BOOLEAN", "INTEGER", "BIT_STRING", "OCTET_STRING", "NULL", "OBJECT_ID", "OBJECT_DESCRIPTOR", "(type 08)", "REAL", "ENUMERATED", "EMBEDDED", "UTF8_STRING", "(type 0d)", "(type 0e)", "(type 0f)", "SEQUENCE", "SET", "NUMERIC_STRING", "PRINTABLE_STRING", "T61_STRING", "VIDEOTEXT_STRING", "IA5_STRING", "UTC_TIME", "GENERALIZED_TIME", "GRAPHIC_STRING", "VISIBLE_STRING", "GENERAL_STRING", "UNIVERSAL_STRING", "(type 1d)", "BMP_STRING", "HIGH_TAG_VALUE" }; static const char *const flag_names[] = { /* flags, right to left */ "OPTIONAL", "EXPLICIT", "ANY", "INLINE", "POINTER", "GROUP", "DYNAMIC", "SKIP", "INNER", "SAVE", "", /* decoder ignores "MAY_STREAM", */ "SKIP_REST", "CHOICE", "NO_STREAM", "DEBUG_BREAK", "unknown 08", "unknown 10", "unknown 20", "unknown 40", "unknown 80" }; static int /* bool */ formatKind(unsigned long kind, char *buf, int space_in_buffer) { int i; unsigned long k = kind & SEC_ASN1_TAGNUM_MASK; unsigned long notag = kind & (SEC_ASN1_CHOICE | SEC_ASN1_POINTER | SEC_ASN1_INLINE | SEC_ASN1_ANY | SEC_ASN1_SAVE); buf[0] = 0; if ((kind & SEC_ASN1_CLASS_MASK) != SEC_ASN1_UNIVERSAL) { space_in_buffer -= snprintf(buf, space_in_buffer, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6]); buf += strlen(buf); } if (kind & SEC_ASN1_METHOD_MASK) { space_in_buffer -= snprintf(buf, space_in_buffer, " %s", method_names[1]); buf += strlen(buf); } if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) { if (k || !notag) { space_in_buffer -= snprintf(buf, space_in_buffer, " %s", type_names[k]); if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) && (kind & SEC_ASN1_GROUP)) { buf += strlen(buf); space_in_buffer -= snprintf(buf, space_in_buffer, "_OF"); } } } else { space_in_buffer -= snprintf(buf, space_in_buffer, " [%lu]", k); } buf += strlen(buf); for (k = kind >> 8, i = 0; k; k >>= 1, ++i) { if (k & 1) { space_in_buffer -= snprintf(buf, space_in_buffer, " %s", flag_names[i]); buf += strlen(buf); } } return notag != 0; } #endif /* DEBUG_ASN1D_STATES */ typedef enum { allDone, decodeError, keepGoing, needBytes } sec_asn1d_parse_status; struct subitem { const void *data; unsigned long len; /* only used for substrings */ struct subitem *next; }; typedef struct sec_asn1d_state_struct { SEC_ASN1DecoderContext *top; const SEC_ASN1Template *theTemplate; void *dest; void *our_mark; /* free on completion */ struct sec_asn1d_state_struct *parent; /* aka prev */ struct sec_asn1d_state_struct *child; /* aka next */ sec_asn1d_parse_place place; /* * XXX explain the next fields as clearly as possible... */ unsigned char found_tag_modifiers; unsigned char expect_tag_modifiers; unsigned long check_tag_mask; unsigned long found_tag_number; unsigned long expect_tag_number; unsigned long underlying_kind; unsigned long contents_length; unsigned long pending; unsigned long consumed; int depth; /* * Bit strings have their length adjusted -- the first octet of the * contents contains a value between 0 and 7 which says how many bits * at the end of the octets are not actually part of the bit string; * when parsing bit strings we put that value here because we need it * later, for adjustment of the length (when the whole string is done). */ unsigned int bit_string_unused_bits; /* * The following are used for indefinite-length constructed strings. */ struct subitem *subitems_head; struct subitem *subitems_tail; PRPackedBool allocate, /* when true, need to allocate the destination */ endofcontents, /* this state ended up parsing its parent's end-of-contents octets */ explicit, /* we are handling an explicit header */ indefinite, /* the current item has indefinite-length encoding */ missing, /* an optional field that was not present */ optional, /* the template says this field may be omitted */ substring; /* this is a substring of a constructed string */ } sec_asn1d_state; #define IS_HIGH_TAG_NUMBER(n) ((n) == SEC_ASN1_HIGH_TAG_NUMBER) #define LAST_TAG_NUMBER_BYTE(b) (((b)&0x80) == 0) #define TAG_NUMBER_BITS 7 #define TAG_NUMBER_MASK 0x7f #define LENGTH_IS_SHORT_FORM(b) (((b)&0x80) == 0) #define LONG_FORM_LENGTH(b) ((b)&0x7f) #define HIGH_BITS(field, cnt) ((field) >> ((sizeof(field) * 8) - (cnt))) /* * An "outsider" will have an opaque pointer to this, created by calling * SEC_ASN1DecoderStart(). It will be passed back in to all subsequent * calls to SEC_ASN1DecoderUpdate(), and when done it is passed to * SEC_ASN1DecoderFinish(). */ struct sec_DecoderContext_struct { PLArenaPool *our_pool; /* for our internal allocs */ PLArenaPool *their_pool; /* for destination structure allocs */ #ifdef SEC_ASN1D_FREE_ON_ERROR /* \ * XXX see comment below (by same \ * ifdef) that explains why this \ * does not work (need more smarts \ * in order to free back to mark) \ */ /* * XXX how to make their_mark work in the case where they do NOT * give us a pool pointer? */ void *their_mark; /* free on error */ #endif sec_asn1d_state *current; sec_asn1d_parse_status status; /* The maximum size the caller is willing to allow a single element * to be before returning an error. * * In the case of an indefinite length element, this is the sum total * of all child elements. * * In the case of a definite length element, this represents the maximum * size of the top-level element. */ unsigned long max_element_size; SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */ void *notify_arg; /* argument to notify_proc */ PRBool during_notify; /* true during call to notify_proc */ SEC_ASN1WriteProc filter_proc; /* pass field bytes to this */ void *filter_arg; /* argument to that function */ PRBool filter_only; /* do not allocate/store fields */ }; /* * XXX this is a fairly generic function that may belong elsewhere */ static void * sec_asn1d_alloc(PLArenaPool *poolp, unsigned long len) { void *thing; if (poolp != NULL) { /* * Allocate from the pool. */ thing = PORT_ArenaAlloc(poolp, len); } else { /* * Allocate generically. */ thing = PORT_Alloc(len); } return thing; } /* * XXX this is a fairly generic function that may belong elsewhere */ static void * sec_asn1d_zalloc(PLArenaPool *poolp, unsigned long len) { void *thing; thing = sec_asn1d_alloc(poolp, len); if (thing != NULL) PORT_Memset(thing, 0, len); return thing; } static sec_asn1d_state * sec_asn1d_push_state(SEC_ASN1DecoderContext *cx, const SEC_ASN1Template *theTemplate, void *dest, PRBool new_depth) { sec_asn1d_state *state, *new_state; state = cx->current; PORT_Assert(state == NULL || state->child == NULL); if (state != NULL) { PORT_Assert(state->our_mark == NULL); state->our_mark = PORT_ArenaMark(cx->our_pool); } if (theTemplate == NULL) { PORT_SetError(SEC_ERROR_BAD_TEMPLATE); goto loser; } new_state = (sec_asn1d_state *)sec_asn1d_zalloc(cx->our_pool, sizeof(*new_state)); if (new_state == NULL) { goto loser; } new_state->top = cx; new_state->parent = state; new_state->theTemplate = theTemplate; new_state->place = notInUse; if (dest != NULL) new_state->dest = (char *)dest + theTemplate->offset; if (state != NULL) { new_state->depth = state->depth; if (new_depth) { if (++new_state->depth > SEC_ASN1D_MAX_DEPTH) { PORT_SetError(SEC_ERROR_BAD_DER); goto loser; } } state->child = new_state; } cx->current = new_state; return new_state; loser: cx->status = decodeError; if (state != NULL) { PORT_ArenaRelease(cx->our_pool, state->our_mark); state->our_mark = NULL; } return NULL; } static void sec_asn1d_scrub_state(sec_asn1d_state *state) { /* * Some default "scrubbing". * XXX right set of initializations? */ state->place = beforeIdentifier; state->endofcontents = PR_FALSE; state->indefinite = PR_FALSE; state->missing = PR_FALSE; PORT_Assert(state->consumed == 0); } static void sec_asn1d_notify_before(SEC_ASN1DecoderContext *cx, void *dest, int depth) { if (cx->notify_proc == NULL) return; cx->during_notify = PR_TRUE; (*cx->notify_proc)(cx->notify_arg, PR_TRUE, dest, depth); cx->during_notify = PR_FALSE; } static void sec_asn1d_notify_after(SEC_ASN1DecoderContext *cx, void *dest, int depth) { if (cx->notify_proc == NULL) return; cx->during_notify = PR_TRUE; (*cx->notify_proc)(cx->notify_arg, PR_FALSE, dest, depth); cx->during_notify = PR_FALSE; } static sec_asn1d_state * sec_asn1d_init_state_based_on_template(sec_asn1d_state *state) { PRBool explicit, optional, universal; unsigned char expect_tag_modifiers; unsigned long encode_kind, under_kind; unsigned long check_tag_mask, expect_tag_number; /* XXX Check that both of these tests are really needed/appropriate. */ if (state == NULL || state->top->status == decodeError) return state; encode_kind = state->theTemplate->kind; if (encode_kind & SEC_ASN1_SAVE) { /* * This is a "magic" field that saves away all bytes, allowing * the immediately following field to still be decoded from this * same spot -- sort of a fork. */ /* check that there are no extraneous bits */ PORT_Assert(encode_kind == SEC_ASN1_SAVE); if (state->top->filter_only) { /* * If we are not storing, then we do not do the SAVE field * at all. Just move ahead to the "real" field instead, * doing the appropriate notify calls before and after. */ sec_asn1d_notify_after(state->top, state->dest, state->depth); /* * Since we are not storing, allow for our current dest value * to be NULL. (This might not actually occur, but right now I * cannot convince myself one way or the other.) If it is NULL, * assume that our parent dest can help us out. */ if (state->dest == NULL) state->dest = state->parent->dest; else state->dest = (char *)state->dest - state->theTemplate->offset; state->theTemplate++; if (state->dest != NULL) state->dest = (char *)state->dest + state->theTemplate->offset; sec_asn1d_notify_before(state->top, state->dest, state->depth); encode_kind = state->theTemplate->kind; PORT_Assert((encode_kind & SEC_ASN1_SAVE) == 0); } else { sec_asn1d_scrub_state(state); state->place = duringSaveEncoding; state = sec_asn1d_push_state(state->top, SEC_AnyTemplate, state->dest, PR_FALSE); if (state != NULL) state = sec_asn1d_init_state_based_on_template(state); return state; } } universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) ? PR_TRUE : PR_FALSE; explicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE; encode_kind &= ~SEC_ASN1_EXPLICIT; optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE; encode_kind &= ~SEC_ASN1_OPTIONAL; PORT_Assert(!(explicit && universal)); /* bad templates */ encode_kind &= ~SEC_ASN1_DYNAMIC; encode_kind &= ~SEC_ASN1_MAY_STREAM; if (encode_kind & SEC_ASN1_CHOICE) { #if 0 /* XXX remove? */ sec_asn1d_state *child = sec_asn1d_push_state(state->top, state->theTemplate, state->dest, PR_FALSE); if ((sec_asn1d_state *)NULL == child) { return (sec_asn1d_state *)NULL; } child->allocate = state->allocate; child->place = beforeChoice; return child; #else state->place = beforeChoice; return state; #endif } if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || (!universal && !explicit)) { const SEC_ASN1Template *subt; void *dest; PRBool child_allocate; PORT_Assert((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0); sec_asn1d_scrub_state(state); child_allocate = PR_FALSE; if (encode_kind & SEC_ASN1_POINTER) { /* * A POINTER means we need to allocate the destination for * this field. But, since it may also be an optional field, * we defer the allocation until later; we just record that * it needs to be done. * * There are two possible scenarios here -- one is just a * plain POINTER (kind of like INLINE, except with allocation) * and the other is an implicitly-tagged POINTER. We don't * need to do anything special here for the two cases, but * since the template definition can be tricky, we do check * that there are no extraneous bits set in encode_kind. * * XXX The same conditions which assert should set an error. */ if (universal) { /* * "universal" means this entry is a standalone POINTER; * there should be no other bits set in encode_kind. */ PORT_Assert(encode_kind == SEC_ASN1_POINTER); } else { /* * If we get here we have an implicitly-tagged field * that needs to be put into a POINTER. The subtemplate * will determine how to decode the field, but encode_kind * describes the (implicit) tag we are looking for. * The non-tag bits of encode_kind will be ignored by * the code below; none of them should be set, however, * except for the POINTER bit itself -- so check that. */ PORT_Assert((encode_kind & ~SEC_ASN1_TAG_MASK) == SEC_ASN1_POINTER); } if (!state->top->filter_only) child_allocate = PR_TRUE; dest = NULL; state->place = afterPointer; } else { dest = state->dest; if (encode_kind & SEC_ASN1_INLINE) { /* check that there are no extraneous bits */ PORT_Assert(encode_kind == SEC_ASN1_INLINE && !optional); state->place = afterInline; } else { state->place = afterImplicit; } } state->optional = optional; subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE); state = sec_asn1d_push_state(state->top, subt, dest, PR_FALSE); if (state == NULL) return NULL; state->allocate = child_allocate; if (universal) { state = sec_asn1d_init_state_based_on_template(state); if (state != NULL) { /* * If this field is optional, we need to record that on * the pushed child so it won't fail if the field isn't * found. I can't think of a way that this new state * could already have optional set (which we would wipe * out below if our local optional is not set) -- but * just to be sure, assert that it isn't set. */ PORT_Assert(!state->optional); state->optional = optional; } return state; } under_kind = state->theTemplate->kind; under_kind &= ~SEC_ASN1_MAY_STREAM; } else if (explicit) { /* * For explicit, we only need to match the encoding tag next, * then we will push another state to handle the entire inner * part. In this case, there is no underlying kind which plays * any part in the determination of the outer, explicit tag. * So we just set under_kind to 0, which is not a valid tag, * and the rest of the tag matching stuff should be okay. */ under_kind = 0; } else { /* * Nothing special; the underlying kind and the given encoding * information are the same. */ under_kind = encode_kind; } /* XXX is this the right set of bits to test here? */ PORT_Assert((under_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)) == 0); if (encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) { PORT_Assert(encode_kind == under_kind); if (encode_kind & SEC_ASN1_SKIP) { PORT_Assert(!optional); PORT_Assert(encode_kind == SEC_ASN1_SKIP); state->dest = NULL; } check_tag_mask = 0; expect_tag_modifiers = 0; expect_tag_number = 0; } else { check_tag_mask = SEC_ASN1_TAG_MASK; expect_tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK; /* * XXX This assumes only single-octet identifiers. To handle * the HIGH TAG form we would need to do some more work, especially * in how to specify them in the template, because right now we * do not provide a way to specify more *tag* bits in encode_kind. */ expect_tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK; switch (under_kind & SEC_ASN1_TAGNUM_MASK) { case SEC_ASN1_SET: /* * XXX A plain old SET (as opposed to a SET OF) is not implemented. * If it ever is, remove this assert... */ PORT_Assert((under_kind & SEC_ASN1_GROUP) != 0); /* fallthru */ case SEC_ASN1_SEQUENCE: expect_tag_modifiers |= SEC_ASN1_CONSTRUCTED; break; case SEC_ASN1_BIT_STRING: case SEC_ASN1_BMP_STRING: case SEC_ASN1_GENERALIZED_TIME: case SEC_ASN1_IA5_STRING: case SEC_ASN1_OCTET_STRING: case SEC_ASN1_PRINTABLE_STRING: case SEC_ASN1_T61_STRING: case SEC_ASN1_UNIVERSAL_STRING: case SEC_ASN1_UTC_TIME: case SEC_ASN1_UTF8_STRING: case SEC_ASN1_VISIBLE_STRING: check_tag_mask &= ~SEC_ASN1_CONSTRUCTED; break; } } state->check_tag_mask = check_tag_mask; state->expect_tag_modifiers = expect_tag_modifiers; state->expect_tag_number = expect_tag_number; state->underlying_kind = under_kind; state->explicit = explicit; state->optional = optional; sec_asn1d_scrub_state(state); return state; } static sec_asn1d_state * sec_asn1d_get_enclosing_construct(sec_asn1d_state *state) { for (state = state->parent; state; state = state->parent) { sec_asn1d_parse_place place = state->place; if (place != afterImplicit && place != afterPointer && place != afterInline && place != afterSaveEncoding && place != duringSaveEncoding && place != duringChoice) { /* we've walked up the stack to a state that represents ** the enclosing construct. */ break; } } return state; } static PRBool sec_asn1d_parent_allows_EOC(sec_asn1d_state *state) { /* get state of enclosing construct. */ state = sec_asn1d_get_enclosing_construct(state); if (state) { sec_asn1d_parse_place place = state->place; /* Is it one of the types that permits an unexpected EOC? */ int eoc_permitted = (place == duringGroup || place == duringConstructedString || state->child->optional); return (state->indefinite && eoc_permitted) ? PR_TRUE : PR_FALSE; } return PR_FALSE; } static unsigned long sec_asn1d_parse_identifier(sec_asn1d_state *state, const char *buf, unsigned long len) { unsigned char byte; unsigned char tag_number; PORT_Assert(state->place == beforeIdentifier); if (len == 0) { state->top->status = needBytes; return 0; } byte = (unsigned char)*buf; #ifdef DEBUG_ASN1D_STATES { int bufsize = 256; char kindBuf[bufsize]; formatKind(byte, kindBuf, bufsize); printf("Found tag %02x %s\n", byte, kindBuf); } #endif tag_number = byte & SEC_ASN1_TAGNUM_MASK; if (IS_HIGH_TAG_NUMBER(tag_number)) { state->place = duringIdentifier; state->found_tag_number = 0; /* * Actually, we have no idea how many bytes are pending, but we * do know that it is at least 1. That is all we know; we have * to look at each byte to know if there is another, etc. */ state->pending = 1; } else { if (byte == 0 && sec_asn1d_parent_allows_EOC(state)) { /* * Our parent has indefinite-length encoding, and the * entire tag found is 0, so it seems that we have hit the * end-of-contents octets. To handle this, we just change * our state to that which expects to get the bytes of the * end-of-contents octets and let that code re-read this byte * so that our categorization of field types is correct. * After that, our parent will then deal with everything else. */ state->place = duringEndOfContents; state->pending = 2; state->found_tag_number = 0; state->found_tag_modifiers = 0; /* * We might be an optional field that is, as we now find out, * missing. Give our parent a clue that this happened. */ if (state->optional) state->missing = PR_TRUE; return 0; } state->place = afterIdentifier; state->found_tag_number = tag_number; } state->found_tag_modifiers = byte & ~SEC_ASN1_TAGNUM_MASK; return 1; } static unsigned long sec_asn1d_parse_more_identifier(sec_asn1d_state *state, const char *buf, unsigned long len) { unsigned char byte; int count; PORT_Assert(state->pending == 1); PORT_Assert(state->place == duringIdentifier); if (len == 0) { state->top->status = needBytes; return 0; } count = 0; while (len && state->pending) { if (HIGH_BITS(state->found_tag_number, TAG_NUMBER_BITS) != 0) { /* * The given high tag number overflows our container; * just give up. This is not likely to *ever* happen. */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } state->found_tag_number <<= TAG_NUMBER_BITS; byte = (unsigned char)buf[count++]; state->found_tag_number |= (byte & TAG_NUMBER_MASK); len--; if (LAST_TAG_NUMBER_BYTE(byte)) state->pending = 0; } if (state->pending == 0) state->place = afterIdentifier; return count; } static void sec_asn1d_confirm_identifier(sec_asn1d_state *state) { PRBool match; PORT_Assert(state->place == afterIdentifier); match = (PRBool)(((state->found_tag_modifiers & state->check_tag_mask) == state->expect_tag_modifiers) && ((state->found_tag_number & state->check_tag_mask) == state->expect_tag_number)); if (match) { state->place = beforeLength; } else { if (state->optional) { state->missing = PR_TRUE; state->place = afterEndOfContents; } else { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } } } static unsigned long sec_asn1d_parse_length(sec_asn1d_state *state, const char *buf, unsigned long len) { unsigned char byte; PORT_Assert(state->place == beforeLength); if (len == 0) { state->top->status = needBytes; return 0; } /* * The default/likely outcome. It may get adjusted below. */ state->place = afterLength; byte = (unsigned char)*buf; if (LENGTH_IS_SHORT_FORM(byte)) { state->contents_length = byte; } else { state->contents_length = 0; state->pending = LONG_FORM_LENGTH(byte); if (state->pending == 0) { state->indefinite = PR_TRUE; } else { state->place = duringLength; } } /* If we're parsing an ANY, SKIP, or SAVE template, and ** the object being saved is definite length encoded and constructed, ** there's no point in decoding that construct's members. ** So, just forget it's constructed and treat it as primitive. ** (SAVE appears as an ANY at this point) */ if (!state->indefinite && (state->underlying_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP))) { state->found_tag_modifiers &= ~SEC_ASN1_CONSTRUCTED; } return 1; } static unsigned long sec_asn1d_parse_more_length(sec_asn1d_state *state, const char *buf, unsigned long len) { int count; PORT_Assert(state->pending > 0); PORT_Assert(state->place == duringLength); if (len == 0) { state->top->status = needBytes; return 0; } count = 0; while (len && state->pending) { if (HIGH_BITS(state->contents_length, 9) != 0) { /* * The given full content length overflows our container; * just give up. */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } state->contents_length <<= 8; state->contents_length |= (unsigned char)buf[count++]; len--; state->pending--; } if (state->pending == 0) state->place = afterLength; return count; } /* * Helper function for sec_asn1d_prepare_for_contents. * Checks that a value representing a number of bytes consumed can be * subtracted from a remaining length. If so, returns PR_TRUE. * Otherwise, sets the error SEC_ERROR_BAD_DER, indicates that there was a * decoding error in the given SEC_ASN1DecoderContext, and returns PR_FALSE. */ static PRBool sec_asn1d_check_and_subtract_length(unsigned long *remaining, unsigned long consumed, SEC_ASN1DecoderContext *cx) { PORT_Assert(remaining); PORT_Assert(cx); if (!remaining || !cx) { PORT_SetError(SEC_ERROR_INVALID_ARGS); cx->status = decodeError; return PR_FALSE; } if (*remaining < consumed) { PORT_SetError(SEC_ERROR_BAD_DER); cx->status = decodeError; return PR_FALSE; } *remaining -= consumed; return PR_TRUE; } static void sec_asn1d_prepare_for_contents(sec_asn1d_state *state) { SECItem *item; PLArenaPool *poolp; unsigned long alloc_len; sec_asn1d_state *parent; #ifdef DEBUG_ASN1D_STATES { printf("Found Length %lu %s\n", state->contents_length, state->indefinite ? "indefinite" : ""); } #endif /** * The maximum length for a child element should be constrained to the * length remaining in the first definite length element in the ancestor * stack. If there is no definite length element in the ancestor stack, * there's nothing to constrain the length of the child, so there's no * further processing necessary. * * It's necessary to walk the ancestor stack, because it's possible to have * definite length children that are part of an indefinite length element, * which is itself part of an indefinite length element, and which is * ultimately part of a definite length element. A simple example of this * would be the handling of constructed OCTET STRINGs in BER encoding. * * This algorithm finds the first definite length element in the ancestor * stack, if any, and if so, ensures that the length of the child element * is consistent with the number of bytes remaining in the constraining * ancestor element (that is, after accounting for any other sibling * elements that may have been read). * * It's slightly complicated by the need to account both for integer * underflow and overflow, as well as ensure that for indefinite length * encodings, there's also enough space for the End-of-Contents (EOC) * octets (Tag = 0x00, Length = 0x00, or two bytes). */ /* Determine the maximum length available for this element by finding the * first definite length ancestor, if any. */ parent = sec_asn1d_get_enclosing_construct(state); while (parent && parent->indefinite) { parent = sec_asn1d_get_enclosing_construct(parent); } /* If parent is null, state is either the outermost state / at the top of * the stack, or the outermost state uses indefinite length encoding. In * these cases, there's nothing external to constrain this element, so * there's nothing to check. */ if (parent) { unsigned long remaining = parent->pending; parent = state; do { if (!sec_asn1d_check_and_subtract_length( &remaining, parent->consumed, state->top) || /* If parent->indefinite is true, parent->contents_length is * zero and this is a no-op. */ !sec_asn1d_check_and_subtract_length( &remaining, parent->contents_length, state->top) || /* If parent->indefinite is true, then ensure there is enough * space for an EOC tag of 2 bytes. */ (parent->indefinite && !sec_asn1d_check_and_subtract_length(&remaining, 2, state->top))) { /* This element is larger than its enclosing element, which is * invalid. */ return; } } while ((parent = sec_asn1d_get_enclosing_construct(parent)) && parent->indefinite); } /* * XXX I cannot decide if this allocation should exclude the case * where state->endofcontents is true -- figure it out! */ if (state->allocate) { void *dest; PORT_Assert(state->dest == NULL); /* * We are handling a POINTER or a member of a GROUP, and need to * allocate for the data structure. */ dest = sec_asn1d_zalloc(state->top->their_pool, state->theTemplate->size); if (dest == NULL) { state->top->status = decodeError; return; } state->dest = (char *)dest + state->theTemplate->offset; /* * For a member of a GROUP, our parent will later put the * pointer wherever it belongs. But for a POINTER, we need * to record the destination now, in case notify or filter * procs need access to it -- they cannot find it otherwise, * until it is too late (for one-pass processing). */ if (state->parent->place == afterPointer) { void **placep; placep = state->parent->dest; *placep = dest; } } /* * Remember, length may be indefinite here! In that case, * both contents_length and pending will be zero. */ state->pending = state->contents_length; /* * An EXPLICIT is nothing but an outer header, which we have * already parsed and accepted. Now we need to do the inner * header and its contents. */ if (state->explicit) { state->place = afterExplicit; state = sec_asn1d_push_state(state->top, SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE), state->dest, PR_TRUE); if (state != NULL) { (void)sec_asn1d_init_state_based_on_template(state); } return; } /* * For GROUP (SET OF, SEQUENCE OF), even if we know the length here * we cannot tell how many items we will end up with ... so push a * state that can keep track of "children" (the individual members * of the group; we will allocate as we go and put them all together * at the end. */ if (state->underlying_kind & SEC_ASN1_GROUP) { /* XXX If this assertion holds (should be able to confirm it via * inspection, too) then move this code into the switch statement * below under cases SET_OF and SEQUENCE_OF; it will be cleaner. */ PORT_Assert(state->underlying_kind == SEC_ASN1_SET_OF || state->underlying_kind == SEC_ASN1_SEQUENCE_OF || state->underlying_kind == (SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC) || state->underlying_kind == (SEC_ASN1_SEQUENCE_OF | SEC_ASN1_DYNAMIC)); if (state->contents_length != 0 || state->indefinite) { const SEC_ASN1Template *subt; state->place = duringGroup; subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE); state = sec_asn1d_push_state(state->top, subt, NULL, PR_TRUE); if (state != NULL) { if (!state->top->filter_only) state->allocate = PR_TRUE; /* XXX propogate this? */ /* * Do the "before" field notification for next in group. */ sec_asn1d_notify_before(state->top, state->dest, state->depth); (void)sec_asn1d_init_state_based_on_template(state); } } else { /* * A group of zero; we are done. * Set state to afterGroup and let that code plant the NULL. */ state->place = afterGroup; } return; } switch (state->underlying_kind) { case SEC_ASN1_SEQUENCE: /* * We need to push a child to handle the individual fields. */ state->place = duringSequence; state = sec_asn1d_push_state(state->top, state->theTemplate + 1, state->dest, PR_TRUE); if (state != NULL) { /* * Do the "before" field notification. */ sec_asn1d_notify_before(state->top, state->dest, state->depth); (void)sec_asn1d_init_state_based_on_template(state); } break; case SEC_ASN1_SET: /* XXX SET is not really implemented */ /* * XXX A plain SET requires special handling; scanning of a * template to see where a field should go (because by definition, * they are not in any particular order, and you have to look at * each tag to disambiguate what the field is). We may never * implement this because in practice, it seems to be unused. */ PORT_Assert(0); PORT_SetError(SEC_ERROR_BAD_DER); /* XXX */ state->top->status = decodeError; break; case SEC_ASN1_NULL: /* * The NULL type, by definition, is "nothing", content length of zero. * An indefinite-length encoding is not alloweed. */ if (state->contents_length || state->indefinite) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; break; } if (state->dest != NULL) { item = (SECItem *)(state->dest); item->data = NULL; item->len = 0; } state->place = afterEndOfContents; break; case SEC_ASN1_BMP_STRING: /* Error if length is not divisable by 2 */ if (state->contents_length % 2) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; break; } /* otherwise, handle as other string types */ goto regular_string_type; case SEC_ASN1_UNIVERSAL_STRING: /* Error if length is not divisable by 4 */ if (state->contents_length % 4) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; break; } /* otherwise, handle as other string types */ goto regular_string_type; case SEC_ASN1_SKIP: case SEC_ASN1_ANY: case SEC_ASN1_ANY_CONTENTS: /* * These are not (necessarily) strings, but they need nearly * identical handling (especially when we need to deal with * constructed sub-pieces), so we pretend they are. */ /* fallthru */ regular_string_type: case SEC_ASN1_BIT_STRING: case SEC_ASN1_IA5_STRING: case SEC_ASN1_OCTET_STRING: case SEC_ASN1_PRINTABLE_STRING: case SEC_ASN1_T61_STRING: case SEC_ASN1_UTC_TIME: case SEC_ASN1_UTF8_STRING: case SEC_ASN1_VISIBLE_STRING: /* * We are allocating for a primitive or a constructed string. * If it is a constructed string, it may also be indefinite-length. * If it is primitive, the length can (legally) be zero. * Our first order of business is to allocate the memory for * the string, if we can (if we know the length). */ item = (SECItem *)(state->dest); /* * If the item is a definite-length constructed string, then * the contents_length is actually larger than what we need * (because it also counts each intermediate header which we * will be throwing away as we go), but it is a perfectly good * upper bound that we just allocate anyway, and then concat * as we go; we end up wasting a few extra bytes but save a * whole other copy. */ alloc_len = state->contents_length; poolp = NULL; /* quiet compiler warnings about unused... */ if (item == NULL || state->top->filter_only) { if (item != NULL) { item->data = NULL; item->len = 0; } alloc_len = 0; } else if (state->substring) { /* * If we are a substring of a constructed string, then we may * not have to allocate anything (because our parent, the * actual constructed string, did it for us). If we are a * substring and we *do* have to allocate, that means our * parent is an indefinite-length, so we allocate from our pool; * later our parent will copy our string into the aggregated * whole and free our pool allocation. */ if (item->data == NULL) { PORT_Assert(item->len == 0); poolp = state->top->our_pool; } else { alloc_len = 0; } } else { item->len = 0; item->data = NULL; poolp = state->top->their_pool; } if (alloc_len || ((!state->indefinite) && (state->subitems_head != NULL))) { struct subitem *subitem; int len; PORT_Assert(item); if (!item) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } PORT_Assert(item->len == 0 && item->data == NULL); /* * Check for and handle an ANY which has stashed aside the * header (identifier and length) bytes for us to include * in the saved contents. */ if (state->subitems_head != NULL) { PORT_Assert(state->underlying_kind == SEC_ASN1_ANY); for (subitem = state->subitems_head; subitem != NULL; subitem = subitem->next) alloc_len += subitem->len; } if (state->top->max_element_size > 0 && alloc_len > state->top->max_element_size) { PORT_SetError(SEC_ERROR_OUTPUT_LEN); state->top->status = decodeError; return; } item->data = (unsigned char *)sec_asn1d_zalloc(poolp, alloc_len); if (item->data == NULL) { state->top->status = decodeError; break; } len = 0; for (subitem = state->subitems_head; subitem != NULL; subitem = subitem->next) { PORT_Memcpy(item->data + len, subitem->data, subitem->len); len += subitem->len; } item->len = len; /* * Because we use arenas and have a mark set, we later free * everything we have allocated, so this does *not* present * a memory leak (it is just temporarily left dangling). */ state->subitems_head = state->subitems_tail = NULL; } if (state->contents_length == 0 && (!state->indefinite)) { /* * A zero-length simple or constructed string; we are done. */ state->place = afterEndOfContents; } else if (state->found_tag_modifiers & SEC_ASN1_CONSTRUCTED) { const SEC_ASN1Template *sub; switch (state->underlying_kind) { case SEC_ASN1_ANY: case SEC_ASN1_ANY_CONTENTS: sub = SEC_AnyTemplate; break; case SEC_ASN1_BIT_STRING: sub = SEC_BitStringTemplate; break; case SEC_ASN1_BMP_STRING: sub = SEC_BMPStringTemplate; break; case SEC_ASN1_GENERALIZED_TIME: sub = SEC_GeneralizedTimeTemplate; break; case SEC_ASN1_IA5_STRING: sub = SEC_IA5StringTemplate; break; case SEC_ASN1_OCTET_STRING: sub = SEC_OctetStringTemplate; break; case SEC_ASN1_PRINTABLE_STRING: sub = SEC_PrintableStringTemplate; break; case SEC_ASN1_T61_STRING: sub = SEC_T61StringTemplate; break; case SEC_ASN1_UNIVERSAL_STRING: sub = SEC_UniversalStringTemplate; break; case SEC_ASN1_UTC_TIME: sub = SEC_UTCTimeTemplate; break; case SEC_ASN1_UTF8_STRING: sub = SEC_UTF8StringTemplate; break; case SEC_ASN1_VISIBLE_STRING: sub = SEC_VisibleStringTemplate; break; case SEC_ASN1_SKIP: sub = SEC_SkipTemplate; break; default: /* redundant given outer switch cases, but */ PORT_Assert(0); /* the compiler does not seem to know that, */ sub = NULL; /* so just do enough to quiet it. */ break; } state->place = duringConstructedString; state = sec_asn1d_push_state(state->top, sub, item, PR_TRUE); if (state != NULL) { state->substring = PR_TRUE; /* XXX propogate? */ (void)sec_asn1d_init_state_based_on_template(state); } } else if (state->indefinite) { /* * An indefinite-length string *must* be constructed! */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { /* * A non-zero-length simple string. */ if (state->underlying_kind == SEC_ASN1_BIT_STRING) state->place = beforeBitString; else state->place = duringLeaf; } break; default: /* * We are allocating for a simple leaf item. */ if (state->contents_length) { if (state->dest != NULL) { item = (SECItem *)(state->dest); item->len = 0; if (state->top->max_element_size > 0 && state->contents_length > state->top->max_element_size) { PORT_SetError(SEC_ERROR_OUTPUT_LEN); state->top->status = decodeError; return; } if (state->top->filter_only) { item->data = NULL; } else { item->data = (unsigned char *) sec_asn1d_zalloc(state->top->their_pool, state->contents_length); if (item->data == NULL) { state->top->status = decodeError; return; } } } state->place = duringLeaf; } else { /* * An indefinite-length or zero-length item is not allowed. * (All legal cases of such were handled above.) */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } } } static void sec_asn1d_free_child(sec_asn1d_state *state, PRBool error) { if (state->child != NULL) { PORT_Assert(error || state->child->consumed == 0); PORT_Assert(state->our_mark != NULL); PORT_ArenaZRelease(state->top->our_pool, state->our_mark); if (error && state->top->their_pool == NULL) { /* * XXX We need to free anything allocated. * At this point, we failed in the middle of decoding. But we * can't free the data we previously allocated with PR_Malloc * unless we keep track of every pointer. So instead we have a * memory leak when decoding fails half-way, unless an arena is * used. See bug 95311 . */ } state->child = NULL; state->our_mark = NULL; } else { /* * It is important that we do not leave a mark unreleased/unmarked. * But I do not think we should ever have one set in this case, only * if we had a child (handled above). So check for that. If this * assertion should ever get hit, then we probably need to add code * here to release back to our_mark (and then set our_mark to NULL). */ PORT_Assert(state->our_mark == NULL); } state->place = beforeEndOfContents; } /* We have just saved an entire encoded ASN.1 object (type) for a SAVE ** template, and now in the next template, we are going to decode that ** saved data by calling SEC_ASN1DecoderUpdate recursively. ** If that recursive call fails with needBytes, it is a fatal error, ** because the encoded object should have been complete. ** If that recursive call fails with decodeError, it will have already ** cleaned up the state stack, so we must bail out quickly. ** ** These checks of the status returned by the recursive call are now ** done in the caller of this function, immediately after it returns. */ static void sec_asn1d_reuse_encoding(sec_asn1d_state *state) { sec_asn1d_state *child; unsigned long consumed; SECItem *item; void *dest; child = state->child; PORT_Assert(child != NULL); consumed = child->consumed; child->consumed = 0; item = (SECItem *)(state->dest); PORT_Assert(item != NULL); PORT_Assert(item->len == consumed); /* * Free any grandchild. */ sec_asn1d_free_child(child, PR_FALSE); /* * Notify after the SAVE field. */ sec_asn1d_notify_after(state->top, state->dest, state->depth); /* * Adjust to get new dest and move forward. */ dest = (char *)state->dest - state->theTemplate->offset; state->theTemplate++; child->dest = (char *)dest + state->theTemplate->offset; child->theTemplate = state->theTemplate; /* * Notify before the "real" field. */ PORT_Assert(state->depth == child->depth); sec_asn1d_notify_before(state->top, child->dest, child->depth); /* * This will tell DecoderUpdate to return when it is done. */ state->place = afterSaveEncoding; /* * We already have a child; "push" it by making it current. */ state->top->current = child; /* * And initialize it so it is ready to parse. */ (void)sec_asn1d_init_state_based_on_template(child); /* * Now parse that out of our data. */ if (SEC_ASN1DecoderUpdate(state->top, (char *)item->data, item->len) != SECSuccess) return; if (state->top->status == needBytes) { return; } PORT_Assert(state->top->current == state); PORT_Assert(state->child == child); /* * That should have consumed what we consumed before. */ PORT_Assert(consumed == child->consumed); child->consumed = 0; /* * Done. */ state->consumed += consumed; child->place = notInUse; state->place = afterEndOfContents; } static unsigned long sec_asn1d_parse_leaf(sec_asn1d_state *state, const char *buf, unsigned long len) { SECItem *item; unsigned long bufLen; if (len == 0) { state->top->status = needBytes; return 0; } if (state->pending < len) len = state->pending; bufLen = len; item = (SECItem *)(state->dest); if (item != NULL && item->data != NULL) { unsigned long offset; /* Strip leading zeroes when target is unsigned integer */ if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER */ item->len == 0 && /* MSB */ item->type == siUnsignedInteger) /* unsigned */ { while (len > 1 && buf[0] == 0) { /* leading 0 */ buf++; len--; } } offset = item->len; if (state->underlying_kind == SEC_ASN1_BIT_STRING) { // The previous bit string must have no unused bits. if (item->len & 0x7) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } // If this is a bit string, the length is bits, not bytes. offset = item->len >> 3; } if (state->underlying_kind == SEC_ASN1_BIT_STRING) { unsigned long len_in_bits; // Protect against overflow during the bytes-to-bits conversion. if (len >= (ULONG_MAX >> 3) + 1) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } len_in_bits = (len << 3) - state->bit_string_unused_bits; // Protect against overflow when computing the total length in bits. if (UINT_MAX - item->len < len_in_bits) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } item->len += len_in_bits; } else { if (UINT_MAX - item->len < len) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } item->len += len; } PORT_Memcpy(item->data + offset, buf, len); } state->pending -= bufLen; if (state->pending == 0) state->place = beforeEndOfContents; return bufLen; } static unsigned long sec_asn1d_parse_bit_string(sec_asn1d_state *state, const char *buf, unsigned long len) { unsigned char byte; /*PORT_Assert (state->pending > 0); */ PORT_Assert(state->place == beforeBitString); if (state->pending == 0) { if (state->dest != NULL) { SECItem *item = (SECItem *)(state->dest); item->data = NULL; item->len = 0; state->place = beforeEndOfContents; return 0; } } if (len == 0) { state->top->status = needBytes; return 0; } byte = (unsigned char)*buf; if (byte > 7) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } state->bit_string_unused_bits = byte; state->place = duringBitString; state->pending -= 1; return 1; } static unsigned long sec_asn1d_parse_more_bit_string(sec_asn1d_state *state, const char *buf, unsigned long len) { PORT_Assert(state->place == duringBitString); if (state->pending == 0) { /* An empty bit string with some unused bits is invalid. */ if (state->bit_string_unused_bits) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { /* An empty bit string with no unused bits is OK. */ state->place = beforeEndOfContents; } return 0; } len = sec_asn1d_parse_leaf(state, buf, len); return len; } /* * XXX All callers should be looking at return value to detect * out-of-memory errors (and stop!). */ static struct subitem * sec_asn1d_add_to_subitems(sec_asn1d_state *state, const void *data, unsigned long len, PRBool copy_data) { struct subitem *thing; thing = (struct subitem *)sec_asn1d_zalloc(state->top->our_pool, sizeof(struct subitem)); if (thing == NULL) { state->top->status = decodeError; return NULL; } if (copy_data) { void *copy; copy = sec_asn1d_alloc(state->top->our_pool, len); if (copy == NULL) { state->top->status = decodeError; if (!state->top->our_pool) PORT_Free(thing); return NULL; } PORT_Memcpy(copy, data, len); thing->data = copy; } else { thing->data = data; } thing->len = len; thing->next = NULL; if (state->subitems_head == NULL) { PORT_Assert(state->subitems_tail == NULL); state->subitems_head = state->subitems_tail = thing; } else { state->subitems_tail->next = thing; state->subitems_tail = thing; } return thing; } static void sec_asn1d_record_any_header(sec_asn1d_state *state, const char *buf, unsigned long len) { SECItem *item; item = (SECItem *)(state->dest); if (item != NULL && item->data != NULL) { PORT_Assert(state->substring); PORT_Memcpy(item->data + item->len, buf, len); item->len += len; } else { sec_asn1d_add_to_subitems(state, buf, len, PR_TRUE); } } /* * We are moving along through the substrings of a constructed string, * and have just finished parsing one -- we need to save our child data * (if the child was not already writing directly into the destination) * and then move forward by one. * * We also have to detect when we are done: * - a definite-length encoding stops when our pending value hits 0 * - an indefinite-length encoding stops when our child is empty * (which means it was the end-of-contents octets) */ static void sec_asn1d_next_substring(sec_asn1d_state *state) { sec_asn1d_state *child; SECItem *item; unsigned long child_consumed; PRBool done; PORT_Assert(state->place == duringConstructedString); PORT_Assert(state->child != NULL); child = state->child; child_consumed = child->consumed; child->consumed = 0; state->consumed += child_consumed; done = PR_FALSE; if (state->pending) { PORT_Assert(!state->indefinite); if (child_consumed > state->pending) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } state->pending -= child_consumed; if (state->pending == 0) done = PR_TRUE; } else { PRBool preallocatedString; sec_asn1d_state *temp_state; PORT_Assert(state->indefinite); item = (SECItem *)(child->dest); /** * At this point, there's three states at play: * child: The element that was just parsed * state: The currently processed element * 'parent' (aka state->parent): The enclosing construct * of state, or NULL if this is the top-most element. * * This state handles both substrings of a constructed string AND * child elements of items whose template type was that of * SEC_ASN1_ANY, SEC_ASN1_SAVE, SEC_ASN1_ANY_CONTENTS, SEC_ASN1_SKIP * template, as described in sec_asn1d_prepare_for_contents. For * brevity, these will be referred to as 'string' and 'any' types. * * This leads to the following possibilities: * 1: This element is an indefinite length string, part of a * definite length string. * 2: This element is an indefinite length string, part of an * indefinite length string. * 3: This element is an indefinite length any, part of a * definite length any. * 4: This element is an indefinite length any, part of an * indefinite length any. * 5: This element is an indefinite length any and does not * meet any of the above criteria. Note that this would include * an indefinite length string type matching an indefinite * length any template. * * In Cases #1 and #3, the definite length 'parent' element will * have allocated state->dest based on the parent elements definite * size. During the processing of 'child', sec_asn1d_parse_leaf will * have copied the (string, any) data directly into the offset of * dest, as appropriate, so there's no need for this class to still * store the child - it's already been processed. * * In Cases #2 and #4, dest will be set to the parent element's dest, * but dest->data will not have been allocated yet, due to the * indefinite length encoding. In this situation, it's necessary to * hold onto child (and all other children) until the EOC, at which * point, it becomes possible to compute 'state's overall length. Once * 'state' has a computed length, this can then be fed to 'parent' (via * this state), and then 'parent' can similarly compute the length of * all of its children up to the EOC, which will ultimately transit to * sec_asn1d_concat_substrings, determine the overall size needed, * allocate, and copy the contents (of all of parent's children, which * would include 'state', just as 'state' will have copied all of its * children via sec_asn1d_concat_substrings) * * The final case, Case #5, will manifest in that item->data and * item->len will be NULL/0, respectively, since this element was * indefinite-length encoded. In that case, both the tag and length will * already exist in state's subitems, via sec_asn1d_record_any_header, * and so the contents (aka 'child') should be added to that list of * items to concatenate in sec_asn1d_concat_substrings once the EOC * is encountered. * * To distinguish #2/#4 from #1/#3, it's sufficient to walk the ancestor * tree. If the current type is a string type, then the enclosing * construct will be that same type (#1/#2). If the current type is an * any type, then the enclosing construct is either an any type (#3/#4) * or some other type (#5). Since this is BER, this nesting relationship * between 'state' and 'parent' may go through several levels of * constructed encoding, so continue walking the ancestor chain until a * clear determination can be made. * * The variable preallocatedString is used to indicate Case #1/#3, * indicating an in-place copy has already occurred, and Cases #2, #4, * and #5 all have the same behaviour of adding a new substring. */ preallocatedString = PR_FALSE; temp_state = state; while (temp_state && item == temp_state->dest && temp_state->indefinite) { sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(temp_state); if (!parent || parent->underlying_kind != temp_state->underlying_kind) { /* Case #5 - Either this is a top-level construct or it is part * of some other element (e.g. a SEQUENCE), in which case, a * new item should be allocated. */ break; } if (!parent->indefinite) { /* Cases #1 / #3 - A definite length ancestor exists, for which * this is a substring that has already copied into dest. */ preallocatedString = PR_TRUE; break; } if (!parent->substring) { /* Cases #2 / #4 - If the parent is not a substring, but is * indefinite, then there's nothing further up that may have * preallocated dest, thus child will not have already * been copied in place, therefore it's necessary to save child * as a subitem. */ break; } temp_state = parent; } if (item != NULL && item->data != NULL && !preallocatedString) { /* * Save the string away for later concatenation. */ PORT_Assert(item->data != NULL); sec_asn1d_add_to_subitems(state, item->data, item->len, PR_FALSE); /* * Clear the child item for the next round. */ item->data = NULL; item->len = 0; } /* * If our child was just our end-of-contents octets, we are done. */ if (child->endofcontents) done = PR_TRUE; } /* * Stop or do the next one. */ if (done) { child->place = notInUse; state->place = afterConstructedString; } else { sec_asn1d_scrub_state(child); state->top->current = child; } } /* * We are doing a SET OF or SEQUENCE OF, and have just finished an item. */ static void sec_asn1d_next_in_group(sec_asn1d_state *state) { sec_asn1d_state *child; unsigned long child_consumed; PORT_Assert(state->place == duringGroup); PORT_Assert(state->child != NULL); child = state->child; child_consumed = child->consumed; child->consumed = 0; state->consumed += child_consumed; /* * If our child was just our end-of-contents octets, we are done. */ if (child->endofcontents) { /* XXX I removed the PORT_Assert (child->dest == NULL) because there * was a bug in that a template that was a sequence of which also had * a child of a sequence of, in an indefinite group was not working * properly. This fix seems to work, (added the if statement below), * and nothing appears broken, but I am putting this note here just * in case. */ /* * XXX No matter how many times I read that comment, * I cannot figure out what case he was fixing. I believe what he * did was deliberate, so I am loathe to touch it. I need to * understand how it could ever be that child->dest != NULL but * child->endofcontents is true, and why it is important to check * that state->subitems_head is NULL. This really needs to be * figured out, as I am not sure if the following code should be * compensating for "offset", as is done a little farther below * in the more normal case. */ /* * XXX We used to assert our overall state was that we were decoding * an indefinite-length object here (state->indefinite == TRUE and no * pending bytes in the decoder), but those assertions aren't correct * as it's legitimate to wrap indefinite sequences inside definite ones * and this code handles that case. Additionally, when compiled in * release mode these assertions aren't checked anyway, yet function * safely. */ if (child->dest && !state->subitems_head) { sec_asn1d_add_to_subitems(state, child->dest, 0, PR_FALSE); child->dest = NULL; } child->place = notInUse; state->place = afterGroup; return; } /* * Do the "after" field notification for next in group. */ sec_asn1d_notify_after(state->top, child->dest, child->depth); /* * Save it away (unless we are not storing). */ if (child->dest != NULL) { void *dest; dest = child->dest; dest = (char *)dest - child->theTemplate->offset; sec_asn1d_add_to_subitems(state, dest, 0, PR_FALSE); child->dest = NULL; } /* * Account for those bytes; see if we are done. */ if (state->pending) { PORT_Assert(!state->indefinite); if (child_consumed > state->pending) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } state->pending -= child_consumed; if (state->pending == 0) { child->place = notInUse; state->place = afterGroup; return; } } /* * Do the "before" field notification for next item in group. */ sec_asn1d_notify_before(state->top, child->dest, child->depth); /* * Now we do the next one. */ sec_asn1d_scrub_state(child); /* Initialize child state from the template */ sec_asn1d_init_state_based_on_template(child); state->top->current = child; } /* * We are moving along through a sequence; move forward by one, * (detecting end-of-sequence when it happens). * XXX The handling of "missing" is ugly. Fix it. */ static void sec_asn1d_next_in_sequence(sec_asn1d_state *state) { sec_asn1d_state *child; unsigned long child_consumed; PRBool child_missing; PORT_Assert(state->place == duringSequence); PORT_Assert(state->child != NULL); child = state->child; /* * Do the "after" field notification. */ sec_asn1d_notify_after(state->top, child->dest, child->depth); child_missing = (PRBool)child->missing; child_consumed = child->consumed; child->consumed = 0; /* * Take care of accounting. */ if (child_missing) { PORT_Assert(child->optional); } else { state->consumed += child_consumed; /* * Free any grandchild. */ sec_asn1d_free_child(child, PR_FALSE); if (state->pending) { PORT_Assert(!state->indefinite); if (child_consumed > state->pending) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } state->pending -= child_consumed; if (state->pending == 0) { child->theTemplate++; while (child->theTemplate->kind != 0) { if ((child->theTemplate->kind & SEC_ASN1_OPTIONAL) == 0) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } child->theTemplate++; } child->place = notInUse; state->place = afterEndOfContents; return; } } } /* * Move forward. */ child->theTemplate++; if (child->theTemplate->kind == 0) { /* * We are done with this sequence. */ child->place = notInUse; if (state->pending) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } else if (child_missing) { /* * We got to the end, but have a child that started parsing * and ended up "missing". The only legitimate reason for * this is that we had one or more optional fields at the * end of our sequence, and we were encoded indefinite-length, * so when we went looking for those optional fields we * found our end-of-contents octets instead. * (Yes, this is ugly; dunno a better way to handle it.) * So, first confirm the situation, and then mark that we * are done. */ if (state->indefinite && child->endofcontents) { PORT_Assert(child_consumed == 2); if (child_consumed != 2) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { state->consumed += child_consumed; state->place = afterEndOfContents; } } else { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; } } else { /* * We have to finish out, maybe reading end-of-contents octets; * let the normal logic do the right thing. */ state->place = beforeEndOfContents; } } else { unsigned char child_found_tag_modifiers = 0; unsigned long child_found_tag_number = 0; /* * Reset state and push. */ if (state->dest != NULL) child->dest = (char *)state->dest + child->theTemplate->offset; /* * Do the "before" field notification. */ sec_asn1d_notify_before(state->top, child->dest, child->depth); if (child_missing) { /* if previous child was missing, copy the tag data we already have */ child_found_tag_modifiers = child->found_tag_modifiers; child_found_tag_number = child->found_tag_number; } state->top->current = child; child = sec_asn1d_init_state_based_on_template(child); if (child_missing && child) { child->place = afterIdentifier; child->found_tag_modifiers = child_found_tag_modifiers; child->found_tag_number = child_found_tag_number; child->consumed = child_consumed; if (child->underlying_kind == SEC_ASN1_ANY && !child->top->filter_only) { /* * If the new field is an ANY, and we are storing, then * we need to save the tag out. We would have done this * already in the normal case, but since we were looking * for an optional field, and we did not find it, we only * now realize we need to save the tag. */ unsigned char identifier; /* * Check that we did not end up with a high tag; for that * we need to re-encode the tag into multiple bytes in order * to store it back to look like what we parsed originally. * In practice this does not happen, but for completeness * sake it should probably be made to work at some point. */ if (child_found_tag_modifiers >= SEC_ASN1_HIGH_TAG_NUMBER) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); state->top->status = decodeError; } else { identifier = (unsigned char)(child_found_tag_modifiers | child_found_tag_number); sec_asn1d_record_any_header(child, (char *)&identifier, 1); } } } } } static void sec_asn1d_concat_substrings(sec_asn1d_state *state) { PORT_Assert(state->place == afterConstructedString); if (state->subitems_head != NULL) { struct subitem *substring; unsigned long alloc_len, item_len; unsigned char *where; SECItem *item; PRBool is_bit_string; item_len = 0; is_bit_string = (state->underlying_kind == SEC_ASN1_BIT_STRING) ? PR_TRUE : PR_FALSE; substring = state->subitems_head; while (substring != NULL) { /* * All bit-string substrings except the last one should be * a clean multiple of 8 bits. */ if (is_bit_string && (substring->next != NULL) && (substring->len & 0x7)) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } item_len += substring->len; substring = substring->next; } if (is_bit_string) { alloc_len = ((item_len + 7) >> 3); } else { /* * Add 2 for the end-of-contents octets of an indefinite-length * ANY that is *not* also an INNER. Because we zero-allocate * below, all we need to do is increase the length here. */ if (state->underlying_kind == SEC_ASN1_ANY && state->indefinite) item_len += 2; alloc_len = item_len; } if (state->top->max_element_size > 0 && alloc_len > state->top->max_element_size) { PORT_SetError(SEC_ERROR_OUTPUT_LEN); state->top->status = decodeError; return; } item = (SECItem *)(state->dest); PORT_Assert(item != NULL); PORT_Assert(item->data == NULL); item->data = (unsigned char *)sec_asn1d_zalloc(state->top->their_pool, alloc_len); if (item->data == NULL) { state->top->status = decodeError; return; } item->len = item_len; where = item->data; substring = state->subitems_head; while (substring != NULL) { if (is_bit_string) item_len = (substring->len + 7) >> 3; else item_len = substring->len; PORT_Memcpy(where, substring->data, item_len); where += item_len; substring = substring->next; } /* * Because we use arenas and have a mark set, we later free * everything we have allocated, so this does *not* present * a memory leak (it is just temporarily left dangling). */ state->subitems_head = state->subitems_tail = NULL; } state->place = afterEndOfContents; } static void sec_asn1d_concat_group(sec_asn1d_state *state) { const void ***placep; PORT_Assert(state->place == afterGroup); placep = (const void ***)state->dest; PORT_Assert(state->subitems_head == NULL || placep != NULL); if (placep != NULL) { struct subitem *item; const void **group; int count; count = 0; item = state->subitems_head; while (item != NULL) { PORT_Assert(item->next != NULL || item == state->subitems_tail); count++; item = item->next; } group = (const void **)sec_asn1d_zalloc(state->top->their_pool, (count + 1) * (sizeof(void *))); if (group == NULL) { state->top->status = decodeError; return; } *placep = group; item = state->subitems_head; while (item != NULL) { *group++ = item->data; item = item->next; } *group = NULL; /* * Because we use arenas and have a mark set, we later free * everything we have allocated, so this does *not* present * a memory leak (it is just temporarily left dangling). */ state->subitems_head = state->subitems_tail = NULL; } state->place = afterEndOfContents; } /* * For those states that push a child to handle a subtemplate, * "absorb" that child (transfer necessary information). */ static void sec_asn1d_absorb_child(sec_asn1d_state *state) { /* * There is absolutely supposed to be a child there. */ PORT_Assert(state->child != NULL); /* * Inherit the missing status of our child, and do the ugly * backing-up if necessary. */ state->missing = state->child->missing; if (state->missing) { state->found_tag_number = state->child->found_tag_number; state->found_tag_modifiers = state->child->found_tag_modifiers; state->endofcontents = state->child->endofcontents; } /* * Add in number of bytes consumed by child. * (Only EXPLICIT should have already consumed bytes itself.) */ PORT_Assert(state->place == afterExplicit || state->consumed == 0); state->consumed += state->child->consumed; /* * Subtract from bytes pending; this only applies to a definite-length * EXPLICIT field. */ if (state->pending) { PORT_Assert(!state->indefinite); PORT_Assert(state->place == afterExplicit); /* * If we had a definite-length explicit, then what the child * consumed should be what was left pending. */ if (state->pending != state->child->consumed) { if (state->pending < state->child->consumed) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return; } /* * Okay, this is a hack. It *should* be an error whether * pending is too big or too small, but it turns out that * we had a bug in our *old* DER encoder that ended up * counting an explicit header twice in the case where * the underlying type was an ANY. So, because we cannot * prevent receiving these (our own certificate server can * send them to us), we need to be lenient and accept them. * To do so, we need to pretend as if we read all of the * bytes that the header said we would find, even though * we actually came up short. */ state->consumed += (state->pending - state->child->consumed); } state->pending = 0; } /* * Indicate that we are done with child. */ state->child->consumed = 0; /* * And move on to final state. * (Technically everybody could move to afterEndOfContents except * for an indefinite-length EXPLICIT; for simplicity though we assert * that but let the end-of-contents code do the real determination.) */ PORT_Assert(state->place == afterExplicit || (!state->indefinite)); state->place = beforeEndOfContents; } static void sec_asn1d_prepare_for_end_of_contents(sec_asn1d_state *state) { PORT_Assert(state->place == beforeEndOfContents); if (state->indefinite) { state->place = duringEndOfContents; state->pending = 2; } else { state->place = afterEndOfContents; } } static unsigned long sec_asn1d_parse_end_of_contents(sec_asn1d_state *state, const char *buf, unsigned long len) { unsigned int i; PORT_Assert(state->pending <= 2); PORT_Assert(state->place == duringEndOfContents); if (len == 0) { state->top->status = needBytes; return 0; } if (state->pending < len) len = state->pending; for (i = 0; i < len; i++) { if (buf[i] != 0) { /* * We expect to find only zeros; if not, just give up. */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return 0; } } state->pending -= len; if (state->pending == 0) { state->place = afterEndOfContents; /* These end-of-contents octets either terminate a SEQUENCE, a GROUP, * or a constructed string. The SEQUENCE case is unique in that the * state parses its own end-of-contents octets and therefore should not * have its `endofcontents` flag set. We identify the SEQUENCE case by * checking whether the child state's template is pointing at a * template terminator (see `sec_asn1d_next_in_sequence`). */ if (state->child && state->child->theTemplate->kind == 0) { state->endofcontents = PR_FALSE; } else { state->endofcontents = PR_TRUE; } } return len; } static void sec_asn1d_pop_state(sec_asn1d_state *state) { #if 0 /* XXX I think this should always be handled explicitly by parent? */ /* * Account for our child. */ if (state->child != NULL) { state->consumed += state->child->consumed; if (state->pending) { PORT_Assert (!state->indefinite); if (state->child->consumed > state->pending) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { state->pending -= state->child->consumed; } } state->child->consumed = 0; } #endif /* XXX */ /* * Free our child. */ sec_asn1d_free_child(state, PR_FALSE); /* * Just make my parent be the current state. It will then clean * up after me and free me (or reuse me). */ state->top->current = state->parent; } static sec_asn1d_state * sec_asn1d_before_choice(sec_asn1d_state *state) { sec_asn1d_state *child; if (state->allocate) { void *dest; dest = sec_asn1d_zalloc(state->top->their_pool, state->theTemplate->size); if ((void *)NULL == dest) { state->top->status = decodeError; return (sec_asn1d_state *)NULL; } state->dest = (char *)dest + state->theTemplate->offset; } child = sec_asn1d_push_state(state->top, state->theTemplate + 1, (char *)state->dest - state->theTemplate->offset, PR_FALSE); if ((sec_asn1d_state *)NULL == child) { return (sec_asn1d_state *)NULL; } sec_asn1d_scrub_state(child); child = sec_asn1d_init_state_based_on_template(child); if ((sec_asn1d_state *)NULL == child) { return (sec_asn1d_state *)NULL; } child->optional = PR_TRUE; state->place = duringChoice; return child; } static sec_asn1d_state * sec_asn1d_during_choice(sec_asn1d_state *state) { sec_asn1d_state *child = state->child; PORT_Assert((sec_asn1d_state *)NULL != child); if (child->missing) { unsigned char child_found_tag_modifiers = 0; unsigned long child_found_tag_number = 0; void *dest; state->consumed += child->consumed; if (child->endofcontents) { /* This choice is probably the first item in a GROUP ** (e.g. SET_OF) that was indefinite-length encoded. ** We're actually at the end of that GROUP. ** We look up the stack to be sure that we find ** a state with indefinite length encoding before we ** find a state (like a SEQUENCE) that is definite. */ child->place = notInUse; state->place = afterChoice; state->endofcontents = PR_TRUE; /* propagate this up */ if (sec_asn1d_parent_allows_EOC(state)) return state; PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return NULL; } dest = (char *)child->dest - child->theTemplate->offset; child->theTemplate++; if (0 == child->theTemplate->kind) { /* Ran out of choices */ PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return (sec_asn1d_state *)NULL; } child->dest = (char *)dest + child->theTemplate->offset; /* cargo'd from next_in_sequence innards */ if (state->pending) { PORT_Assert(!state->indefinite); if (child->consumed > state->pending) { PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return NULL; } state->pending -= child->consumed; if (0 == state->pending) { /* XXX uh.. not sure if I should have stopped this * from happening before. */ PORT_Assert(0); PORT_SetError(SEC_ERROR_BAD_DER); state->top->status = decodeError; return (sec_asn1d_state *)NULL; } } child->consumed = 0; sec_asn1d_scrub_state(child); /* move it on top again */ state->top->current = child; child_found_tag_modifiers = child->found_tag_modifiers; child_found_tag_number = child->found_tag_number; child = sec_asn1d_init_state_based_on_template(child); if ((sec_asn1d_state *)NULL == child) { return (sec_asn1d_state *)NULL; } /* copy our findings to the new top */ child->found_tag_modifiers = child_found_tag_modifiers; child->found_tag_number = child_found_tag_number; child->optional = PR_TRUE; child->place = afterIdentifier; return child; } if ((void *)NULL != state->dest) { /* Store the enum */ int *which = (int *)state->dest; *which = (int)child->theTemplate->size; } child->place = notInUse; state->place = afterChoice; return state; } static void sec_asn1d_after_choice(sec_asn1d_state *state) { state->consumed += state->child->consumed; state->child->consumed = 0; state->place = afterEndOfContents; sec_asn1d_pop_state(state); } unsigned long sec_asn1d_uinteger(SECItem *src) { unsigned long value; int len; if (src->len > 5 || (src->len > 4 && src->data[0] == 0)) return 0; value = 0; len = src->len; while (len) { value <<= 8; value |= src->data[--len]; } return value; } SECStatus SEC_ASN1DecodeInteger(SECItem *src, unsigned long *value) { unsigned long v; unsigned int i; if (src == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (src->len > sizeof(unsigned long)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (src->data == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (src->data[0] & 0x80) v = -1; /* signed and negative - start with all 1's */ else v = 0; for (i = 0; i < src->len; i++) { /* shift in next byte */ v <<= 8; v |= src->data[i]; } *value = v; return SECSuccess; } #ifdef DEBUG_ASN1D_STATES static void dump_states(SEC_ASN1DecoderContext *cx) { sec_asn1d_state *state; int bufsize = 256; char kindBuf[bufsize]; for (state = cx->current; state->parent; state = state->parent) { ; } for (; state; state = state->child) { int i; for (i = 0; i < state->depth; i++) { printf(" "); } i = formatKind(state->theTemplate->kind, kindBuf, bufsize); printf("%s: tmpl kind %s", (state == cx->current) ? "STATE" : "State", kindBuf); printf(" %s", (state->place >= 0 && state->place <= notInUse) ? place_names[state->place] : "(undefined)"); if (!i) printf(", expect 0x%02lx", state->expect_tag_number | state->expect_tag_modifiers); printf("%s%s%s %lu\n", state->indefinite ? ", indef" : "", state->missing ? ", miss" : "", state->endofcontents ? ", EOC" : "", state->pending); } return; } #endif /* DEBUG_ASN1D_STATES */ SECStatus SEC_ASN1DecoderUpdate(SEC_ASN1DecoderContext *cx, const char *buf, unsigned long len) { sec_asn1d_state *state = NULL; unsigned long consumed; SEC_ASN1EncodingPart what; if (cx->status == needBytes) cx->status = keepGoing; while (cx->status == keepGoing) { state = cx->current; what = SEC_ASN1_Contents; consumed = 0; #ifdef DEBUG_ASN1D_STATES printf("\nPLACE = %s, next byte = 0x%02x, %p[%lu]\n", (state->place >= 0 && state->place <= notInUse) ? place_names[state->place] : "(undefined)", len ? (unsigned int)((unsigned char *)buf)[consumed] : 0, buf, consumed); dump_states(cx); #endif /* DEBUG_ASN1D_STATES */ switch (state->place) { case beforeIdentifier: consumed = sec_asn1d_parse_identifier(state, buf, len); what = SEC_ASN1_Identifier; break; case duringIdentifier: consumed = sec_asn1d_parse_more_identifier(state, buf, len); what = SEC_ASN1_Identifier; break; case afterIdentifier: sec_asn1d_confirm_identifier(state); break; case beforeLength: consumed = sec_asn1d_parse_length(state, buf, len); what = SEC_ASN1_Length; break; case duringLength: consumed = sec_asn1d_parse_more_length(state, buf, len); what = SEC_ASN1_Length; break; case afterLength: sec_asn1d_prepare_for_contents(state); break; case beforeBitString: consumed = sec_asn1d_parse_bit_string(state, buf, len); break; case duringBitString: consumed = sec_asn1d_parse_more_bit_string(state, buf, len); break; case duringConstructedString: sec_asn1d_next_substring(state); break; case duringGroup: sec_asn1d_next_in_group(state); break; case duringLeaf: consumed = sec_asn1d_parse_leaf(state, buf, len); break; case duringSaveEncoding: sec_asn1d_reuse_encoding(state); if (cx->status == decodeError) { /* recursive call has already popped all states from stack. ** Bail out quickly. */ return SECFailure; } if (cx->status == needBytes) { /* recursive call wanted more data. Fatal. Clean up below. */ PORT_SetError(SEC_ERROR_BAD_DER); cx->status = decodeError; } break; case duringSequence: sec_asn1d_next_in_sequence(state); break; case afterConstructedString: sec_asn1d_concat_substrings(state); break; case afterExplicit: case afterImplicit: case afterInline: case afterPointer: sec_asn1d_absorb_child(state); break; case afterGroup: sec_asn1d_concat_group(state); break; case afterSaveEncoding: /* SEC_ASN1DecoderUpdate has called itself recursively to ** decode SAVEd encoded data, and now is done decoding that. ** Return to the calling copy of SEC_ASN1DecoderUpdate. */ return SECSuccess; case beforeEndOfContents: sec_asn1d_prepare_for_end_of_contents(state); break; case duringEndOfContents: consumed = sec_asn1d_parse_end_of_contents(state, buf, len); what = SEC_ASN1_EndOfContents; break; case afterEndOfContents: sec_asn1d_pop_state(state); break; case beforeChoice: state = sec_asn1d_before_choice(state); break; case duringChoice: state = sec_asn1d_during_choice(state); break; case afterChoice: sec_asn1d_after_choice(state); break; case notInUse: default: /* This is not an error, but rather a plain old BUG! */ PORT_Assert(0); PORT_SetError(SEC_ERROR_BAD_DER); cx->status = decodeError; break; } if (cx->status == decodeError) break; /* We should not consume more than we have. */ PORT_Assert(consumed <= len); if (consumed > len) { PORT_SetError(SEC_ERROR_BAD_DER); cx->status = decodeError; break; } /* It might have changed, so we have to update our local copy. */ state = cx->current; /* If it is NULL, we have popped all the way to the top. */ if (state == NULL) { PORT_Assert(consumed == 0); #if 0 /* XXX I want this here, but it seems that we have situations (like \ * downloading a pkcs7 cert chain from some issuers) that give us a \ * length which is greater than the entire encoding. So, we cannot \ * have this be an error. \ */ if (len > 0) { PORT_SetError (SEC_ERROR_BAD_DER); cx->status = decodeError; } else #endif cx->status = allDone; break; } else if (state->theTemplate->kind == SEC_ASN1_SKIP_REST) { cx->status = allDone; break; } if (consumed == 0) continue; /* * The following check is specifically looking for an ANY * that is *not* also an INNER, because we need to save aside * all bytes in that case -- the contents parts will get * handled like all other contents, and the end-of-contents * bytes are added by the concat code, but the outer header * bytes need to get saved too, so we do them explicitly here. */ if (state->underlying_kind == SEC_ASN1_ANY && !cx->filter_only && (what == SEC_ASN1_Identifier || what == SEC_ASN1_Length)) { sec_asn1d_record_any_header(state, buf, consumed); } /* * We had some number of good, accepted bytes. If the caller * has registered to see them, pass them along. */ if (state->top->filter_proc != NULL) { int depth; depth = state->depth; if (what == SEC_ASN1_EndOfContents && !state->indefinite) { PORT_Assert(state->parent != NULL && state->parent->indefinite); depth--; PORT_Assert(depth == state->parent->depth); } (*state->top->filter_proc)(state->top->filter_arg, buf, consumed, depth, what); } state->consumed += consumed; buf += consumed; len -= consumed; } if (cx->status == decodeError) { while (state != NULL) { sec_asn1d_free_child(state, PR_TRUE); state = state->parent; } #ifdef SEC_ASN1D_FREE_ON_ERROR /* \ * XXX This does not work because we can \ * end up leaving behind dangling pointers \ * to stuff that was allocated. In order \ * to make this really work (which would \ * be a good thing, I think), we need to \ * keep track of every place/pointer that \ * was allocated and make sure to NULL it \ * out before we then free back to the mark. \ */ if (cx->their_pool != NULL) { PORT_Assert(cx->their_mark != NULL); PORT_ArenaRelease(cx->their_pool, cx->their_mark); cx->their_mark = NULL; } #endif return SECFailure; } #if 0 /* XXX This is what I want, but cannot have because it seems we \ * have situations (like when downloading a pkcs7 cert chain from \ * some issuers) that give us a total length which is greater than \ * the entire encoding. So, we have to allow allDone to have a \ * remaining length greater than zero. I wanted to catch internal \ * bugs with this, noticing when we do not have the right length. \ * Oh well. \ */ PORT_Assert (len == 0 && (cx->status == needBytes || cx->status == allDone)); #else PORT_Assert((len == 0 && cx->status == needBytes) || cx->status == allDone); #endif return SECSuccess; } SECStatus SEC_ASN1DecoderFinish(SEC_ASN1DecoderContext *cx) { SECStatus rv; if (!cx || cx->status == needBytes) { PORT_SetError(SEC_ERROR_BAD_DER); rv = SECFailure; } else { rv = SECSuccess; } /* * XXX anything else that needs to be finished? */ if (cx) { PORT_FreeArena(cx->our_pool, PR_TRUE); } return rv; } SEC_ASN1DecoderContext * SEC_ASN1DecoderStart(PLArenaPool *their_pool, void *dest, const SEC_ASN1Template *theTemplate) { PLArenaPool *our_pool; SEC_ASN1DecoderContext *cx; our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); if (our_pool == NULL) return NULL; cx = (SEC_ASN1DecoderContext *)PORT_ArenaZAlloc(our_pool, sizeof(*cx)); if (cx == NULL) { PORT_FreeArena(our_pool, PR_FALSE); return NULL; } cx->our_pool = our_pool; if (their_pool != NULL) { cx->their_pool = their_pool; #ifdef SEC_ASN1D_FREE_ON_ERROR cx->their_mark = PORT_ArenaMark(their_pool); #endif } cx->status = needBytes; if (sec_asn1d_push_state(cx, theTemplate, dest, PR_FALSE) == NULL || sec_asn1d_init_state_based_on_template(cx->current) == NULL) { /* * Trouble initializing (probably due to failed allocations) * requires that we just give up. */ PORT_FreeArena(our_pool, PR_FALSE); return NULL; } return cx; } void SEC_ASN1DecoderSetFilterProc(SEC_ASN1DecoderContext *cx, SEC_ASN1WriteProc fn, void *arg, PRBool only) { /* check that we are "between" fields here */ PORT_Assert(cx->during_notify); cx->filter_proc = fn; cx->filter_arg = arg; cx->filter_only = only; } void SEC_ASN1DecoderClearFilterProc(SEC_ASN1DecoderContext *cx) { /* check that we are "between" fields here */ PORT_Assert(cx->during_notify); cx->filter_proc = NULL; cx->filter_arg = NULL; cx->filter_only = PR_FALSE; } void SEC_ASN1DecoderSetNotifyProc(SEC_ASN1DecoderContext *cx, SEC_ASN1NotifyProc fn, void *arg) { cx->notify_proc = fn; cx->notify_arg = arg; } void SEC_ASN1DecoderClearNotifyProc(SEC_ASN1DecoderContext *cx) { cx->notify_proc = NULL; cx->notify_arg = NULL; /* not necessary; just being clean */ } void SEC_ASN1DecoderSetMaximumElementSize(SEC_ASN1DecoderContext *cx, unsigned long max_size) { cx->max_element_size = max_size; } void SEC_ASN1DecoderAbort(SEC_ASN1DecoderContext *cx, int error) { PORT_Assert(cx); PORT_SetError(error); cx->status = decodeError; } SECStatus SEC_ASN1Decode(PLArenaPool *poolp, void *dest, const SEC_ASN1Template *theTemplate, const char *buf, long len) { SEC_ASN1DecoderContext *dcx; SECStatus urv, frv; dcx = SEC_ASN1DecoderStart(poolp, dest, theTemplate); if (dcx == NULL) return SECFailure; /* In one-shot mode, there's no possibility of streaming data beyond the * length of len */ SEC_ASN1DecoderSetMaximumElementSize(dcx, len); urv = SEC_ASN1DecoderUpdate(dcx, buf, len); frv = SEC_ASN1DecoderFinish(dcx); if (urv != SECSuccess) return urv; return frv; } SECStatus SEC_ASN1DecodeItem(PLArenaPool *poolp, void *dest, const SEC_ASN1Template *theTemplate, const SECItem *src) { return SEC_ASN1Decode(poolp, dest, theTemplate, (const char *)src->data, src->len); } #ifdef DEBUG_ASN1D_STATES void sec_asn1d_Assert(const char *s, const char *file, PRIntn ln) { printf("Assertion failed, \"%s\", file %s, line %d\n", s, file, ln); fflush(stdout); } #endif /* * Generic templates for individual/simple items and pointers to * and sets of same. * * If you need to add a new one, please note the following: * - For each new basic type you should add *four* templates: * one plain, one PointerTo, one SequenceOf and one SetOf. * - If the new type can be constructed (meaning, it is a * *string* type according to BER/DER rules), then you should * or-in SEC_ASN1_MAY_STREAM to the type in the basic template. * See the definition of the OctetString template for an example. * - It may not be obvious, but these are in *alphabetical* * order based on the SEC_ASN1_XXX name; so put new ones in * the appropriate place. */ const SEC_ASN1Template SEC_SequenceOfAnyTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate } }; #if 0 const SEC_ASN1Template SEC_PointerToBitStringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_BitStringTemplate } }; const SEC_ASN1Template SEC_SequenceOfBitStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_BitStringTemplate } }; const SEC_ASN1Template SEC_SetOfBitStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_BitStringTemplate } }; const SEC_ASN1Template SEC_PointerToBMPStringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_BMPStringTemplate } }; const SEC_ASN1Template SEC_SequenceOfBMPStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_BMPStringTemplate } }; const SEC_ASN1Template SEC_SetOfBMPStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_BMPStringTemplate } }; const SEC_ASN1Template SEC_PointerToBooleanTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_BooleanTemplate } }; const SEC_ASN1Template SEC_SequenceOfBooleanTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_BooleanTemplate } }; const SEC_ASN1Template SEC_SetOfBooleanTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_BooleanTemplate } }; #endif const SEC_ASN1Template SEC_EnumeratedTemplate[] = { { SEC_ASN1_ENUMERATED, 0, NULL, sizeof(SECItem) } }; const SEC_ASN1Template SEC_PointerToEnumeratedTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_EnumeratedTemplate } }; #if 0 const SEC_ASN1Template SEC_SequenceOfEnumeratedTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_EnumeratedTemplate } }; #endif const SEC_ASN1Template SEC_SetOfEnumeratedTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_EnumeratedTemplate } }; const SEC_ASN1Template SEC_PointerToGeneralizedTimeTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_GeneralizedTimeTemplate } }; #if 0 const SEC_ASN1Template SEC_SequenceOfGeneralizedTimeTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_GeneralizedTimeTemplate } }; const SEC_ASN1Template SEC_SetOfGeneralizedTimeTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_GeneralizedTimeTemplate } }; const SEC_ASN1Template SEC_PointerToIA5StringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_IA5StringTemplate } }; const SEC_ASN1Template SEC_SequenceOfIA5StringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_IA5StringTemplate } }; const SEC_ASN1Template SEC_SetOfIA5StringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_IA5StringTemplate } }; const SEC_ASN1Template SEC_PointerToIntegerTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_IntegerTemplate } }; const SEC_ASN1Template SEC_SequenceOfIntegerTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_IntegerTemplate } }; const SEC_ASN1Template SEC_SetOfIntegerTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_IntegerTemplate } }; const SEC_ASN1Template SEC_PointerToNullTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_NullTemplate } }; const SEC_ASN1Template SEC_SequenceOfNullTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_NullTemplate } }; const SEC_ASN1Template SEC_SetOfNullTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_NullTemplate } }; const SEC_ASN1Template SEC_PointerToObjectIDTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_ObjectIDTemplate } }; #endif const SEC_ASN1Template SEC_SequenceOfObjectIDTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_ObjectIDTemplate } }; #if 0 const SEC_ASN1Template SEC_SetOfObjectIDTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_ObjectIDTemplate } }; const SEC_ASN1Template SEC_SequenceOfOctetStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_OctetStringTemplate } }; const SEC_ASN1Template SEC_SetOfOctetStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_OctetStringTemplate } }; #endif const SEC_ASN1Template SEC_PrintableStringTemplate[] = { { SEC_ASN1_PRINTABLE_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } }; #if 0 const SEC_ASN1Template SEC_PointerToPrintableStringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_PrintableStringTemplate } }; const SEC_ASN1Template SEC_SequenceOfPrintableStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_PrintableStringTemplate } }; const SEC_ASN1Template SEC_SetOfPrintableStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_PrintableStringTemplate } }; #endif const SEC_ASN1Template SEC_T61StringTemplate[] = { { SEC_ASN1_T61_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } }; #if 0 const SEC_ASN1Template SEC_PointerToT61StringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_T61StringTemplate } }; const SEC_ASN1Template SEC_SequenceOfT61StringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_T61StringTemplate } }; const SEC_ASN1Template SEC_SetOfT61StringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_T61StringTemplate } }; #endif const SEC_ASN1Template SEC_UniversalStringTemplate[] = { { SEC_ASN1_UNIVERSAL_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } }; #if 0 const SEC_ASN1Template SEC_PointerToUniversalStringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_UniversalStringTemplate } }; const SEC_ASN1Template SEC_SequenceOfUniversalStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_UniversalStringTemplate } }; const SEC_ASN1Template SEC_SetOfUniversalStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_UniversalStringTemplate } }; const SEC_ASN1Template SEC_PointerToUTCTimeTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_UTCTimeTemplate } }; const SEC_ASN1Template SEC_SequenceOfUTCTimeTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_UTCTimeTemplate } }; const SEC_ASN1Template SEC_SetOfUTCTimeTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_UTCTimeTemplate } }; const SEC_ASN1Template SEC_PointerToUTF8StringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_UTF8StringTemplate } }; const SEC_ASN1Template SEC_SequenceOfUTF8StringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_UTF8StringTemplate } }; const SEC_ASN1Template SEC_SetOfUTF8StringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_UTF8StringTemplate } }; #endif const SEC_ASN1Template SEC_VisibleStringTemplate[] = { { SEC_ASN1_VISIBLE_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } }; #if 0 const SEC_ASN1Template SEC_PointerToVisibleStringTemplate[] = { { SEC_ASN1_POINTER, 0, SEC_VisibleStringTemplate } }; const SEC_ASN1Template SEC_SequenceOfVisibleStringTemplate[] = { { SEC_ASN1_SEQUENCE_OF, 0, SEC_VisibleStringTemplate } }; const SEC_ASN1Template SEC_SetOfVisibleStringTemplate[] = { { SEC_ASN1_SET_OF, 0, SEC_VisibleStringTemplate } }; #endif /* * Template for skipping a subitem. * * Note that it only makes sense to use this for decoding (when you want * to decode something where you are only interested in one or two of * the fields); you cannot encode a SKIP! */ const SEC_ASN1Template SEC_SkipTemplate[] = { { SEC_ASN1_SKIP } }; /* These functions simply return the address of the above-declared templates. ** This is necessary for Windows DLLs. Sigh. */ SEC_ASN1_CHOOSER_IMPLEMENT(SEC_EnumeratedTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToEnumeratedTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfAnyTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfObjectIDTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SkipTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_UniversalStringTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PrintableStringTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_T61StringTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToGeneralizedTimeTemplate)