Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / cmd / lib / berparse.c
blob930d0b7c1335526cdedfde2fc0fe2548a3946caf
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
36 #include "secutil.h"
38 typedef enum {
39 tagDone, lengthDone, leafDone, compositeDone,
40 notDone,
41 parseError, parseComplete
42 } ParseState;
44 typedef unsigned char Byte;
45 typedef void (*ParseProc)(BERParse *h, unsigned char **buf, int *len);
46 typedef struct {
47 SECArb arb;
48 int pos; /* length from global start to item start */
49 SECArb *parent;
50 } ParseStackElem;
52 struct BERParseStr {
53 PRArenaPool *his;
54 PRArenaPool *mine;
55 ParseProc proc;
56 int stackDepth;
57 ParseStackElem *stackPtr;
58 ParseStackElem *stack;
59 int pending; /* bytes remaining to complete this part */
60 int pos; /* running length of consumed characters */
61 ParseState state;
62 PRBool keepLeaves;
63 PRBool derOnly;
64 BERFilterProc filter;
65 void *filterArg;
66 BERNotifyProc before;
67 void *beforeArg;
68 BERNotifyProc after;
69 void *afterArg;
72 #define UNKNOWN -1
74 static unsigned char NextChar(BERParse *h, unsigned char **buf, int *len)
76 unsigned char c = *(*buf)++;
77 (*len)--;
78 h->pos++;
79 if (h->filter)
80 (*h->filter)(h->filterArg, &c, 1);
81 return c;
84 static void ParseTag(BERParse *h, unsigned char **buf, int *len)
86 SECArb* arb = &(h->stackPtr->arb);
87 arb->tag = NextChar(h, buf, len);
89 PORT_Assert(h->state == notDone);
92 * NOTE: This does not handle the high-tag-number form
94 if ((arb->tag & DER_HIGH_TAG_NUMBER) == DER_HIGH_TAG_NUMBER) {
95 PORT_SetError(SEC_ERROR_BAD_DER);
96 h->state = parseError;
97 return;
100 h->pending = UNKNOWN;
101 arb->length = UNKNOWN;
102 if (arb->tag & DER_CONSTRUCTED) {
103 arb->body.cons.numSubs = 0;
104 arb->body.cons.subs = NULL;
105 } else {
106 arb->body.item.len = UNKNOWN;
107 arb->body.item.data = NULL;
110 h->state = tagDone;
113 static void ParseLength(BERParse *h, unsigned char **buf, int *len)
115 Byte b;
116 SECArb *arb = &(h->stackPtr->arb);
118 PORT_Assert(h->state == notDone);
120 if (h->pending == UNKNOWN) {
121 b = NextChar(h, buf, len);
122 if ((b & 0x80) == 0) { /* short form */
123 arb->length = b;
125 * if the tag and the length are both zero bytes, then this
126 * should be the marker showing end of list for the
127 * indefinite length composite
129 if (arb->length == 0 && arb->tag == 0)
130 h->state = compositeDone;
131 else
132 h->state = lengthDone;
133 return;
136 h->pending = b & 0x7f;
137 /* 0 implies this is an indefinite length */
138 if (h->pending > 4) {
139 PORT_SetError(SEC_ERROR_BAD_DER);
140 h->state = parseError;
141 return;
143 arb->length = 0;
146 while ((*len > 0) && (h->pending > 0)) {
147 b = NextChar(h, buf, len);
148 arb->length = (arb->length << 8) + b;
149 h->pending--;
151 if (h->pending == 0) {
152 if (h->derOnly && (arb->length == 0))
153 h->state = parseError;
154 else
155 h->state = lengthDone;
157 return;
160 static void ParseLeaf(BERParse *h, unsigned char **buf, int *len)
162 int count;
163 SECArb *arb = &(h->stackPtr->arb);
165 PORT_Assert(h->state == notDone);
166 PORT_Assert(h->pending >= 0);
168 if (*len < h->pending)
169 count = *len;
170 else
171 count = h->pending;
173 if (h->keepLeaves)
174 memcpy(arb->body.item.data + arb->body.item.len, *buf, count);
175 if (h->filter)
176 (*h->filter)(h->filterArg, *buf, count);
177 *buf += count;
178 *len -= count;
179 arb->body.item.len += count;
180 h->pending -= count;
181 h->pos += count;
182 if (h->pending == 0) {
183 h->state = leafDone;
185 return;
188 static void CreateArbNode(BERParse *h)
190 SECArb *arb = PORT_ArenaAlloc(h->his, sizeof(SECArb));
192 *arb = h->stackPtr->arb;
195 * Special case closing the root
197 if (h->stackPtr == h->stack) {
198 PORT_Assert(arb->tag & DER_CONSTRUCTED);
199 h->state = parseComplete;
200 } else {
201 SECArb *parent = h->stackPtr->parent;
202 parent->body.cons.subs = DS_ArenaGrow(
203 h->his, parent->body.cons.subs,
204 (parent->body.cons.numSubs) * sizeof(SECArb*),
205 (parent->body.cons.numSubs + 1) * sizeof(SECArb*));
206 parent->body.cons.subs[parent->body.cons.numSubs] = arb;
207 parent->body.cons.numSubs++;
208 h->proc = ParseTag;
209 h->state = notDone;
210 h->pending = UNKNOWN;
212 if (h->after)
213 (*h->after)(h->afterArg, arb, h->stackPtr - h->stack, PR_FALSE);
216 SECStatus BER_ParseSome(BERParse *h, unsigned char *buf, int len)
218 if (h->state == parseError) return PR_TRUE;
220 while (len) {
221 (*h->proc)(h, &buf, &len);
222 if (h->state == parseComplete) {
223 PORT_SetError(SEC_ERROR_BAD_DER);
224 h->state = parseError;
225 return PR_TRUE;
227 if (h->state == parseError) return PR_TRUE;
228 PORT_Assert(h->state != parseComplete);
230 if (h->state <= compositeDone) {
231 if (h->proc == ParseTag) {
232 PORT_Assert(h->state == tagDone);
233 h->proc = ParseLength;
234 h->state = notDone;
235 } else if (h->proc == ParseLength) {
236 SECArb *arb = &(h->stackPtr->arb);
237 PORT_Assert(h->state == lengthDone || h->state == compositeDone);
239 if (h->before)
240 (*h->before)(h->beforeArg, arb,
241 h->stackPtr - h->stack, PR_TRUE);
244 * Check to see if this is the end of an indefinite
245 * length composite
247 if (h->state == compositeDone) {
248 SECArb *parent = h->stackPtr->parent;
249 PORT_Assert(parent);
250 PORT_Assert(parent->tag & DER_CONSTRUCTED);
251 if (parent->length != 0) {
252 PORT_SetError(SEC_ERROR_BAD_DER);
253 h->state = parseError;
254 return PR_TRUE;
257 * NOTE: This does not check for an indefinite length
258 * composite being contained inside a definite length
259 * composite. It is not clear that is legal.
261 h->stackPtr--;
262 CreateArbNode(h);
263 } else {
264 h->stackPtr->pos = h->pos;
267 if (arb->tag & DER_CONSTRUCTED) {
268 SECArb *parent;
270 * Make sure there is room on the stack before we
271 * stick anything else there.
273 PORT_Assert(h->stackPtr - h->stack < h->stackDepth);
274 if (h->stackPtr - h->stack == h->stackDepth - 1) {
275 int newDepth = h->stackDepth * 2;
276 h->stack = DS_ArenaGrow(h->mine, h->stack,
277 sizeof(ParseStackElem) * h->stackDepth,
278 sizeof(ParseStackElem) * newDepth);
279 h->stackPtr = h->stack + h->stackDepth + 1;
280 h->stackDepth = newDepth;
282 parent = &(h->stackPtr->arb);
283 h->stackPtr++;
284 h->stackPtr->parent = parent;
285 h->proc = ParseTag;
286 h->state = notDone;
287 h->pending = UNKNOWN;
288 } else {
289 if (arb->length < 0) {
290 PORT_SetError(SEC_ERROR_BAD_DER);
291 h->state = parseError;
292 return PR_TRUE;
294 arb->body.item.len = 0;
295 if (arb->length > 0 && h->keepLeaves) {
296 arb->body.item.data =
297 PORT_ArenaAlloc(h->his, arb->length);
298 } else {
299 arb->body.item.data = NULL;
301 h->proc = ParseLeaf;
302 h->state = notDone;
303 h->pending = arb->length;
306 } else {
307 ParseStackElem *parent;
308 PORT_Assert(h->state = leafDone);
309 PORT_Assert(h->proc == ParseLeaf);
311 for (;;) {
312 CreateArbNode(h);
313 if (h->stackPtr == h->stack)
314 break;
315 parent = (h->stackPtr - 1);
316 PORT_Assert(parent->arb.tag & DER_CONSTRUCTED);
317 if (parent->arb.length == 0) /* need explicit end */
318 break;
319 if (parent->pos + parent->arb.length > h->pos)
320 break;
321 if (parent->pos + parent->arb.length < h->pos) {
322 PORT_SetError(SEC_ERROR_BAD_DER);
323 h->state = parseError;
324 return PR_TRUE;
326 h->stackPtr = parent;
332 return PR_FALSE;
334 BERParse *BER_ParseInit(PRArenaPool *arena, PRBool derOnly)
336 BERParse *h;
337 PRArenaPool *temp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
338 if (temp == NULL) {
339 PORT_SetError(SEC_ERROR_NO_MEMORY);
340 return NULL;
342 h = PORT_ArenaAlloc(temp, sizeof(BERParse));
343 if (h == NULL) {
344 PORT_FreeArena(temp, PR_FALSE);
345 PORT_SetError(SEC_ERROR_NO_MEMORY);
346 return NULL;
348 h->his = arena;
349 h->mine = temp;
350 h->proc = ParseTag;
351 h->stackDepth = 20;
352 h->stack = PORT_ArenaZAlloc(h->mine,
353 sizeof(ParseStackElem) * h->stackDepth);
354 h->stackPtr = h->stack;
355 h->state = notDone;
356 h->pos = 0;
357 h->keepLeaves = PR_TRUE;
358 h->before = NULL;
359 h->after = NULL;
360 h->filter = NULL;
361 h->derOnly = derOnly;
362 return h;
365 SECArb *BER_ParseFini(BERParse *h)
367 PRArenaPool *myArena = h->mine;
368 SECArb *arb;
370 if (h->state != parseComplete) {
371 arb = NULL;
372 } else {
373 arb = PORT_ArenaAlloc(h->his, sizeof(SECArb));
374 *arb = h->stackPtr->arb;
377 PORT_FreeArena(myArena, PR_FALSE);
379 return arb;
383 void BER_SetFilter(BERParse *h, BERFilterProc proc, void *instance)
385 h->filter = proc;
386 h->filterArg = instance;
389 void BER_SetLeafStorage(BERParse *h, PRBool keep)
391 h->keepLeaves = keep;
394 void BER_SetNotifyProc(BERParse *h, BERNotifyProc proc, void *instance,
395 PRBool beforeData)
397 if (beforeData) {
398 h->before = proc;
399 h->beforeArg = instance;
400 } else {
401 h->after = proc;
402 h->afterArg = instance;