Home | History | Annotate | Download | only in common
      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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