Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Support for DEcoding ASN.1 data based on BER/DER (Basic/Distinguished
* Encoding Rules).
*/
/* #define DEBUG_ASN1D_STATES 1 */
#ifdef DEBUG_ASN1D_STATES
#include <stdio.h>
#define PR_Assert sec_asn1d_Assert
#endif
#include <limits.h>
#include "secasn1.h"
#include "secerr.h"
typedef enum {
beforeIdentifier,
duringIdentifier,
afterIdentifier,
beforeLength,
duringLength,
afterLength,
beforeBitString,
duringBitString,
duringConstructedString,
duringGroup,
duringLeaf,
duringSaveEncoding,
duringSequence,
afterConstructedString,
afterGroup,
afterExplicit,
afterImplicit,
afterInline,
afterPointer,
afterSaveEncoding,
beforeEndOfContents,
duringEndOfContents,
afterEndOfContents,
beforeChoice,
duringChoice,
afterChoice,
notInUse
} sec_asn1d_parse_place;
#ifdef DEBUG_ASN1D_STATES
static const char *const place_names[] = {
"beforeIdentifier",
"duringIdentifier",
"afterIdentifier",
"beforeLength",
"duringLength",
"afterLength",
"beforeBitString",
"duringBitString",
"duringConstructedString",
"duringGroup",
"duringLeaf",
"duringSaveEncoding",
"duringSequence",
"afterConstructedString",
"afterGroup",
"afterExplicit",
"afterImplicit",
"afterInline",
"afterPointer",
"afterSaveEncoding",
"beforeEndOfContents",
"duringEndOfContents",
"afterEndOfContents",
"beforeChoice",
"duringChoice",
"afterChoice",
"notInUse"
};
static const char *const class_names[] = {
"UNIVERSAL",
"APPLICATION",
"CONTEXT_SPECIFIC",
"PRIVATE"
};
static const char *const method_names[] = { "PRIMITIVE", "CONSTRUCTED" };
static const char *const type_names[] = {
"END_OF_CONTENTS",
"BOOLEAN",
"INTEGER",
"BIT_STRING",
"OCTET_STRING",
"NULL",
"OBJECT_ID",
"OBJECT_DESCRIPTOR",
"(type 08)",
"REAL",
"ENUMERATED",
"EMBEDDED",
"UTF8_STRING",
"(type 0d)",
"(type 0e)",
"(type 0f)",
"SEQUENCE",
"SET",
"NUMERIC_STRING",
"PRINTABLE_STRING",
"T61_STRING",
"VIDEOTEXT_STRING",
"IA5_STRING",
"UTC_TIME",
"GENERALIZED_TIME",
"GRAPHIC_STRING",
"VISIBLE_STRING",
"GENERAL_STRING",
"UNIVERSAL_STRING",
"(type 1d)",
"BMP_STRING",
"HIGH_TAG_VALUE"
};
static const char *const flag_names[] = {
/* flags, right to left */
"OPTIONAL",
"EXPLICIT",
"ANY",
"INLINE",
"POINTER",
"GROUP",
"DYNAMIC",
"SKIP",
"INNER",
"SAVE",
"", /* decoder ignores "MAY_STREAM", */
"SKIP_REST",
"CHOICE",
"NO_STREAM",
"DEBUG_BREAK",
"unknown 08",
"unknown 10",
"unknown 20",
"unknown 40",
"unknown 80"
};
static int /* bool */
formatKind(unsigned long kind, char *buf)
{
int i;
unsigned long k = kind & SEC_ASN1_TAGNUM_MASK;
unsigned long notag = kind & (SEC_ASN1_CHOICE | SEC_ASN1_POINTER |
SEC_ASN1_INLINE | SEC_ASN1_ANY | SEC_ASN1_SAVE);
buf[0] = 0;
if ((kind & SEC_ASN1_CLASS_MASK) != SEC_ASN1_UNIVERSAL) {
sprintf(buf, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6]);
buf += strlen(buf);
}
if (kind & SEC_ASN1_METHOD_MASK) {
sprintf(buf, " %s", method_names[1]);
buf += strlen(buf);
}
if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) {
if (k || !notag) {
sprintf(buf, " %s", type_names[k]);
if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) &&
(kind & SEC_ASN1_GROUP)) {
buf += strlen(buf);
sprintf(buf, "_OF");
}
}
} else {
sprintf(buf, " [%lu]", k);
}
buf += strlen(buf);
for (k = kind >> 8, i = 0; k; k >>= 1, ++i) {
if (k & 1) {
sprintf(buf, " %s", flag_names[i]);
buf += strlen(buf);
}
}
return notag != 0;
}
#endif /* DEBUG_ASN1D_STATES */
typedef enum {
allDone,
decodeError,
keepGoing,
needBytes
} sec_asn1d_parse_status;
struct subitem {
const void *data;
unsigned long len; /* only used for substrings */
struct subitem *next;
};
typedef struct sec_asn1d_state_struct {
SEC_ASN1DecoderContext *top;
const SEC_ASN1Template *theTemplate;
void *dest;
void *our_mark; /* free on completion */
struct sec_asn1d_state_struct *parent; /* aka prev */
struct sec_asn1d_state_struct *child; /* aka next */
sec_asn1d_parse_place place;
/*
* XXX explain the next fields as clearly as possible...
*/
unsigned char found_tag_modifiers;
unsigned char expect_tag_modifiers;
unsigned long check_tag_mask;
unsigned long found_tag_number;
unsigned long expect_tag_number;
unsigned long underlying_kind;
unsigned long contents_length;
unsigned long pending;
unsigned long consumed;
int depth;
/*
* Bit strings have their length adjusted -- the first octet of the
* contents contains a value between 0 and 7 which says how many bits
* at the end of the octets are not actually part of the bit string;
* when parsing bit strings we put that value here because we need it
* later, for adjustment of the length (when the whole string is done).
*/
unsigned int bit_string_unused_bits;
/*
* The following are used for indefinite-length constructed strings.
*/
struct subitem *subitems_head;
struct subitem *subitems_tail;
PRPackedBool
allocate, /* when true, need to allocate the destination */
endofcontents, /* this state ended up parsing its parent's end-of-contents octets */
explicit, /* we are handling an explicit header */
indefinite, /* the current item has indefinite-length encoding */
missing, /* an optional field that was not present */
optional, /* the template says this field may be omitted */
substring; /* this is a substring of a constructed string */
} sec_asn1d_state;
#define IS_HIGH_TAG_NUMBER(n) ((n) == SEC_ASN1_HIGH_TAG_NUMBER)
#define LAST_TAG_NUMBER_BYTE(b) (((b)&0x80) == 0)
#define TAG_NUMBER_BITS 7
#define TAG_NUMBER_MASK 0x7f
#define LENGTH_IS_SHORT_FORM(b) (((b)&0x80) == 0)
#define LONG_FORM_LENGTH(b) ((b)&0x7f)
#define HIGH_BITS(field, cnt) ((field) >> ((sizeof(field) * 8) - (cnt)))
/*
* An "outsider" will have an opaque pointer to this, created by calling
* SEC_ASN1DecoderStart(). It will be passed back in to all subsequent
* calls to SEC_ASN1DecoderUpdate(), and when done it is passed to
* SEC_ASN1DecoderFinish().
*/
struct sec_DecoderContext_struct {
PLArenaPool *our_pool; /* for our internal allocs */
PLArenaPool *their_pool; /* for destination structure allocs */
#ifdef SEC_ASN1D_FREE_ON_ERROR /* \
* XXX see comment below (by same \
* ifdef) that explains why this \
* does not work (need more smarts \
* in order to free back to mark) \
*/
/*
* XXX how to make their_mark work in the case where they do NOT
* give us a pool pointer?
*/
void *their_mark; /* free on error */
#endif
sec_asn1d_state *current;
sec_asn1d_parse_status status;
/* The maximum size the caller is willing to allow a single element
* to be before returning an error.
*
* In the case of an indefinite length element, this is the sum total
* of all child elements.
*
* In the case of a definite length element, this represents the maximum
* size of the top-level element.
*/
unsigned long max_element_size;
SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */
void *notify_arg; /* argument to notify_proc */
PRBool during_notify; /* true during call to notify_proc */
SEC_ASN1WriteProc filter_proc; /* pass field bytes to this */
void *filter_arg; /* argument to that function */
PRBool filter_only; /* do not allocate/store fields */
};
/*
* XXX this is a fairly generic function that may belong elsewhere
*/
static void *
sec_asn1d_alloc(PLArenaPool *poolp, unsigned long len)
{
void *thing;
if (poolp != NULL) {
/*
* Allocate from the pool.
*/
thing = PORT_ArenaAlloc(poolp, len);
} else {
/*
* Allocate generically.
*/
thing = PORT_Alloc(len);
}
return thing;
}
/*
* XXX this is a fairly generic function that may belong elsewhere
*/
static void *
sec_asn1d_zalloc(PLArenaPool *poolp, unsigned long len)
{
void *thing;
thing = sec_asn1d_alloc(poolp, len);
if (thing != NULL)
PORT_Memset(thing, 0, len);
return thing;
}
static sec_asn1d_state *
sec_asn1d_push_state(SEC_ASN1DecoderContext *cx,
const SEC_ASN1Template *theTemplate,
void *dest, PRBool new_depth)
{
sec_asn1d_state *state, *new_state;
state = cx->current;
PORT_Assert(state == NULL || state->child == NULL);
if (state != NULL) {
PORT_Assert(state->our_mark == NULL);
state->our_mark = PORT_ArenaMark(cx->our_pool);
}
if (theTemplate == NULL) {
PORT_SetError(SEC_ERROR_BAD_TEMPLATE);
goto loser;
}
new_state = (sec_asn1d_state *)sec_asn1d_zalloc(cx->our_pool,
sizeof(*new_state));
if (new_state == NULL) {
goto loser;
}
new_state->top = cx;
new_state->parent = state;
new_state->theTemplate = theTemplate;
new_state->place = notInUse;
if (dest != NULL)
new_state->dest = (char *)dest + theTemplate->offset;
if (state != NULL) {
new_state->depth = state->depth;
if (new_depth) {
if (++new_state->depth > SEC_ASN1D_MAX_DEPTH) {
PORT_SetError(SEC_ERROR_BAD_DER);
goto loser;
}
}
state->child = new_state;
}
cx->current = new_state;
return new_state;
loser:
cx->status = decodeError;
if (state != NULL) {
PORT_ArenaRelease(cx->our_pool, state->our_mark);
state->our_mark = NULL;
}
return NULL;
}
static void
sec_asn1d_scrub_state(sec_asn1d_state *state)
{
/*
* Some default "scrubbing".
* XXX right set of initializations?
*/
state->place = beforeIdentifier;
state->endofcontents = PR_FALSE;
state->indefinite = PR_FALSE;
state->missing = PR_FALSE;
PORT_Assert(state->consumed == 0);
}
static void
sec_asn1d_notify_before(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
if (cx->notify_proc == NULL)
return;
cx->during_notify = PR_TRUE;
(*cx->notify_proc)(cx->notify_arg, PR_TRUE, dest, depth);
cx->during_notify = PR_FALSE;
}
static void
sec_asn1d_notify_after(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
if (cx->notify_proc == NULL)
return;
cx->during_notify = PR_TRUE;
(*cx->notify_proc)(cx->notify_arg, PR_FALSE, dest, depth);
cx->during_notify = PR_FALSE;
}
static sec_asn1d_state *
sec_asn1d_init_state_based_on_template(sec_asn1d_state *state)
{
PRBool explicit, optional, universal;
unsigned char expect_tag_modifiers;
unsigned long encode_kind, under_kind;
unsigned long check_tag_mask, expect_tag_number;
/* XXX Check that both of these tests are really needed/appropriate. */
if (state == NULL || state->top->status == decodeError)
return state;
encode_kind = state->theTemplate->kind;
if (encode_kind & SEC_ASN1_SAVE) {
/*
* This is a "magic" field that saves away all bytes, allowing
* the immediately following field to still be decoded from this
* same spot -- sort of a fork.
*/
/* check that there are no extraneous bits */
PORT_Assert(encode_kind == SEC_ASN1_SAVE);
if (state->top->filter_only) {
/*
* If we are not storing, then we do not do the SAVE field
* at all. Just move ahead to the "real" field instead,
* doing the appropriate notify calls before and after.
*/
sec_asn1d_notify_after(state->top, state->dest, state->depth);
/*
* Since we are not storing, allow for our current dest value
* to be NULL. (This might not actually occur, but right now I
* cannot convince myself one way or the other.) If it is NULL,
* assume that our parent dest can help us out.
*/
if (state->dest == NULL)
state->dest = state->parent->dest;
else
state->dest = (char *)state->dest - state->theTemplate->offset;
state->theTemplate++;
if (state->dest != NULL)
state->dest = (char *)state->dest + state->theTemplate->offset;
sec_asn1d_notify_before(state->top, state->dest, state->depth);
encode_kind = state->theTemplate->kind;
PORT_Assert((encode_kind & SEC_ASN1_SAVE) == 0);
} else {
sec_asn1d_scrub_state(state);
state->place = duringSaveEncoding;
state = sec_asn1d_push_state(state->top, SEC_AnyTemplate,
state->dest, PR_FALSE);
if (state != NULL)
state = sec_asn1d_init_state_based_on_template(state);
return state;
}
}
universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
? PR_TRUE
: PR_FALSE;
explicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_EXPLICIT;
optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_OPTIONAL;
PORT_Assert(!(explicit && universal)); /* bad templates */
encode_kind &= ~SEC_ASN1_DYNAMIC;
encode_kind &= ~SEC_ASN1_MAY_STREAM;
if (encode_kind & SEC_ASN1_CHOICE) {
#if 0 /* XXX remove? */
sec_asn1d_state *child = sec_asn1d_push_state(state->top, state->theTemplate, state->dest, PR_FALSE);
if ((sec_asn1d_state *)NULL == child) {
return (sec_asn1d_state *)NULL;
}
child->allocate = state->allocate;
child->place = beforeChoice;
return child;
#else
state->place = beforeChoice;
return state;
#endif
}
if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || (!universal && !explicit)) {
const SEC_ASN1Template *subt;
void *dest;
PRBool child_allocate;
PORT_Assert((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);
sec_asn1d_scrub_state(state);
child_allocate = PR_FALSE;
if (encode_kind & SEC_ASN1_POINTER) {
/*
* A POINTER means we need to allocate the destination for
* this field. But, since it may also be an optional field,
* we defer the allocation until later; we just record that
* it needs to be done.
*
* There are two possible scenarios here -- one is just a
* plain POINTER (kind of like INLINE, except with allocation)
* and the other is an implicitly-tagged POINTER. We don't
* need to do anything special here for the two cases, but
* since the template definition can be tricky, we do check
* that there are no extraneous bits set in encode_kind.
*
* XXX The same conditions which assert should set an error.
*/
if (universal) {
/*
* "universal" means this entry is a standalone POINTER;
* there should be no other bits set in encode_kind.
*/
PORT_Assert(encode_kind == SEC_ASN1_POINTER);
} else {
/*
* If we get here we have an implicitly-tagged field
* that needs to be put into a POINTER. The subtemplate
* will determine how to decode the field, but encode_kind
* describes the (implicit) tag we are looking for.
* The non-tag bits of encode_kind will be ignored by
* the code below; none of them should be set, however,
* except for the POINTER bit itself -- so check that.
*/
PORT_Assert((encode_kind & ~SEC_ASN1_TAG_MASK) == SEC_ASN1_POINTER);
}
if (!state->top->filter_only)
child_allocate = PR_TRUE;
dest = NULL;
state->place = afterPointer;
} else {
dest = state->dest;
if (encode_kind & SEC_ASN1_INLINE) {
/* check that there are no extraneous bits */
PORT_Assert(encode_kind == SEC_ASN1_INLINE && !optional);
state->place = afterInline;
} else {
state->place = afterImplicit;
}
}
state->optional = optional;
subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE);
state = sec_asn1d_push_state(state->top, subt, dest, PR_FALSE);
if (state == NULL)
return NULL;
state->allocate = child_allocate;
if (universal) {
state = sec_asn1d_init_state_based_on_template(state);
if (state != NULL) {
/*
* If this field is optional, we need to record that on
* the pushed child so it won't fail if the field isn't
* found. I can't think of a way that this new state
* could already have optional set (which we would wipe
* out below if our local optional is not set) -- but
* just to be sure, assert that it isn't set.
*/
PORT_Assert(!state->optional);
state->optional = optional;
}
return state;
}
under_kind = state->theTemplate->kind;
under_kind &= ~SEC_ASN1_MAY_STREAM;
} else if (explicit) {
/*
* For explicit, we only need to match the encoding tag next,
* then we will push another state to handle the entire inner
* part. In this case, there is no underlying kind which plays
* any part in the determination of the outer, explicit tag.
* So we just set under_kind to 0, which is not a valid tag,
* and the rest of the tag matching stuff should be okay.
*/
under_kind = 0;
} else {
/*
* Nothing special; the underlying kind and the given encoding
* information are the same.
*/
under_kind = encode_kind;
}
/* XXX is this the right set of bits to test here? */
PORT_Assert((under_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)) == 0);
if (encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) {
PORT_Assert(encode_kind == under_kind);
if (encode_kind & SEC_ASN1_SKIP) {
PORT_Assert(!optional);
PORT_Assert(encode_kind == SEC_ASN1_SKIP);
state->dest = NULL;
}
check_tag_mask = 0;
expect_tag_modifiers = 0;
expect_tag_number = 0;
} else {
check_tag_mask = SEC_ASN1_TAG_MASK;
expect_tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK;
/*
* XXX This assumes only single-octet identifiers. To handle
* the HIGH TAG form we would need to do some more work, especially
* in how to specify them in the template, because right now we
* do not provide a way to specify more *tag* bits in encode_kind.
*/
expect_tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;
switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
case SEC_ASN1_SET:
/*
* XXX A plain old SET (as opposed to a SET OF) is not implemented.
* If it ever is, remove this assert...
*/
PORT_Assert((under_kind & SEC_ASN1_GROUP) != 0);
/* fallthru */
case SEC_ASN1_SEQUENCE:
expect_tag_modifiers |= SEC_ASN1_CONSTRUCTED;
break;
case SEC_ASN1_BIT_STRING:
case SEC_ASN1_BMP_STRING:
case SEC_ASN1_GENERALIZED_TIME:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_OCTET_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_T61_STRING:
case SEC_ASN1_UNIVERSAL_STRING:
case SEC_ASN1_UTC_TIME:
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_VISIBLE_STRING:
check_tag_mask &= ~SEC_ASN1_CONSTRUCTED;
break;
}
}
state->check_tag_mask = check_tag_mask;
state->expect_tag_modifiers = expect_tag_modifiers;
state->expect_tag_number = expect_tag_number;
state->underlying_kind = under_kind;
state->explicit = explicit;
state->optional = optional;
sec_asn1d_scrub_state(state);
return state;
}
static sec_asn1d_state *
sec_asn1d_get_enclosing_construct(sec_asn1d_state *state)
{
for (state = state->parent; state; state = state->parent) {
sec_asn1d_parse_place place = state->place;
if (place != afterImplicit &&
place != afterPointer &&
place != afterInline &&
place != afterSaveEncoding &&
place != duringSaveEncoding &&
place != duringChoice) {
/* we've walked up the stack to a state that represents
** the enclosing construct.
*/
break;
}
}
return state;
}
static PRBool
sec_asn1d_parent_allows_EOC(sec_asn1d_state *state)
{
/* get state of enclosing construct. */
state = sec_asn1d_get_enclosing_construct(state);
if (state) {
sec_asn1d_parse_place place = state->place;
/* Is it one of the types that permits an unexpected EOC? */
int eoc_permitted =
(place == duringGroup ||
place == duringConstructedString ||
state->child->optional);
return (state->indefinite && eoc_permitted) ? PR_TRUE : PR_FALSE;
}
return PR_FALSE;
}
static unsigned long
sec_asn1d_parse_identifier(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
unsigned char tag_number;
PORT_Assert(state->place == beforeIdentifier);
if (len == 0) {
state->top->status = needBytes;
return 0;
}
byte = (unsigned char)*buf;
#ifdef DEBUG_ASN1D_STATES
{
char kindBuf[256];
formatKind(byte, kindBuf);
printf("Found tag %02x %s\n", byte, kindBuf);
}
#endif
tag_number = byte & SEC_ASN1_TAGNUM_MASK;
if (IS_HIGH_TAG_NUMBER(tag_number)) {
state->place = duringIdentifier;
state->found_tag_number = 0;
/*
* Actually, we have no idea how many bytes are pending, but we
* do know that it is at least 1. That is all we know; we have
* to look at each byte to know if there is another, etc.
*/
state->pending = 1;
} else {
if (byte == 0 && sec_asn1d_parent_allows_EOC(state)) {
/*
* Our parent has indefinite-length encoding, and the
* entire tag found is 0, so it seems that we have hit the
* end-of-contents octets. To handle this, we just change
* our state to that which expects to get the bytes of the
* end-of-contents octets and let that code re-read this byte
* so that our categorization of field types is correct.
* After that, our parent will then deal with everything else.
*/
state->place = duringEndOfContents;
state->pending = 2;
state->found_tag_number = 0;
state->found_tag_modifiers = 0;
/*
* We might be an optional field that is, as we now find out,
* missing. Give our parent a clue that this happened.
*/
if (state->optional)
state->missing = PR_TRUE;
return 0;
}
state->place = afterIdentifier;
state->found_tag_number = tag_number;
}
state->found_tag_modifiers = byte & ~SEC_ASN1_TAGNUM_MASK;
return 1;
}
static unsigned long
sec_asn1d_parse_more_identifier(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
int count;
PORT_Assert(state->pending == 1);
PORT_Assert(state->place == duringIdentifier);
if (len == 0) {
state->top->status = needBytes;
return 0;
}
count = 0;
while (len && state->pending) {
if (HIGH_BITS(state->found_tag_number, TAG_NUMBER_BITS) != 0) {
/*
* The given high tag number overflows our container;
* just give up. This is not likely to *ever* happen.
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
state->found_tag_number <<= TAG_NUMBER_BITS;
byte = (unsigned char)buf[count++];
state->found_tag_number |= (byte & TAG_NUMBER_MASK);
len--;
if (LAST_TAG_NUMBER_BYTE(byte))
state->pending = 0;
}
if (state->pending == 0)
state->place = afterIdentifier;
return count;
}
static void
sec_asn1d_confirm_identifier(sec_asn1d_state *state)
{
PRBool match;
PORT_Assert(state->place == afterIdentifier);
match = (PRBool)(((state->found_tag_modifiers & state->check_tag_mask) == state->expect_tag_modifiers) && ((state->found_tag_number & state->check_tag_mask) == state->expect_tag_number));
if (match) {
state->place = beforeLength;
} else {
if (state->optional) {
state->missing = PR_TRUE;
state->place = afterEndOfContents;
} else {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
}
}
}
static unsigned long
sec_asn1d_parse_length(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
PORT_Assert(state->place == beforeLength);
if (len == 0) {
state->top->status = needBytes;
return 0;
}
/*
* The default/likely outcome. It may get adjusted below.
*/
state->place = afterLength;
byte = (unsigned char)*buf;
if (LENGTH_IS_SHORT_FORM(byte)) {
state->contents_length = byte;
} else {
state->contents_length = 0;
state->pending = LONG_FORM_LENGTH(byte);
if (state->pending == 0) {
state->indefinite = PR_TRUE;
} else {
state->place = duringLength;
}
}
/* If we're parsing an ANY, SKIP, or SAVE template, and
** the object being saved is definite length encoded and constructed,
** there's no point in decoding that construct's members.
** So, just forget it's constructed and treat it as primitive.
** (SAVE appears as an ANY at this point)
*/
if (!state->indefinite &&
(state->underlying_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP))) {
state->found_tag_modifiers &= ~SEC_ASN1_CONSTRUCTED;
}
return 1;
}
static unsigned long
sec_asn1d_parse_more_length(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
int count;
PORT_Assert(state->pending > 0);
PORT_Assert(state->place == duringLength);
if (len == 0) {
state->top->status = needBytes;
return 0;
}
count = 0;
while (len && state->pending) {
if (HIGH_BITS(state->contents_length, 9) != 0) {
/*
* The given full content length overflows our container;
* just give up.
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
state->contents_length <<= 8;
state->contents_length |= (unsigned char)buf[count++];
len--;
state->pending--;
}
if (state->pending == 0)
state->place = afterLength;
return count;
}
/*
* Helper function for sec_asn1d_prepare_for_contents.
* Checks that a value representing a number of bytes consumed can be
* subtracted from a remaining length. If so, returns PR_TRUE.
* Otherwise, sets the error SEC_ERROR_BAD_DER, indicates that there was a
* decoding error in the given SEC_ASN1DecoderContext, and returns PR_FALSE.
*/
static PRBool
sec_asn1d_check_and_subtract_length(unsigned long *remaining,
unsigned long consumed,
SEC_ASN1DecoderContext *cx)
{
PORT_Assert(remaining);
PORT_Assert(cx);
if (!remaining || !cx) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
cx->status = decodeError;
return PR_FALSE;
}
if (*remaining < consumed) {
PORT_SetError(SEC_ERROR_BAD_DER);
cx->status = decodeError;
return PR_FALSE;
}
*remaining -= consumed;
return PR_TRUE;
}
static void
sec_asn1d_prepare_for_contents(sec_asn1d_state *state)
{
SECItem *item;
PLArenaPool *poolp;
unsigned long alloc_len;
sec_asn1d_state *parent;
#ifdef DEBUG_ASN1D_STATES
{
printf("Found Length %lu %s\n", state->contents_length,
state->indefinite ? "indefinite" : "");
}
#endif
/**
* The maximum length for a child element should be constrained to the
* length remaining in the first definite length element in the ancestor
* stack. If there is no definite length element in the ancestor stack,
* there's nothing to constrain the length of the child, so there's no
* further processing necessary.
*
* It's necessary to walk the ancestor stack, because it's possible to have
* definite length children that are part of an indefinite length element,
* which is itself part of an indefinite length element, and which is
* ultimately part of a definite length element. A simple example of this
* would be the handling of constructed OCTET STRINGs in BER encoding.
*
* This algorithm finds the first definite length element in the ancestor
* stack, if any, and if so, ensures that the length of the child element
* is consistent with the number of bytes remaining in the constraining
* ancestor element (that is, after accounting for any other sibling
* elements that may have been read).
*
* It's slightly complicated by the need to account both for integer
* underflow and overflow, as well as ensure that for indefinite length
* encodings, there's also enough space for the End-of-Contents (EOC)
* octets (Tag = 0x00, Length = 0x00, or two bytes).
*/
/* Determine the maximum length available for this element by finding the
* first definite length ancestor, if any. */
parent = sec_asn1d_get_enclosing_construct(state);
while (parent && parent->indefinite) {
parent = sec_asn1d_get_enclosing_construct(parent);
}
/* If parent is null, state is either the outermost state / at the top of
* the stack, or the outermost state uses indefinite length encoding. In
* these cases, there's nothing external to constrain this element, so
* there's nothing to check. */
if (parent) {
unsigned long remaining = parent->pending;
parent = state;
do {
if (!sec_asn1d_check_and_subtract_length(
&remaining, parent->consumed, state->top) ||
/* If parent->indefinite is true, parent->contents_length is
* zero and this is a no-op. */
!sec_asn1d_check_and_subtract_length(
&remaining, parent->contents_length, state->top) ||
/* If parent->indefinite is true, then ensure there is enough
* space for an EOC tag of 2 bytes. */
(parent->indefinite && !sec_asn1d_check_and_subtract_length(&remaining, 2, state->top))) {
/* This element is larger than its enclosing element, which is
* invalid. */
return;
}
} while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
parent->indefinite);
}
/*
* XXX I cannot decide if this allocation should exclude the case
* where state->endofcontents is true -- figure it out!
*/
if (state->allocate) {
void *dest;
PORT_Assert(state->dest == NULL);
/*
* We are handling a POINTER or a member of a GROUP, and need to
* allocate for the data structure.
*/
dest = sec_asn1d_zalloc(state->top->their_pool,
state->theTemplate->size);
if (dest == NULL) {
state->top->status = decodeError;
return;
}
state->dest = (char *)dest + state->theTemplate->offset;
/*
* For a member of a GROUP, our parent will later put the
* pointer wherever it belongs. But for a POINTER, we need
* to record the destination now, in case notify or filter
* procs need access to it -- they cannot find it otherwise,
* until it is too late (for one-pass processing).
*/
if (state->parent->place == afterPointer) {
void **placep;
placep = state->parent->dest;
*placep = dest;
}
}
/*
* Remember, length may be indefinite here! In that case,
* both contents_length and pending will be zero.
*/
state->pending = state->contents_length;
/*
* An EXPLICIT is nothing but an outer header, which we have
* already parsed and accepted. Now we need to do the inner
* header and its contents.
*/
if (state->explicit) {
state->place = afterExplicit;
state = sec_asn1d_push_state(state->top,
SEC_ASN1GetSubtemplate(state->theTemplate,
state->dest,
PR_FALSE),
state->dest, PR_TRUE);
if (state != NULL) {
(void)sec_asn1d_init_state_based_on_template(state);
}
return;
}
/*
* For GROUP (SET OF, SEQUENCE OF), even if we know the length here
* we cannot tell how many items we will end up with ... so push a
* state that can keep track of "children" (the individual members
* of the group; we will allocate as we go and put them all together
* at the end.
*/
if (state->underlying_kind & SEC_ASN1_GROUP) {
/* XXX If this assertion holds (should be able to confirm it via
* inspection, too) then move this code into the switch statement
* below under cases SET_OF and SEQUENCE_OF; it will be cleaner.
*/
PORT_Assert(state->underlying_kind == SEC_ASN1_SET_OF || state->underlying_kind == SEC_ASN1_SEQUENCE_OF || state->underlying_kind == (SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC) || state->underlying_kind == (SEC_ASN1_SEQUENCE_OF | SEC_ASN1_DYNAMIC));
if (state->contents_length != 0 || state->indefinite) {
const SEC_ASN1Template *subt;
state->place = duringGroup;
subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest,
PR_FALSE);
state = sec_asn1d_push_state(state->top, subt, NULL, PR_TRUE);
if (state != NULL) {
if (!state->top->filter_only)
state->allocate = PR_TRUE; /* XXX propogate this? */
/*
* Do the "before" field notification for next in group.
*/
sec_asn1d_notify_before(state->top, state->dest, state->depth);
(void)sec_asn1d_init_state_based_on_template(state);
}
} else {
/*
* A group of zero; we are done.
* Set state to afterGroup and let that code plant the NULL.
*/
state->place = afterGroup;
}
return;
}
switch (state->underlying_kind) {
case SEC_ASN1_SEQUENCE:
/*
* We need to push a child to handle the individual fields.
*/
state->place = duringSequence;
state = sec_asn1d_push_state(state->top, state->theTemplate + 1,
state->dest, PR_TRUE);
if (state != NULL) {
/*
* Do the "before" field notification.
*/
sec_asn1d_notify_before(state->top, state->dest, state->depth);
(void)sec_asn1d_init_state_based_on_template(state);
}
break;
case SEC_ASN1_SET: /* XXX SET is not really implemented */
/*
* XXX A plain SET requires special handling; scanning of a
* template to see where a field should go (because by definition,
* they are not in any particular order, and you have to look at
* each tag to disambiguate what the field is). We may never
* implement this because in practice, it seems to be unused.
*/
PORT_Assert(0);
PORT_SetError(SEC_ERROR_BAD_DER); /* XXX */
state->top->status = decodeError;
break;
case SEC_ASN1_NULL:
/*
* The NULL type, by definition, is "nothing", content length of zero.
* An indefinite-length encoding is not alloweed.
*/
if (state->contents_length || state->indefinite) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break;
}
if (state->dest != NULL) {
item = (SECItem *)(state->dest);
item->data = NULL;
item->len = 0;
}
state->place = afterEndOfContents;
break;
case SEC_ASN1_BMP_STRING:
/* Error if length is not divisable by 2 */
if (state->contents_length % 2) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break;
}
/* otherwise, handle as other string types */
goto regular_string_type;
case SEC_ASN1_UNIVERSAL_STRING:
/* Error if length is not divisable by 4 */
if (state->contents_length % 4) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break;
}
/* otherwise, handle as other string types */
goto regular_string_type;
case SEC_ASN1_SKIP:
case SEC_ASN1_ANY:
case SEC_ASN1_ANY_CONTENTS:
/*
* These are not (necessarily) strings, but they need nearly
* identical handling (especially when we need to deal with
* constructed sub-pieces), so we pretend they are.
*/
/* fallthru */
regular_string_type:
case SEC_ASN1_BIT_STRING:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_OCTET_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_T61_STRING:
case SEC_ASN1_UTC_TIME:
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_VISIBLE_STRING:
/*
* We are allocating for a primitive or a constructed string.
* If it is a constructed string, it may also be indefinite-length.
* If it is primitive, the length can (legally) be zero.
* Our first order of business is to allocate the memory for
* the string, if we can (if we know the length).
*/
item = (SECItem *)(state->dest);
/*
* If the item is a definite-length constructed string, then
* the contents_length is actually larger than what we need
* (because it also counts each intermediate header which we
* will be throwing away as we go), but it is a perfectly good
* upper bound that we just allocate anyway, and then concat
* as we go; we end up wasting a few extra bytes but save a
* whole other copy.
*/
alloc_len = state->contents_length;
poolp = NULL; /* quiet compiler warnings about unused... */
if (item == NULL || state->top->filter_only) {
if (item != NULL) {
item->data = NULL;
item->len = 0;
}
alloc_len = 0;
} else if (state->substring) {
/*
* If we are a substring of a constructed string, then we may
* not have to allocate anything (because our parent, the
* actual constructed string, did it for us). If we are a
* substring and we *do* have to allocate, that means our
* parent is an indefinite-length, so we allocate from our pool;
* later our parent will copy our string into the aggregated
* whole and free our pool allocation.
*/
if (item->data == NULL) {
PORT_Assert(item->len == 0);
poolp = state->top->our_pool;
} else {
alloc_len = 0;
}
} else {
item->len = 0;
item->data = NULL;
poolp = state->top->their_pool;
}
if (alloc_len || ((!state->indefinite) && (state->subitems_head != NULL))) {
struct subitem *subitem;
int len;
PORT_Assert(item);
if (!item) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return;
}
PORT_Assert(item->len == 0 && item->data == NULL);
/*
* Check for and handle an ANY which has stashed aside the
* header (identifier and length) bytes for us to include
* in the saved contents.
*/
if (state->subitems_head != NULL) {
PORT_Assert(state->underlying_kind == SEC_ASN1_ANY);
for (subitem = state->subitems_head;
subitem != NULL; subitem = subitem->next)
alloc_len += subitem->len;
}
if (state->top->max_element_size > 0 &&
alloc_len > state->top->max_element_size) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
state->top->status = decodeError;
return;
}
item->data = (unsigned char *)sec_asn1d_zalloc(poolp, alloc_len);
if (item->data == NULL) {
state->top->status = decodeError;
break;
}
len = 0;
for (subitem = state->subitems_head;
subitem != NULL; subitem = subitem->next) {
PORT_Memcpy(item->data + len, subitem->data, subitem->len);
len += subitem->len;
}
item->len = len;
/*
* Because we use arenas and have a mark set, we later free
* everything we have allocated, so this does *not* present
* a memory leak (it is just temporarily left dangling).
*/
state->subitems_head = state->subitems_tail = NULL;
}
if (state->contents_length == 0 && (!state->indefinite)) {
/*
* A zero-length simple or constructed string; we are done.
*/
state->place = afterEndOfContents;
} else if (state->found_tag_modifiers & SEC_ASN1_CONSTRUCTED) {
const SEC_ASN1Template *sub;
switch (state->underlying_kind) {
case SEC_ASN1_ANY:
case SEC_ASN1_ANY_CONTENTS:
sub = SEC_AnyTemplate;
break;
case SEC_ASN1_BIT_STRING:
sub = SEC_BitStringTemplate;
break;
case SEC_ASN1_BMP_STRING:
sub = SEC_BMPStringTemplate;
break;
case SEC_ASN1_GENERALIZED_TIME:
sub = SEC_GeneralizedTimeTemplate;
break;
case SEC_ASN1_IA5_STRING:
sub = SEC_IA5StringTemplate;
break;
case SEC_ASN1_OCTET_STRING:
sub = SEC_OctetStringTemplate;
break;
case SEC_ASN1_PRINTABLE_STRING:
sub = SEC_PrintableStringTemplate;
break;
case SEC_ASN1_T61_STRING:
sub = SEC_T61StringTemplate;
break;
case SEC_ASN1_UNIVERSAL_STRING:
sub = SEC_UniversalStringTemplate;
break;
case SEC_ASN1_UTC_TIME:
sub = SEC_UTCTimeTemplate;
break;
case SEC_ASN1_UTF8_STRING:
sub = SEC_UTF8StringTemplate;
break;
case SEC_ASN1_VISIBLE_STRING:
sub = SEC_VisibleStringTemplate;
break;
case SEC_ASN1_SKIP:
sub = SEC_SkipTemplate;
break;
default: /* redundant given outer switch cases, but */
PORT_Assert(0); /* the compiler does not seem to know that, */
sub = NULL; /* so just do enough to quiet it. */
break;
}
state->place = duringConstructedString;
state = sec_asn1d_push_state(state->top, sub, item, PR_TRUE);
if (state != NULL) {
state->substring = PR_TRUE; /* XXX propogate? */
(void)sec_asn1d_init_state_based_on_template(state);
}
} else if (state->indefinite) {
/*
* An indefinite-length string *must* be constructed!
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else {
/*
* A non-zero-length simple string.
*/
if (state->underlying_kind == SEC_ASN1_BIT_STRING)
state->place = beforeBitString;
else
state->place = duringLeaf;
}
break;
default:
/*
* We are allocating for a simple leaf item.
*/
if (state->contents_length) {
if (state->dest != NULL) {
item = (SECItem *)(state->dest);
item->len = 0;
if (state->top->max_element_size > 0 &&
state->contents_length > state->top->max_element_size) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
state->top->status = decodeError;
return;
}
if (state->top->filter_only) {
item->data = NULL;
} else {
item->data = (unsigned char *)
sec_asn1d_zalloc(state->top->their_pool,
state->contents_length);
if (item->data == NULL) {
state->top->status = decodeError;
return;
}
}
}
state->place = duringLeaf;
} else {
/*
* An indefinite-length or zero-length item is not allowed.
* (All legal cases of such were handled above.)
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
}
}
}
static void
sec_asn1d_free_child(sec_asn1d_state *state, PRBool error)
{
if (state->child != NULL) {
PORT_Assert(error || state->child->consumed == 0);
PORT_Assert(state->our_mark != NULL);
PORT_ArenaZRelease(state->top->our_pool, state->our_mark);
if (error && state->top->their_pool == NULL) {
/*
* XXX We need to free anything allocated.
* At this point, we failed in the middle of decoding. But we
* can't free the data we previously allocated with PR_Malloc
* unless we keep track of every pointer. So instead we have a
* memory leak when decoding fails half-way, unless an arena is
* used. See bug 95311 .
*/
}
state->child = NULL;
state->our_mark = NULL;
} else {
/*
* It is important that we do not leave a mark unreleased/unmarked.
* But I do not think we should ever have one set in this case, only
* if we had a child (handled above). So check for that. If this
* assertion should ever get hit, then we probably need to add code
* here to release back to our_mark (and then set our_mark to NULL).
*/
PORT_Assert(state->our_mark == NULL);
}
state->place = beforeEndOfContents;
}
/* We have just saved an entire encoded ASN.1 object (type) for a SAVE
** template, and now in the next template, we are going to decode that
** saved data by calling SEC_ASN1DecoderUpdate recursively.
** If that recursive call fails with needBytes, it is a fatal error,
** because the encoded object should have been complete.
** If that recursive call fails with decodeError, it will have already
** cleaned up the state stack, so we must bail out quickly.
**
** These checks of the status returned by the recursive call are now
** done in the caller of this function, immediately after it returns.
*/
static void
sec_asn1d_reuse_encoding(sec_asn1d_state *state)
{
sec_asn1d_state *child;
unsigned long consumed;
SECItem *item;
void *dest;
child = state->child;
PORT_Assert(child != NULL);
consumed = child->consumed;
child->consumed = 0;
item = (SECItem *)(state->dest);
PORT_Assert(item != NULL);
PORT_Assert(item->len == consumed);
/*
* Free any grandchild.
*/
sec_asn1d_free_child(child, PR_FALSE);
/*
* Notify after the SAVE field.
*/
sec_asn1d_notify_after(state->top, state->dest, state->depth);
/*
* Adjust to get new dest and move forward.
*/
dest = (char *)state->dest - state->theTemplate->offset;
state->theTemplate++;
child->dest = (char *)dest + state->theTemplate->offset;
child->theTemplate = state->theTemplate;
/*
* Notify before the "real" field.
*/
PORT_Assert(state->depth == child->depth);
sec_asn1d_notify_before(state->top, child->dest, child->depth);
/*
* This will tell DecoderUpdate to return when it is done.
*/
state->place = afterSaveEncoding;
/*
* We already have a child; "push" it by making it current.
*/
state->top->current = child;
/*
* And initialize it so it is ready to parse.
*/
(void)sec_asn1d_init_state_based_on_template(child);
/*
* Now parse that out of our data.
*/
if (SEC_ASN1DecoderUpdate(state->top,
(char *)item->data, item->len) != SECSuccess)
return;
if (state->top->status == needBytes) {
return;
}
PORT_Assert(state->top->current == state);
PORT_Assert(state->child == child);
/*
* That should have consumed what we consumed before.
*/
PORT_Assert(consumed == child->consumed);
child->consumed = 0;
/*
* Done.
*/
state->consumed += consumed;
child->place = notInUse;
state->place = afterEndOfContents;
}
static unsigned long
sec_asn1d_parse_leaf(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
SECItem *item;
unsigned long bufLen;
if (len == 0) {
state->top->status = needBytes;
return 0;
}
if (state->pending < len)
len = state->pending;
bufLen = len;
item = (SECItem *)(state->dest);
if (item != NULL && item->data != NULL) {
unsigned long offset;
/* Strip leading zeroes when target is unsigned integer */
if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER */
item->len == 0 && /* MSB */
item->type == siUnsignedInteger) /* unsigned */
{
while (len > 1 && buf[0] == 0) { /* leading 0 */
buf++;
len--;
}
}
offset = item->len;
if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
// The previous bit string must have no unused bits.
if (item->len & 0x7) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
// If this is a bit string, the length is bits, not bytes.
offset = item->len >> 3;
}
if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
unsigned long len_in_bits;
// Protect against overflow during the bytes-to-bits conversion.
if (len >= (ULONG_MAX >> 3) + 1) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
len_in_bits = (len << 3) - state->bit_string_unused_bits;
// Protect against overflow when computing the total length in bits.
if (UINT_MAX - item->len < len_in_bits) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
item->len += len_in_bits;
} else {
if (UINT_MAX - item->len < len) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0;
}
item->len += len;
}
PORT_Memcpy(item->data + offset, buf, len);
}
state->pending -= bufLen;
if (state->pending == 0)
state->place = beforeEndOfContents;
return bufLen;
}
static unsigned long
sec_asn1d_parse_bit_string(sec_asn1d_state *state,
const