xref: /titanic_50/usr/src/lib/libkmf/ber_der/common/encode.c (revision 292f4c1c373bd6e2c3c0b6e199a87392f265291f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
24  *
25  * The contents of this file are subject to the Netscape Public License
26  * Version 1.0 (the "NPL"); you may not use this file except in
27  * compliance with the NPL.  You may obtain a copy of the NPL at
28  * http://www.mozilla.org/NPL/
29  *
30  * Software distributed under the NPL is distributed on an "AS IS" basis,
31  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
32  * for the specific language governing rights and limitations under the
33  * NPL.
34  *
35  * The Initial Developer of this code under the NPL is Netscape
36  * Communications Corporation.  Portions created by Netscape are
37  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
38  * Reserved.
39  */
40 
41 /*
42  * Copyright (c) 1990 Regents of the University of Michigan.
43  * All rights reserved.
44  *
45  * Redistribution and use in source and binary forms are permitted
46  * provided that this notice is preserved and that due credit is given
47  * to the University of Michigan at Ann Arbor. The name of the University
48  * may not be used to endorse or promote products derived from this
49  * software without specific prior written permission. This software
50  * is provided ``as is'' without express or implied warranty.
51  */
52 
53 /*
54  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
55  * Use is subject to license terms.
56  */
57 
58 #pragma ident	"%Z%%M%	%I%	%E% SMI"
59 
60 #include <sys/types.h>
61 #include <netinet/in.h>
62 #include <inttypes.h>
63 
64 #include <ber_der.h>
65 #include "kmfber_int.h"
66 
67 /* the following constants are used in kmfber_calc_lenlen */
68 
69 #define	LENMASK1	0xFF
70 #define	LENMASK2 	0xFFFF
71 #define	LENMASK3	0xFFFFFF
72 #define	LENMASK4	0xFFFFFFFF
73 #define	_MASK		0x80
74 
75 int
76 kmfber_calc_taglen(ber_tag_t tag)
77 {
78 	int		i;
79 	ber_int_t	mask;
80 
81 	/* find the first non-all-zero byte in the tag */
82 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
83 		mask = (LENMASK3 << (i * 8));
84 		/* not all zero */
85 		if (tag & mask)
86 			break;
87 	}
88 
89 	return (i + 1);
90 }
91 
92 static int
93 ber_put_tag(BerElement	*ber, ber_tag_t tag, int nosos)
94 {
95 	ber_int_t	taglen;
96 	ber_tag_t	ntag;
97 
98 	taglen = kmfber_calc_taglen(tag);
99 
100 	ntag = htonl(tag);
101 
102 	return (kmfber_write(ber,
103 		((char *) &ntag) + sizeof (ber_int_t) - taglen,
104 		taglen, nosos));
105 }
106 
107 int
108 kmfber_calc_lenlen(ber_int_t len)
109 {
110 	/*
111 	 * short len if it's less than 128 - one byte giving the len,
112 	 * with bit 8 0.
113 	 */
114 
115 	if (len <= 0x7F)
116 		return (1);
117 
118 	/*
119 	 * long len otherwise - one byte with bit 8 set, giving the
120 	 * length of the length, followed by the length itself.
121 	 */
122 
123 	if (len <= LENMASK1)
124 		return (2);
125 	if (len <= LENMASK2)
126 		return (3);
127 	if (len <= LENMASK3)
128 		return (4);
129 
130 	return (5);
131 }
132 
133 int
134 kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
135 {
136 	int		i;
137 	char		lenlen;
138 	ber_int_t	mask, netlen;
139 
140 	/*
141 	 * short len if it's less than 128 - one byte giving the len,
142 	 * with bit 8 0.
143 	 */
144 	if (len <= 127) {
145 		netlen = htonl(len);
146 		return (kmfber_write(ber,
147 			(char *)&netlen + sizeof (ber_int_t) - 1,
148 			1, nosos));
149 	}
150 
151 	/*
152 	 * long len otherwise - one byte with bit 8 set, giving the
153 	 * length of the length, followed by the length itself.
154 	 */
155 
156 	/* find the first non-all-zero byte */
157 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
158 		mask = (LENMASK1 << (i * 8));
159 		/* not all zero */
160 		if (len & mask)
161 			break;
162 	}
163 	lenlen = ++i;
164 	if (lenlen > 4)
165 		return (-1);
166 	lenlen |= 0x80;
167 
168 	/* write the length of the length */
169 	if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
170 		return (-1);
171 
172 	/* write the length itself */
173 	netlen = htonl(len);
174 	if (kmfber_write(ber,
175 		(char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
176 		return (-1);
177 
178 	return (i + 1);
179 }
180 
181 static int
182 ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
183 {
184 	int		i, sign;
185 	ber_int_t	len, lenlen, taglen, netnum, mask;
186 
187 	sign = (num < 0);
188 
189 	/*
190 	 * high bit is set - look for first non-all-one byte
191 	 * high bit is clear - look for first non-all-zero byte
192 	 */
193 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
194 		mask = (LENMASK1 << (i * 8));
195 
196 		if (sign) {
197 			/* not all ones */
198 			if ((num & mask) != mask)
199 				break;
200 		} else {
201 			/* not all zero */
202 			if (num & mask)
203 				break;
204 		}
205 	}
206 
207 	/*
208 	 * we now have the "leading byte".  if the high bit on this
209 	 * byte matches the sign bit, we need to "back up" a byte.
210 	 */
211 	mask = (num & (_MASK << (i * 8)));
212 	if ((mask && !sign) || (sign && !mask))
213 		i++;
214 
215 	len = i + 1;
216 
217 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
218 		return (-1);
219 
220 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
221 		return (-1);
222 	i++;
223 	netnum = htonl(num);
224 	if (kmfber_write(ber,
225 		(char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
226 		/* length of tag + length + contents */
227 		return (taglen + lenlen + i);
228 
229 	return (-1);
230 }
231 
232 static int
233 kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
234 {
235 	if (tag == KMFBER_DEFAULT)
236 		tag = BER_ENUMERATED;
237 
238 	return (ber_put_int_or_enum(ber, num, tag));
239 }
240 
241 int
242 ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
243 {
244 	if (tag == KMFBER_DEFAULT)
245 		tag = BER_INTEGER;
246 
247 	return (ber_put_int_or_enum(ber, num, tag));
248 }
249 
250 int
251 ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
252 {
253 	ber_int_t taglen, lenlen, rc, len;
254 
255 	if (tag == KMFBER_DEFAULT)
256 		tag = 0x06; 	/* TODO: Add new OID constant to header */
257 
258 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
259 		return (-1);
260 
261 	len = (ber_int_t)oid->bv_len;
262 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
263 		kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
264 		(ber_int_t)oid->bv_len) {
265 		rc = -1;
266 	} else {
267 		/* return length of tag + length + contents */
268 		rc = taglen + lenlen + oid->bv_len;
269 	}
270 	return (rc);
271 }
272 
273 int
274 ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
275 	ber_len_t len)
276 {
277 	ber_int_t taglen, lenlen, ilen, rc;
278 
279 	if (tag == KMFBER_DEFAULT)
280 		tag = BER_INTEGER;
281 
282 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
283 		return (-1);
284 
285 	ilen = (ber_int_t)len;
286 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
287 		kmfber_write(ber, data, len, 0) != (ber_int_t)len) {
288 		rc = -1;
289 	} else {
290 		/* return length of tag + length + contents */
291 		rc = taglen + lenlen + len;
292 	}
293 	return (rc);
294 }
295 
296 static int
297 kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
298 	ber_tag_t tag)
299 {
300 	ber_int_t	taglen, lenlen, ilen, rc;
301 #ifdef STR_TRANSLATION
302 	int	free_str;
303 #endif /* STR_TRANSLATION */
304 
305 	if (tag == KMFBER_DEFAULT)
306 		tag = BER_OCTET_STRING;
307 
308 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
309 		return (-1);
310 
311 #ifdef STR_TRANSLATION
312 	if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
313 	    ber->ber_encode_translate_proc != NULL) {
314 		if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
315 		    != 0) {
316 			return (-1);
317 		}
318 		free_str = 1;
319 	} else {
320 		free_str = 0;
321 	}
322 #endif /* STR_TRANSLATION */
323 
324 	/*
325 	 *  Note:  below is a spot where we limit ber_write
326 	 *	to signed long (instead of unsigned long)
327 	 */
328 	ilen = (ber_int_t)len;
329 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
330 		kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
331 		rc = -1;
332 	} else {
333 		/* return length of tag + length + contents */
334 		rc = taglen + lenlen + len;
335 	}
336 
337 #ifdef STR_TRANSLATION
338 	if (free_str) {
339 		free(str);
340 	}
341 #endif /* STR_TRANSLATION */
342 
343 	return (rc);
344 }
345 
346 static int
347 kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
348 {
349 	return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
350 }
351 
352 static int
353 kmfber_put_bitstring(BerElement *ber, char *str,
354 	ber_len_t blen /* in bits */, ber_tag_t tag)
355 {
356 	ber_int_t	taglen, lenlen, len;
357 	unsigned char	unusedbits;
358 
359 	if (tag == KMFBER_DEFAULT)
360 		tag = BER_BIT_STRING;
361 
362 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
363 		return (-1);
364 
365 	len = (blen + 7) / 8;
366 	unusedbits = (unsigned char) (len * 8 - blen);
367 	if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
368 		return (-1);
369 
370 	if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
371 		return (-1);
372 
373 	if (kmfber_write(ber, str, len, 0) != len)
374 		return (-1);
375 
376 	/* return length of tag + length + unused bit count + contents */
377 	return (taglen + 1 + lenlen + len);
378 }
379 
380 static int
381 kmfber_put_null(BerElement *ber, ber_tag_t tag)
382 {
383 	int	taglen;
384 
385 	if (tag == KMFBER_DEFAULT)
386 		tag = BER_NULL;
387 
388 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
389 		return (-1);
390 
391 	if (kmfber_put_len(ber, 0, 0) != 1)
392 		return (-1);
393 
394 	return (taglen + 1);
395 }
396 
397 static int
398 kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
399 {
400 	int		taglen;
401 	unsigned char	trueval = 0xff;
402 	unsigned char	falseval = 0x00;
403 
404 	if (tag == KMFBER_DEFAULT)
405 		tag = BER_BOOLEAN;
406 
407 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
408 		return (-1);
409 
410 	if (kmfber_put_len(ber, 1, 0) != 1)
411 		return (-1);
412 
413 	if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
414 	    != 1)
415 		return (-1);
416 
417 	return (taglen + 2);
418 }
419 
420 #define	FOUR_BYTE_LEN	5
421 
422 
423 /*
424  * The idea here is roughly this: we maintain a stack of these Seqorset
425  * structures. This is pushed when we see the beginning of a new set or
426  * sequence. It is popped when we see the end of a set or sequence.
427  * Since we don't want to malloc and free these structures all the time,
428  * we pre-allocate a small set of them within the ber element structure.
429  * thus we need to spot when we've overflowed this stack and fall back to
430  * malloc'ing instead.
431  */
432 static int
433 ber_start_seqorset(BerElement *ber, ber_tag_t tag)
434 {
435 	Seqorset	*new_sos;
436 
437 	/* can we fit into the local stack ? */
438 	if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
439 		/* yes */
440 		new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
441 	} else {
442 		/* no */
443 		if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
444 		    == NULLSEQORSET) {
445 			return (-1);
446 		}
447 	}
448 	ber->ber_sos_stack_posn++;
449 
450 	if (ber->ber_sos == NULLSEQORSET)
451 		new_sos->sos_first = ber->ber_ptr;
452 	else
453 		new_sos->sos_first = ber->ber_sos->sos_ptr;
454 
455 	/* Set aside room for a 4 byte length field */
456 	new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
457 		FOUR_BYTE_LEN;
458 	new_sos->sos_tag = tag;
459 
460 	new_sos->sos_next = ber->ber_sos;
461 	new_sos->sos_clen = 0;
462 
463 	ber->ber_sos = new_sos;
464 	if (ber->ber_sos->sos_ptr > ber->ber_end) {
465 		(void) realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end);
466 	}
467 	return (0);
468 }
469 
470 static int
471 kmfber_start_seq(BerElement *ber, ber_tag_t tag)
472 {
473 	if (tag == KMFBER_DEFAULT)
474 		tag = BER_CONSTRUCTED_SEQUENCE;
475 
476 	return (ber_start_seqorset(ber, tag));
477 }
478 
479 static int
480 kmfber_start_set(BerElement *ber, ber_tag_t tag)
481 {
482 	if (tag == KMFBER_DEFAULT)
483 		tag = BER_CONSTRUCTED_SET;
484 
485 	return (ber_start_seqorset(ber, tag));
486 }
487 
488 static int
489 ber_put_seqorset(BerElement *ber)
490 {
491 	ber_int_t	netlen, len, taglen, lenlen;
492 	unsigned char	ltag = 0x80 + FOUR_BYTE_LEN - 1;
493 	Seqorset	*next;
494 	Seqorset	**sos = &ber->ber_sos;
495 
496 	/*
497 	 * If this is the toplevel sequence or set, we need to actually
498 	 * write the stuff out.  Otherwise, it's already been put in
499 	 * the appropriate buffer and will be written when the toplevel
500 	 * one is written.  In this case all we need to do is update the
501 	 * length and tag.
502 	 */
503 
504 	len = (*sos)->sos_clen;
505 	netlen = (ber_len_t)htonl(len);
506 
507 	if (ber->ber_options & KMFBER_OPT_USE_DER) {
508 		lenlen = kmfber_calc_lenlen(len);
509 	} else {
510 		lenlen = FOUR_BYTE_LEN;
511 	}
512 
513 	if ((next = (*sos)->sos_next) == NULLSEQORSET) {
514 		/* write the tag */
515 		if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
516 			return (-1);
517 
518 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
519 			/* Write the length in the minimum # of octets */
520 			if (kmfber_put_len(ber, len, 1) == -1)
521 				return (-1);
522 
523 			if (lenlen != FOUR_BYTE_LEN) {
524 				/*
525 				 * We set aside FOUR_BYTE_LEN bytes for
526 				 * the length field.  Move the data if
527 				 * we don't actually need that much
528 				 */
529 				(void) memmove((*sos)->sos_first + taglen +
530 				    lenlen, (*sos)->sos_first + taglen +
531 				    FOUR_BYTE_LEN, len);
532 			}
533 		} else {
534 			/* Fill FOUR_BYTE_LEN bytes for length field */
535 			/* one byte of length length */
536 			if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
537 				return (-1);
538 
539 			/* the length itself */
540 			if (kmfber_write(ber,
541 				(char *)&netlen + sizeof (ber_int_t)
542 				- (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
543 				FOUR_BYTE_LEN - 1)
544 				return (-1);
545 		}
546 		/* The ber_ptr is at the set/seq start - move it to the end */
547 		ber->ber_ptr += len;
548 	} else {
549 		ber_tag_t	ntag;
550 
551 		/* the tag */
552 		taglen = kmfber_calc_taglen((*sos)->sos_tag);
553 		ntag = htonl((*sos)->sos_tag);
554 		(void) memmove((*sos)->sos_first, (char *)&ntag +
555 		    sizeof (ber_int_t) - taglen, taglen);
556 
557 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
558 			ltag = (lenlen == 1) ? (unsigned char)len :
559 				(unsigned char) (0x80 + (lenlen - 1));
560 		}
561 
562 		/* one byte of length length */
563 		(void) memmove((*sos)->sos_first + 1, &ltag, 1);
564 
565 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
566 			if (lenlen > 1) {
567 				/* Write the length itself */
568 				(void) memmove((*sos)->sos_first + 2,
569 				    (char *)&netlen + sizeof (ber_uint_t) -
570 				    (lenlen - 1),
571 				    lenlen - 1);
572 			}
573 			if (lenlen != FOUR_BYTE_LEN) {
574 				/*
575 				 * We set aside FOUR_BYTE_LEN bytes for
576 				 * the length field.  Move the data if
577 				 * we don't actually need that much
578 				 */
579 				(void) memmove((*sos)->sos_first + taglen +
580 				    lenlen, (*sos)->sos_first + taglen +
581 				    FOUR_BYTE_LEN, len);
582 			}
583 		} else {
584 			/* the length itself */
585 			(void) memmove((*sos)->sos_first + taglen + 1,
586 			    (char *) &netlen + sizeof (ber_int_t) -
587 			    (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
588 		}
589 
590 		next->sos_clen += (taglen + lenlen + len);
591 		next->sos_ptr += (taglen + lenlen + len);
592 	}
593 
594 	/* we're done with this seqorset, so free it up */
595 	/* was this one from the local stack ? */
596 	if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
597 		free((char *)(*sos));
598 	}
599 	ber->ber_sos_stack_posn--;
600 	*sos = next;
601 
602 	return (taglen + lenlen + len);
603 }
604 
605 /* VARARGS */
606 int
607 kmfber_printf(BerElement *ber, const char *fmt, ...)
608 {
609 	va_list		ap;
610 	char		*s, **ss;
611 	struct berval	**bv, *oid;
612 	int		rc, i, t;
613 	ber_int_t	len;
614 
615 	va_start(ap, fmt);
616 
617 #ifdef KMFBER_DEBUG
618 	if (lber_debug & 64) {
619 		char msg[80];
620 		sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
621 		ber_err_print(msg);
622 	}
623 #endif
624 
625 	for (rc = 0; *fmt && rc != -1; fmt++) {
626 		switch (*fmt) {
627 		case 'b':	/* boolean */
628 			i = va_arg(ap, int);
629 			rc = kmfber_put_boolean(ber, i, ber->ber_tag);
630 			break;
631 
632 		case 'i':	/* int */
633 			i = va_arg(ap, int);
634 			rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
635 			break;
636 
637 		case 'D':	/* Object ID */
638 			if ((oid = va_arg(ap, struct berval *)) == NULL)
639 				break;
640 			rc = ber_put_oid(ber, oid, ber->ber_tag);
641 			break;
642 		case 'I':	/* int */
643 			s = va_arg(ap, char *);
644 			len = va_arg(ap, ber_int_t);
645 			rc = ber_put_big_int(ber, ber->ber_tag, s, len);
646 			break;
647 
648 		case 'e':	/* enumeration */
649 			i = va_arg(ap, int);
650 			rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
651 			break;
652 
653 		case 'l':
654 			t = va_arg(ap, int);
655 			rc = kmfber_put_len(ber, t, 0);
656 			break;
657 		case 'n':	/* null */
658 			rc = kmfber_put_null(ber, ber->ber_tag);
659 			break;
660 
661 		case 'o':	/* octet string (non-null terminated) */
662 			s = va_arg(ap, char *);
663 			len = va_arg(ap, int);
664 			rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
665 			break;
666 
667 		case 's':	/* string */
668 			s = va_arg(ap, char *);
669 			rc = kmfber_put_string(ber, s, ber->ber_tag);
670 			break;
671 
672 		case 'B':	/* bit string */
673 			s = va_arg(ap, char *);
674 			len = va_arg(ap, int);	/* in bits */
675 			rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
676 			break;
677 
678 		case 't':	/* tag for the next element */
679 			ber->ber_tag = va_arg(ap, ber_tag_t);
680 			ber->ber_usertag = 1;
681 			break;
682 
683 		case 'T': /* Write an explicit tag, but don't change current */
684 			t = va_arg(ap, int);
685 			rc = ber_put_tag(ber, t, 0);
686 			break;
687 
688 		case 'v':	/* vector of strings */
689 			if ((ss = va_arg(ap, char **)) == NULL)
690 				break;
691 			for (i = 0; ss[i] != NULL; i++) {
692 				if ((rc = kmfber_put_string(ber, ss[i],
693 				    ber->ber_tag)) == -1)
694 					break;
695 			}
696 			break;
697 
698 		case 'V':	/* sequences of strings + lengths */
699 			if ((bv = va_arg(ap, struct berval **)) == NULL)
700 				break;
701 			for (i = 0; bv[i] != NULL; i++) {
702 				if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
703 				    bv[i]->bv_len, ber->ber_tag)) == -1)
704 					break;
705 			}
706 			break;
707 
708 		case '{':	/* begin sequence */
709 			rc = kmfber_start_seq(ber, ber->ber_tag);
710 			break;
711 
712 		case '}':	/* end sequence */
713 			rc = ber_put_seqorset(ber);
714 			break;
715 
716 		case '[':	/* begin set */
717 			rc = kmfber_start_set(ber, ber->ber_tag);
718 			break;
719 
720 		case ']':	/* end set */
721 			rc = ber_put_seqorset(ber);
722 			break;
723 
724 		default: {
725 #ifdef KMFBER_DEBUG
726 				char msg[80];
727 				sprintf(msg, "unknown fmt %c\n", *fmt);
728 				ber_err_print(msg);
729 #endif
730 				rc = -1;
731 				break;
732 			}
733 		}
734 
735 		if (ber->ber_usertag == 0)
736 			ber->ber_tag = KMFBER_DEFAULT;
737 		else
738 			ber->ber_usertag = 0;
739 	}
740 
741 	va_end(ap);
742 
743 	return (rc);
744 }
745