| /* 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 <stdio.h> |
| #define PR_Assert sec_asn1d_Assert |
| #endif |
| |
| #include <limits.h> |
| |
| #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 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) { |
| sprintf(buf, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6]); |
| buf += strlen(buf); |
| } |
| if (kind & SEC_ASN1_METHOD_MASK) { |
| sprintf(buf, " %s", method_names[1]); |
| buf += strlen(buf); |
| } |
| if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) { |
| if (k || !notag) { |
| sprintf(buf, " %s", type_names[k]); |
| if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) && |
| (kind & SEC_ASN1_GROUP)) { |
| buf += strlen(buf); |
| sprintf(buf, "_OF"); |
| } |
| } |
| } else { |
| sprintf(buf, " [%lu]", k); |
| } |
| buf += strlen(buf); |
| |
| for (k = kind >> 8, i = 0; k; k >>= 1, ++i) { |
| if (k & 1) { |
| sprintf(buf, " %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 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); |
| } |
| |
| 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 |
| { |
| char kindBuf[256]; |
| formatKind(byte, kindBuf); |
| 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_SEQUENCE_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. |
| */ |
| PORT_Assert(child_found_tag_number < SEC_ASN1_HIGH_TAG_NUMBER); |
| 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; |
| 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; |
| char kindBuf[256]; |
| |
| 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); |
| 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; |
| sec_asn1d_state *stateEnd = cx->current; |
| |
| 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 && stateEnd->parent != state) { |
| 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) |