Exemplo n.º 1
0
static SECStatus DecodeInline(void* dest,
                     const SEC_ASN1Template* templateEntry,
                     SECItem* src, PLArenaPool* arena, PRBool checkTag)
{
    const SEC_ASN1Template* inlineTemplate = 
        SEC_ASN1GetSubtemplate (templateEntry, dest, PR_FALSE);
    return DecodeItem((void*)((char*)dest + templateEntry->offset),
                            inlineTemplate, src, arena, checkTag);
}
Exemplo n.º 2
0
static SECStatus DecodePointer(void* dest,
                     const SEC_ASN1Template* templateEntry,
                     SECItem* src, PLArenaPool* arena, PRBool checkTag)
{
    const SEC_ASN1Template* ptrTemplate = 
        SEC_ASN1GetSubtemplate (templateEntry, dest, PR_FALSE);
    void* subdata = PORT_ArenaZAlloc(arena, ptrTemplate->size);
    *(void**)((char*)dest + templateEntry->offset) = subdata;
    if (subdata)
    {
        return DecodeItem(subdata, ptrTemplate, src, arena, checkTag);
    }
    else
    {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return SECFailure;
    }
}
Exemplo n.º 3
0
static void
sec_asn1e_write_header (sec_asn1e_state *state)
{
    unsigned long contents_length;
    unsigned char tag_number, tag_modifiers;
    sec_asn1e_hdr_encoding hdrException = hdr_normal;
    PRBool indefinite = PR_FALSE;

    PORT_Assert (state->place == beforeHeader);

    tag_number = state->tag_number;
    tag_modifiers = state->tag_modifiers;

    if (state->underlying_kind == SEC_ASN1_ANY) {
	state->place = duringContents;
	return;
    }

    if (state->underlying_kind & SEC_ASN1_CHOICE) {
	int indx = sec_asn1e_which_choice(state->src, state->theTemplate);
	if( 0 == indx ) {
	    /* XXX set an error? "choice not found" */
	    state->top->status = encodeError;
	    return;
	}
	state->place = afterChoice;
	state = sec_asn1e_push_state(state->top, &state->theTemplate[indx],
			       (char *)state->src - state->theTemplate->offset, 
			       PR_TRUE);
	if (state) {
	    /*
	     * Do the "before" field notification.
	     */
	    sec_asn1e_notify_before (state->top, state->src, state->depth);
	    state = sec_asn1e_init_state_based_on_template (state);
	}
	return;
    }

    /* The !isString test below is apparently intended to ensure that all 
    ** constructed types receive indefinite length encoding.
    */
   indefinite = (PRBool) 
	(state->top->streaming && state->may_stream && 
	 (state->top->from_buf || !state->is_string));

    /*
     * If we are doing a definite-length encoding, first we have to
     * walk the data structure to calculate the entire contents length.
     * If we are doing an indefinite-length encoding, we still need to 
     * know if the contents is:
     *    optional and to be omitted, or 
     *    an ANY (header is pre-encoded), or 
     *    a SAVE or some other kind of template used only by the decoder.
     * So, we call this function either way.
     */
    contents_length = sec_asn1e_contents_length (state->theTemplate,
						 state->src, 
                                                 state->disallowStreaming,
						 indefinite,
                                                 &hdrException);
    /*
     * We might be told explicitly not to put out a header.
     * But it can also be the case, via a pushed subtemplate, that
     * sec_asn1e_contents_length could not know that this field is
     * really optional.  So check for that explicitly, too.
     */
    if (hdrException != hdr_normal || 
	(contents_length == 0 && state->optional)) {
	state->place = afterContents;
	if (state->top->streaming && 
	    state->may_stream && 
	    state->top->from_buf) {
	    /* we did not find an optional indefinite string, so we 
	     * don't encode it.  However, if TakeFromBuf is on, we stop 
	     * here anyway to give our caller a chance to intercept at the 
	     * same point where we would stop if the field were present. 
	     */
	    state->top->status = needBytes;
	}
	return;
    }

    if (indefinite) {
	/*
	 * We need to put out an indefinite-length encoding.
	 * The only universal types that can be constructed are SETs,
	 * SEQUENCEs, and strings; so check that it is one of those,
	 * or that it is not universal (e.g. context-specific).
	 */
	state->indefinite = PR_TRUE;
	PORT_Assert ((tag_number == SEC_ASN1_SET)
		     || (tag_number == SEC_ASN1_SEQUENCE)
		     || ((tag_modifiers & SEC_ASN1_CLASS_MASK) != 0)
		     || state->is_string);
	tag_modifiers |= SEC_ASN1_CONSTRUCTED;
	contents_length = 0;
    }

    sec_asn1e_write_identifier_bytes (state, 
                                (unsigned char)(tag_number | tag_modifiers));
    sec_asn1e_write_length_bytes (state, contents_length, state->indefinite);

    if (contents_length == 0 && !state->indefinite) {
	/*
	 * If no real contents to encode, then we are done with this field.
	 */
	state->place = afterContents;
	return;
    }

    /*
     * An EXPLICIT is nothing but an outer header, which we have already
     * written.  Now we need to do the inner header and contents.
     */
    if (state->isExplicit) {
	const SEC_ASN1Template *subt =
	      SEC_ASN1GetSubtemplate(state->theTemplate, state->src, PR_TRUE);
	state->place = afterContents;
	state = sec_asn1e_push_state (state->top, subt, state->src, PR_TRUE);
	if (state != NULL)
	    state = sec_asn1e_init_state_based_on_template (state);
	return;
    }

    switch (state->underlying_kind) {
      case SEC_ASN1_SET_OF:
      case SEC_ASN1_SEQUENCE_OF:
	/*
	 * We need to push a child to handle each member.
	 */
	{
	    void **group;
	    const SEC_ASN1Template *subt;

	    group = *(void ***)state->src;
	    if (group == NULL || *group == NULL) {
		/*
		 * Group is empty; we are done.
		 */
		state->place = afterContents;
		return;
	    }
	    state->place = duringGroup;
	    subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src,
					   PR_TRUE);
	    state = sec_asn1e_push_state (state->top, subt, *group, PR_TRUE);
	    if (state != NULL)
		state = sec_asn1e_init_state_based_on_template (state);
	}
	break;

      case SEC_ASN1_SEQUENCE:
      case SEC_ASN1_SET:
	/*
	 * We need to push a child to handle the individual fields.
	 */
	state->place = duringSequence;
	state = sec_asn1e_push_state (state->top, state->theTemplate + 1,
				      state->src, PR_TRUE);
	if (state != NULL) {
	    /*
	     * Do the "before" field notification.
	     */
	    sec_asn1e_notify_before (state->top, state->src, state->depth);
	    state = sec_asn1e_init_state_based_on_template (state);
	}
	break;

      default:
	/*
	 * I think we do not need to do anything else.
	 * XXX Correct?
	 */
	state->place = duringContents;
	break;
    }
}
Exemplo n.º 4
0
static unsigned long
sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
			   PRBool disallowStreaming, PRBool insideIndefinite,
			   sec_asn1e_hdr_encoding *pHdrException)
{
    unsigned long encode_kind, underlying_kind;
    PRBool isExplicit, optional, universal, may_stream;
    unsigned long len;

    /*
     * This function currently calculates the length in all cases
     * except the following: when writing out the contents of a 
     * template that belongs to a state where it was a sub-template
     * with the SEC_ASN1_MAY_STREAM bit set and it's parent had the
     * optional bit set.  The information that the parent is optional
     * and that we should return the length of 0 when that length is 
     * present since that means the optional field is no longer present.
     * So we add the disallowStreaming flag which is passed in when
     * writing the contents, but for all recursive calls to 
     * sec_asn1e_contents_length, we pass PR_FALSE, because this
     * function correctly calculates the length for children templates
     * from that point on.  Confused yet?  At least you didn't have
     * to figure it out.  ;)  -javi
     */
    encode_kind = theTemplate->kind;

    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
		? PR_TRUE : PR_FALSE;

    isExplicit = (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 (!(isExplicit && universal));	/* bad templates */

    may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_MAY_STREAM;

    /* Just clear this to get it out of the way; we do not need it here */
    encode_kind &= ~SEC_ASN1_DYNAMIC;

    if (encode_kind & SEC_ASN1_NO_STREAM) {
	disallowStreaming = PR_TRUE;
    }
    encode_kind &= ~SEC_ASN1_NO_STREAM;

    if (encode_kind & SEC_ASN1_CHOICE) {
	void *src2;
	int indx = sec_asn1e_which_choice(src, theTemplate);
	if (0 == indx) {
	    /* XXX set an error? "choice not found" */
	    /* state->top->status = encodeError; */
	    return 0;
	}

        src2 = (void *)
	        ((char *)src - theTemplate->offset + theTemplate[indx].offset);

        return sec_asn1e_contents_length(&theTemplate[indx], src2, 
					 disallowStreaming, insideIndefinite,
					 pHdrException);
    }

    if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) {
	/* XXX any bits we want to disallow (PORT_Assert against) here? */
	theTemplate = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);
	if (encode_kind & SEC_ASN1_POINTER) {
	    src = *(void **)src;
	    if (src == NULL) {
		*pHdrException = optional ? hdr_optional : hdr_normal;
		return 0;
	    }
	} else if (encode_kind & SEC_ASN1_INLINE) {
	    /* check that there are no extraneous bits */
	    if (optional) {
		if (PR_FALSE != SEC_ASN1IsTemplateSimple(theTemplate)) {
		    /* we now know that the target is a SECItem*, so we can check
		       if the source contains one */
		    SECItem* target = (SECItem*)src;
		    if (!target || !target->data || !target->len) {
			/* no valid data to encode subtemplate */
			*pHdrException = hdr_optional;
			return 0;
		    }
		} else {
		    PORT_Assert(0); /* complex templates not handled as inline
                                       optional */
		}
	    }
	}

	src = (char *)src + theTemplate->offset;

	/* recurse to find the length of the subtemplate */
	len = sec_asn1e_contents_length (theTemplate, src, disallowStreaming, 
	                                 insideIndefinite, pHdrException);
	if (len == 0 && optional) {
	    *pHdrException = hdr_optional;
	} else if (isExplicit) {
	    if (*pHdrException == hdr_any) {
		/* *we* do not want to add in a header, 
		** but our caller still does. 
		*/
		*pHdrException = hdr_normal;
	    } else if (*pHdrException == hdr_normal) {
		/* if the inner content exists, our length is
		 * len(identifier) + len(length) + len(innercontent)
		 * XXX we currently assume len(identifier) == 1;
		 * to support a high-tag-number this would need to be smarter.
		 */
		len += 1 + SEC_ASN1LengthLength (len);
	    }
	}
	return len;
    }
    underlying_kind = encode_kind;

    /* This is only used in decoding; it plays no part in encoding.  */
    if (underlying_kind & SEC_ASN1_SAVE) {
	/* check that there are no extraneous bits */
	PORT_Assert (underlying_kind == SEC_ASN1_SAVE);
	*pHdrException = hdr_decoder;
	return 0;
    }

#define UNEXPECTED_FLAGS \
 (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_INLINE | SEC_ASN1_POINTER |\
  SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_SAVE | SEC_ASN1_SKIP)

    /* Having any of these bits is not expected here...  */
    PORT_Assert ((underlying_kind & UNEXPECTED_FLAGS) == 0);
    underlying_kind &= ~UNEXPECTED_FLAGS;
#undef UNEXPECTED_FLAGS

    if (underlying_kind & SEC_ASN1_CHOICE) {
	void *src2;
	int indx = sec_asn1e_which_choice(src, theTemplate);
	if (0 == indx) {
	    /* XXX set an error? "choice not found" */
	    /* state->top->status = encodeError; */
	    return 0;
	}

        src2 = (void *)
		((char *)src - theTemplate->offset + theTemplate[indx].offset);
        len = sec_asn1e_contents_length(&theTemplate[indx], src2, 
	                                disallowStreaming, insideIndefinite, 
					pHdrException);
    } else {
      switch (underlying_kind) {
      case SEC_ASN1_SEQUENCE_OF:
      case SEC_ASN1_SET_OF:
	{
	    const SEC_ASN1Template *tmpt;
	    void *sub_src;
	    unsigned long sub_len;
	    void **group;

	    len = 0;

	    group = *(void ***)src;
	    if (group == NULL)
		break;

	    tmpt = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);

	    for (; *group != NULL; group++) {
		sub_src = (char *)(*group) + tmpt->offset;
		sub_len = sec_asn1e_contents_length (tmpt, sub_src, 
		                                     disallowStreaming,
						     insideIndefinite,
                                                     pHdrException);
		len += sub_len;
		/*
		 * XXX The 1 below is the presumed length of the identifier;
		 * to support a high-tag-number this would need to be smarter.
		 */
		if (*pHdrException == hdr_normal)
		    len += 1 + SEC_ASN1LengthLength (sub_len);
	    }
	}
	break;

      case SEC_ASN1_SEQUENCE:
      case SEC_ASN1_SET:
	{
	    const SEC_ASN1Template *tmpt;
	    void *sub_src;
	    unsigned long sub_len;

	    len = 0;
	    for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) {
		sub_src = (char *)src + tmpt->offset;
		sub_len = sec_asn1e_contents_length (tmpt, sub_src, 
		                                     disallowStreaming,
						     insideIndefinite,
                                                     pHdrException);
		len += sub_len;
		/*
		 * XXX The 1 below is the presumed length of the identifier;
		 * to support a high-tag-number this would need to be smarter.
		 */
		if (*pHdrException == hdr_normal)
		    len += 1 + SEC_ASN1LengthLength (sub_len);
	    }
	}
	break;

      case SEC_ASN1_BIT_STRING:
	/* convert bit length to byte */
	len = (((SECItem *)src)->len + 7) >> 3;
	/* bit string contents involve an extra octet */
	if (len)
	    len++;
	break;

      case SEC_ASN1_INTEGER:
	/* ASN.1 INTEGERs are signed.
	 * If the source is an unsigned integer, the encoder will need 
	 * to handle the conversion here.
	 */
	{
	    unsigned char *buf = ((SECItem *)src)->data;
	    SECItemType integerType = ((SECItem *)src)->type;
	    len = ((SECItem *)src)->len;
	    while (len > 0) {
		if (*buf != 0) {
		    if (*buf & 0x80 && integerType == siUnsignedInteger) {
			len++; /* leading zero needed to make number signed */
		    }
		    break; /* reached beginning of number */
		}
		if (len == 1) {
		    break; /* the number 0 */
		}
		if (buf[1] & 0x80) {
		    break; /* leading zero already present */
		} 
		/* extraneous leading zero, keep going */
		buf++;
		len--;
	    }
	}
	break;

      default:
	len = ((SECItem *)src)->len;
	break;
      }  /* end switch */

#ifndef WHAT_PROBLEM_DOES_THIS_SOLVE
      /* if we're streaming, we may have a secitem w/len 0 as placeholder */
      if (!len && insideIndefinite && may_stream && !disallowStreaming) {
	  len = 1;
      }
#endif
    }    /* end else */

    if (len == 0 && optional)
	*pHdrException = hdr_optional;
    else if (underlying_kind == SEC_ASN1_ANY)
	*pHdrException = hdr_any;
    else 
	*pHdrException = hdr_normal;

    return len;
}
Exemplo n.º 5
0
static sec_asn1e_state *
sec_asn1e_init_state_based_on_template (sec_asn1e_state *state)
{
    PRBool isExplicit, is_string, may_stream, optional, universal; 
    PRBool disallowStreaming;
    unsigned char tag_modifiers;
    unsigned long encode_kind, under_kind;
    unsigned long tag_number;
    PRBool isInline = PR_FALSE;


    encode_kind = state->theTemplate->kind;

    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
		? PR_TRUE : PR_FALSE;

    isExplicit = (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 (!(isExplicit && universal));	/* bad templates */

    may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_MAY_STREAM;

    disallowStreaming = (encode_kind & SEC_ASN1_NO_STREAM) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_NO_STREAM;

    /* Just clear this to get it out of the way; we do not need it here */
    encode_kind &= ~SEC_ASN1_DYNAMIC;

    if( encode_kind & SEC_ASN1_CHOICE ) {
      under_kind = SEC_ASN1_CHOICE;
    } else if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || 
        (!universal && !isExplicit)) {
	const SEC_ASN1Template *subt;
	void *src = NULL;

	PORT_Assert ((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);

	sec_asn1e_scrub_state (state);

	if (encode_kind & SEC_ASN1_POINTER) {
	    src = *(void **)state->src;
	    state->place = afterPointer;

	    if (src == NULL) {
		/*
		 * If this is optional, but NULL, then the field does
		 * not need to be encoded.  In this case we are done;
		 * we do not want to push a subtemplate.
		 */
		if (optional)
		    return state;

		/*
		 * XXX this is an error; need to figure out
		 * how to handle this
		 */
	    }
	} else {
	    src = state->src;
	    if (encode_kind & SEC_ASN1_INLINE) {
		/* check that there are no extraneous bits */
		/* PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional); */
		state->place = afterInline;
		isInline = PR_TRUE;
	    } else {
		/*
		 * Save the tag modifiers and tag number here before moving
		 * on to the next state in case this is a member of a
		 * SEQUENCE OF
		 */
		state->tag_modifiers = (unsigned char)
		    (encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK));
		state->tag_number = (unsigned char)
		    (encode_kind & SEC_ASN1_TAGNUM_MASK);
		
		state->place = afterImplicit;
		state->optional = optional;
	    }
	}

	subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src, PR_TRUE);
	if (isInline && optional) {
	    /* we only handle a very limited set of optional inline cases at
	       this time */
	    if (PR_FALSE != SEC_ASN1IsTemplateSimple(subt)) {
		/* we now know that the target is a SECItem*, so we can check
		   if the source contains one */
		SECItem* target = (SECItem*)state->src;
		if (!target || !target->data || !target->len) {
		    /* no valid data to encode subtemplate */
		    return state;
		}
	    } else {
		PORT_Assert(0); /* complex templates are not handled as
				   inline optional */
	    }
	}
	state = sec_asn1e_push_state (state->top, subt, src, PR_FALSE);
	if (state == NULL)
	    return state;

	if (universal) {
	    /*
	     * This is a POINTER or INLINE; just init based on that
	     * and we are done.
	     */
	    return sec_asn1e_init_state_based_on_template (state);
	}

	/*
	 * This is an implicit, non-universal (meaning, application-private
	 * or context-specific) field.  This results in a "magic" tag but
	 * encoding based on the underlying type.  We pushed a new state
	 * that is based on the subtemplate (the underlying type), but
	 * now we will sort of alias it to give it some of our properties
	 * (tag, optional status, etc.).
	 *
	 * NB: ALL the following flags in the subtemplate are disallowed
	 *     and/or ignored: EXPLICIT, OPTIONAL, INNER, INLINE, POINTER.
	 */

	under_kind = state->theTemplate->kind;
	if ((under_kind & SEC_ASN1_MAY_STREAM) && !disallowStreaming) {
	    may_stream = PR_TRUE;
	}
	under_kind &= ~(SEC_ASN1_MAY_STREAM | SEC_ASN1_DYNAMIC);
    } else {
	under_kind = encode_kind;
    }

    /*
     * Sanity check that there are no unwanted bits marked in under_kind.
     * These bits were either removed above (after we recorded them) or
     * they simply should not be found (signalling a bad/broken template).
     * XXX is this the right set of bits to test here? (i.e. need to add
     * or remove any?)
     */
#define UNEXPECTED_FLAGS \
 (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_SKIP | SEC_ASN1_INNER | \
  SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)

    PORT_Assert ((under_kind & UNEXPECTED_FLAGS) == 0);
    under_kind &= ~UNEXPECTED_FLAGS;
#undef UNEXPECTED_FLAGS

    if (encode_kind & SEC_ASN1_ANY) {
	PORT_Assert (encode_kind == under_kind);
	tag_modifiers = 0;
	tag_number = 0;
	is_string = PR_TRUE;
    } else {
	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.
	 */
	tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;

	is_string = PR_FALSE;
	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:
	    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: 
	    /*
	     * We do not yet know if we will be constructing the string,
	     * so we have to wait to do this final tag modification.
	     */
	    is_string = PR_TRUE;
	    break;
	}
    }

    state->tag_modifiers = tag_modifiers;
    state->tag_number = (unsigned char)tag_number;
    state->underlying_kind = under_kind;
    state->isExplicit = isExplicit;
    state->may_stream = may_stream;
    state->is_string = is_string;
    state->optional = optional;
    state->disallowStreaming = disallowStreaming;

    sec_asn1e_scrub_state (state);

    return state;
}
Exemplo n.º 6
0
static SECStatus DecodeGroup(void* dest,
                     const SEC_ASN1Template* templateEntry,
                     SECItem* src, PLArenaPool* arena)
{
    SECStatus rv = SECSuccess;
    SECItem source;
    SECItem group;
    PRUint32 totalEntries = 0;
    PRUint32 entryIndex = 0;
    void** entries = NULL;

    const SEC_ASN1Template* subTemplate =
        SEC_ASN1GetSubtemplate (templateEntry, dest, PR_FALSE);

    source = *src;

    /* get the group */
    if (SECSuccess == rv)
    {
        rv = GetItem(&source, &group, PR_FALSE);
    }

    /* XXX we should check the subtemplate in debug builds */
    if (SECSuccess == rv)
    {
        /* first, count the number of entries. Benchmarking showed that this
           counting pass is more efficient than trying to allocate entries as
           we read the DER, even if allocating many entries at a time
        */
        SECItem counter = group;
        do
        {
            SECItem anitem;
            rv = GetItem(&counter, &anitem, PR_TRUE);
            if (SECSuccess == rv && (anitem.len) )
            {
                totalEntries++;
            }
        }  while ( (SECSuccess == rv) && (counter.len) );

        if (SECSuccess == rv)
        {
            /* allocate room for pointer array and entries */
            /* we want to allocate the array even if there is 0 entry */
            entries = (void**)PORT_ArenaZAlloc(arena, sizeof(void*)*
                                          (totalEntries + 1 ) + /* the extra one is for NULL termination */
                                          subTemplate->size*totalEntries); 

            if (entries)
            {
                entries[totalEntries] = NULL; /* terminate the array */
            }
            else
            {
                PORT_SetError(SEC_ERROR_NO_MEMORY);
                rv = SECFailure;
            }
            if (SECSuccess == rv)
            {
                void* entriesData = (unsigned char*)entries + (unsigned long)(sizeof(void*)*(totalEntries + 1 ));
                /* and fix the pointers in the array */
                PRUint32 entriesIndex = 0;
                for (entriesIndex = 0;entriesIndex<totalEntries;entriesIndex++)
                {
                    entries[entriesIndex] =
                        (char*)entriesData + (subTemplate->size*entriesIndex);
                }
            }
        }
    }

    if (SECSuccess == rv && totalEntries)
    do
    {
        if (!(entryIndex<totalEntries))
        {
            rv = SECFailure;
            break;
        }
        rv = DecodeItem(entries[entryIndex++], subTemplate, &group, arena, PR_TRUE);
    } while ( (SECSuccess == rv) && (group.len) );
    /* we should be at the end of the set by now */    
    /* save the entries where requested */
    memcpy(((char*)dest + templateEntry->offset), &entries, sizeof(void**));

    return rv;
}
Exemplo n.º 7
0
static SECStatus MatchComponentType(const SEC_ASN1Template* templateEntry,
                                    SECItem* item, PRBool* match, void* dest)
{
    unsigned long kind = 0;
    unsigned char tag = 0;

    if ( (!item) || (!item->data && item->len) || (!templateEntry) || (!match) )
    {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (!item->len)
    {
        *match = PR_FALSE;
        return SECSuccess;
    }

    kind = templateEntry->kind;
    tag = *(unsigned char*) item->data;

    if ( ( (kind & SEC_ASN1_INLINE) ||
           (kind & SEC_ASN1_POINTER) ) &&
           (0 == (kind & SEC_ASN1_TAG_MASK) ) )
    {
        /* These cases are special because the template's "kind" does not
           give us the information for the ASN.1 tag of the next item. It can
           only be figured out from the subtemplate. */
        if (!(kind & SEC_ASN1_OPTIONAL))
        {
            /* This is a required component. If there is a type mismatch,
               the decoding of the subtemplate will fail, so assume this
               is a match at the parent level and let it fail later. This
               avoids a redundant check in matching cases */
            *match = PR_TRUE;
            return SECSuccess;
        }
        else
        {
            /* optional component. This is the hard case. Now we need to
               look at the subtemplate to get the expected kind */
            const SEC_ASN1Template* subTemplate = 
                SEC_ASN1GetSubtemplate (templateEntry, dest, PR_FALSE);
            if (!subTemplate)
            {
                PORT_SetError(SEC_ERROR_BAD_TEMPLATE);
                return SECFailure;
            }
            if ( (subTemplate->kind & SEC_ASN1_INLINE) ||
                 (subTemplate->kind & SEC_ASN1_POINTER) )
            {
                /* disallow nesting SEC_ASN1_POINTER and SEC_ASN1_INLINE,
                   otherwise you may get a false positive due to the recursion
                   optimization above that always matches the type if the
                   component is required . Nesting these should never be
                   required, so that no one should miss this ability */
                PORT_SetError(SEC_ERROR_BAD_TEMPLATE);
                return SECFailure;
            }
            return MatchComponentType(subTemplate, item, match,
                                      (void*)((char*)dest + templateEntry->offset));
        }
    }

    if (kind & SEC_ASN1_CHOICE)
    {
        /* we need to check the component's tag against each choice's tag */
        /* XXX it would be nice to save the index of the choice here so that
           DecodeChoice wouldn't have to do this again. However, due to the
           recursivity of MatchComponentType, we don't know if we are in a
           required or optional component, so we can't write anywhere in
           the destination within this function */
        unsigned choiceIndex = 1;
        const SEC_ASN1Template* choiceEntry;
        while ( (choiceEntry = &templateEntry[choiceIndex++]) && (choiceEntry->kind))
        {
            if ( (SECSuccess == MatchComponentType(choiceEntry, item, match,
                                (void*)((char*)dest + choiceEntry->offset))) &&
                 (PR_TRUE == *match) )
            {
                return SECSuccess;
            }
        }
	/* no match, caller must decide if this is BAD DER, or not. */
        *match = PR_FALSE;
        return SECSuccess;
    }

    if (kind & SEC_ASN1_ANY)
    {
        /* SEC_ASN1_ANY always matches */
        *match = PR_TRUE;
        return SECSuccess;
    }

    if ( (0 == ((unsigned char)kind & SEC_ASN1_TAGNUM_MASK)) &&
         (!(kind & SEC_ASN1_EXPLICIT)) &&
         ( ( (kind & SEC_ASN1_SAVE) ||
             (kind & SEC_ASN1_SKIP) ) &&
           (!(kind & SEC_ASN1_OPTIONAL)) 
         )
       )
    {
        /* when saving or skipping a required component,  a type is not
           required in the template. This is for legacy support of
           SEC_ASN1_SAVE and SEC_ASN1_SKIP only. XXX I would like to
           deprecate these usages and always require a type, as this
           disables type checking, and effectively forbids us from
           transparently ignoring optional components we aren't aware of */
        *match = PR_TRUE;
        return SECSuccess;
    }

    /* first, do a class check */
    if ( (tag & SEC_ASN1_CLASS_MASK) !=
         (((unsigned char)kind) & SEC_ASN1_CLASS_MASK) )
    {
#ifdef DEBUG
        /* this is only to help debugging of the decoder in case of problems */
        unsigned char tagclass = tag & SEC_ASN1_CLASS_MASK;
        unsigned char expectedclass = (unsigned char)kind & SEC_ASN1_CLASS_MASK;
        tagclass = tagclass;
        expectedclass = expectedclass;
#endif
        *match = PR_FALSE;
        return SECSuccess;
    }

    /* now do a tag check */
    if ( ((unsigned char)kind & SEC_ASN1_TAGNUM_MASK) !=
         (tag & SEC_ASN1_TAGNUM_MASK))
    {
        *match = PR_FALSE;
        return SECSuccess;
    }

    /* now, do a method check. This depends on the class */
    switch (tag & SEC_ASN1_CLASS_MASK)
    {
    case SEC_ASN1_UNIVERSAL:
        /* For types of the SEC_ASN1_UNIVERSAL class, we know which must be
           primitive or constructed based on the tag */
        switch (tag & SEC_ASN1_TAGNUM_MASK)
        {
        case SEC_ASN1_SEQUENCE:
        case SEC_ASN1_SET:
        case SEC_ASN1_EMBEDDED_PDV:
            /* this component must be a constructed type */
            /* XXX add any new universal constructed type here */
            if (tag & SEC_ASN1_CONSTRUCTED)
            {
                *match = PR_TRUE;
                return SECSuccess;
            }
            break;

        default:
            /* this component must be a primitive type */
            if (! (tag & SEC_ASN1_CONSTRUCTED))
            {
                *match = PR_TRUE;
                return SECSuccess;
            }
            break;
        }
        break;

    default:
        /* for all other classes, we check the method based on the template */
        if ( (unsigned char)(kind & SEC_ASN1_METHOD_MASK) ==
             (tag & SEC_ASN1_METHOD_MASK) )
        {
            *match = PR_TRUE;
            return SECSuccess;
        }
        /* method does not match between template and component */
        break;
    }

    *match = PR_FALSE;
    return SECSuccess;
}