|
|
2925c3 |
diff --git a/lib/util/secasn1d.c b/lib/util/secasn1d.c
|
|
|
2925c3 |
--- a/lib/util/secasn1d.c
|
|
|
2925c3 |
+++ b/lib/util/secasn1d.c
|
|
|
2925c3 |
@@ -946,31 +946,116 @@ sec_asn1d_parse_more_length (sec_asn1d_s
|
|
|
2925c3 |
}
|
|
|
2925c3 |
|
|
|
2925c3 |
if (state->pending == 0)
|
|
|
2925c3 |
state->place = afterLength;
|
|
|
2925c3 |
|
|
|
2925c3 |
return count;
|
|
|
2925c3 |
}
|
|
|
2925c3 |
|
|
|
2925c3 |
+/*
|
|
|
2925c3 |
+ * Helper function for sec_asn1d_prepare_for_contents.
|
|
|
2925c3 |
+ * Checks that a value representing a number of bytes consumed can be
|
|
|
2925c3 |
+ * subtracted from a remaining length. If so, returns PR_TRUE.
|
|
|
2925c3 |
+ * Otherwise, sets the error SEC_ERROR_BAD_DER, indicates that there was a
|
|
|
2925c3 |
+ * decoding error in the given SEC_ASN1DecoderContext, and returns PR_FALSE.
|
|
|
2925c3 |
+ */
|
|
|
2925c3 |
+static PRBool
|
|
|
2925c3 |
+sec_asn1d_check_and_subtract_length (unsigned long *remaining,
|
|
|
2925c3 |
+ unsigned long consumed,
|
|
|
2925c3 |
+ SEC_ASN1DecoderContext *cx)
|
|
|
2925c3 |
+{
|
|
|
2925c3 |
+ PORT_Assert(remaining);
|
|
|
2925c3 |
+ PORT_Assert(cx);
|
|
|
2925c3 |
+ if (!remaining || !cx) {
|
|
|
2925c3 |
+ PORT_SetError (SEC_ERROR_INVALID_ARGS);
|
|
|
2925c3 |
+ cx->status = decodeError;
|
|
|
2925c3 |
+ return PR_FALSE;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ if (*remaining < consumed) {
|
|
|
2925c3 |
+ PORT_SetError (SEC_ERROR_BAD_DER);
|
|
|
2925c3 |
+ cx->status = decodeError;
|
|
|
2925c3 |
+ return PR_FALSE;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ *remaining -= consumed;
|
|
|
2925c3 |
+ return PR_TRUE;
|
|
|
2925c3 |
+}
|
|
|
2925c3 |
|
|
|
2925c3 |
static void
|
|
|
2925c3 |
sec_asn1d_prepare_for_contents (sec_asn1d_state *state)
|
|
|
2925c3 |
{
|
|
|
2925c3 |
SECItem *item;
|
|
|
2925c3 |
PLArenaPool *poolp;
|
|
|
2925c3 |
unsigned long alloc_len;
|
|
|
2925c3 |
+ sec_asn1d_state *parent;
|
|
|
2925c3 |
|
|
|
2925c3 |
#ifdef DEBUG_ASN1D_STATES
|
|
|
2925c3 |
{
|
|
|
2925c3 |
printf("Found Length %d %s\n", state->contents_length,
|
|
|
2925c3 |
state->indefinite ? "indefinite" : "");
|
|
|
2925c3 |
}
|
|
|
2925c3 |
#endif
|
|
|
2925c3 |
|
|
|
2925c3 |
+ /**
|
|
|
2925c3 |
+ * The maximum length for a child element should be constrained to the
|
|
|
2925c3 |
+ * length remaining in the first definite length element in the ancestor
|
|
|
2925c3 |
+ * stack. If there is no definite length element in the ancestor stack,
|
|
|
2925c3 |
+ * there's nothing to constrain the length of the child, so there's no
|
|
|
2925c3 |
+ * further processing necessary.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * It's necessary to walk the ancestor stack, because it's possible to have
|
|
|
2925c3 |
+ * definite length children that are part of an indefinite length element,
|
|
|
2925c3 |
+ * which is itself part of an indefinite length element, and which is
|
|
|
2925c3 |
+ * ultimately part of a definite length element. A simple example of this
|
|
|
2925c3 |
+ * would be the handling of constructed OCTET STRINGs in BER encoding.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * This algorithm finds the first definite length element in the ancestor
|
|
|
2925c3 |
+ * stack, if any, and if so, ensures that the length of the child element
|
|
|
2925c3 |
+ * is consistent with the number of bytes remaining in the constraining
|
|
|
2925c3 |
+ * ancestor element (that is, after accounting for any other sibling
|
|
|
2925c3 |
+ * elements that may have been read).
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * It's slightly complicated by the need to account both for integer
|
|
|
2925c3 |
+ * underflow and overflow, as well as ensure that for indefinite length
|
|
|
2925c3 |
+ * encodings, there's also enough space for the End-of-Contents (EOC)
|
|
|
2925c3 |
+ * octets (Tag = 0x00, Length = 0x00, or two bytes).
|
|
|
2925c3 |
+ */
|
|
|
2925c3 |
+
|
|
|
2925c3 |
+ /* Determine the maximum length available for this element by finding the
|
|
|
2925c3 |
+ * first definite length ancestor, if any. */
|
|
|
2925c3 |
+ parent = sec_asn1d_get_enclosing_construct(state);
|
|
|
2925c3 |
+ while (parent && parent->indefinite) {
|
|
|
2925c3 |
+ parent = sec_asn1d_get_enclosing_construct(parent);
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ /* If parent is null, state is either the outermost state / at the top of
|
|
|
2925c3 |
+ * the stack, or the outermost state uses indefinite length encoding. In
|
|
|
2925c3 |
+ * these cases, there's nothing external to constrain this element, so
|
|
|
2925c3 |
+ * there's nothing to check. */
|
|
|
2925c3 |
+ if (parent) {
|
|
|
2925c3 |
+ unsigned long remaining = parent->pending;
|
|
|
2925c3 |
+ parent = state;
|
|
|
2925c3 |
+ do {
|
|
|
2925c3 |
+ if (!sec_asn1d_check_and_subtract_length(
|
|
|
2925c3 |
+ &remaining, parent->consumed, state->top) ||
|
|
|
2925c3 |
+ /* If parent->indefinite is true, parent->contents_length is
|
|
|
2925c3 |
+ * zero and this is a no-op. */
|
|
|
2925c3 |
+ !sec_asn1d_check_and_subtract_length(
|
|
|
2925c3 |
+ &remaining, parent->contents_length, state->top) ||
|
|
|
2925c3 |
+ /* If parent->indefinite is true, then ensure there is enough
|
|
|
2925c3 |
+ * space for an EOC tag of 2 bytes. */
|
|
|
2925c3 |
+ (parent->indefinite && !sec_asn1d_check_and_subtract_length(
|
|
|
2925c3 |
+ &remaining, 2, state->top))) {
|
|
|
2925c3 |
+ /* This element is larger than its enclosing element, which is
|
|
|
2925c3 |
+ * invalid. */
|
|
|
2925c3 |
+ return;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ } while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
|
|
|
2925c3 |
+ parent->indefinite);
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+
|
|
|
2925c3 |
/*
|
|
|
2925c3 |
* XXX I cannot decide if this allocation should exclude the case
|
|
|
2925c3 |
* where state->endofcontents is true -- figure it out!
|
|
|
2925c3 |
*/
|
|
|
2925c3 |
if (state->allocate) {
|
|
|
2925c3 |
void *dest;
|
|
|
2925c3 |
|
|
|
2925c3 |
PORT_Assert (state->dest == NULL);
|
|
|
2925c3 |
@@ -1002,31 +1087,16 @@ sec_asn1d_prepare_for_contents (sec_asn1
|
|
|
2925c3 |
}
|
|
|
2925c3 |
|
|
|
2925c3 |
/*
|
|
|
2925c3 |
* Remember, length may be indefinite here! In that case,
|
|
|
2925c3 |
* both contents_length and pending will be zero.
|
|
|
2925c3 |
*/
|
|
|
2925c3 |
state->pending = state->contents_length;
|
|
|
2925c3 |
|
|
|
2925c3 |
- /* If this item has definite length encoding, and
|
|
|
2925c3 |
- ** is enclosed by a definite length constructed type,
|
|
|
2925c3 |
- ** make sure it isn't longer than the remaining space in that
|
|
|
2925c3 |
- ** constructed type.
|
|
|
2925c3 |
- */
|
|
|
2925c3 |
- if (state->contents_length > 0) {
|
|
|
2925c3 |
- sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(state);
|
|
|
2925c3 |
- if (parent && !parent->indefinite &&
|
|
|
2925c3 |
- state->consumed + state->contents_length > parent->pending) {
|
|
|
2925c3 |
- PORT_SetError (SEC_ERROR_BAD_DER);
|
|
|
2925c3 |
- state->top->status = decodeError;
|
|
|
2925c3 |
- return;
|
|
|
2925c3 |
- }
|
|
|
2925c3 |
- }
|
|
|
2925c3 |
-
|
|
|
2925c3 |
/*
|
|
|
2925c3 |
* An EXPLICIT is nothing but an outer header, which we have
|
|
|
2925c3 |
* already parsed and accepted. Now we need to do the inner
|
|
|
2925c3 |
* header and its contents.
|
|
|
2925c3 |
*/
|
|
|
2925c3 |
if (state->explicit) {
|
|
|
2925c3 |
state->place = afterExplicit;
|
|
|
2925c3 |
state = sec_asn1d_push_state (state->top,
|
|
|
2925c3 |
@@ -1715,20 +1785,117 @@ sec_asn1d_next_substring (sec_asn1d_stat
|
|
|
2925c3 |
state->top->status = decodeError;
|
|
|
2925c3 |
return;
|
|
|
2925c3 |
}
|
|
|
2925c3 |
|
|
|
2925c3 |
state->pending -= child_consumed;
|
|
|
2925c3 |
if (state->pending == 0)
|
|
|
2925c3 |
done = PR_TRUE;
|
|
|
2925c3 |
} else {
|
|
|
2925c3 |
+ PRBool preallocatedString;
|
|
|
2925c3 |
+ sec_asn1d_state *temp_state;
|
|
|
2925c3 |
PORT_Assert (state->indefinite);
|
|
|
2925c3 |
|
|
|
2925c3 |
item = (SECItem *)(child->dest);
|
|
|
2925c3 |
- if (item != NULL && item->data != NULL) {
|
|
|
2925c3 |
+
|
|
|
2925c3 |
+ /**
|
|
|
2925c3 |
+ * At this point, there's three states at play:
|
|
|
2925c3 |
+ * child: The element that was just parsed
|
|
|
2925c3 |
+ * state: The currently processed element
|
|
|
2925c3 |
+ * 'parent' (aka state->parent): The enclosing construct
|
|
|
2925c3 |
+ * of state, or NULL if this is the top-most element.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * This state handles both substrings of a constructed string AND
|
|
|
2925c3 |
+ * child elements of items whose template type was that of
|
|
|
2925c3 |
+ * SEC_ASN1_ANY, SEC_ASN1_SAVE, SEC_ASN1_ANY_CONTENTS, SEC_ASN1_SKIP
|
|
|
2925c3 |
+ * template, as described in sec_asn1d_prepare_for_contents. For
|
|
|
2925c3 |
+ * brevity, these will be referred to as 'string' and 'any' types.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * This leads to the following possibilities:
|
|
|
2925c3 |
+ * 1: This element is an indefinite length string, part of a
|
|
|
2925c3 |
+ * definite length string.
|
|
|
2925c3 |
+ * 2: This element is an indefinite length string, part of an
|
|
|
2925c3 |
+ * indefinite length string.
|
|
|
2925c3 |
+ * 3: This element is an indefinite length any, part of a
|
|
|
2925c3 |
+ * definite length any.
|
|
|
2925c3 |
+ * 4: This element is an indefinite length any, part of an
|
|
|
2925c3 |
+ * indefinite length any.
|
|
|
2925c3 |
+ * 5: This element is an indefinite length any and does not
|
|
|
2925c3 |
+ * meet any of the above criteria. Note that this would include
|
|
|
2925c3 |
+ * an indefinite length string type matching an indefinite
|
|
|
2925c3 |
+ * length any template.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * In Cases #1 and #3, the definite length 'parent' element will
|
|
|
2925c3 |
+ * have allocated state->dest based on the parent elements definite
|
|
|
2925c3 |
+ * size. During the processing of 'child', sec_asn1d_parse_leaf will
|
|
|
2925c3 |
+ * have copied the (string, any) data directly into the offset of
|
|
|
2925c3 |
+ * dest, as appropriate, so there's no need for this class to still
|
|
|
2925c3 |
+ * store the child - it's already been processed.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * In Cases #2 and #4, dest will be set to the parent element's dest,
|
|
|
2925c3 |
+ * but dest->data will not have been allocated yet, due to the
|
|
|
2925c3 |
+ * indefinite length encoding. In this situation, it's necessary to
|
|
|
2925c3 |
+ * hold onto child (and all other children) until the EOC, at which
|
|
|
2925c3 |
+ * point, it becomes possible to compute 'state's overall length. Once
|
|
|
2925c3 |
+ * 'state' has a computed length, this can then be fed to 'parent' (via
|
|
|
2925c3 |
+ * this state), and then 'parent' can similarly compute the length of
|
|
|
2925c3 |
+ * all of its children up to the EOC, which will ultimately transit to
|
|
|
2925c3 |
+ * sec_asn1d_concat_substrings, determine the overall size needed,
|
|
|
2925c3 |
+ * allocate, and copy the contents (of all of parent's children, which
|
|
|
2925c3 |
+ * would include 'state', just as 'state' will have copied all of its
|
|
|
2925c3 |
+ * children via sec_asn1d_concat_substrings)
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * The final case, Case #5, will manifest in that item->data and
|
|
|
2925c3 |
+ * item->len will be NULL/0, respectively, since this element was
|
|
|
2925c3 |
+ * indefinite-length encoded. In that case, both the tag and length will
|
|
|
2925c3 |
+ * already exist in state's subitems, via sec_asn1d_record_any_header,
|
|
|
2925c3 |
+ * and so the contents (aka 'child') should be added to that list of
|
|
|
2925c3 |
+ * items to concatenate in sec_asn1d_concat_substrings once the EOC
|
|
|
2925c3 |
+ * is encountered.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * To distinguish #2/#4 from #1/#3, it's sufficient to walk the ancestor
|
|
|
2925c3 |
+ * tree. If the current type is a string type, then the enclosing
|
|
|
2925c3 |
+ * construct will be that same type (#1/#2). If the current type is an
|
|
|
2925c3 |
+ * any type, then the enclosing construct is either an any type (#3/#4)
|
|
|
2925c3 |
+ * or some other type (#5). Since this is BER, this nesting relationship
|
|
|
2925c3 |
+ * between 'state' and 'parent' may go through several levels of
|
|
|
2925c3 |
+ * constructed encoding, so continue walking the ancestor chain until a
|
|
|
2925c3 |
+ * clear determination can be made.
|
|
|
2925c3 |
+ *
|
|
|
2925c3 |
+ * The variable preallocatedString is used to indicate Case #1/#3,
|
|
|
2925c3 |
+ * indicating an in-place copy has already occurred, and Cases #2, #4,
|
|
|
2925c3 |
+ * and #5 all have the same behaviour of adding a new substring.
|
|
|
2925c3 |
+ */
|
|
|
2925c3 |
+ preallocatedString = PR_FALSE;
|
|
|
2925c3 |
+ temp_state = state;
|
|
|
2925c3 |
+ while (temp_state && item == temp_state->dest && temp_state->indefinite) {
|
|
|
2925c3 |
+ sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(temp_state);
|
|
|
2925c3 |
+ if (!parent || parent->underlying_kind != temp_state->underlying_kind) {
|
|
|
2925c3 |
+ /* Case #5 - Either this is a top-level construct or it is part
|
|
|
2925c3 |
+ * of some other element (e.g. a SEQUENCE), in which case, a
|
|
|
2925c3 |
+ * new item should be allocated. */
|
|
|
2925c3 |
+ break;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ if (!parent->indefinite) {
|
|
|
2925c3 |
+ /* Cases #1 / #3 - A definite length ancestor exists, for which
|
|
|
2925c3 |
+ * this is a substring that has already copied into dest. */
|
|
|
2925c3 |
+ preallocatedString = PR_TRUE;
|
|
|
2925c3 |
+ break;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ if (!parent->substring) {
|
|
|
2925c3 |
+ /* Cases #2 / #4 - If the parent is not a substring, but is
|
|
|
2925c3 |
+ * indefinite, then there's nothing further up that may have
|
|
|
2925c3 |
+ * preallocated dest, thus child will not have already
|
|
|
2925c3 |
+ * been copied in place, therefore it's necessary to save child
|
|
|
2925c3 |
+ * as a subitem. */
|
|
|
2925c3 |
+ break;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ temp_state = parent;
|
|
|
2925c3 |
+ }
|
|
|
2925c3 |
+ if (item != NULL && item->data != NULL && !preallocatedString) {
|
|
|
2925c3 |
/*
|
|
|
2925c3 |
* Save the string away for later concatenation.
|
|
|
2925c3 |
*/
|
|
|
2925c3 |
PORT_Assert (item->data != NULL);
|
|
|
2925c3 |
sec_asn1d_add_to_subitems (state, item->data, item->len, PR_FALSE);
|
|
|
2925c3 |
/*
|
|
|
2925c3 |
* Clear the child item for the next round.
|
|
|
2925c3 |
*/
|