1/*	$NetBSD: encode.c,v 1.1.1.3 2010/12/12 15:21:28 adam Exp $	*/
2
3/* encode.c - ber output encoding routines */
4/* OpenLDAP: pkg/ldap/libraries/liblber/encode.c,v 1.64.2.9 2010/04/13 20:22:53 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* Portions Copyright (c) 1990 Regents of the University of Michigan.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms are permitted
22 * provided that this notice is preserved and that due credit is given
23 * to the University of Michigan at Ann Arbor. The name of the University
24 * may not be used to endorse or promote products derived from this
25 * software without specific prior written permission. This software
26 * is provided ``as is'' without express or implied warranty.
27 */
28/* ACKNOWLEDGEMENTS:
29 * This work was originally developed by the University of Michigan
30 * (as part of U-MICH LDAP).
31 */
32
33#include "portable.h"
34
35#include <ctype.h>
36#include <limits.h>
37#include <stdio.h>
38
39#include <ac/stdlib.h>
40
41#include <ac/stdarg.h>
42#include <ac/socket.h>
43#include <ac/string.h>
44
45#include "lber-int.h"
46
47
48#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8)
49#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t)
50#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t))
51#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE)
52
53/*
54 * BER element size constrains:
55 *
56 * - We traditionally support a length of max 0xffffffff.  However
57 *   some functions return an int length so that is their max.
58 *   MAXINT_BERSIZE is the max for those functions.
59 *
60 * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets.
61 *
62 * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS:
63 *   Big enough for MAXINT_BERSIZE, but not more.  (Larger wastes
64 *   space in the working encoding and DER encoding of a sequence
65 *   or set.  Smaller further limits sizes near a sequence/set.)
66 *
67 * ber_len_t is mostly unrelated to this.  Which may be for the best,
68 * since it is also used for lengths of data that are never encoded.
69 */
70#define MAXINT_BERSIZE \
71	(INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE)
72#define MAXINT_BERSIZE_OCTETS 4
73typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */
74
75
76/* Prepend tag to ptr, which points to the end of a tag buffer */
77static unsigned char *
78ber_prepend_tag( unsigned char *ptr, ber_tag_t tag )
79{
80	do {
81		*--ptr = (unsigned char) tag & 0xffU;
82	} while ( (tag >>= 8) != 0 );
83
84	return ptr;
85}
86
87/* Prepend ber length to ptr, which points to the end of a length buffer */
88static unsigned char *
89ber_prepend_len( unsigned char *ptr, ber_len_t len )
90{
91	/*
92	 * short len if it's less than 128 - one byte giving the len,
93	 * with bit 8 0.
94	 * long len otherwise - one byte with bit 8 set, giving the
95	 * length of the length, followed by the length itself.
96	 */
97
98	*--ptr = (unsigned char) len & 0xffU;
99
100	if ( len >= 0x80 ) {
101		unsigned char *endptr = ptr--;
102
103		while ( (len >>= 8) != 0 ) {
104			*ptr-- = (unsigned char) len & 0xffU;
105		}
106		*ptr = (unsigned char) (endptr - ptr) + 0x80U;
107	}
108
109	return ptr;
110}
111
112/* out->bv_len should be the buffer size on input */
113int
114ber_encode_oid( BerValue *in, BerValue *out )
115{
116	unsigned char *der;
117	unsigned long val1, val;
118	int i, j, len;
119	char *ptr, *end, *inend;
120
121	assert( in != NULL );
122	assert( out != NULL );
123
124	if ( !out->bv_val || out->bv_len < in->bv_len/2 )
125		return -1;
126
127	der = (unsigned char *) out->bv_val;
128	ptr = in->bv_val;
129	inend = ptr + in->bv_len;
130
131	/* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */
132	if ( !isdigit( (unsigned char) *ptr )) return -1;
133	val1 = strtoul( ptr, &end, 10 );
134	if ( end == ptr || val1 > 2 ) return -1;
135	if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1;
136	val = strtoul( end, &ptr, 10 );
137	if ( ptr == end ) return -1;
138	if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1;
139	val += val1 * 40;
140
141	for (;;) {
142		if ( ptr > inend ) return -1;
143
144		/* Write the OID component little-endian, then reverse it */
145		len = 0;
146		do {
147			der[len++] = (val & 0xff) | 0x80;
148		} while ( (val >>= 7) != 0 );
149		der[0] &= 0x7f;
150		for ( i = 0, j = len; i < --j; i++ ) {
151			unsigned char tmp = der[i];
152			der[i] = der[j];
153			der[j] = tmp;
154		}
155		der += len;
156
157		if ( ptr == inend )
158			break;
159
160		if ( *ptr++ != '.' ) return -1;
161		if ( !isdigit( (unsigned char) *ptr )) return -1;
162		val = strtoul( ptr, &end, 10 );
163		if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1;
164		ptr = end;
165	}
166
167	out->bv_len = (char *)der - out->bv_val;
168	return 0;
169}
170
171static int
172ber_put_int_or_enum(
173	BerElement *ber,
174	ber_int_t num,
175	ber_tag_t tag )
176{
177	ber_uint_t unum;
178	unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr;
179
180	sign = 0;
181	unum = num;	/* Bit fiddling should be done with unsigned values */
182	if ( num < 0 ) {
183		sign = 0xffU;
184		unum = ~unum;
185	}
186	for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) {
187		*ptr-- = (sign ^ (unsigned char) unum) & 0xffU;
188		if ( unum < 0x80 )	/* top bit at *ptr is sign bit */
189			break;
190	}
191
192	*ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */
193	ptr = ber_prepend_tag( ptr, tag );
194
195	return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
196}
197
198int
199ber_put_enum(
200	BerElement *ber,
201	ber_int_t num,
202	ber_tag_t tag )
203{
204	if ( tag == LBER_DEFAULT ) {
205		tag = LBER_ENUMERATED;
206	}
207
208	return ber_put_int_or_enum( ber, num, tag );
209}
210
211int
212ber_put_int(
213	BerElement *ber,
214	ber_int_t num,
215	ber_tag_t tag )
216{
217	if ( tag == LBER_DEFAULT ) {
218		tag = LBER_INTEGER;
219	}
220
221	return ber_put_int_or_enum( ber, num, tag );
222}
223
224int
225ber_put_ostring(
226	BerElement *ber,
227	LDAP_CONST char *str,
228	ber_len_t len,
229	ber_tag_t tag )
230{
231	int rc;
232	unsigned char header[HEADER_SIZE], *ptr;
233
234	if ( tag == LBER_DEFAULT ) {
235		tag = LBER_OCTETSTRING;
236	}
237
238	if ( len > MAXINT_BERSIZE ) {
239		return -1;
240	}
241
242	ptr = ber_prepend_len( &header[sizeof(header)], len );
243	ptr = ber_prepend_tag( ptr, tag );
244
245	rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 );
246	if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) {
247		/* length(tag + length + contents) */
248		return rc + (int) len;
249	}
250
251	return -1;
252}
253
254int
255ber_put_berval(
256	BerElement *ber,
257	struct berval *bv,
258	ber_tag_t tag )
259{
260	if( bv == NULL || bv->bv_len == 0 ) {
261		return ber_put_ostring( ber, "", (ber_len_t) 0, tag );
262	}
263
264	return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag );
265}
266
267int
268ber_put_string(
269	BerElement *ber,
270	LDAP_CONST char *str,
271	ber_tag_t tag )
272{
273	assert( str != NULL );
274
275	return ber_put_ostring( ber, str, strlen( str ), tag );
276}
277
278int
279ber_put_bitstring(
280	BerElement *ber,
281	LDAP_CONST char *str,
282	ber_len_t blen /* in bits */,
283	ber_tag_t tag )
284{
285	int rc;
286	ber_len_t		len;
287	unsigned char	unusedbits, header[HEADER_SIZE + 1], *ptr;
288
289	if ( tag == LBER_DEFAULT ) {
290		tag = LBER_BITSTRING;
291	}
292
293	unusedbits = (unsigned char) -blen & 7;
294	len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */
295	if ( len >= MAXINT_BERSIZE ) {
296		return -1;
297	}
298
299	header[sizeof(header) - 1] = unusedbits;
300	ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 );
301	ptr = ber_prepend_tag( ptr, tag );
302
303	rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 );
304	if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) {
305		/* length(tag + length + unused bit count + bitstring) */
306		return rc + (int) len;
307	}
308
309	return -1;
310}
311
312int
313ber_put_null( BerElement *ber, ber_tag_t tag )
314{
315	unsigned char data[TAGBUF_SIZE + 1], *ptr;
316
317	if ( tag == LBER_DEFAULT ) {
318		tag = LBER_NULL;
319	}
320
321	data[sizeof(data) - 1] = 0;			/* length */
322	ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag );
323
324	return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
325}
326
327int
328ber_put_boolean(
329	BerElement *ber,
330	ber_int_t boolval,
331	ber_tag_t tag )
332{
333	unsigned char data[TAGBUF_SIZE + 2], *ptr;
334
335	if ( tag == LBER_DEFAULT )
336		tag = LBER_BOOLEAN;
337
338	data[sizeof(data) - 1] = boolval ? 0xff : 0;
339	data[sizeof(data) - 2] = 1;			/* length */
340	ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag );
341
342	return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
343}
344
345
346/* Max number of length octets in a sequence or set, normally 5 */
347#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \
348		(ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS))
349
350/* Header of incomplete sequence or set */
351typedef struct seqorset_header {
352	char xtagbuf[TAGBUF_SIZE + 1];	/* room for tag + len(tag or len) */
353	union {
354		ber_elem_size_t offset;		/* enclosing seqence/set */
355		char padding[SOS_LENLEN-1];	/* for final length encoding */
356	} next_sos;
357#	define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1)
358} Seqorset_header;
359
360/* Start a sequence or set */
361static int
362ber_start_seqorset(
363	BerElement *ber,
364	ber_tag_t tag )
365{
366	/*
367	 * Write the tag and SOS_LENLEN octets reserved for length, to ber.
368	 * For now, length octets = (tag length, previous ber_sos_inner).
369	 *
370	 * Update ber_sos_inner and the write-cursor ber_sos_ptr.  ber_ptr
371	 * will not move until the outermost sequence or set is complete.
372	 */
373
374	Seqorset_header	header;
375	unsigned char	*headptr;
376	ber_len_t		taglen, headlen;
377	char			*dest, **p;
378
379	assert( ber != NULL );
380	assert( LBER_VALID( ber ) );
381
382	if ( ber->ber_sos_ptr == NULL ) {	/* outermost sequence/set? */
383		header.next_sos.offset = 0;
384		p = &ber->ber_ptr;
385	} else {
386		if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) {
387			if ( ber->ber_sos_inner > (ber_elem_size_t) -1 )
388				return -1;
389		}
390		header.next_sos.offset = ber->ber_sos_inner;
391		p = &ber->ber_sos_ptr;
392	}
393	headptr = ber_prepend_tag( SOS_TAG_END(header), tag );
394	*SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr;
395	headlen = taglen + SOS_LENLEN;
396
397	/* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */
398	if ( headlen > (ber_len_t) (ber->ber_end - *p) ) {
399		if ( ber_realloc( ber, headlen ) != 0 )
400			return -1;
401	}
402	dest = *p;
403	AC_MEMCPY( dest, headptr, headlen );
404	ber->ber_sos_ptr = dest + headlen;
405
406	ber->ber_sos_inner = dest + taglen - ber->ber_buf;
407
408	/*
409	 * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset()
410	 * should return lenlen - SOS_LENLEN + len, which can be < 0.
411	 */
412	return 0;
413}
414
415int
416ber_start_seq( BerElement *ber, ber_tag_t tag )
417{
418	if ( tag == LBER_DEFAULT ) {
419		tag = LBER_SEQUENCE;
420	}
421
422	return ber_start_seqorset( ber, tag );
423}
424
425int
426ber_start_set( BerElement *ber, ber_tag_t tag )
427{
428	if ( tag == LBER_DEFAULT ) {
429		tag = LBER_SET;
430	}
431
432	return ber_start_seqorset( ber, tag );
433}
434
435/* End a sequence or set */
436static int
437ber_put_seqorset( BerElement *ber )
438{
439	Seqorset_header	header;
440	unsigned char	*lenptr;	/* length octets in the sequence/set */
441	ber_len_t		len;		/* length(contents) */
442	ber_len_t		xlen;		/* len + length(length) */
443
444	assert( ber != NULL );
445	assert( LBER_VALID( ber ) );
446
447	if ( ber->ber_sos_ptr == NULL ) return -1;
448
449	lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner;
450	xlen = ber->ber_sos_ptr - (char *) lenptr;
451	if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) {
452		return -1;
453	}
454
455	/* Extract sequence/set information from length octets */
456	memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN );
457
458	/* Store length, and close gap of leftover reserved length octets */
459	len = xlen - SOS_LENLEN;
460	if ( !(ber->ber_options & LBER_USE_DER) ) {
461		int i;
462		lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */
463		for( i = SOS_LENLEN; --i > 0; len >>= 8 ) {
464			lenptr[i] = len & 0xffU;
465		}
466	} else {
467		unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len );
468		ber_len_t unused = p - lenptr;
469		if ( unused != 0 ) {
470			/* length(length) < the reserved SOS_LENLEN bytes */
471			xlen -= unused;
472			AC_MEMCPY( lenptr, p, xlen );
473			ber->ber_sos_ptr = (char *) lenptr + xlen;
474		}
475	}
476
477	ber->ber_sos_inner = header.next_sos.offset;
478	if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */
479		/* The ber_ptr is at the set/seq start - move it to the end */
480		ber->ber_ptr = ber->ber_sos_ptr;
481		ber->ber_sos_ptr = NULL;
482	}
483
484	return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */
485}
486
487int
488ber_put_seq( BerElement *ber )
489{
490	return ber_put_seqorset( ber );
491}
492
493int
494ber_put_set( BerElement *ber )
495{
496	return ber_put_seqorset( ber );
497}
498
499/* N tag */
500static ber_tag_t lber_int_null = 0;
501
502/* VARARGS */
503int
504ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... )
505{
506	va_list		ap;
507	char		*s, **ss;
508	struct berval	*bv, **bvp;
509	int		rc;
510	ber_int_t	i;
511	ber_len_t	len;
512
513	assert( ber != NULL );
514	assert( fmt != NULL );
515	assert( LBER_VALID( ber ) );
516
517	va_start( ap, fmt );
518
519	for ( rc = 0; *fmt && rc != -1; fmt++ ) {
520		switch ( *fmt ) {
521		case '!': { /* hook */
522				BEREncodeCallback *f;
523				void *p;
524
525				ber->ber_usertag = 0;
526
527				f = va_arg( ap, BEREncodeCallback * );
528				p = va_arg( ap, void * );
529				rc = (*f)( ber, p );
530
531				if ( ber->ber_usertag ) {
532					goto next;
533				}
534			} break;
535
536		case 'b':	/* boolean */
537			i = va_arg( ap, ber_int_t );
538			rc = ber_put_boolean( ber, i, ber->ber_tag );
539			break;
540
541		case 'i':	/* int */
542			i = va_arg( ap, ber_int_t );
543			rc = ber_put_int( ber, i, ber->ber_tag );
544			break;
545
546		case 'e':	/* enumeration */
547			i = va_arg( ap, ber_int_t );
548			rc = ber_put_enum( ber, i, ber->ber_tag );
549			break;
550
551		case 'n':	/* null */
552			rc = ber_put_null( ber, ber->ber_tag );
553			break;
554
555		case 'N':	/* Debug NULL */
556			rc = 0;
557			if( lber_int_null != 0 ) {
558				/* Insert NULL to ensure peer ignores unknown tags */
559				rc = ber_put_null( ber, lber_int_null );
560			}
561			break;
562
563		case 'o':	/* octet string (non-null terminated) */
564			s = va_arg( ap, char * );
565			len = va_arg( ap, ber_len_t );
566			rc = ber_put_ostring( ber, s, len, ber->ber_tag );
567			break;
568
569		case 'O':	/* berval octet string */
570			bv = va_arg( ap, struct berval * );
571			if( bv == NULL ) break;
572			rc = ber_put_berval( ber, bv, ber->ber_tag );
573			break;
574
575		case 's':	/* string */
576			s = va_arg( ap, char * );
577			rc = ber_put_string( ber, s, ber->ber_tag );
578			break;
579
580		case 'B':	/* bit string */
581		case 'X':	/* bit string (deprecated) */
582			s = va_arg( ap, char * );
583			len = va_arg( ap, ber_len_t );	/* in bits */
584			rc = ber_put_bitstring( ber, s, len, ber->ber_tag );
585			break;
586
587		case 't':	/* tag for the next element */
588			ber->ber_tag = va_arg( ap, ber_tag_t );
589			goto next;
590
591		case 'v':	/* vector of strings */
592			if ( (ss = va_arg( ap, char ** )) == NULL )
593				break;
594			for ( i = 0; ss[i] != NULL; i++ ) {
595				if ( (rc = ber_put_string( ber, ss[i],
596				    ber->ber_tag )) == -1 )
597					break;
598			}
599			break;
600
601		case 'V':	/* sequences of strings + lengths */
602			if ( (bvp = va_arg( ap, struct berval ** )) == NULL )
603				break;
604			for ( i = 0; bvp[i] != NULL; i++ ) {
605				if ( (rc = ber_put_berval( ber, bvp[i],
606				    ber->ber_tag )) == -1 )
607					break;
608			}
609			break;
610
611		case 'W':	/* BerVarray */
612			if ( (bv = va_arg( ap, BerVarray )) == NULL )
613				break;
614			for ( i = 0; bv[i].bv_val != NULL; i++ ) {
615				if ( (rc = ber_put_berval( ber, &bv[i],
616				    ber->ber_tag )) == -1 )
617					break;
618			}
619			break;
620
621		case '{':	/* begin sequence */
622			rc = ber_start_seq( ber, ber->ber_tag );
623			break;
624
625		case '}':	/* end sequence */
626			rc = ber_put_seqorset( ber );
627			break;
628
629		case '[':	/* begin set */
630			rc = ber_start_set( ber, ber->ber_tag );
631			break;
632
633		case ']':	/* end set */
634			rc = ber_put_seqorset( ber );
635			break;
636
637		default:
638			if( ber->ber_debug ) {
639				ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
640					"ber_printf: unknown fmt %c\n", *fmt );
641			}
642			rc = -1;
643			break;
644		}
645
646		ber->ber_tag = LBER_DEFAULT;
647	next:;
648	}
649
650	va_end( ap );
651
652	return rc;
653}
654