1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
7 *
8 * The contents of this file are subject to the Netscape Public License
9 * Version 1.0 (the "NPL"); you may not use this file except in
10 * compliance with the NPL. You may obtain a copy of the NPL at
11 * http://www.mozilla.org/NPL/
12 *
13 * Software distributed under the NPL is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
15 * for the specific language governing rights and limitations under the
16 * NPL.
17 *
18 * The Initial Developer of this code under the NPL is Netscape
19 * Communications Corporation. Portions created by Netscape are
20 * Copyright (C) 1998 Netscape Communications Corporation. All Rights
21 * Reserved.
22 */
23
24 /*
25 * Copyright (c) 1990 Regents of the University of Michigan.
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms are permitted
29 * provided that this notice is preserved and that due credit is given
30 * to the University of Michigan at Ann Arbor. The name of the University
31 * may not be used to endorse or promote products derived from this
32 * software without specific prior written permission. This software
33 * is provided ``as is'' without express or implied warranty.
34 */
35
36 /* encode.c - ber output encoding routines */
37
38 #include "lber-int.h"
39
40 /* the following constants are used in ber_calc_lenlen */
41
42 #define LENMASK1 0xFF
43 #define LENMASK2 0xFFFF
44 #define LENMASK3 0xFFFFFF
45 #define LENMASK4 0xFFFFFFFF
46 #define _MASK 0x80
47
48 static int
ber_calc_taglen(ber_tag_t tag)49 ber_calc_taglen( ber_tag_t tag )
50 {
51 int i;
52 ber_int_t mask;
53
54 /* find the first non-all-zero byte in the tag */
55 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) {
56 mask = (LENMASK3 << (i * 8));
57 /* not all zero */
58 if ( tag & mask )
59 break;
60 }
61
62 return( i + 1 );
63 }
64
65 static int
ber_put_tag(BerElement * ber,ber_tag_t tag,int nosos)66 ber_put_tag( BerElement *ber, ber_tag_t tag, int nosos )
67 {
68 int taglen;
69 ber_tag_t ntag;
70
71 taglen = ber_calc_taglen( tag );
72
73 ntag = LBER_HTONL( tag );
74
75 return( ber_write( ber, ((char *) &ntag) + sizeof(ber_int_t) - taglen,
76 taglen, nosos ) );
77 }
78
79 static int
ber_calc_lenlen(ber_len_t len)80 ber_calc_lenlen( ber_len_t len )
81 {
82 /*
83 * short len if it's less than 128 - one byte giving the len,
84 * with bit 8 0.
85 */
86
87 if ( len <= 0x7F )
88 return( 1 );
89
90 /*
91 * long len otherwise - one byte with bit 8 set, giving the
92 * length of the length, followed by the length itself.
93 */
94
95 if ( len <= LENMASK1 )
96 return( 2 );
97 if ( len <= LENMASK2 )
98 return( 3 );
99 if ( len <= LENMASK3 )
100 return( 4 );
101
102 return( 5 );
103 }
104
105 static int
ber_put_len(BerElement * ber,ber_len_t len,int nosos)106 ber_put_len( BerElement *ber, ber_len_t len, int nosos )
107 {
108 int i;
109 char lenlen;
110 ber_int_t mask;
111 ber_len_t netlen;
112
113 /*
114 * short len if it's less than 128 - one byte giving the len,
115 * with bit 8 0.
116 */
117
118 if ( len <= 127 ) {
119 netlen = LBER_HTONL( len );
120 return( ber_write( ber, (char *) &netlen + sizeof(ber_int_t) - 1,
121 1, nosos ) );
122 }
123
124 /*
125 * long len otherwise - one byte with bit 8 set, giving the
126 * length of the length, followed by the length itself.
127 */
128
129 /* find the first non-all-zero byte */
130 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) {
131 mask = (LENMASK1 << (i * 8));
132 /* not all zero */
133 if ( len & mask )
134 break;
135 }
136 lenlen = ++i;
137 if ( lenlen > 4 )
138 return( -1 );
139 lenlen |= 0x80;
140
141 /* write the length of the length */
142 if ( ber_write( ber, &lenlen, 1, nosos ) != 1 )
143 return( -1 );
144
145 /* write the length itself */
146 netlen = LBER_HTONL( len );
147 if ( ber_write( ber, (char *) &netlen + (sizeof(ber_int_t) - i), i, nosos )
148 != i )
149 return( -1 );
150
151 return( i + 1 );
152 }
153
154 static int
ber_put_int_or_enum(BerElement * ber,ber_int_t num,ber_tag_t tag)155 ber_put_int_or_enum( BerElement *ber, ber_int_t num, ber_tag_t tag )
156 {
157 int i, sign, taglen;
158 int len, lenlen;
159 ber_int_t netnum, mask;
160
161 sign = (num < 0);
162
163 /*
164 * high bit is set - look for first non-all-one byte
165 * high bit is clear - look for first non-all-zero byte
166 */
167 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) {
168 mask = (LENMASK1 << (i * 8));
169
170 if ( sign ) {
171 /* not all ones */
172 if ( (num & mask) != mask )
173 break;
174 } else {
175 /* not all zero */
176 if ( num & mask )
177 break;
178 }
179 }
180
181 /*
182 * we now have the "leading byte". if the high bit on this
183 * byte matches the sign bit, we need to "back up" a byte.
184 */
185 mask = (num & (_MASK << (i * 8)));
186 if ( (mask && !sign) || (sign && !mask) )
187 i++;
188
189 len = i + 1;
190
191 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
192 return( -1 );
193
194 if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 )
195 return( -1 );
196 i++;
197 netnum = LBER_HTONL( num );
198 if ( ber_write( ber, (char *) &netnum + (sizeof(ber_int_t) - i), i, 0 )
199 == i)
200 /* length of tag + length + contents */
201 return( taglen + lenlen + i );
202
203 return( -1 );
204 }
205
206 int
207 LDAP_CALL
ber_put_enum(BerElement * ber,ber_int_t num,ber_tag_t tag)208 ber_put_enum( BerElement *ber, ber_int_t num, ber_tag_t tag )
209 {
210 if ( tag == LBER_DEFAULT )
211 tag = LBER_ENUMERATED;
212
213 return( ber_put_int_or_enum( ber, num, tag ) );
214 }
215
216 int
217 LDAP_CALL
ber_put_int(BerElement * ber,ber_int_t num,ber_tag_t tag)218 ber_put_int( BerElement *ber, ber_int_t num, ber_tag_t tag )
219 {
220 if ( tag == LBER_DEFAULT )
221 tag = LBER_INTEGER;
222
223 return( ber_put_int_or_enum( ber, num, tag ) );
224 }
225
226 int
227 LDAP_CALL
ber_put_ostring(BerElement * ber,char * str,ber_len_t len,ber_tag_t tag)228 ber_put_ostring( BerElement *ber, char *str, ber_len_t len,
229 ber_tag_t tag )
230 {
231 int taglen, lenlen, rc;
232 #ifdef STR_TRANSLATION
233 int free_str;
234 #endif /* STR_TRANSLATION */
235
236 if ( tag == LBER_DEFAULT )
237 tag = LBER_OCTETSTRING;
238
239 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
240 return( -1 );
241
242 #ifdef STR_TRANSLATION
243 if ( len > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) != 0
244 && ber->ber_encode_translate_proc != NULL ) {
245 if ( (*(ber->ber_encode_translate_proc))( &str, &len, 0 )
246 != 0 ) {
247 return( -1 );
248 }
249 free_str = 1;
250 } else {
251 free_str = 0;
252 }
253 #endif /* STR_TRANSLATION */
254
255 /*
256 * Note: below is a spot where we limit ber_write
257 * to signed long (instead of unsigned long)
258 */
259
260 if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ||
261 ber_write( ber, str, len, 0 ) != (ber_int_t) len ) {
262 rc = -1;
263 } else {
264 /* return length of tag + length + contents */
265 rc = taglen + lenlen + len;
266 }
267
268 #ifdef STR_TRANSLATION
269 if ( free_str ) {
270 NSLBERI_FREE( str );
271 }
272 #endif /* STR_TRANSLATION */
273
274 return( rc );
275 }
276
277 int
278 LDAP_CALL
ber_put_string(BerElement * ber,char * str,ber_tag_t tag)279 ber_put_string( BerElement *ber, char *str, ber_tag_t tag )
280 {
281 return( ber_put_ostring( ber, str, (ber_len_t) strlen( str ), tag ));
282 }
283
284 int
285 LDAP_CALL
ber_put_bitstring(BerElement * ber,char * str,ber_len_t blen,ber_tag_t tag)286 ber_put_bitstring( BerElement *ber, char *str,
287 ber_len_t blen /* in bits */, ber_tag_t tag )
288 {
289 int taglen, lenlen, len;
290 unsigned char unusedbits;
291
292 if ( tag == LBER_DEFAULT )
293 tag = LBER_BITSTRING;
294
295 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
296 return( -1 );
297
298 len = ( blen + 7 ) / 8;
299 unusedbits = (unsigned char) (len * 8 - blen);
300 if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 )
301 return( -1 );
302
303 if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 )
304 return( -1 );
305
306 if ( ber_write( ber, str, len, 0 ) != len )
307 return( -1 );
308
309 /* return length of tag + length + unused bit count + contents */
310 return( taglen + 1 + lenlen + len );
311 }
312
313 int
314 LDAP_CALL
ber_put_null(BerElement * ber,ber_tag_t tag)315 ber_put_null( BerElement *ber, ber_tag_t tag )
316 {
317 int taglen;
318
319 if ( tag == LBER_DEFAULT )
320 tag = LBER_NULL;
321
322 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
323 return( -1 );
324
325 if ( ber_put_len( ber, 0, 0 ) != 1 )
326 return( -1 );
327
328 return( taglen + 1 );
329 }
330
331 int
332 LDAP_CALL
ber_put_boolean(BerElement * ber,int boolval,ber_tag_t tag)333 ber_put_boolean( BerElement *ber, int boolval, ber_tag_t tag )
334 {
335 int taglen;
336 unsigned char trueval = 0xff;
337 unsigned char falseval = 0x00;
338
339 if ( tag == LBER_DEFAULT )
340 tag = LBER_BOOLEAN;
341
342 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
343 return( -1 );
344
345 if ( ber_put_len( ber, 1, 0 ) != 1 )
346 return( -1 );
347
348 if ( ber_write( ber, (char *)(boolval ? &trueval : &falseval), 1, 0 )
349 != 1 )
350 return( -1 );
351
352 return( taglen + 2 );
353 }
354
355 #define FOUR_BYTE_LEN 5
356
357
358 /* the idea here is roughly this: we maintain a stack of these Seqorset
359 * structures. This is pushed when we see the beginning of a new set or
360 * sequence. It is popped when we see the end of a set or sequence.
361 * Since we don't want to malloc and free these structures all the time,
362 * we pre-allocate a small set of them within the ber element structure.
363 * thus we need to spot when we've overflowed this stack and fall back to
364 * malloc'ing instead.
365 */
366 static int
ber_start_seqorset(BerElement * ber,ber_tag_t tag)367 ber_start_seqorset( BerElement *ber, ber_tag_t tag )
368 {
369 Seqorset *new_sos;
370
371 /* can we fit into the local stack ? */
372 if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
373 /* yes */
374 new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
375 } else {
376 /* no */
377 if ( (new_sos = (Seqorset *)NSLBERI_MALLOC( sizeof(Seqorset)))
378 == NULLSEQORSET ) {
379 return( -1 );
380 }
381 }
382 ber->ber_sos_stack_posn++;
383
384 if ( ber->ber_sos == NULLSEQORSET )
385 new_sos->sos_first = ber->ber_ptr;
386 else
387 new_sos->sos_first = ber->ber_sos->sos_ptr;
388
389 /* Set aside room for a 4 byte length field */
390 new_sos->sos_ptr = new_sos->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN;
391 new_sos->sos_tag = tag;
392
393 new_sos->sos_next = ber->ber_sos;
394 new_sos->sos_clen = 0;
395
396 ber->ber_sos = new_sos;
397 if (ber->ber_sos->sos_ptr > ber->ber_end) {
398 nslberi_ber_realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end);
399 }
400 return( 0 );
401 }
402
403 int
404 LDAP_CALL
ber_start_seq(BerElement * ber,ber_tag_t tag)405 ber_start_seq( BerElement *ber, ber_tag_t tag )
406 {
407 if ( tag == LBER_DEFAULT )
408 tag = LBER_SEQUENCE;
409
410 return( ber_start_seqorset( ber, tag ) );
411 }
412
413 int
414 LDAP_CALL
ber_start_set(BerElement * ber,ber_tag_t tag)415 ber_start_set( BerElement *ber, ber_tag_t tag )
416 {
417 if ( tag == LBER_DEFAULT )
418 tag = LBER_SET;
419
420 return( ber_start_seqorset( ber, tag ) );
421 }
422
423 static int
ber_put_seqorset(BerElement * ber)424 ber_put_seqorset( BerElement *ber )
425 {
426 ber_len_t len, netlen;
427 int taglen, lenlen;
428 unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1;
429 Seqorset *next;
430 Seqorset **sos = &ber->ber_sos;
431
432 /*
433 * If this is the toplevel sequence or set, we need to actually
434 * write the stuff out. Otherwise, it's already been put in
435 * the appropriate buffer and will be written when the toplevel
436 * one is written. In this case all we need to do is update the
437 * length and tag.
438 */
439
440 len = (*sos)->sos_clen;
441 netlen = LBER_HTONL( len );
442 if ( sizeof(ber_int_t) > 4 && len > LENMASK4 )
443 return( -1 );
444
445 if ( ber->ber_options & LBER_OPT_USE_DER ) {
446 lenlen = ber_calc_lenlen( len );
447 } else {
448 lenlen = FOUR_BYTE_LEN;
449 }
450
451 if ( (next = (*sos)->sos_next) == NULLSEQORSET ) {
452 /* write the tag */
453 if ( (taglen = ber_put_tag( ber, (*sos)->sos_tag, 1 )) == -1 )
454 return( -1 );
455
456 if ( ber->ber_options & LBER_OPT_USE_DER ) {
457 /* Write the length in the minimum # of octets */
458 if ( ber_put_len( ber, len, 1 ) == -1 )
459 return( -1 );
460
461 if (lenlen != FOUR_BYTE_LEN) {
462 /*
463 * We set aside FOUR_BYTE_LEN bytes for
464 * the length field. Move the data if
465 * we don't actually need that much
466 */
467 SAFEMEMCPY( (*sos)->sos_first + taglen +
468 lenlen, (*sos)->sos_first + taglen +
469 FOUR_BYTE_LEN, len );
470 }
471 } else {
472 /* Fill FOUR_BYTE_LEN bytes for length field */
473 /* one byte of length length */
474 if ( ber_write( ber, (char *)<ag, 1, 1 ) != 1 )
475 return( -1 );
476
477 /* the length itself */
478 if ( ber_write( ber, (char *) &netlen + sizeof(ber_int_t)
479 - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1 )
480 != FOUR_BYTE_LEN - 1 )
481 return( -1 );
482 }
483 /* The ber_ptr is at the set/seq start - move it to the end */
484 ber->ber_ptr += len;
485 } else {
486 ber_tag_t ntag;
487
488 /* the tag */
489 taglen = ber_calc_taglen( (*sos)->sos_tag );
490 ntag = LBER_HTONL( (*sos)->sos_tag );
491 SAFEMEMCPY( (*sos)->sos_first, (char *) &ntag +
492 sizeof(ber_int_t) - taglen, taglen );
493
494 if ( ber->ber_options & LBER_OPT_USE_DER ) {
495 ltag = (lenlen == 1) ? (unsigned char)len :
496 (unsigned char) (0x80 + (lenlen - 1));
497 }
498
499 /* one byte of length length */
500 SAFEMEMCPY( (*sos)->sos_first + 1, <ag, 1 );
501
502 if ( ber->ber_options & LBER_OPT_USE_DER ) {
503 if (lenlen > 1) {
504 /* Write the length itself */
505 SAFEMEMCPY( (*sos)->sos_first + 2,
506 (char *)&netlen + sizeof(ber_uint_t) -
507 (lenlen - 1),
508 lenlen - 1 );
509 }
510 if (lenlen != FOUR_BYTE_LEN) {
511 /*
512 * We set aside FOUR_BYTE_LEN bytes for
513 * the length field. Move the data if
514 * we don't actually need that much
515 */
516 SAFEMEMCPY( (*sos)->sos_first + taglen +
517 lenlen, (*sos)->sos_first + taglen +
518 FOUR_BYTE_LEN, len );
519 }
520 } else {
521 /* the length itself */
522 SAFEMEMCPY( (*sos)->sos_first + taglen + 1,
523 (char *) &netlen + sizeof(ber_int_t) -
524 (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1 );
525 }
526
527 next->sos_clen += (taglen + lenlen + len);
528 next->sos_ptr += (taglen + lenlen + len);
529 }
530
531 /* we're done with this seqorset, so free it up */
532 /* was this one from the local stack ? */
533 if (ber->ber_sos_stack_posn <= SOS_STACK_SIZE) {
534 /* yes */
535 } else {
536 /* no */
537 NSLBERI_FREE( (char *) (*sos) );
538 }
539 ber->ber_sos_stack_posn--;
540 *sos = next;
541
542 return( taglen + lenlen + len );
543 }
544
545 int
546 LDAP_CALL
ber_put_seq(BerElement * ber)547 ber_put_seq( BerElement *ber )
548 {
549 return( ber_put_seqorset( ber ) );
550 }
551
552 int
553 LDAP_CALL
ber_put_set(BerElement * ber)554 ber_put_set( BerElement *ber )
555 {
556 return( ber_put_seqorset( ber ) );
557 }
558
559 /* VARARGS */
560 int
561 LDAP_C
ber_printf(BerElement * ber,const char * fmt,...)562 ber_printf( BerElement *ber, const char *fmt, ... )
563 {
564 va_list ap;
565 char *s, **ss;
566 struct berval *bval, **bv;
567 int rc, i;
568 ber_len_t len;
569
570 va_start( ap, fmt );
571
572 #ifdef LDAP_DEBUG
573 if ( lber_debug & 64 ) {
574 char msg[80];
575 sprintf( msg, "ber_printf fmt (%s)\n", fmt );
576 ber_err_print( msg );
577 }
578 #endif
579
580 for ( rc = 0; *fmt && rc != -1; fmt++ ) {
581 switch ( *fmt ) {
582 case 'b': /* boolean */
583 i = va_arg( ap, int );
584 rc = ber_put_boolean( ber, i, ber->ber_tag );
585 break;
586
587 case 'i': /* int */
588 i = va_arg( ap, int );
589 rc = ber_put_int( ber, (ber_int_t)i, ber->ber_tag );
590 break;
591
592 case 'e': /* enumeration */
593 i = va_arg( ap, int );
594 rc = ber_put_enum( ber, (ber_int_t)i, ber->ber_tag );
595 break;
596
597 case 'n': /* null */
598 rc = ber_put_null( ber, ber->ber_tag );
599 break;
600
601 case 'o': /* octet string (non-null terminated) */
602 s = va_arg( ap, char * );
603 len = va_arg( ap, int );
604 rc = ber_put_ostring( ber, s, len, ber->ber_tag );
605 break;
606
607 case 'O': /* berval octet string */
608 if( ( bval = va_arg( ap, struct berval * ) ) == NULL )
609 break;
610 if( bval->bv_len == 0 ) {
611 rc = ber_put_ostring( ber, "", 0, ber->ber_tag );
612 } else {
613 rc = ber_put_ostring( ber, bval->bv_val, bval->bv_len,
614 ber->ber_tag );
615 }
616 break;
617
618 case 's': /* string */
619 s = va_arg( ap, char * );
620 rc = ber_put_string( ber, s, ber->ber_tag );
621 break;
622
623 case 'B': /* bit string */
624 s = va_arg( ap, char * );
625 len = va_arg( ap, int ); /* in bits */
626 rc = ber_put_bitstring( ber, s, len, ber->ber_tag );
627 break;
628
629 case 't': /* tag for the next element */
630 ber->ber_tag = va_arg( ap, ber_tag_t );
631 ber->ber_usertag = 1;
632 break;
633
634 case 'v': /* vector of strings */
635 if ( (ss = va_arg( ap, char ** )) == NULL )
636 break;
637 for ( i = 0; ss[i] != NULL; i++ ) {
638 if ( (rc = ber_put_string( ber, ss[i],
639 ber->ber_tag )) == -1 )
640 break;
641 }
642 break;
643
644 case 'V': /* sequences of strings + lengths */
645 if ( (bv = va_arg( ap, struct berval ** )) == NULL )
646 break;
647 for ( i = 0; bv[i] != NULL; i++ ) {
648 if ( (rc = ber_put_ostring( ber, bv[i]->bv_val,
649 bv[i]->bv_len, ber->ber_tag )) == -1 )
650 break;
651 }
652 break;
653
654 case '{': /* begin sequence */
655 rc = ber_start_seq( ber, ber->ber_tag );
656 break;
657
658 case '}': /* end sequence */
659 rc = ber_put_seqorset( ber );
660 break;
661
662 case '[': /* begin set */
663 rc = ber_start_set( ber, ber->ber_tag );
664 break;
665
666 case ']': /* end set */
667 rc = ber_put_seqorset( ber );
668 break;
669
670 default: {
671 char msg[80];
672 sprintf( msg, "unknown fmt %c\n", *fmt );
673 ber_err_print( msg );
674 rc = -1;
675 break;
676 }
677 }
678
679 if ( ber->ber_usertag == 0 )
680 ber->ber_tag = LBER_DEFAULT;
681 else
682 ber->ber_usertag = 0;
683 }
684
685 va_end( ap );
686
687 return( rc );
688 }
689