/* 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/. */ #include "secutil.h" typedef enum { tagDone, lengthDone, leafDone, compositeDone, notDone, parseError, parseComplete } ParseState; typedef unsigned char Byte; typedef void (*ParseProc)(BERParse *h, unsigned char **buf, int *len); typedef struct { SECArb arb; int pos; /* length from global start to item start */ SECArb *parent; } ParseStackElem; struct BERParseStr { PLArenaPool *his; PLArenaPool *mine; ParseProc proc; int stackDepth; ParseStackElem *stackPtr; ParseStackElem *stack; int pending; /* bytes remaining to complete this part */ int pos; /* running length of consumed characters */ ParseState state; PRBool keepLeaves; PRBool derOnly; BERFilterProc filter; void *filterArg; BERNotifyProc before; void *beforeArg; BERNotifyProc after; void *afterArg; }; #define UNKNOWN -1 static unsigned char NextChar(BERParse *h, unsigned char **buf, int *len) { unsigned char c = *(*buf)++; (*len)--; h->pos++; if (h->filter) (*h->filter)(h->filterArg, &c, 1); return c; } static void ParseTag(BERParse *h, unsigned char **buf, int *len) { SECArb *arb = &(h->stackPtr->arb); arb->tag = NextChar(h, buf, len); PORT_Assert(h->state == notDone); /* * NOTE: This does not handle the high-tag-number form */ if ((arb->tag & DER_HIGH_TAG_NUMBER) == DER_HIGH_TAG_NUMBER) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return; } h->pending = UNKNOWN; arb->length = UNKNOWN; if (arb->tag & DER_CONSTRUCTED) { arb->body.cons.numSubs = 0; arb->body.cons.subs = NULL; } else { arb->body.item.len = UNKNOWN; arb->body.item.data = NULL; } h->state = tagDone; } static void ParseLength(BERParse *h, unsigned char **buf, int *len) { Byte b; SECArb *arb = &(h->stackPtr->arb); PORT_Assert(h->state == notDone); if (h->pending == UNKNOWN) { b = NextChar(h, buf, len); if ((b & 0x80) == 0) { /* short form */ arb->length = b; /* * if the tag and the length are both zero bytes, then this * should be the marker showing end of list for the * indefinite length composite */ if (arb->length == 0 && arb->tag == 0) h->state = compositeDone; else h->state = lengthDone; return; } h->pending = b & 0x7f; /* 0 implies this is an indefinite length */ if (h->pending > 4) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return; } arb->length = 0; } while ((*len > 0) && (h->pending > 0)) { b = NextChar(h, buf, len); arb->length = (arb->length << 8) + b; h->pending--; } if (h->pending == 0) { if (h->derOnly && (arb->length == 0)) h->state = parseError; else h->state = lengthDone; } return; } static void ParseLeaf(BERParse *h, unsigned char **buf, int *len) { int count; SECArb *arb = &(h->stackPtr->arb); PORT_Assert(h->state == notDone); PORT_Assert(h->pending >= 0); if (*len < h->pending) count = *len; else count = h->pending; if (h->keepLeaves) memcpy(arb->body.item.data + arb->body.item.len, *buf, count); if (h->filter) (*h->filter)(h->filterArg, *buf, count); *buf += count; *len -= count; arb->body.item.len += count; h->pending -= count; h->pos += count; if (h->pending == 0) { h->state = leafDone; } return; } static void CreateArbNode(BERParse *h) { SECArb *arb = PORT_ArenaAlloc(h->his, sizeof(SECArb)); *arb = h->stackPtr->arb; /* * Special case closing the root */ if (h->stackPtr == h->stack) { PORT_Assert(arb->tag & DER_CONSTRUCTED); h->state = parseComplete; } else { SECArb *parent = h->stackPtr->parent; parent->body.cons.subs = DS_ArenaGrow( h->his, parent->body.cons.subs, (parent->body.cons.numSubs) * sizeof(SECArb *), (parent->body.cons.numSubs + 1) * sizeof(SECArb *)); parent->body.cons.subs[parent->body.cons.numSubs] = arb; parent->body.cons.numSubs++; h->proc = ParseTag; h->state = notDone; h->pending = UNKNOWN; } if (h->after) (*h->after)(h->afterArg, arb, h->stackPtr - h->stack, PR_FALSE); } SECStatus BER_ParseSome(BERParse *h, unsigned char *buf, int len) { if (h->state == parseError) return PR_TRUE; while (len) { (*h->proc)(h, &buf, &len); if (h->state == parseComplete) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return PR_TRUE; } if (h->state == parseError) return PR_TRUE; PORT_Assert(h->state != parseComplete); if (h->state <= compositeDone) { if (h->proc == ParseTag) { PORT_Assert(h->state == tagDone); h->proc = ParseLength; h->state = notDone; } else if (h->proc == ParseLength) { SECArb *arb = &(h->stackPtr->arb); PORT_Assert(h->state == lengthDone || h->state == compositeDone); if (h->before) (*h->before)(h->beforeArg, arb, h->stackPtr - h->stack, PR_TRUE); /* * Check to see if this is the end of an indefinite * length composite */ if (h->state == compositeDone) { SECArb *parent = h->stackPtr->parent; PORT_Assert(parent); PORT_Assert(parent->tag & DER_CONSTRUCTED); if (parent->length != 0) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return PR_TRUE; } /* * NOTE: This does not check for an indefinite length * composite being contained inside a definite length * composite. It is not clear that is legal. */ h->stackPtr--; CreateArbNode(h); } else { h->stackPtr->pos = h->pos; if (arb->tag & DER_CONSTRUCTED) { SECArb *parent; /* * Make sure there is room on the stack before we * stick anything else there. */ PORT_Assert(h->stackPtr - h->stack < h->stackDepth); if (h->stackPtr - h->stack == h->stackDepth - 1) { int newDepth = h->stackDepth * 2; h->stack = DS_ArenaGrow(h->mine, h->stack, sizeof(ParseStackElem) * h->stackDepth, sizeof(ParseStackElem) * newDepth); h->stackPtr = h->stack + h->stackDepth + 1; h->stackDepth = newDepth; } parent = &(h->stackPtr->arb); h->stackPtr++; h->stackPtr->parent = parent; h->proc = ParseTag; h->state = notDone; h->pending = UNKNOWN; } else { if (arb->length < 0) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return PR_TRUE; } arb->body.item.len = 0; if (arb->length > 0 && h->keepLeaves) { arb->body.item.data = PORT_ArenaAlloc(h->his, arb->length); } else { arb->body.item.data = NULL; } h->proc = ParseLeaf; h->state = notDone; h->pending = arb->length; } } } else { ParseStackElem *parent; PORT_Assert(h->state = leafDone); PORT_Assert(h->proc == ParseLeaf); for (;;) { CreateArbNode(h); if (h->stackPtr == h->stack) break; parent = (h->stackPtr - 1); PORT_Assert(parent->arb.tag & DER_CONSTRUCTED); if (parent->arb.length == 0) /* need explicit end */ break; if (parent->pos + parent->arb.length > h->pos) break; if (parent->pos + parent->arb.length < h->pos) { PORT_SetError(SEC_ERROR_BAD_DER); h->state = parseError; return PR_TRUE; } h->stackPtr = parent; } } } } return PR_FALSE; } BERParse * BER_ParseInit(PLArenaPool *arena, PRBool derOnly) { BERParse *h; PLArenaPool *temp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (temp == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return NULL; } h = PORT_ArenaAlloc(temp, sizeof(BERParse)); if (h == NULL) { PORT_FreeArena(temp, PR_FALSE); PORT_SetError(SEC_ERROR_NO_MEMORY); return NULL; } h->his = arena; h->mine = temp; h->proc = ParseTag; h->stackDepth = 20; h->stack = PORT_ArenaZAlloc(h->mine, sizeof(ParseStackElem) * h->stackDepth); h->stackPtr = h->stack; h->state = notDone; h->pos = 0; h->keepLeaves = PR_TRUE; h->before = NULL; h->after = NULL; h->filter = NULL; h->derOnly = derOnly; return h; } SECArb * BER_ParseFini(BERParse *h) { PLArenaPool *myArena = h->mine; SECArb *arb; if (h->state != parseComplete) { arb = NULL; } else { arb = PORT_ArenaAlloc(h->his, sizeof(SECArb)); *arb = h->stackPtr->arb; } PORT_FreeArena(myArena, PR_FALSE); return arb; } void BER_SetFilter(BERParse *h, BERFilterProc proc, void *instance) { h->filter = proc; h->filterArg = instance; } void BER_SetLeafStorage(BERParse *h, PRBool keep) { h->keepLeaves = keep; } void BER_SetNotifyProc(BERParse *h, BERNotifyProc proc, void *instance, PRBool beforeData) { if (beforeData) { h->before = proc; h->beforeArg = instance; } else { h->after = proc; h->afterArg = instance; } }