gecko/security/nss/cmd/lib/berparse.c
2008-06-06 08:40:11 -04:00

408 lines
10 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#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 {
PRArenaPool *his;
PRArenaPool *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(PRArenaPool *arena, PRBool derOnly)
{
BERParse *h;
PRArenaPool *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)
{
PRArenaPool *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;
}
}