xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblber/encode.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /* encode.c - ber output encoding routines */
2 /* $OpenLDAP: pkg/ldap/libraries/liblber/encode.c,v 1.64.2.3 2008/02/11 23:26:41 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26 /* ACKNOWLEDGEMENTS:
27  * This work was originally developed by the University of Michigan
28  * (as part of U-MICH LDAP).
29  */
30 
31 #include "portable.h"
32 
33 #include <ctype.h>
34 #include <stdio.h>
35 
36 #include <ac/stdlib.h>
37 
38 #include <ac/stdarg.h>
39 #include <ac/socket.h>
40 #include <ac/string.h>
41 
42 #include "lber-int.h"
43 
44 static int ber_put_len LDAP_P((
45 	BerElement *ber,
46 	ber_len_t len,
47 	int nosos ));
48 
49 static int ber_start_seqorset LDAP_P((
50 	BerElement *ber,
51 	ber_tag_t tag ));
52 
53 static int ber_put_seqorset LDAP_P(( BerElement *ber ));
54 
55 static int ber_put_int_or_enum LDAP_P((
56 	BerElement *ber,
57 	ber_int_t num,
58 	ber_tag_t tag ));
59 
60 #define	BER_TOP_BYTE(type)	(sizeof(type)-1)
61 #define	BER_TOP_MASK(type)	((type)0xffU << (BER_TOP_BYTE(type)*8))
62 
63 static int
64 ber_calc_taglen( ber_tag_t tag )
65 {
66 	int	i = BER_TOP_BYTE(ber_tag_t);
67 	ber_tag_t	mask = BER_TOP_MASK(ber_tag_t);
68 
69 	/* find the first non-all-zero byte in the tag */
70 	for ( ; i > 0; i-- ) {
71 		/* not all zero */
72 		if ( tag & mask ) break;
73 		mask >>= 8;
74 	}
75 
76 	return i + 1;
77 }
78 
79 static int
80 ber_put_tag(
81 	BerElement	*ber,
82 	ber_tag_t tag,
83 	int nosos )
84 {
85 	int rc;
86 	int taglen;
87 	int	i;
88 	unsigned char nettag[sizeof(ber_tag_t)];
89 
90 	assert( ber != NULL );
91 	assert( LBER_VALID( ber ) );
92 
93 	taglen = ber_calc_taglen( tag );
94 
95 	for( i=taglen-1; i>=0; i-- ) {
96 		nettag[i] = (unsigned char)(tag & 0xffU);
97 		tag >>= 8;
98 	}
99 
100 	rc = ber_write( ber, (char *) nettag, taglen, nosos );
101 
102 	return rc;
103 }
104 
105 static ber_len_t
106 ber_calc_lenlen( ber_len_t len )
107 {
108 	/*
109 	 * short len if it's less than 128 - one byte giving the len,
110 	 * with bit 8 0.
111 	 */
112 
113 	if ( len <= (ber_len_t) 0x7FU ) return 1;
114 
115 	/*
116 	 * long len otherwise - one byte with bit 8 set, giving the
117 	 * length of the length, followed by the length itself.
118 	 */
119 
120 	if ( len <= (ber_len_t) 0xffU ) return 2;
121 	if ( len <= (ber_len_t) 0xffffU ) return 3;
122 	if ( len <= (ber_len_t) 0xffffffU ) return 4;
123 
124 	return 5;
125 }
126 
127 static int
128 ber_put_len( BerElement *ber, ber_len_t len, int nosos )
129 {
130 	int rc;
131 	int		i,j;
132 	char		lenlen;
133 	ber_len_t	mask;
134 	unsigned char netlen[sizeof(ber_len_t)];
135 
136 	assert( ber != NULL );
137 	assert( LBER_VALID( ber ) );
138 
139 	/*
140 	 * short len if it's less than 128 - one byte giving the len,
141 	 * with bit 8 0.
142 	 */
143 
144 	if ( len <= 127 ) {
145 		char length_byte = (char) len;
146 		return ber_write( ber, &length_byte, 1, nosos );
147 	}
148 
149 	/*
150 	 * long len otherwise - one byte with bit 8 set, giving the
151 	 * length of the length, followed by the length itself.
152 	 */
153 
154 	/* find the first non-all-zero byte */
155 	i = BER_TOP_BYTE(ber_len_t);
156 	mask = BER_TOP_MASK(ber_len_t);
157 	for ( ; i > 0; i-- ) {
158 		/* not all zero */
159 		if ( len & mask ) break;
160 		mask >>= 8;
161 	}
162 	lenlen = (unsigned char) ++i;
163 	if ( lenlen > 4 ) return -1;
164 
165 	lenlen |= 0x80UL;
166 
167 	/* write the length of the length */
168 	if ( ber_write( ber, &lenlen, 1, nosos ) != 1 ) return -1;
169 
170 	for( j=i-1; j>=0; j-- ) {
171 		netlen[j] = (unsigned char)(len & 0xffU);
172 		len >>= 8;
173 	}
174 
175 	/* write the length itself */
176 	rc = ber_write( ber, (char *) netlen, i, nosos );
177 
178 	return rc == i ?  i+1 : -1;
179 }
180 
181 /* out->bv_len should be the buffer size on input */
182 int
183 ber_encode_oid( BerValue *in, BerValue *out )
184 {
185 	unsigned char *der;
186 	unsigned long val1, val;
187 	int i, j, len;
188 	char *ptr, *end, *inend;
189 
190 	assert( in != NULL );
191 	assert( out != NULL );
192 
193 	if ( !out->bv_val || out->bv_len < in->bv_len/2 )
194 		return -1;
195 
196 	der = (unsigned char *) out->bv_val;
197 	ptr = in->bv_val;
198 	inend = ptr + in->bv_len;
199 
200 	/* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */
201 	if ( !isdigit( (unsigned char) *ptr )) return -1;
202 	val1 = strtoul( ptr, &end, 10 );
203 	if ( end == ptr || val1 > 2 ) return -1;
204 	if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1;
205 	val = strtoul( end, &ptr, 10 );
206 	if ( ptr == end ) return -1;
207 	if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1;
208 	val += val1 * 40;
209 
210 	for (;;) {
211 		if ( ptr > inend ) return -1;
212 
213 		len = 0;
214 		do {
215 			der[len++] = (val & 0xff) | 0x80;
216 		} while ( (val >>= 7) != 0 );
217 		der[0] &= 0x7f;
218 		for ( i = 0, j = len; i < --j; i++ ) {
219 			unsigned char tmp = der[i];
220 			der[i] = der[j];
221 			der[j] = tmp;
222 		}
223 		der += len;
224 		if ( ptr == inend )
225 			break;
226 
227 		if ( *ptr++ != '.' ) return -1;
228 		if ( !isdigit( (unsigned char) *ptr )) return -1;
229 		val = strtoul( ptr, &end, 10 );
230 		if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1;
231 		ptr = end;
232 	}
233 
234 	out->bv_len = (char *)der - out->bv_val;
235 	return 0;
236 }
237 
238 static int
239 ber_put_int_or_enum(
240 	BerElement *ber,
241 	ber_int_t num,
242 	ber_tag_t tag )
243 {
244 	int rc;
245 	int	i, j, sign, taglen, lenlen;
246 	ber_len_t	len;
247 	ber_uint_t	unum, mask;
248 	unsigned char netnum[sizeof(ber_uint_t)];
249 
250 	assert( ber != NULL );
251 	assert( LBER_VALID( ber ) );
252 
253 	sign = (num < 0);
254 	unum = num;	/* Bit fiddling should be done with unsigned values */
255 
256 	/*
257 	 * high bit is set - look for first non-all-one byte
258 	 * high bit is clear - look for first non-all-zero byte
259 	 */
260 	i = BER_TOP_BYTE(ber_int_t);
261 	mask = BER_TOP_MASK(ber_uint_t);
262 	for ( ; i > 0; i-- ) {
263 		if ( sign ) {
264 			/* not all ones */
265 			if ( (unum & mask) != mask ) break;
266 		} else {
267 			/* not all zero */
268 			if ( unum & mask ) break;
269 		}
270 		mask >>= 8;
271 	}
272 
273 	/*
274 	 * we now have the "leading byte".  if the high bit on this
275 	 * byte matches the sign bit, we need to "back up" a byte.
276 	 */
277 	mask = (unum & ((ber_uint_t)0x80U << (i * 8)));
278 	if ( (mask && !sign) || (sign && !mask) ) {
279 		i++;
280 	}
281 
282 	len = i + 1;
283 
284 	if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
285 		return -1;
286 	}
287 
288 	if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ) {
289 		return -1;
290 	}
291 	i++;
292 
293 	for( j=i-1; j>=0; j-- ) {
294 		netnum[j] = (unsigned char)(unum & 0xffU);
295 		unum >>= 8;
296 	}
297 
298 	rc = ber_write( ber, (char *) netnum, i, 0 );
299 
300 	/* length of tag + length + contents */
301 	return rc == i ? taglen + lenlen + i : -1;
302 }
303 
304 int
305 ber_put_enum(
306 	BerElement *ber,
307 	ber_int_t num,
308 	ber_tag_t tag )
309 {
310 	assert( ber != NULL );
311 	assert( LBER_VALID( ber ) );
312 
313 	if ( tag == LBER_DEFAULT ) {
314 		tag = LBER_ENUMERATED;
315 	}
316 
317 	return ber_put_int_or_enum( ber, num, tag );
318 }
319 
320 int
321 ber_put_int(
322 	BerElement *ber,
323 	ber_int_t num,
324 	ber_tag_t tag )
325 {
326 	assert( ber != NULL );
327 	assert( LBER_VALID( ber ) );
328 
329 	if ( tag == LBER_DEFAULT ) {
330 		tag = LBER_INTEGER;
331 	}
332 
333 	return ber_put_int_or_enum( ber, num, tag );
334 }
335 
336 int
337 ber_put_ostring(
338 	BerElement *ber,
339 	LDAP_CONST char *str,
340 	ber_len_t len,
341 	ber_tag_t tag )
342 {
343 	int taglen, lenlen, rc;
344 
345 	assert( ber != NULL );
346 	assert( str != NULL );
347 
348 	assert( LBER_VALID( ber ) );
349 
350 	if ( tag == LBER_DEFAULT ) {
351 		tag = LBER_OCTETSTRING;
352 	}
353 
354 	if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
355 		return -1;
356 
357 	if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ||
358 		(ber_len_t) ber_write( ber, str, len, 0 ) != len )
359 	{
360 		rc = -1;
361 	} else {
362 		/* return length of tag + length + contents */
363 		rc = taglen + lenlen + len;
364 	}
365 
366 	return rc;
367 }
368 
369 int
370 ber_put_berval(
371 	BerElement *ber,
372 	struct berval *bv,
373 	ber_tag_t tag )
374 {
375 	assert( ber != NULL );
376 	assert( LBER_VALID( ber ) );
377 
378 	if( bv == NULL || bv->bv_len == 0 ) {
379 		return ber_put_ostring( ber, "", (ber_len_t) 0, tag );
380 	}
381 
382 	return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag );
383 }
384 
385 int
386 ber_put_string(
387 	BerElement *ber,
388 	LDAP_CONST char *str,
389 	ber_tag_t tag )
390 {
391 	assert( ber != NULL );
392 	assert( str != NULL );
393 
394 	assert( LBER_VALID( ber ) );
395 
396 	return ber_put_ostring( ber, str, strlen( str ), tag );
397 }
398 
399 int
400 ber_put_bitstring(
401 	BerElement *ber,
402 	LDAP_CONST char *str,
403 	ber_len_t blen /* in bits */,
404 	ber_tag_t tag )
405 {
406 	int				taglen, lenlen;
407 	ber_len_t		len;
408 	unsigned char	unusedbits;
409 
410 	assert( ber != NULL );
411 	assert( str != NULL );
412 
413 	assert( LBER_VALID( ber ) );
414 
415 	if ( tag == LBER_DEFAULT ) {
416 		tag = LBER_BITSTRING;
417 	}
418 
419 	if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
420 		return -1;
421 	}
422 
423 	len = ( blen + 7 ) / 8;
424 	unusedbits = (unsigned char) ((len * 8) - blen);
425 	if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 ) {
426 		return -1;
427 	}
428 
429 	if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 ) {
430 		return -1;
431 	}
432 
433 	if ( (ber_len_t) ber_write( ber, str, len, 0 ) != len ) {
434 		return -1;
435 	}
436 
437 	/* return length of tag + length + unused bit count + contents */
438 	return taglen + 1 + lenlen + len;
439 }
440 
441 int
442 ber_put_null( BerElement *ber, ber_tag_t tag )
443 {
444 	int	taglen;
445 
446 	assert( ber != NULL );
447 	assert( LBER_VALID( ber ) );
448 
449 	if ( tag == LBER_DEFAULT ) {
450 		tag = LBER_NULL;
451 	}
452 
453 	if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
454 		return -1;
455 	}
456 
457 	if ( ber_put_len( ber, 0, 0 ) != 1 ) {
458 		return -1;
459 	}
460 
461 	return taglen + 1;
462 }
463 
464 int
465 ber_put_boolean(
466 	BerElement *ber,
467 	ber_int_t boolval,
468 	ber_tag_t tag )
469 {
470 	int				taglen;
471 	unsigned char	c;
472 
473 	assert( ber != NULL );
474 	assert( LBER_VALID( ber ) );
475 
476 	if ( tag == LBER_DEFAULT )
477 		tag = LBER_BOOLEAN;
478 
479 	if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
480 		return -1;
481 	}
482 
483 	if ( ber_put_len( ber, 1, 0 ) != 1 ) {
484 		return -1;
485 	}
486 
487 	c = boolval ? (unsigned char) ~0U : (unsigned char) 0U;
488 
489 	if ( ber_write( ber, (char *) &c, 1, 0 ) != 1 ) {
490 		return -1;
491 	}
492 
493 	return taglen + 2;
494 }
495 
496 #define FOUR_BYTE_LEN	5
497 
498 static int
499 ber_start_seqorset(
500 	BerElement *ber,
501 	ber_tag_t tag )
502 {
503 	Seqorset	*new;
504 
505 	assert( ber != NULL );
506 	assert( LBER_VALID( ber ) );
507 
508 	new = (Seqorset *) ber_memcalloc_x( 1, sizeof(Seqorset), ber->ber_memctx );
509 
510 	if ( new == NULL ) {
511 		return -1;
512 	}
513 
514 	new->sos_ber = ber;
515 	if ( ber->ber_sos == NULL ) {
516 		new->sos_first = ber->ber_ptr;
517 	} else {
518 		new->sos_first = ber->ber_sos->sos_ptr;
519 	}
520 
521 	/* Set aside room for a 4 byte length field */
522 	new->sos_ptr = new->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN;
523 	new->sos_tag = tag;
524 
525 	new->sos_next = ber->ber_sos;
526 	ber->ber_sos = new;
527 
528 	return 0;
529 }
530 
531 int
532 ber_start_seq( BerElement *ber, ber_tag_t tag )
533 {
534 	assert( ber != NULL );
535 	assert( LBER_VALID( ber ) );
536 
537 	if ( tag == LBER_DEFAULT ) {
538 		tag = LBER_SEQUENCE;
539 	}
540 
541 	return ber_start_seqorset( ber, tag );
542 }
543 
544 int
545 ber_start_set( BerElement *ber, ber_tag_t tag )
546 {
547 	assert( ber != NULL );
548 	assert( LBER_VALID( ber ) );
549 
550 	if ( tag == LBER_DEFAULT ) {
551 		tag = LBER_SET;
552 	}
553 
554 	return ber_start_seqorset( ber, tag );
555 }
556 
557 static int
558 ber_put_seqorset( BerElement *ber )
559 {
560 	int rc;
561 	ber_len_t	len;
562 	unsigned char netlen[sizeof(ber_len_t)];
563 	int			taglen;
564 	ber_len_t	lenlen;
565 	unsigned char	ltag = 0x80U + FOUR_BYTE_LEN - 1;
566 	Seqorset	*next;
567 	Seqorset	**sos = &ber->ber_sos;
568 
569 	assert( ber != NULL );
570 	assert( LBER_VALID( ber ) );
571 
572 	if( *sos == NULL ) return -1;
573 
574 	/*
575 	 * If this is the toplevel sequence or set, we need to actually
576 	 * write the stuff out.	 Otherwise, it's already been put in
577 	 * the appropriate buffer and will be written when the toplevel
578 	 * one is written.  In this case all we need to do is update the
579 	 * length and tag.
580 	 */
581 
582 	len = (*sos)->sos_clen;
583 
584 	if ( sizeof(ber_len_t) > 4 && len > 0xffffffffUL ) {
585 		return -1;
586 	}
587 
588 	if ( ber->ber_options & LBER_USE_DER ) {
589 		lenlen = ber_calc_lenlen( len );
590 
591 	} else {
592 		lenlen = FOUR_BYTE_LEN;
593 	}
594 
595 	if( lenlen > 1 ) {
596 		int i;
597 		ber_len_t j = len;
598 		for( i=lenlen-2; i >= 0; i-- ) {
599 			netlen[i] = j & 0xffU;
600 			j >>= 8;
601 		}
602 	} else {
603 		netlen[0] = (unsigned char)(len & 0x7fU);
604 	}
605 
606 	if ( (next = (*sos)->sos_next) == NULL ) {
607 		/* write the tag */
608 		if ( (taglen = ber_put_tag( ber, (*sos)->sos_tag, 1 )) == -1 ) {
609 			return( -1 );
610 		}
611 
612 		if ( ber->ber_options & LBER_USE_DER ) {
613 			/* Write the length in the minimum # of octets */
614 			if ( ber_put_len( ber, len, 1 ) == -1 ) {
615 				return -1;
616 			}
617 
618 			if (lenlen != FOUR_BYTE_LEN) {
619 				/*
620 				 * We set aside FOUR_BYTE_LEN bytes for
621 				 * the length field.  Move the data if
622 				 * we don't actually need that much
623 				 */
624 				AC_MEMCPY( (*sos)->sos_first + taglen +
625 				    lenlen, (*sos)->sos_first + taglen +
626 				    FOUR_BYTE_LEN, len );
627 			}
628 		} else {
629 			/* Fill FOUR_BYTE_LEN bytes for length field */
630 			/* one byte of length length */
631 			if ( ber_write( ber, (char *)&ltag, 1, 1 ) != 1 ) {
632 				return -1;
633 			}
634 
635 			/* the length itself */
636 			rc  = ber_write( ber, (char *) netlen, FOUR_BYTE_LEN-1, 1 );
637 
638 			if( rc != FOUR_BYTE_LEN - 1 ) {
639 				return -1;
640 			}
641 		}
642 		/* The ber_ptr is at the set/seq start - move it to the end */
643 		(*sos)->sos_ber->ber_ptr += len;
644 
645 	} else {
646 		int i;
647 		unsigned char nettag[sizeof(ber_tag_t)];
648 		ber_tag_t tmptag = (*sos)->sos_tag;
649 
650 		if( ber->ber_sos->sos_ptr > ber->ber_end ) {
651 			/* The sos_ptr exceeds the end of the BerElement
652 			 * this can happen, for example, when the sos_ptr
653 			 * is near the end and no data was written for the
654 			 * 'V'.	 We must realloc the BerElement to ensure
655 			 * we don't overwrite the buffer when writing
656 			 * the tag and length fields.
657 			 */
658 			ber_len_t ext = ber->ber_sos->sos_ptr - ber->ber_end;
659 
660 			if( ber_realloc( ber,  ext ) != 0 ) {
661 				return -1;
662 			}
663 		}
664 
665 		/* the tag */
666 		taglen = ber_calc_taglen( tmptag );
667 
668 		for( i = taglen-1; i >= 0; i-- ) {
669 			nettag[i] = (unsigned char)(tmptag & 0xffU);
670 			tmptag >>= 8;
671 		}
672 
673 		AC_FMEMCPY( (*sos)->sos_first, nettag, taglen );
674 
675 		if ( ber->ber_options & LBER_USE_DER ) {
676 			ltag = (lenlen == 1)
677 				? (unsigned char) len
678 				: (unsigned char) (0x80U + (lenlen - 1));
679 		}
680 
681 		/* one byte of length length */
682 		(*sos)->sos_first[1] = ltag;
683 
684 		if ( ber->ber_options & LBER_USE_DER ) {
685 			if (lenlen > 1) {
686 				/* Write the length itself */
687 				AC_FMEMCPY( (*sos)->sos_first + 2, netlen, lenlen - 1 );
688 			}
689 			if (lenlen != FOUR_BYTE_LEN) {
690 				/*
691 				 * We set aside FOUR_BYTE_LEN bytes for
692 				 * the length field.  Move the data if
693 				 * we don't actually need that much
694 				 */
695 				AC_FMEMCPY( (*sos)->sos_first + taglen +
696 				    lenlen, (*sos)->sos_first + taglen +
697 				    FOUR_BYTE_LEN, len );
698 			}
699 		} else {
700 			/* the length itself */
701 			AC_FMEMCPY( (*sos)->sos_first + taglen + 1,
702 			    netlen, FOUR_BYTE_LEN - 1 );
703 		}
704 
705 		next->sos_clen += (taglen + lenlen + len);
706 		next->sos_ptr += (taglen + lenlen + len);
707 	}
708 
709 	/* we're done with this seqorset, so free it up */
710 	ber_memfree_x( (char *) (*sos), ber->ber_memctx );
711 	*sos = next;
712 
713 	return taglen + lenlen + len;
714 }
715 
716 int
717 ber_put_seq( BerElement *ber )
718 {
719 	assert( ber != NULL );
720 	assert( LBER_VALID( ber ) );
721 
722 	return ber_put_seqorset( ber );
723 }
724 
725 int
726 ber_put_set( BerElement *ber )
727 {
728 	assert( ber != NULL );
729 	assert( LBER_VALID( ber ) );
730 
731 	return ber_put_seqorset( ber );
732 }
733 
734 /* N tag */
735 static ber_tag_t lber_int_null = 0;
736 
737 /* VARARGS */
738 int
739 ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... )
740 {
741 	va_list		ap;
742 	char		*s, **ss;
743 	struct berval	*bv, **bvp;
744 	int		rc;
745 	ber_int_t	i;
746 	ber_len_t	len;
747 
748 	assert( ber != NULL );
749 	assert( fmt != NULL );
750 
751 	assert( LBER_VALID( ber ) );
752 
753 	va_start( ap, fmt );
754 
755 	for ( rc = 0; *fmt && rc != -1; fmt++ ) {
756 		switch ( *fmt ) {
757 		case '!': { /* hook */
758 				BEREncodeCallback *f;
759 				void *p;
760 
761 				f = va_arg( ap, BEREncodeCallback * );
762 				p = va_arg( ap, void * );
763 
764 				rc = (*f)( ber, p );
765 			} break;
766 
767 		case 'b':	/* boolean */
768 			i = va_arg( ap, ber_int_t );
769 			rc = ber_put_boolean( ber, i, ber->ber_tag );
770 			break;
771 
772 		case 'i':	/* int */
773 			i = va_arg( ap, ber_int_t );
774 			rc = ber_put_int( ber, i, ber->ber_tag );
775 			break;
776 
777 		case 'e':	/* enumeration */
778 			i = va_arg( ap, ber_int_t );
779 			rc = ber_put_enum( ber, i, ber->ber_tag );
780 			break;
781 
782 		case 'n':	/* null */
783 			rc = ber_put_null( ber, ber->ber_tag );
784 			break;
785 
786 		case 'N':	/* Debug NULL */
787 			if( lber_int_null != 0 ) {
788 				/* Insert NULL to ensure peer ignores unknown tags */
789 				rc = ber_put_null( ber, lber_int_null );
790 			} else {
791 				rc = 0;
792 			}
793 			break;
794 
795 		case 'o':	/* octet string (non-null terminated) */
796 			s = va_arg( ap, char * );
797 			len = va_arg( ap, ber_len_t );
798 			rc = ber_put_ostring( ber, s, len, ber->ber_tag );
799 			break;
800 
801 		case 'O':	/* berval octet string */
802 			bv = va_arg( ap, struct berval * );
803 			if( bv == NULL ) break;
804 			rc = ber_put_berval( ber, bv, ber->ber_tag );
805 			break;
806 
807 		case 's':	/* string */
808 			s = va_arg( ap, char * );
809 			rc = ber_put_string( ber, s, ber->ber_tag );
810 			break;
811 
812 		case 'B':	/* bit string */
813 		case 'X':	/* bit string (deprecated) */
814 			s = va_arg( ap, char * );
815 			len = va_arg( ap, int );	/* in bits */
816 			rc = ber_put_bitstring( ber, s, len, ber->ber_tag );
817 			break;
818 
819 		case 't':	/* tag for the next element */
820 			ber->ber_tag = va_arg( ap, ber_tag_t );
821 			ber->ber_usertag = 1;
822 			break;
823 
824 		case 'v':	/* vector of strings */
825 			if ( (ss = va_arg( ap, char ** )) == NULL )
826 				break;
827 			for ( i = 0; ss[i] != NULL; i++ ) {
828 				if ( (rc = ber_put_string( ber, ss[i],
829 				    ber->ber_tag )) == -1 )
830 					break;
831 			}
832 			break;
833 
834 		case 'V':	/* sequences of strings + lengths */
835 			if ( (bvp = va_arg( ap, struct berval ** )) == NULL )
836 				break;
837 			for ( i = 0; bvp[i] != NULL; i++ ) {
838 				if ( (rc = ber_put_berval( ber, bvp[i],
839 				    ber->ber_tag )) == -1 )
840 					break;
841 			}
842 			break;
843 
844 		case 'W':	/* BerVarray */
845 			if ( (bv = va_arg( ap, BerVarray )) == NULL )
846 				break;
847 			for ( i = 0; bv[i].bv_val != NULL; i++ ) {
848 				if ( (rc = ber_put_berval( ber, &bv[i],
849 				    ber->ber_tag )) == -1 )
850 					break;
851 			}
852 			break;
853 
854 		case '{':	/* begin sequence */
855 			rc = ber_start_seq( ber, ber->ber_tag );
856 			break;
857 
858 		case '}':	/* end sequence */
859 			rc = ber_put_seqorset( ber );
860 			break;
861 
862 		case '[':	/* begin set */
863 			rc = ber_start_set( ber, ber->ber_tag );
864 			break;
865 
866 		case ']':	/* end set */
867 			rc = ber_put_seqorset( ber );
868 			break;
869 
870 		default:
871 			if( ber->ber_debug ) {
872 				ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
873 					"ber_printf: unknown fmt %c\n", *fmt );
874 			}
875 			rc = -1;
876 			break;
877 		}
878 
879 		if ( ber->ber_usertag == 0 ) {
880 			ber->ber_tag = LBER_DEFAULT;
881 		} else {
882 			ber->ber_usertag = 0;
883 		}
884 	}
885 
886 	va_end( ap );
887 
888 	return rc;
889 }
890