mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
376 lines
8.9 KiB
C
376 lines
8.9 KiB
C
/* 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;
|
|
}
|
|
}
|
|
|
|
|
|
|