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 *)<ag, 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, <ag, 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