xref: /illumos-gate/usr/src/lib/libkmf/ber_der/common/decode.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 /*
6  * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
7  *
8  * The contents of this file are subject to the Netscape Public License
9  * Version 1.0 (the "NPL"); you may not use this file except in
10  * compliance with the NPL.  You may obtain a copy of the NPL at
11  * http://www.mozilla.org/NPL/
12  *
13  * Software distributed under the NPL is distributed on an "AS IS" basis,
14  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
15  * for the specific language governing rights and limitations under the
16  * NPL.
17  *
18  * The Initial Developer of this code under the NPL is Netscape
19  * Communications Corporation.  Portions created by Netscape are
20  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
21  * Reserved.
22  */
23 
24 /*
25  * Copyright (c) 1990 Regents of the University of Michigan.
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms are permitted
29  * provided that this notice is preserved and that due credit is given
30  * to the University of Michigan at Ann Arbor. The name of the University
31  * may not be used to endorse or promote products derived from this
32  * software without specific prior written permission. This software
33  * is provided ``as is'' without express or implied warranty.
34  */
35 
36 /* decode.c - ber input decoding routines */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 #include <strings.h>
41 #include <sys/types.h>
42 #include <netinet/in.h>
43 #include <inttypes.h>
44 
45 #include <ber_der.h>
46 #include "kmfber_int.h"
47 
48 static void
49 ber_svecfree(char **vals)
50 {
51 	int	i;
52 
53 	if (vals == NULL)
54 		return;
55 	for (i = 0; vals[i] != NULL; i++)
56 		free(vals[i]);
57 	free((char *)vals);
58 }
59 
60 /*
61  * Note: kmfber_get_tag() only uses the ber_end and ber_ptr elements of ber.
62  * If that changes, the kmfber_peek_tag() and/or
63  * kmfkmfber_skip_tag() implementations will need to be changed.
64  */
65 /* return the tag - KMFBER_DEFAULT returned means trouble */
66 static ber_tag_t
67 kmfber_get_tag(BerElement *ber)
68 {
69 	unsigned char	xbyte;
70 	ber_tag_t	tag;
71 	char		*tagp;
72 	int		i;
73 
74 	if (kmfber_read(ber, (char *)&xbyte, 1) != 1)
75 		return (KMFBER_DEFAULT);
76 
77 	if ((xbyte & KMFBER_BIG_TAG_MASK) != KMFBER_BIG_TAG_MASK)
78 		return ((ber_uint_t)xbyte);
79 
80 	tagp = (char *)&tag;
81 	tagp[0] = xbyte;
82 	for (i = 1; i < sizeof (ber_int_t); i++) {
83 		if (kmfber_read(ber, (char *)&xbyte, 1) != 1)
84 			return (KMFBER_DEFAULT);
85 
86 		tagp[i] = xbyte;
87 
88 		if (! (xbyte & KMFBER_MORE_TAG_MASK))
89 			break;
90 	}
91 
92 	/* tag too big! */
93 	if (i == sizeof (ber_int_t))
94 		return (KMFBER_DEFAULT);
95 
96 	/* want leading, not trailing 0's */
97 	return (tag >> (sizeof (ber_int_t)- i - 1));
98 }
99 
100 /*
101  * Note: kmfber_skip_tag() only uses the ber_end and ber_ptr elements of ber.
102  * If that changes, the implementation of kmfber_peek_tag() will need to
103  * be changed.
104  */
105 ber_tag_t
106 kmfber_skip_tag(BerElement *ber, ber_len_t *len)
107 {
108 	ber_tag_t	tag;
109 	unsigned char	lc;
110 	int		noctets, diff;
111 	uint32_t	netlen;
112 
113 	/*
114 	 * Any ber element looks like this: tag length contents.
115 	 * Assuming everything's ok, we return the tag byte (we
116 	 * can assume a single byte), and return the length in len.
117 	 *
118 	 * Assumptions:
119 	 *	1) definite lengths
120 	 *	2) primitive encodings used whenever possible
121 	 */
122 
123 	/*
124 	 * First, we read the tag.
125 	 */
126 
127 	if ((tag = kmfber_get_tag(ber)) == KMFBER_DEFAULT)
128 		return (KMFBER_DEFAULT);
129 
130 	/*
131 	 * Next, read the length.  The first byte contains the length of
132 	 * the length.  If bit 8 is set, the length is the long form,
133 	 * otherwise it's the short form.  We don't allow a length that's
134 	 * greater than what we can hold in an unsigned long.
135 	 */
136 
137 	*len = 0;
138 	netlen = 0;
139 	if (kmfber_read(ber, (char *)&lc, 1) != 1)
140 		return (KMFBER_DEFAULT);
141 	if (lc & 0x80) {
142 		noctets = (lc & 0x7f);
143 		if (noctets > sizeof (ber_uint_t))
144 			return (KMFBER_DEFAULT);
145 		diff = sizeof (ber_int_t) - noctets;
146 		if (kmfber_read(ber, (char *)&netlen + diff, noctets)
147 		    != noctets)
148 			return (KMFBER_DEFAULT);
149 		*len = ntohl(netlen);
150 	} else {
151 		*len = lc;
152 	}
153 
154 	return (tag);
155 }
156 
157 
158 /*
159  * Note: Previously, we passed the "ber" parameter directly to
160  * kmfber_skip_tag(), saving and restoring the ber_ptr element only.
161  * We now take advantage of the fact that the only ber structure
162  * elements touched by kmfber_skip_tag() are ber_end and ber_ptr.
163  * If that changes, this code must change too.
164  */
165 static ber_tag_t
166 kmfber_peek_tag(BerElement *ber, ber_len_t *len)
167 {
168 	BerElement	bercopy;
169 
170 	bercopy.ber_end = ber->ber_end;
171 	bercopy.ber_ptr = ber->ber_ptr;
172 	return (kmfber_skip_tag(&bercopy, len));
173 }
174 
175 static int
176 ber_getnint(BerElement *ber, ber_int_t *num, ber_slen_t len)
177 {
178 	int i;
179 	ber_int_t value;
180 	unsigned char buffer[sizeof (ber_int_t)];
181 	/*
182 	 * The tag and length have already been stripped off.  We should
183 	 * be sitting right before len bytes of 2's complement integer,
184 	 * ready to be read straight into an int.  We may have to sign
185 	 * extend after we read it in.
186 	 */
187 
188 	if (len > sizeof (ber_slen_t))
189 		return (-1);
190 
191 	/* read into the low-order bytes of netnum */
192 	if (kmfber_read(ber, (char *)buffer, len) != len)
193 		return (-1);
194 
195 	/* This sets the required sign extension */
196 	if (len != 0) {
197 		value = 0x80 & buffer[0] ? (-1) : 0;
198 	} else {
199 		value = 0;
200 	}
201 
202 	for (i = 0; i < len; i++)
203 		value = (value << 8) | buffer[i];
204 
205 	*num = value;
206 
207 	return (len);
208 }
209 
210 static ber_tag_t
211 kmfber_get_int(BerElement *ber, ber_int_t *num)
212 {
213 	ber_tag_t	tag;
214 	ber_len_t	len;
215 
216 	if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
217 		return (KMFBER_DEFAULT);
218 
219 	/*
220 	 * len is being demoted to a long here --  possible conversion error
221 	 */
222 
223 	if (ber_getnint(ber, num, (int)len) != (ber_slen_t)len)
224 		return (KMFBER_DEFAULT);
225 	else
226 		return (tag);
227 }
228 
229 static ber_tag_t
230 kmfber_get_stringb(BerElement *ber, char *buf, ber_len_t *len)
231 {
232 	ber_len_t	datalen;
233 	ber_tag_t	tag;
234 #ifdef STR_TRANSLATION
235 	char		*transbuf;
236 #endif /* STR_TRANSLATION */
237 
238 	if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
239 		return (KMFBER_DEFAULT);
240 	if (datalen > (*len - 1))
241 		return (KMFBER_DEFAULT);
242 
243 	/*
244 	 * datalen is being demoted to a long here --  possible conversion error
245 	 */
246 
247 	if (kmfber_read(ber, buf, datalen) != (ber_slen_t)datalen)
248 		return (KMFBER_DEFAULT);
249 
250 	buf[datalen] = '\0';
251 
252 #ifdef STR_TRANSLATION
253 	if (datalen > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS)
254 	    != 0 && ber->ber_decode_translate_proc != NULL) {
255 
256 		transbuf = buf;
257 		++datalen;
258 		if ((*(ber->ber_decode_translate_proc))(&transbuf, &datalen,
259 		    0) != 0) {
260 			return (KMFBER_DEFAULT);
261 		}
262 		if (datalen > *len) {
263 			free(transbuf);
264 			return (KMFBER_DEFAULT);
265 		}
266 		(void) memmove(buf, transbuf, datalen);
267 		free(transbuf);
268 		--datalen;
269 	}
270 #endif /* STR_TRANSLATION */
271 
272 	*len = datalen;
273 	return (tag);
274 }
275 
276 static ber_tag_t
277 kmfber_get_stringa(BerElement *ber, char **buf)
278 {
279 	ber_len_t	datalen;
280 	ber_tag_t	tag;
281 
282 	if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
283 		return (KMFBER_DEFAULT);
284 
285 	if ((*buf = (char *)malloc((size_t)datalen + 1)) == NULL)
286 		return (KMFBER_DEFAULT);
287 
288 	/*
289 	 * datalen is being demoted to a long here --  possible conversion error
290 	 */
291 	if (kmfber_read(ber, *buf, datalen) != (ber_slen_t)datalen)
292 		return (KMFBER_DEFAULT);
293 	(*buf)[datalen] = '\0';
294 
295 	return (tag);
296 }
297 
298 ber_tag_t
299 ber_get_oid(BerElement *ber, struct berval *oid)
300 {
301 	ber_len_t	len;
302 	ber_tag_t	tag;
303 
304 	if ((tag = kmfber_skip_tag(ber, &len)) != 0x06) {
305 		return (KMFBER_DEFAULT);
306 	}
307 
308 	if ((oid->bv_val = (char *)malloc((size_t)len + 1)) == NULL) {
309 		return (KMFBER_DEFAULT);
310 	}
311 	oid->bv_len = len;
312 
313 	if (kmfber_read(ber, oid->bv_val, oid->bv_len) !=
314 	    (ber_slen_t)oid->bv_len)
315 		return (KMFBER_DEFAULT);
316 
317 	return (tag);
318 }
319 
320 ber_tag_t
321 ber_get_bigint(BerElement *ber, struct berval **bv)
322 {
323 	ber_len_t	len;
324 	ber_tag_t	tag;
325 
326 	if ((*bv = (struct berval *)malloc(sizeof (struct berval)))
327 	    == NULL) {
328 		return (KMFBER_DEFAULT);
329 	}
330 	(*bv)->bv_len = 0;
331 	(*bv)->bv_val = NULL;
332 
333 	if ((tag = kmfber_skip_tag(ber, &len)) != BER_INTEGER) {
334 		return (KMFBER_DEFAULT);
335 	}
336 
337 	if (((*bv)->bv_val = (char *)malloc((size_t)len + 1))
338 	    == NULL) {
339 		return (KMFBER_DEFAULT);
340 	}
341 
342 	/*
343 	 * len is being demoted to a long here --  possible conversion error
344 	 */
345 	if (kmfber_read(ber, (*bv)->bv_val, len) != (ber_slen_t)len)
346 		return (KMFBER_DEFAULT);
347 
348 	(*bv)->bv_len = len;
349 
350 	/* If DER encoding, strip leading 0's */
351 	if (ber->ber_options & KMFBER_OPT_USE_DER) {
352 		char *p = (*bv)->bv_val;
353 		while ((*p == 0x00) && ((*bv)->bv_len > 0)) {
354 			p++;
355 			(*bv)->bv_len--;
356 		}
357 		/*
358 		 * Shift the buffer to the beginning of the allocated space
359 		 * so it can be properly freed later.
360 		 */
361 		if ((p > (*bv)->bv_val) && ((*bv)->bv_len > 0))
362 			(void) bcopy(p, (*bv)->bv_val, (*bv)->bv_len);
363 	}
364 
365 	return (tag);
366 }
367 
368 static ber_tag_t
369 kmfber_get_stringal(BerElement *ber, struct berval **bv)
370 {
371 	ber_len_t	len;
372 	ber_tag_t	tag;
373 
374 	if ((*bv = (struct berval *)malloc(sizeof (struct berval)))
375 	    == NULL) {
376 		return (KMFBER_DEFAULT);
377 	}
378 
379 	if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT) {
380 		return (KMFBER_DEFAULT);
381 	}
382 
383 	if (((*bv)->bv_val = (char *)malloc((size_t)len + 1))
384 	    == NULL) {
385 		return (KMFBER_DEFAULT);
386 	}
387 
388 	/*
389 	 * len is being demoted to a long here --  possible conversion error
390 	 */
391 	if (kmfber_read(ber, (*bv)->bv_val, len) != (ber_slen_t)len)
392 		return (KMFBER_DEFAULT);
393 	((*bv)->bv_val)[len] = '\0';
394 	(*bv)->bv_len = len;
395 
396 	return (tag);
397 }
398 
399 static ber_tag_t
400 kmfber_get_bitstringa(BerElement *ber, char **buf, ber_len_t *blen)
401 {
402 	ber_len_t	datalen;
403 	ber_tag_t	tag;
404 	unsigned char	unusedbits;
405 
406 	if ((tag = kmfber_skip_tag(ber, &datalen)) == KMFBER_DEFAULT)
407 		return (KMFBER_DEFAULT);
408 
409 	if ((*buf = (char *)malloc((size_t)datalen - 1)) == NULL)
410 		return (KMFBER_DEFAULT);
411 
412 	if (kmfber_read(ber, (char *)&unusedbits, 1) != 1)
413 		return (KMFBER_DEFAULT);
414 
415 	/* Subtract 1 for the unused bits */
416 	datalen--;
417 
418 	/*
419 	 * datalen is being demoted to a long here --  possible conversion error
420 	 */
421 	if (kmfber_read(ber, *buf, datalen) != (ber_slen_t)datalen)
422 		return (KMFBER_DEFAULT);
423 
424 	*blen = datalen * 8 - unusedbits;
425 	return (tag);
426 }
427 
428 static ber_tag_t
429 kmfber_get_null(BerElement *ber)
430 {
431 	ber_len_t	len;
432 	ber_tag_t tag;
433 
434 	if ((tag = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
435 		return (KMFBER_DEFAULT);
436 
437 	if (len != 0)
438 		return (KMFBER_DEFAULT);
439 
440 	return (tag);
441 }
442 
443 static ber_tag_t
444 kmfber_get_boolean(BerElement *ber, int *boolval)
445 {
446 	ber_int_t	longbool;
447 	int		rc;
448 
449 	rc = kmfber_get_int(ber, &longbool);
450 	*boolval = longbool;
451 
452 	return (rc);
453 }
454 
455 ber_tag_t
456 kmfber_first_element(BerElement *ber, ber_len_t *len, char **last)
457 {
458 	/* skip the sequence header, use the len to mark where to stop */
459 	if (kmfber_skip_tag(ber, len) == KMFBER_DEFAULT) {
460 		return (KMFBER_ERROR);
461 	}
462 
463 	*last = ber->ber_ptr + *len;
464 
465 	if (*last == ber->ber_ptr) {
466 		return (KMFBER_END_OF_SEQORSET);
467 	}
468 
469 	return (kmfber_peek_tag(ber, len));
470 }
471 
472 ber_tag_t
473 kmfber_next_element(BerElement *ber, ber_len_t *len, char *last)
474 {
475 	if (ber->ber_ptr == last) {
476 		return (KMFBER_END_OF_SEQORSET);
477 	}
478 
479 	return (kmfber_peek_tag(ber, len));
480 }
481 
482 void
483 kmfber_bvfree(struct berval *bv)
484 {
485 	if (bv != NULL) {
486 		if (bv->bv_val != NULL) {
487 			free(bv->bv_val);
488 		}
489 		free((char *)bv);
490 	}
491 }
492 
493 void
494 kmfber_bvecfree(struct berval **bv)
495 {
496 	int	i;
497 
498 	if (bv != NULL) {
499 		for (i = 0; bv[i] != NULL; i++) {
500 			kmfber_bvfree(bv[i]);
501 		}
502 		free((char *)bv);
503 	}
504 }
505 
506 /* VARARGS */
507 ber_tag_t
508 kmfber_scanf(BerElement *ber, const char *fmt, ...)
509 {
510 	va_list		ap;
511 	char		*last, *p;
512 	char		*s, **ss, ***sss;
513 	struct berval 	***bv, **bvp, *bval;
514 	int		*i, j;
515 	ber_slen_t	*l;
516 	ber_int_t	rc, tag, *b_int;
517 	ber_tag_t	*t;
518 	ber_len_t	len;
519 	size_t		array_size;
520 
521 	va_start(ap, fmt);
522 
523 	for (rc = 0, p = (char *)fmt; *p && rc != KMFBER_DEFAULT; p++) {
524 	switch (*p) {
525 		case 'a':	/* octet string - allocate storage as needed */
526 		ss = va_arg(ap, char **);
527 		rc = kmfber_get_stringa(ber, ss);
528 		break;
529 
530 		case 'b':	/* boolean */
531 		i = va_arg(ap, int *);
532 		rc = kmfber_get_boolean(ber, i);
533 		break;
534 
535 		case 'D':	/* Object ID */
536 		bval = va_arg(ap, struct berval *);
537 		rc = ber_get_oid(ber, bval);
538 		break;
539 		case 'e':	/* enumerated */
540 		case 'i':	/* int */
541 		b_int = va_arg(ap, ber_int_t *);
542 		rc = kmfber_get_int(ber, b_int);
543 		break;
544 
545 		case 'l':	/* length of next item */
546 		l = va_arg(ap, ber_slen_t *);
547 		rc = kmfber_peek_tag(ber, (ber_len_t *)l);
548 		break;
549 
550 		case 'n':	/* null */
551 		rc = kmfber_get_null(ber);
552 		break;
553 
554 		case 's':	/* octet string - in a buffer */
555 		s = va_arg(ap, char *);
556 		l = va_arg(ap, ber_slen_t *);
557 		rc = kmfber_get_stringb(ber, s, (ber_len_t *)l);
558 		break;
559 
560 		case 'o':	/* octet string in a supplied berval */
561 		bval = va_arg(ap, struct berval *);
562 		(void) kmfber_peek_tag(ber, &bval->bv_len);
563 		rc = kmfber_get_stringa(ber, &bval->bv_val);
564 		break;
565 
566 		case 'I': /* variable length Integer */
567 		/* Treat INTEGER same as an OCTET string, but ignore the tag */
568 		bvp = va_arg(ap, struct berval **);
569 		rc = ber_get_bigint(ber, bvp);
570 		break;
571 		case 'O': /* octet string - allocate & include length */
572 		bvp = va_arg(ap, struct berval **);
573 		rc = kmfber_get_stringal(ber, bvp);
574 		break;
575 
576 		case 'B':	/* bit string - allocate storage as needed */
577 		ss = va_arg(ap, char **);
578 		l = va_arg(ap, ber_slen_t *); /* for length, in bits */
579 		rc = kmfber_get_bitstringa(ber, ss, (ber_len_t *)l);
580 		break;
581 
582 		case 't':	/* tag of next item */
583 		t = va_arg(ap, ber_tag_t *);
584 		*t = kmfber_peek_tag(ber, &len);
585 		rc = (ber_int_t)(*t);
586 		break;
587 
588 		case 'T':	/* skip tag of next item */
589 		t = va_arg(ap, ber_tag_t *);
590 		*t = kmfber_skip_tag(ber, &len);
591 		rc = (ber_int_t)(*t);
592 		break;
593 
594 		case 'v':	/* sequence of strings */
595 		sss = va_arg(ap, char ***);
596 		if (sss == NULL)
597 			break;
598 		*sss = NULL;
599 		j = 0;
600 		array_size = 0;
601 		for (tag = kmfber_first_element(ber, &len, &last);
602 		    (tag != KMFBER_DEFAULT &&
603 		    tag != KMFBER_END_OF_SEQORSET &&
604 		    rc != KMFBER_DEFAULT);
605 		    tag = kmfber_next_element(ber, &len, last)) {
606 			if (*sss == NULL) {
607 				/* Make room for at least 15 strings */
608 				*sss = (char **)malloc(16 * sizeof (char *));
609 				array_size = 16;
610 			} else {
611 				if ((size_t)(j+2) > array_size) {
612 					/* We'v overflowed our buffer */
613 					*sss = (char **)realloc(*sss,
614 					    (array_size * 2) * sizeof (char *));
615 					array_size = array_size * 2;
616 				}
617 			}
618 			rc = kmfber_get_stringa(ber, &((*sss)[j]));
619 			j++;
620 		}
621 		if (rc != KMFBER_DEFAULT && tag != KMFBER_END_OF_SEQORSET) {
622 			rc = KMFBER_DEFAULT;
623 		}
624 		if (j > 0)
625 			(*sss)[j] = NULL;
626 		break;
627 
628 		case 'V':	/* sequence of strings + lengths */
629 		bv = va_arg(ap, struct berval ***);
630 		*bv = NULL;
631 		j = 0;
632 		for (tag = kmfber_first_element(ber, &len, &last);
633 		    (tag != KMFBER_DEFAULT &&
634 		    tag != KMFBER_END_OF_SEQORSET &&
635 		    rc != KMFBER_DEFAULT);
636 		    tag = kmfber_next_element(ber, &len, last)) {
637 			if (*bv == NULL) {
638 				*bv = (struct berval **)malloc(
639 				    2 * sizeof (struct berval *));
640 			} else {
641 				*bv = (struct berval **)realloc(*bv,
642 				    (j + 2) * sizeof (struct berval *));
643 			}
644 			rc = kmfber_get_stringal(ber, &((*bv)[j]));
645 			j++;
646 		}
647 		if (rc != KMFBER_DEFAULT &&
648 		    tag != KMFBER_END_OF_SEQORSET) {
649 			rc = KMFBER_DEFAULT;
650 		}
651 		if (j > 0)
652 			(*bv)[j] = NULL;
653 		break;
654 
655 		case 'x':	/* skip the next element - whatever it is */
656 		if ((rc = kmfber_skip_tag(ber, &len)) == KMFBER_DEFAULT)
657 			break;
658 		ber->ber_ptr += len;
659 		break;
660 
661 		case '{':	/* begin sequence */
662 		case '[':	/* begin set */
663 		if (*(p + 1) != 'v' && *(p + 1) != 'V')
664 			rc = kmfber_skip_tag(ber, &len);
665 		break;
666 
667 		case '}':	/* end sequence */
668 		case ']':	/* end set */
669 		break;
670 
671 		default:
672 		rc = KMFBER_DEFAULT;
673 		break;
674 		}
675 	}
676 
677 
678 	va_end(ap);
679 	if (rc == KMFBER_DEFAULT) {
680 	va_start(ap, fmt);
681 	for (p--; fmt < p && *fmt; fmt++) {
682 		switch (*fmt) {
683 		case 'a':	/* octet string - allocate storage as needed */
684 			ss = va_arg(ap, char **);
685 			if (ss != NULL && *ss != NULL) {
686 				free(*ss);
687 				*ss = NULL;
688 			}
689 			break;
690 
691 		case 'b':	/* boolean */
692 			i = va_arg(ap, int *);
693 			break;
694 
695 		case 'e':	/* enumerated */
696 		case 'i':	/* int */
697 			l = va_arg(ap, ber_slen_t *);
698 			break;
699 
700 		case 'l':	/* length of next item */
701 			l = va_arg(ap, ber_slen_t *);
702 			break;
703 
704 		case 'n':	/* null */
705 			break;
706 
707 		case 's':	/* octet string - in a buffer */
708 			s = va_arg(ap, char *);
709 			l = va_arg(ap, ber_slen_t *);
710 			break;
711 
712 		case 'o':	/* octet string in a supplied berval */
713 			bval = va_arg(ap, struct berval *);
714 			if (bval->bv_val) free(bval->bv_val);
715 			(void) memset(bval, 0, sizeof (struct berval));
716 			break;
717 
718 		case 'O':	/* octet string - allocate & include length */
719 			bvp = va_arg(ap, struct berval **);
720 			kmfber_bvfree(*bvp);
721 			bvp = NULL;
722 			break;
723 
724 		case 'B':	/* bit string - allocate storage as needed */
725 			ss = va_arg(ap, char **);
726 			l = va_arg(ap, ber_slen_t *); /* for length, in bits */
727 			if (ss != NULL && *ss != NULL) {
728 				free(*ss);
729 				*ss = NULL;
730 			}
731 			break;
732 
733 		case 't':	/* tag of next item */
734 			t = va_arg(ap, ber_tag_t *);
735 			break;
736 		case 'T':	/* skip tag of next item */
737 			t = va_arg(ap, ber_tag_t *);
738 			break;
739 
740 		case 'v':	/* sequence of strings */
741 			sss = va_arg(ap, char ***);
742 			if (sss != NULL && *sss != NULL) {
743 				ber_svecfree(*sss);
744 				*sss = NULL;
745 			}
746 			break;
747 
748 		case 'V':	/* sequence of strings + lengths */
749 			bv = va_arg(ap, struct berval ***);
750 			kmfber_bvecfree(*bv);
751 			*bv = NULL;
752 			break;
753 
754 		case 'x':	/* skip the next element - whatever it is */
755 			break;
756 
757 		case '{':	/* begin sequence */
758 		case '[':	/* begin set */
759 			break;
760 
761 		case '}':	/* end sequence */
762 		case ']':	/* end set */
763 			break;
764 
765 		default:
766 			break;
767 		}
768 	} /* for */
769 	va_end(ap);
770 	} /* if */
771 
772 	return (rc);
773 }
774 
775 struct berval *
776 kmfber_bvdup(const struct berval *bv)
777 {
778 	struct berval	*new;
779 
780 	if ((new = (struct berval *)malloc(sizeof (struct berval)))
781 	    == NULL) {
782 		return (NULL);
783 	}
784 	if (bv->bv_val == NULL) {
785 		new->bv_val = NULL;
786 		new->bv_len = 0;
787 	} else {
788 		if ((new->bv_val = (char *)malloc(bv->bv_len + 1))
789 		    == NULL) {
790 			return (NULL);
791 		}
792 		(void) memmove(new->bv_val, bv->bv_val, (size_t)bv->bv_len);
793 		new->bv_val[bv->bv_len] = '\0';
794 		new->bv_len = bv->bv_len;
795 	}
796 
797 	return (new);
798 }
799