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