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