diff options
author | nelsonb%netscape.com <devnull@localhost> | 2003-11-07 08:15:01 +0000 |
---|---|---|
committer | nelsonb%netscape.com <devnull@localhost> | 2003-11-07 08:15:01 +0000 |
commit | 115af0351bea83b4d826d1ffb293dda9186fc841 (patch) | |
tree | 6a559787c3bfe3ad9732175fed7a8b65e1a82935 | |
parent | 54466d4bf645d7ec4ca67708db5598426b5b603d (diff) | |
download | nss-hg-115af0351bea83b4d826d1ffb293dda9186fc841.tar.gz |
This is revision 1.26, whomped onto the NSS 3.4 branch, in anticipation
of the next revision.
-rw-r--r-- | security/nss/lib/util/secasn1d.c | 465 |
1 files changed, 324 insertions, 141 deletions
diff --git a/security/nss/lib/util/secasn1d.c b/security/nss/lib/util/secasn1d.c index bb40a2b75..d46d53f34 100644 --- a/security/nss/lib/util/secasn1d.c +++ b/security/nss/lib/util/secasn1d.c @@ -38,6 +38,13 @@ * $Id$ */ +/* #define DEBUG_ASN1D_STATES 1 */ + +#ifdef DEBUG_ASN1D_STATES +#include <stdio.h> +#define PR_Assert sec_asn1d_Assert +#endif + #include "secasn1.h" #include "secerr.h" @@ -72,7 +79,7 @@ typedef enum { } sec_asn1d_parse_place; #ifdef DEBUG_ASN1D_STATES -static const char *place_names[] = { +static const char * const place_names[] = { "beforeIdentifier", "duringIdentifier", "afterIdentifier", @@ -101,6 +108,114 @@ static const char *place_names[] = { "afterChoice", "notInUse" }; + +static const char * const class_names[] = { + "UNIVERSAL", + "APPLICATION", + "CONTEXT_SPECIFIC", + "PRIVATE" +}; + +static const char * const method_names[] = { "PRIMITIVE", "CONSTRUCTED" }; + +static const char * const type_names[] = { + "END_OF_CONTENTS", + "BOOLEAN", + "INTEGER", + "BIT_STRING", + "OCTET_STRING", + "NULL", + "OBJECT_ID", + "OBJECT_DESCRIPTOR", + "(type 08)", + "REAL", + "ENUMERATED", + "EMBEDDED", + "UTF8_STRING", + "(type 0d)", + "(type 0e)", + "(type 0f)", + "SEQUENCE", + "SET", + "NUMERIC_STRING", + "PRINTABLE_STRING", + "T61_STRING", + "VIDEOTEXT_STRING", + "IA5_STRING", + "UTC_TIME", + "GENERALIZED_TIME", + "GRAPHIC_STRING", + "VISIBLE_STRING", + "GENERAL_STRING", + "UNIVERSAL_STRING", + "(type 1d)", + "BMP_STRING", + "HIGH_TAG_VALUE" +}; + +static const char * const flag_names[] = { /* flags, right to left */ + "OPTIONAL", + "EXPLICIT", + "ANY", + "INLINE", + "POINTER", + "GROUP", + "DYNAMIC", + "SKIP", + "INNER", + "SAVE", + "", /* decoder ignores "MAY_STREAM", */ + "SKIP_REST", + "CHOICE", + "NO_STREAM", + "DEBUG_BREAK", + "unknown 08", + "unknown 10", + "unknown 20", + "unknown 40", + "unknown 80" +}; + +static int /* bool */ +formatKind(unsigned long kind, char * buf) +{ + int i; + unsigned long k = kind & SEC_ASN1_TAGNUM_MASK; + unsigned long notag = kind & (SEC_ASN1_CHOICE | SEC_ASN1_POINTER | + SEC_ASN1_INLINE | SEC_ASN1_ANY | SEC_ASN1_SAVE); + + buf[0] = 0; + if ((kind & SEC_ASN1_CLASS_MASK) != SEC_ASN1_UNIVERSAL) { + sprintf(buf, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6] ); + buf += strlen(buf); + } + if (kind & SEC_ASN1_METHOD_MASK) { + sprintf(buf, " %s", method_names[1]); + buf += strlen(buf); + } + if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) { + if (k || !notag) { + sprintf(buf, " %s", type_names[k] ); + if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) && + (kind & SEC_ASN1_GROUP)) { + buf += strlen(buf); + sprintf(buf, "_OF"); + } + } + } else { + sprintf(buf, " [%d]", k); + } + buf += strlen(buf); + + for (k = kind >> 8, i = 0; k; k >>= 1, ++i) { + if (k & 1) { + sprintf(buf, " %s", flag_names[i]); + buf += strlen(buf); + } + } + return notag != 0; +} + #endif /* DEBUG_ASN1D_STATES */ typedef enum { @@ -412,10 +527,10 @@ sec_asn1d_init_state_based_on_template (sec_asn1d_state *state) encode_kind &= ~SEC_ASN1_DYNAMIC; encode_kind &= ~SEC_ASN1_MAY_STREAM; - if( encode_kind & SEC_ASN1_CHOICE ) { + if (encode_kind & SEC_ASN1_CHOICE) { #if 0 /* XXX remove? */ sec_asn1d_state *child = sec_asn1d_push_state(state->top, state->theTemplate, state->dest, PR_FALSE); - if( (sec_asn1d_state *)NULL == child ) { + if ((sec_asn1d_state *)NULL == child) { return (sec_asn1d_state *)NULL; } @@ -551,7 +666,7 @@ sec_asn1d_init_state_based_on_template (sec_asn1d_state *state) expect_tag_number = 0; } else { check_tag_mask = SEC_ASN1_TAG_MASK; - expect_tag_modifiers = encode_kind & SEC_ASN1_TAG_MASK + expect_tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK; /* * XXX This assumes only single-octet identifiers. To handle @@ -600,6 +715,32 @@ sec_asn1d_init_state_based_on_template (sec_asn1d_state *state) return state; } +static PRBool +sec_asn1d_parent_is_indefinite(sec_asn1d_state *state) +{ + for (state = state->parent; state; state = state->parent) { + sec_asn1d_parse_place place = state->place; + if (place != afterImplicit && + place != afterPointer && + place != afterInline && + place != afterSaveEncoding && + place != duringSaveEncoding && + place != duringChoice) { + + /* we've walked up the stack to a state that represents + ** the enclosing construct. Is it one of the types that + ** permits an unexpected EOC? + */ + int eoc_permitted = + (place == duringGroup || + place == duringConstructedString || + state->child->optional); + return (state->indefinite && eoc_permitted) ? PR_TRUE : PR_FALSE; + + } + } + return PR_FALSE; +} static unsigned long sec_asn1d_parse_identifier (sec_asn1d_state *state, @@ -616,6 +757,13 @@ sec_asn1d_parse_identifier (sec_asn1d_state *state, } byte = (unsigned char) *buf; +#ifdef DEBUG_ASN1D_STATES + { + char kindBuf[256]; + formatKind(byte, kindBuf); + printf("Found tag %02x %s\n", byte, kindBuf); + } +#endif tag_number = byte & SEC_ASN1_TAGNUM_MASK; if (IS_HIGH_TAG_NUMBER (tag_number)) { @@ -628,15 +776,7 @@ sec_asn1d_parse_identifier (sec_asn1d_state *state, */ state->pending = 1; } else { - if (byte == 0 && state->parent != NULL && - (state->parent->indefinite || - ( - (state->parent->place == afterImplicit || - state->parent->place == afterPointer) - && state->parent->parent != NULL && state->parent->parent->indefinite - ) - ) - ) { + if (byte == 0 && sec_asn1d_parent_is_indefinite(state)) { /* * Our parent has indefinite-length encoding, and the * entire tag found is 0, so it seems that we have hit the @@ -821,6 +961,12 @@ sec_asn1d_prepare_for_contents (sec_asn1d_state *state) PRArenaPool *poolp; unsigned long alloc_len; +#ifdef DEBUG_ASN1D_STATES + { + printf("Found Length %d %s\n", state->contents_length, + state->indefinite ? "indefinite" : ""); + } +#endif /* * XXX I cannot decide if this allocation should exclude the case @@ -1215,8 +1361,12 @@ sec_asn1d_free_child (sec_asn1d_state *state, PRBool error) if (error && state->top->their_pool == NULL) { /* * XXX We need to free anything allocated. + * At this point, we failed in the middle of decoding. But we + * can't free the data we previously allocated with PR_Malloc + * unless we keep track of every pointer. So instead we have a + * memory leak when decoding fails half-way, unless an arena is + * used. See bug 95311 . */ - PORT_Assert (0); } state->child = NULL; state->our_mark = NULL; @@ -1512,7 +1662,7 @@ sec_asn1d_next_substring (sec_asn1d_state *state) if (state->pending) { PORT_Assert (!state->indefinite); - if( child_consumed > state->pending ) { + if (child_consumed > state->pending) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; return; @@ -1631,7 +1781,7 @@ sec_asn1d_next_in_group (sec_asn1d_state *state) */ if (state->pending) { PORT_Assert (!state->indefinite); - if( child_consumed > state->pending ) { + if (child_consumed > state->pending) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; return; @@ -1701,7 +1851,7 @@ sec_asn1d_next_in_sequence (sec_asn1d_state *state) sec_asn1d_free_child (child, PR_FALSE); if (state->pending) { PORT_Assert (!state->indefinite); - if( child_consumed > state->pending ) { + if (child_consumed > state->pending) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; return; @@ -1750,7 +1900,7 @@ sec_asn1d_next_in_sequence (sec_asn1d_state *state) */ if (state->indefinite && child->endofcontents) { PORT_Assert (child_consumed == 2); - if( child_consumed != 2 ) { + if (child_consumed != 2) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { @@ -1813,7 +1963,7 @@ sec_asn1d_next_in_sequence (sec_asn1d_state *state) * sake it should probably be made to work at some point. */ PORT_Assert (child_found_tag_number < SEC_ASN1_HIGH_TAG_NUMBER); - identifier = child_found_tag_modifiers | child_found_tag_number; + identifier = (unsigned char)(child_found_tag_modifiers | child_found_tag_number); sec_asn1d_record_any_header (child, (char *) &identifier, 1); } } @@ -2058,7 +2208,7 @@ static unsigned long sec_asn1d_parse_end_of_contents (sec_asn1d_state *state, const char *buf, unsigned long len) { - int i; + unsigned int i; PORT_Assert (state->pending <= 2); PORT_Assert (state->place == duringEndOfContents); @@ -2104,7 +2254,7 @@ sec_asn1d_pop_state (sec_asn1d_state *state) state->consumed += state->child->consumed; if (state->pending) { PORT_Assert (!state->indefinite); - if( state->child->consumed > state->pending ) { + if (state->child->consumed > state->pending) { PORT_SetError (SEC_ERROR_BAD_DER); state->top->status = decodeError; } else { @@ -2128,134 +2278,146 @@ sec_asn1d_pop_state (sec_asn1d_state *state) } static sec_asn1d_state * -sec_asn1d_before_choice -( - sec_asn1d_state *state -) +sec_asn1d_before_choice (sec_asn1d_state *state) { - sec_asn1d_state *child; + sec_asn1d_state *child; - if( state->allocate ) { - void *dest; + if (state->allocate) { + void *dest; - dest = sec_asn1d_zalloc(state->top->their_pool, state->theTemplate->size); - if( (void *)NULL == dest ) { - state->top->status = decodeError; - return (sec_asn1d_state *)NULL; - } + dest = sec_asn1d_zalloc(state->top->their_pool, state->theTemplate->size); + if ((void *)NULL == dest) { + state->top->status = decodeError; + return (sec_asn1d_state *)NULL; + } - state->dest = (char *)dest + state->theTemplate->offset; - } + state->dest = (char *)dest + state->theTemplate->offset; + } - child = sec_asn1d_push_state(state->top, state->theTemplate + 1, - state->dest, PR_FALSE); - if( (sec_asn1d_state *)NULL == child ) { - return (sec_asn1d_state *)NULL; - } + child = sec_asn1d_push_state(state->top, state->theTemplate + 1, + (char *)state->dest - state->theTemplate->offset, + PR_FALSE); + if ((sec_asn1d_state *)NULL == child) { + return (sec_asn1d_state *)NULL; + } - sec_asn1d_scrub_state(child); - child = sec_asn1d_init_state_based_on_template(child); - if( (sec_asn1d_state *)NULL == child ) { - return (sec_asn1d_state *)NULL; - } + sec_asn1d_scrub_state(child); + child = sec_asn1d_init_state_based_on_template(child); + if ((sec_asn1d_state *)NULL == child) { + return (sec_asn1d_state *)NULL; + } - child->optional = PR_TRUE; + child->optional = PR_TRUE; - state->place = duringChoice; + state->place = duringChoice; - return child; + return child; } static sec_asn1d_state * -sec_asn1d_during_choice -( - sec_asn1d_state *state -) +sec_asn1d_during_choice (sec_asn1d_state *state) { - sec_asn1d_state *child = state->child; - - PORT_Assert((sec_asn1d_state *)NULL != child); - - if( child->missing ) { - unsigned char child_found_tag_modifiers = 0; - unsigned long child_found_tag_number = 0; + sec_asn1d_state *child = state->child; + + PORT_Assert((sec_asn1d_state *)NULL != child); - child->theTemplate++; + if (child->missing) { + unsigned char child_found_tag_modifiers = 0; + unsigned long child_found_tag_number = 0; + void * dest; + + state->consumed += child->consumed; + + if (child->endofcontents) { + /* This choice is probably the first item in a GROUP + ** (e.g. SET_OF) that was indefinite-length encoded. + ** We're actually at the end of that GROUP. + ** We should look up the stack to be sure that we find + ** a state with indefinite length encoding before we + ** find a state (like a SEQUENCE) that is definite. + */ + child->place = notInUse; + state->place = afterChoice; + state->endofcontents = PR_TRUE; /* propagate this up */ + if (sec_asn1d_parent_is_indefinite(state)) + return state; + PORT_SetError(SEC_ERROR_BAD_DER); + state->top->status = decodeError; + return NULL; + } - if( 0 == child->theTemplate->kind ) { - /* Ran out of choices */ - PORT_SetError(SEC_ERROR_BAD_DER); - state->top->status = decodeError; - return (sec_asn1d_state *)NULL; - } + dest = (char *)child->dest - child->theTemplate->offset; + child->theTemplate++; - state->consumed += child->consumed; + if (0 == child->theTemplate->kind) { + /* Ran out of choices */ + PORT_SetError(SEC_ERROR_BAD_DER); + state->top->status = decodeError; + return (sec_asn1d_state *)NULL; + } + child->dest = (char *)dest + child->theTemplate->offset; - /* cargo'd from next_in_sequence innards */ - if( state->pending ) { - PORT_Assert(!state->indefinite); - if( child->consumed > state->pending ) { - PORT_SetError (SEC_ERROR_BAD_DER); - state->top->status = decodeError; - return NULL; - } - state->pending -= child->consumed; - if( 0 == state->pending ) { - /* XXX uh.. not sure if I should have stopped this - * from happening before. */ - PORT_Assert(0); - PORT_SetError(SEC_ERROR_BAD_DER); - state->top->status = decodeError; - return (sec_asn1d_state *)NULL; - } - } + /* cargo'd from next_in_sequence innards */ + if (state->pending) { + PORT_Assert(!state->indefinite); + if (child->consumed > state->pending) { + PORT_SetError (SEC_ERROR_BAD_DER); + state->top->status = decodeError; + return NULL; + } + state->pending -= child->consumed; + if (0 == state->pending) { + /* XXX uh.. not sure if I should have stopped this + * from happening before. */ + PORT_Assert(0); + PORT_SetError(SEC_ERROR_BAD_DER); + state->top->status = decodeError; + return (sec_asn1d_state *)NULL; + } + } - child->consumed = 0; - sec_asn1d_scrub_state(child); + child->consumed = 0; + sec_asn1d_scrub_state(child); - /* move it on top again */ - state->top->current = child; + /* move it on top again */ + state->top->current = child; - child_found_tag_modifiers = child->found_tag_modifiers; - child_found_tag_number = child->found_tag_number; + child_found_tag_modifiers = child->found_tag_modifiers; + child_found_tag_number = child->found_tag_number; - child = sec_asn1d_init_state_based_on_template(child); - if( (sec_asn1d_state *)NULL == child ) { - return (sec_asn1d_state *)NULL; - } + child = sec_asn1d_init_state_based_on_template(child); + if ((sec_asn1d_state *)NULL == child) { + return (sec_asn1d_state *)NULL; + } - /* copy our findings to the new top */ - child->found_tag_modifiers = child_found_tag_modifiers; - child->found_tag_number = child_found_tag_number; + /* copy our findings to the new top */ + child->found_tag_modifiers = child_found_tag_modifiers; + child->found_tag_number = child_found_tag_number; - child->optional = PR_TRUE; - child->place = afterIdentifier; + child->optional = PR_TRUE; + child->place = afterIdentifier; - return child; - } else { - if( (void *)NULL != state->dest ) { - /* Store the enum */ - int *which = (int *)((char *)state->dest + state->theTemplate->offset); - *which = (int)child->theTemplate->size; + return child; + } + if ((void *)NULL != state->dest) { + /* Store the enum */ + int *which = (int *)state->dest; + *which = (int)child->theTemplate->size; } child->place = notInUse; state->place = afterChoice; return state; - } } static void -sec_asn1d_after_choice -( - sec_asn1d_state *state -) +sec_asn1d_after_choice (sec_asn1d_state *state) { - state->consumed += state->child->consumed; - state->child->consumed = 0; - state->place = afterEndOfContents; - sec_asn1d_pop_state(state); + state->consumed += state->child->consumed; + state->child->consumed = 0; + state->place = afterEndOfContents; + sec_asn1d_pop_state(state); } unsigned long @@ -2280,7 +2442,7 @@ SECStatus SEC_ASN1DecodeInteger(SECItem *src, unsigned long *value) { unsigned long v; - int i; + unsigned int i; if (src == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -2313,29 +2475,42 @@ SEC_ASN1DecodeInteger(SECItem *src, unsigned long *value) #ifdef DEBUG_ASN1D_STATES static void -dump_states -( - SEC_ASN1DecoderContext *cx -) +dump_states(SEC_ASN1DecoderContext *cx) { - sec_asn1d_state *state; - - for( state = cx->current; state->parent; state = state->parent ) { - ; - } - - for( ; state; state = state->child ) { - int i; - for( i = 0; i < state->depth; i++ ) { - printf(" "); - } - - printf("%s: template[0]->kind = 0x%02x, expect tag number = 0x%02x\n", - (state == cx->current) ? "STATE" : "State", - state->theTemplate->kind, state->expect_tag_number); - } - - return; + sec_asn1d_state *state; + char kindBuf[256]; + + for (state = cx->current; state->parent; state = state->parent) { + ; + } + + for (; state; state = state->child) { + int i; + for (i = 0; i < state->depth; i++) { + printf(" "); + } + + i = formatKind(state->theTemplate->kind, kindBuf); + printf("%s: tmpl %08x, kind%s", + (state == cx->current) ? "STATE" : "State", + state->theTemplate, + kindBuf); + printf(" %s", (state->place >= 0 && state->place <= notInUse) + ? place_names[ state->place ] + : "(undefined)"); + if (!i) + printf(", expect 0x%02x", + state->expect_tag_number | state->expect_tag_modifiers); + + printf("%s%s%s %d\n", + state->indefinite ? ", indef" : "", + state->missing ? ", miss" : "", + state->endofcontents ? ", EOC" : "", + state->pending + ); + } + + return; } #endif /* DEBUG_ASN1D_STATES */ @@ -2356,10 +2531,11 @@ SEC_ASN1DecoderUpdate (SEC_ASN1DecoderContext *cx, what = SEC_ASN1_Contents; consumed = 0; #ifdef DEBUG_ASN1D_STATES - printf("\nPLACE = %s, next byte = 0x%02x\n", + printf("\nPLACE = %s, next byte = 0x%02x, %08x[%d]\n", (state->place >= 0 && state->place <= notInUse) ? place_names[ state->place ] : "(undefined)", - (unsigned int)((unsigned char *)buf)[ consumed ]); + (unsigned int)((unsigned char *)buf)[ consumed ], + buf, consumed); dump_states(cx); #endif /* DEBUG_ASN1D_STATES */ switch (state->place) { @@ -2454,7 +2630,7 @@ SEC_ASN1DecoderUpdate (SEC_ASN1DecoderContext *cx, /* We should not consume more than we have. */ PORT_Assert (consumed <= len); - if( consumed > len ) { + if (consumed > len) { PORT_SetError (SEC_ERROR_BAD_DER); cx->status = decodeError; break; @@ -2701,6 +2877,13 @@ SEC_ASN1DecodeItem (PRArenaPool *poolp, void *dest, (char *) item->data, item->len); } +#ifdef DEBUG_ASN1D_STATES +void sec_asn1d_Assert(const char *s, const char *file, PRIntn ln) +{ + printf("Assertion failed, \"%s\", file %s, line %d\n", s, file, ln); + fflush(stdout); +} +#endif /* * Generic templates for individual/simple items and pointers to |