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