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