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