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