summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornelsonb%netscape.com <devnull@localhost>2003-11-07 08:15:01 +0000
committernelsonb%netscape.com <devnull@localhost>2003-11-07 08:15:01 +0000
commit115af0351bea83b4d826d1ffb293dda9186fc841 (patch)
tree6a559787c3bfe3ad9732175fed7a8b65e1a82935
parent54466d4bf645d7ec4ca67708db5598426b5b603d (diff)
downloadnss-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.c465
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