2 * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/
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
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
21 * Copyright (c) 1990 Regents of the University of Michigan.
22 * All rights reserved.
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.
33 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
37 #include <sys/types.h>
38 #include <netinet/in.h>
42 #include "kmfber_int.h"
44 /* the following constants are used in kmfber_calc_lenlen */
47 #define LENMASK2 0xFFFF
48 #define LENMASK3 0xFFFFFF
49 #define LENMASK4 0xFFFFFFFF
53 kmfber_calc_taglen(ber_tag_t tag
)
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));
70 ber_put_tag(BerElement
*ber
, ber_tag_t tag
, int nosos
)
75 taglen
= kmfber_calc_taglen(tag
);
79 return (kmfber_write(ber
,
80 ((char *) &ntag
) + sizeof (ber_int_t
) - taglen
,
85 kmfber_calc_lenlen(ber_int_t len
)
88 * short len if it's less than 128 - one byte giving the len,
96 * long len otherwise - one byte with bit 8 set, giving the
97 * length of the length, followed by the length itself.
111 kmfber_put_len(BerElement
*ber
, ber_int_t len
, int nosos
)
115 ber_int_t mask
, netlen
;
118 * short len if it's less than 128 - one byte giving the len,
123 return (kmfber_write(ber
,
124 (char *)&netlen
+ sizeof (ber_int_t
) - 1,
129 * long len otherwise - one byte with bit 8 set, giving the
130 * length of the length, followed by the length itself.
133 /* find the first non-all-zero byte */
134 for (i
= sizeof (ber_int_t
) - 1; i
> 0; i
--) {
135 mask
= (LENMASK1
<< (i
* 8));
145 /* write the length of the length */
146 if (kmfber_write(ber
, &lenlen
, 1, nosos
) != 1)
149 /* write the length itself */
151 if (kmfber_write(ber
,
152 (char *) &netlen
+ (sizeof (ber_int_t
) - i
), i
, nosos
) != i
)
159 ber_put_int_or_enum(BerElement
*ber
, ber_int_t num
, ber_tag_t tag
)
162 ber_int_t len
, lenlen
, taglen
, netnum
, mask
;
167 * high bit is set - look for first non-all-one byte
168 * high bit is clear - look for first non-all-zero byte
170 for (i
= sizeof (ber_int_t
) - 1; i
> 0; i
--) {
171 mask
= (LENMASK1
<< (i
* 8));
175 if ((num
& mask
) != mask
)
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.
188 mask
= (num
& (_MASK
<< (i
* 8)));
189 if ((mask
&& !sign
) || (sign
&& !mask
))
194 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
197 if ((lenlen
= kmfber_put_len(ber
, len
, 0)) == -1)
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
);
210 kmfber_put_enum(BerElement
*ber
, ber_int_t num
, ber_tag_t tag
)
212 if (tag
== KMFBER_DEFAULT
)
213 tag
= BER_ENUMERATED
;
215 return (ber_put_int_or_enum(ber
, num
, tag
));
219 ber_put_int(BerElement
*ber
, ber_int_t num
, ber_tag_t tag
)
221 if (tag
== KMFBER_DEFAULT
)
224 return (ber_put_int_or_enum(ber
, num
, tag
));
228 ber_put_oid(BerElement
*ber
, struct berval
*oid
, ber_tag_t tag
)
230 ber_int_t taglen
, lenlen
, rc
, len
;
232 if (tag
== KMFBER_DEFAULT
)
233 tag
= 0x06; /* TODO: Add new OID constant to header */
235 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
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
) {
244 /* return length of tag + length + contents */
245 rc
= taglen
+ lenlen
+ oid
->bv_len
;
251 ber_put_big_int(BerElement
*ber
, ber_tag_t tag
, char *data
,
254 ber_int_t taglen
, lenlen
, ilen
, rc
;
257 if (tag
== KMFBER_DEFAULT
)
260 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
263 /* Add a leading 0 if the high order bit is set */
267 ilen
= (ber_int_t
)len
;
268 if ((lenlen
= kmfber_put_len(ber
, ilen
, 0)) == -1)
271 /* add leading 0 if hi bit set */
272 if ((data
[0] & 0x80) && kmfber_write(ber
, &zero
, 1, 0) != 1)
275 /* Adjust the length of the write if hi-order bit is set */
278 if (kmfber_write(ber
, data
, ilen
, 0) != (ber_int_t
)ilen
) {
281 /* return length of tag + length + contents */
282 rc
= taglen
+ lenlen
+ len
;
288 kmfber_put_ostring(BerElement
*ber
, char *str
, ber_len_t len
,
291 ber_int_t taglen
, lenlen
, ilen
, rc
;
292 #ifdef STR_TRANSLATION
294 #endif /* STR_TRANSLATION */
296 if (tag
== KMFBER_DEFAULT
)
297 tag
= BER_OCTET_STRING
;
299 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
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)
313 #endif /* STR_TRANSLATION */
316 * Note: below is a spot where we limit ber_write
317 * to signed long (instead of unsigned long)
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
) {
324 /* return length of tag + length + contents */
325 rc
= taglen
+ lenlen
+ len
;
328 #ifdef STR_TRANSLATION
332 #endif /* STR_TRANSLATION */
338 kmfber_put_string(BerElement
*ber
, char *str
, ber_tag_t tag
)
340 return (kmfber_put_ostring(ber
, str
, (ber_len_t
)strlen(str
), tag
));
344 kmfber_put_bitstring(BerElement
*ber
, char *str
,
345 ber_len_t blen
/* in bits */, ber_tag_t tag
)
347 ber_int_t taglen
, lenlen
, len
;
348 unsigned char unusedbits
;
350 if (tag
== KMFBER_DEFAULT
)
351 tag
= BER_BIT_STRING
;
353 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
356 len
= (blen
+ 7) / 8;
357 unusedbits
= (unsigned char) (len
* 8 - blen
);
358 if ((lenlen
= kmfber_put_len(ber
, len
+ 1, 0)) == -1)
361 if (kmfber_write(ber
, (char *)&unusedbits
, 1, 0) != 1)
364 if (kmfber_write(ber
, str
, len
, 0) != len
)
367 /* return length of tag + length + unused bit count + contents */
368 return (taglen
+ 1 + lenlen
+ len
);
372 kmfber_put_null(BerElement
*ber
, ber_tag_t tag
)
376 if (tag
== KMFBER_DEFAULT
)
379 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
382 if (kmfber_put_len(ber
, 0, 0) != 1)
389 kmfber_put_boolean(BerElement
*ber
, int boolval
, ber_tag_t tag
)
392 unsigned char trueval
= 0xff;
393 unsigned char falseval
= 0x00;
395 if (tag
== KMFBER_DEFAULT
)
398 if ((taglen
= ber_put_tag(ber
, tag
, 0)) == -1)
401 if (kmfber_put_len(ber
, 1, 0) != 1)
404 if (kmfber_write(ber
, (char *)(boolval
? &trueval
: &falseval
), 1, 0)
411 #define FOUR_BYTE_LEN 5
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.
424 ber_start_seqorset(BerElement
*ber
, ber_tag_t tag
)
428 /* can we fit into the local stack ? */
429 if (ber
->ber_sos_stack_posn
< SOS_STACK_SIZE
) {
431 new_sos
= &ber
->ber_sos_stack
[ber
->ber_sos_stack_posn
];
434 if ((new_sos
= (Seqorset
*)malloc(sizeof (Seqorset
)))
439 ber
->ber_sos_stack_posn
++;
441 if (ber
->ber_sos
== NULLSEQORSET
)
442 new_sos
->sos_first
= ber
->ber_ptr
;
444 new_sos
->sos_first
= ber
->ber_sos
->sos_ptr
;
446 /* Set aside room for a 4 byte length field */
447 new_sos
->sos_ptr
= new_sos
->sos_first
+ kmfber_calc_taglen(tag
) +
449 new_sos
->sos_tag
= tag
;
451 new_sos
->sos_next
= ber
->ber_sos
;
452 new_sos
->sos_clen
= 0;
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
-
464 kmfber_start_seq(BerElement
*ber
, ber_tag_t tag
)
466 if (tag
== KMFBER_DEFAULT
)
467 tag
= BER_CONSTRUCTED_SEQUENCE
;
469 return (ber_start_seqorset(ber
, tag
));
473 kmfber_start_set(BerElement
*ber
, ber_tag_t tag
)
475 if (tag
== KMFBER_DEFAULT
)
476 tag
= BER_CONSTRUCTED_SET
;
478 return (ber_start_seqorset(ber
, tag
));
482 ber_put_seqorset(BerElement
*ber
)
484 ber_int_t netlen
, len
, taglen
, lenlen
;
485 unsigned char ltag
= 0x80 + FOUR_BYTE_LEN
- 1;
487 Seqorset
**sos
= &ber
->ber_sos
;
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
497 len
= (*sos
)->sos_clen
;
498 netlen
= (ber_len_t
)htonl(len
);
500 if (ber
->ber_options
& KMFBER_OPT_USE_DER
) {
501 lenlen
= kmfber_calc_lenlen(len
);
503 lenlen
= FOUR_BYTE_LEN
;
506 if ((next
= (*sos
)->sos_next
) == NULLSEQORSET
) {
508 if ((taglen
= ber_put_tag(ber
, (*sos
)->sos_tag
, 1)) == -1)
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)
516 if (lenlen
!= FOUR_BYTE_LEN
) {
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
522 (void) memmove((*sos
)->sos_first
+ taglen
+
523 lenlen
, (*sos
)->sos_first
+ taglen
+
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)
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) !=
539 /* The ber_ptr is at the set/seq start - move it to the end */
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
);
550 if (ber
->ber_options
& KMFBER_OPT_USE_DER
) {
551 ltag
= (lenlen
== 1) ? (unsigned char)len
:
552 (unsigned char) (0x80 + (lenlen
- 1));
555 /* one byte of length length */
556 (void) memmove((*sos
)->sos_first
+ 1, <ag
, 1);
558 if (ber
->ber_options
& KMFBER_OPT_USE_DER
) {
560 /* Write the length itself */
561 (void) memmove((*sos
)->sos_first
+ 2,
562 (char *)&netlen
+ sizeof (ber_uint_t
) -
566 if (lenlen
!= FOUR_BYTE_LEN
) {
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
572 (void) memmove((*sos
)->sos_first
+ taglen
+
573 lenlen
, (*sos
)->sos_first
+ taglen
+
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);
583 next
->sos_clen
+= (taglen
+ lenlen
+ len
);
584 next
->sos_ptr
+= (taglen
+ lenlen
+ len
);
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
));
592 ber
->ber_sos_stack_posn
--;
595 return (taglen
+ lenlen
+ len
);
600 kmfber_printf(BerElement
*ber
, const char *fmt
, ...)
604 struct berval
**bv
, *oid
;
611 if (lber_debug
& 64) {
613 sprintf(msg
, "kmfber_printf fmt (%s)\n", fmt
);
618 for (rc
= 0; *fmt
&& rc
!= -1; fmt
++) {
620 case 'b': /* boolean */
622 rc
= kmfber_put_boolean(ber
, i
, ber
->ber_tag
);
627 rc
= ber_put_int(ber
, (ber_int_t
)i
, ber
->ber_tag
);
630 case 'D': /* Object ID */
631 if ((oid
= va_arg(ap
, struct berval
*)) == NULL
)
633 rc
= ber_put_oid(ber
, oid
, ber
->ber_tag
);
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
);
641 case 'e': /* enumeration */
643 rc
= kmfber_put_enum(ber
, (ber_int_t
)i
, ber
->ber_tag
);
648 rc
= kmfber_put_len(ber
, t
, 0);
651 rc
= kmfber_put_null(ber
, ber
->ber_tag
);
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
);
660 case 's': /* string */
661 s
= va_arg(ap
, char *);
662 rc
= kmfber_put_string(ber
, s
, ber
->ber_tag
);
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
);
671 case 't': /* tag for the next element */
672 ber
->ber_tag
= va_arg(ap
, ber_tag_t
);
673 ber
->ber_usertag
= 1;
676 case 'T': /* Write an explicit tag, but don't change current */
678 rc
= ber_put_tag(ber
, t
, 0);
681 case 'v': /* vector of strings */
682 if ((ss
= va_arg(ap
, char **)) == NULL
)
684 for (i
= 0; ss
[i
] != NULL
; i
++) {
685 if ((rc
= kmfber_put_string(ber
, ss
[i
],
686 ber
->ber_tag
)) == -1)
691 case 'V': /* sequences of strings + lengths */
692 if ((bv
= va_arg(ap
, struct berval
**)) == NULL
)
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)
701 case '{': /* begin sequence */
702 rc
= kmfber_start_seq(ber
, ber
->ber_tag
);
705 case '}': /* end sequence */
706 rc
= ber_put_seqorset(ber
);
709 case '[': /* begin set */
710 rc
= kmfber_start_set(ber
, ber
->ber_tag
);
713 case ']': /* end set */
714 rc
= ber_put_seqorset(ber
);
720 sprintf(msg
, "unknown fmt %c\n", *fmt
);
728 if (ber
->ber_usertag
== 0)
729 ber
->ber_tag
= KMFBER_DEFAULT
;
731 ber
->ber_usertag
= 0;