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