decode.c revision 1.2
1/*	$NetBSD: decode.c,v 1.2 2020/08/11 13:15:37 christos Exp $	*/
2
3/* decode.c - ber input decoding routines */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2020 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 <sys/cdefs.h>
34__RCSID("$NetBSD: decode.c,v 1.2 2020/08/11 13:15:37 christos Exp $");
35
36#include "portable.h"
37
38#include <stdio.h>
39
40#include <ac/stdlib.h>
41#include <ac/stdarg.h>
42#include <ac/string.h>
43#include <ac/socket.h>
44
45#include "lber-int.h"
46
47
48/* out->bv_len should be the buffer size on input */
49int
50ber_decode_oid( BerValue *in, BerValue *out )
51{
52	const unsigned char *der;
53	unsigned long val;
54	unsigned val1;
55	ber_len_t i;
56	char *ptr;
57
58	assert( in != NULL );
59	assert( out != NULL );
60
61	/* need 4 chars/inbyte + \0 for input={7f 7f 7f...} */
62	if ( !out->bv_val || (out->bv_len+3)/4 <= in->bv_len )
63		return -1;
64
65	ptr = NULL;
66	der = (unsigned char *) in->bv_val;
67	val = 0;
68	for ( i=0; i < in->bv_len; i++ ) {
69		val |= der[i] & 0x7f;
70		if ( !( der[i] & 0x80 )) {
71			if ( ptr == NULL ) {
72				/* Initial "x.y": val=x*40+y, x<=2, y<40 if x<2 */
73				ptr = out->bv_val;
74				val1 = (val < 80 ? val/40 : 2);
75				val -= val1*40;
76				ptr += sprintf( ptr, "%u", val1 );
77			}
78			ptr += sprintf( ptr, ".%lu", val );
79			val = 0;
80		} else if ( val - 1UL < LBER_OID_COMPONENT_MAX >> 7 ) {
81			val <<= 7;
82		} else {
83			/* val would overflow, or is 0 from invalid initial 0x80 octet */
84			return -1;
85		}
86	}
87	if ( ptr == NULL || val != 0 )
88		return -1;
89
90	out->bv_len = ptr - out->bv_val;
91	return 0;
92}
93
94/* Return tag, with *bv = rest of element (starting at length octets) */
95static ber_tag_t
96ber_tag_and_rest( const BerElement *ber, struct berval *bv )
97{
98	ber_tag_t	tag;
99	ptrdiff_t	rest;
100	unsigned char	*ptr;
101
102	assert( ber != NULL );
103	assert( LBER_VALID( ber ) );
104
105	ptr = (unsigned char *) ber->ber_ptr;
106	rest = (unsigned char *) ber->ber_end - ptr;
107	if ( rest <= 0 ) {
108		goto fail;
109	}
110
111	tag = ber->ber_tag;
112	if ( (char *) ptr == ber->ber_buf ) {
113		tag = *ptr;
114	}
115	ptr++;
116	rest--;
117	if ( (tag & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) {
118		goto done;
119	}
120
121	do {
122		if ( rest <= 0 ) {
123			break;
124		}
125		tag <<= 8;
126		tag |= *ptr++ & 0xffU;
127		rest--;
128
129		if ( ! (tag & LBER_MORE_TAG_MASK) ) {
130			goto done;
131		}
132	} while ( tag <= (ber_tag_t)-1 / 256 );
133
134 fail:
135	/* Error or unsupported tag size */
136	tag = LBER_DEFAULT;
137
138 done:
139	bv->bv_len = rest;
140	bv->bv_val = (char *) ptr;
141	return tag;
142}
143
144/* Return the tag - LBER_DEFAULT returned means trouble */
145ber_tag_t
146ber_get_tag( BerElement *ber )
147{
148	struct berval bv;
149	ber_tag_t tag = ber_tag_and_rest( ber, &bv );
150
151	ber->ber_ptr = bv.bv_val;
152	return tag;
153}
154
155/* Return next element's tag and point *bv at its contents in-place */
156ber_tag_t
157ber_peek_element( const BerElement *ber, struct berval *bv )
158{
159	ber_tag_t	tag;
160	ber_len_t	len, rest;
161	unsigned	i;
162	unsigned char *ptr;
163
164	assert( bv != NULL );
165
166	/*
167	 * Any ber element looks like this: tag length contents.
168	 * Assuming everything's ok, we return the tag, and point
169	 * bv at the contents.
170	 *
171	 * Assumptions:
172	 *	1) definite lengths
173	 *	2) primitive encodings used whenever possible
174	 */
175
176	len = 0;
177
178	/*
179	 * First, we read the tag.
180	 */
181	tag = ber_tag_and_rest( ber, bv );
182
183	rest = bv->bv_len;
184	ptr = (unsigned char *) bv->bv_val;
185	if ( tag == LBER_DEFAULT || rest == 0 ) {
186		goto fail;
187	}
188
189	/*
190	 * Next, read the length.  The first octet determines the length
191	 * of the length.	If bit 8 is 0, the length is the short form,
192	 * otherwise if the octet != 0x80 it's the long form, otherwise
193	 * the ber element has the unsupported indefinite-length format.
194	 * Lengths that do not fit in a ber_len_t are not accepted.
195	 */
196
197	len = *ptr++;
198	rest--;
199
200	if ( len & 0x80U ) {
201		len &= 0x7fU;
202		if ( len - 1U > sizeof(ber_len_t) - 1U || rest < len ) {
203			/* Indefinite-length/too long length/not enough data */
204			goto fail;
205		}
206
207		rest -= len;
208		i = len;
209		for( len = *ptr++ & 0xffU; --i; len |= *ptr++ & 0xffU ) {
210			len <<= 8;
211		}
212	}
213
214	/* BER element should have enough data left */
215	if( len > rest ) {
216	fail:
217		tag = LBER_DEFAULT;
218	}
219
220	bv->bv_len = len;
221	bv->bv_val = (char *) ptr;
222	return tag;
223}
224
225/* Move past next element, point *bv at it in-place, and return its tag.
226 * The caller may \0-terminate *bv, as next octet is saved in ber->ber_tag.
227 * Similar to ber_get_stringbv(ber, bv, LBER_BV_NOTERM) except on error.
228 */
229ber_tag_t
230ber_skip_element( BerElement *ber, struct berval *bv )
231{
232	ber_tag_t tag = ber_peek_element( ber, bv );
233
234	if ( tag != LBER_DEFAULT ) {
235		ber->ber_ptr = bv->bv_val + bv->bv_len;
236		ber->ber_tag = *(unsigned char *) ber->ber_ptr;
237	}
238
239	return tag;
240}
241
242ber_tag_t
243ber_peek_tag(
244	BerElement *ber,
245	ber_len_t *len )
246{
247	struct berval bv;
248	ber_tag_t tag = ber_peek_element( ber, &bv );
249
250	*len = bv.bv_len;
251	return tag;
252}
253
254ber_tag_t
255ber_skip_tag( BerElement *ber, ber_len_t *lenp )
256{
257	struct berval bv;
258	ber_tag_t tag = ber_peek_element( ber, &bv );
259
260	ber->ber_ptr = bv.bv_val;
261	ber->ber_tag = *(unsigned char *) ber->ber_ptr;
262
263	*lenp = bv.bv_len;
264	return tag;
265}
266
267ber_tag_t
268ber_get_int(
269	BerElement *ber,
270	ber_int_t *num )
271{
272	ber_tag_t	tag;
273	ber_len_t	len;
274	struct berval bv;
275
276	assert( num != NULL );
277
278	tag = ber_skip_element( ber, &bv );
279	len = bv.bv_len;
280	if ( tag == LBER_DEFAULT || len > sizeof(ber_int_t) ) {
281		return LBER_DEFAULT;
282	}
283
284	/* parse two's complement integer */
285	if( len ) {
286		unsigned char *buf = (unsigned char *) bv.bv_val;
287		ber_len_t i;
288		ber_int_t netnum = buf[0] & 0xff;
289
290		/* sign extend */
291		netnum = (netnum ^ 0x80) - 0x80;
292
293		/* shift in the bytes */
294		for( i = 1; i < len; i++ ) {
295			netnum = (netnum << 8 ) | buf[i];
296		}
297
298		*num = netnum;
299
300	} else {
301		*num = 0;
302	}
303
304	return tag;
305}
306
307ber_tag_t
308ber_get_enum(
309	BerElement *ber,
310	ber_int_t *num )
311{
312	return ber_get_int( ber, num );
313}
314
315ber_tag_t
316ber_get_stringb(
317	BerElement *ber,
318	char *buf,
319	ber_len_t *len )
320{
321	struct berval bv;
322	ber_tag_t	tag;
323
324	if ( (tag = ber_skip_element( ber, &bv )) == LBER_DEFAULT ) {
325		return LBER_DEFAULT;
326	}
327
328	/* must fit within allocated space with termination */
329	if ( bv.bv_len >= *len ) {
330		return LBER_DEFAULT;
331	}
332
333	memcpy( buf, bv.bv_val, bv.bv_len );
334	buf[bv.bv_len] = '\0';
335
336	*len = bv.bv_len;
337	return tag;
338}
339
340/* Definitions for get_string vector
341 *
342 * ChArray, BvArray, and BvVec are self-explanatory.
343 * BvOff is a struct berval embedded in an array of larger structures
344 * of siz bytes at off bytes from the beginning of the struct.
345 */
346enum bgbvc { ChArray, BvArray, BvVec, BvOff };
347
348/* Use this single cookie for state, to keep actual
349 * stack use to the absolute minimum.
350 */
351typedef struct bgbvr {
352	const enum bgbvc choice;
353	const int option;	/* (ALLOC unless BvOff) | (STRING if ChArray) */
354	ber_len_t siz;		/* input array element size, output count */
355	ber_len_t off;		/* BvOff offset to the struct berval */
356	void *result;
357} bgbvr;
358
359static ber_tag_t
360ber_get_stringbvl( BerElement *ber, bgbvr *b )
361{
362	int i = 0, n;
363	ber_tag_t tag;
364	ber_len_t tot_size = 0, siz = b->siz;
365	char *last, *orig;
366	struct berval bv, *bvp = NULL;
367	union stringbvl_u {
368		char **ca;				/* ChArray */
369		BerVarray ba;			/* BvArray */
370		struct berval **bv;		/* BvVec */
371		char *bo;				/* BvOff */
372	} res;
373
374	tag = ber_skip_tag( ber, &bv.bv_len );
375
376	if ( tag != LBER_DEFAULT ) {
377		tag = 0;
378		orig = ber->ber_ptr;
379		last = orig + bv.bv_len;
380
381		for ( ; ber->ber_ptr < last; i++, tot_size += siz ) {
382			if ( ber_skip_element( ber, &bv ) == LBER_DEFAULT )
383				break;
384		}
385		if ( ber->ber_ptr != last ) {
386			i = 0;
387			tag = LBER_DEFAULT;
388		}
389
390		ber->ber_ptr = orig;
391		ber->ber_tag = *(unsigned char *) orig;
392	}
393
394	b->siz = i;
395	if ( i == 0 ) {
396		return tag;
397	}
398
399	/* Allocate and NULL-terminate the result vector */
400	b->result = ber_memalloc_x( tot_size + siz, ber->ber_memctx );
401	if ( b->result == NULL ) {
402		return LBER_DEFAULT;
403	}
404	switch (b->choice) {
405	case ChArray:
406		res.ca = b->result;
407		res.ca[i] = NULL;
408		break;
409	case BvArray:
410		res.ba = b->result;
411		res.ba[i].bv_val = NULL;
412		break;
413	case BvVec:
414		res.bv = b->result;
415		res.bv[i] = NULL;
416		break;
417	case BvOff:
418		res.bo = (char *) b->result + b->off;
419		((struct berval *) (res.bo + tot_size))->bv_val = NULL;
420		tot_size = 0;
421		break;
422	}
423
424	n = 0;
425	do {
426		tag = ber_get_stringbv( ber, &bv, b->option );
427		if ( tag == LBER_DEFAULT ) {
428			goto failed;
429		}
430
431		/* store my result */
432		switch (b->choice) {
433		case ChArray:
434			res.ca[n] = bv.bv_val;
435			break;
436		case BvArray:
437			res.ba[n] = bv;
438			break;
439		case BvVec:
440			bvp = ber_memalloc_x( sizeof( struct berval ),
441				ber->ber_memctx );
442			if ( !bvp ) {
443				ber_memfree_x( bv.bv_val, ber->ber_memctx );
444				goto failed;
445			}
446			res.bv[n] = bvp;
447			*bvp = bv;
448			break;
449		case BvOff:
450			*(struct berval *)(res.bo + tot_size) = bv;
451			tot_size += siz;
452			break;
453		}
454	} while (++n < i);
455	return tag;
456
457failed:
458	if (b->choice != BvOff) { /* BvOff does not have LBER_BV_ALLOC set */
459		while (--n >= 0) {
460			switch(b->choice) {
461			case ChArray:
462				ber_memfree_x(res.ca[n], ber->ber_memctx);
463				break;
464			case BvArray:
465				ber_memfree_x(res.ba[n].bv_val, ber->ber_memctx);
466				break;
467			case BvVec:
468				ber_memfree_x(res.bv[n]->bv_val, ber->ber_memctx);
469				ber_memfree_x(res.bv[n], ber->ber_memctx);
470				break;
471			default:
472				break;
473			}
474		}
475	}
476	ber_memfree_x(b->result, ber->ber_memctx);
477	b->result = NULL;
478	return LBER_DEFAULT;
479}
480
481ber_tag_t
482ber_get_stringbv( BerElement *ber, struct berval *bv, int option )
483{
484	ber_tag_t	tag;
485	char		*data;
486
487	tag = ber_skip_element( ber, bv );
488	if ( tag == LBER_DEFAULT ||
489		(( option & LBER_BV_STRING ) &&
490		 bv->bv_len && memchr( bv->bv_val, 0, bv->bv_len - 1 )))
491	{
492		bv->bv_val = NULL;
493		return LBER_DEFAULT;
494	}
495
496	data = bv->bv_val;
497	if ( option & LBER_BV_ALLOC ) {
498		bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1,
499			ber->ber_memctx );
500		if ( bv->bv_val == NULL ) {
501			return LBER_DEFAULT;
502		}
503
504		if ( bv->bv_len != 0 ) {
505			memcpy( bv->bv_val, data, bv->bv_len );
506		}
507		data = bv->bv_val;
508	}
509	if ( !( option & LBER_BV_NOTERM ))
510		data[bv->bv_len] = '\0';
511
512	return tag;
513}
514
515ber_tag_t
516ber_get_stringbv_null( BerElement *ber, struct berval *bv, int option )
517{
518	ber_tag_t	tag;
519	char		*data;
520
521	tag = ber_skip_element( ber, bv );
522	if ( tag == LBER_DEFAULT || bv->bv_len == 0 ) {
523		bv->bv_val = NULL;
524		return tag;
525	}
526
527	if (( option & LBER_BV_STRING ) &&
528		memchr( bv->bv_val, 0, bv->bv_len - 1 ))
529	{
530		bv->bv_val = NULL;
531		return LBER_DEFAULT;
532	}
533
534	data = bv->bv_val;
535	if ( option & LBER_BV_ALLOC ) {
536		bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1,
537			ber->ber_memctx );
538		if ( bv->bv_val == NULL ) {
539			return LBER_DEFAULT;
540		}
541
542		memcpy( bv->bv_val, data, bv->bv_len );
543		data = bv->bv_val;
544	}
545	if ( !( option & LBER_BV_NOTERM ))
546		data[bv->bv_len] = '\0';
547
548	return tag;
549}
550
551ber_tag_t
552ber_get_stringa( BerElement *ber, char **buf )
553{
554	BerValue	bv;
555	ber_tag_t	tag;
556
557	assert( buf != NULL );
558
559	tag = ber_get_stringbv( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING );
560	*buf = bv.bv_val;
561
562	return tag;
563}
564
565ber_tag_t
566ber_get_stringa_null( BerElement *ber, char **buf )
567{
568	BerValue	bv;
569	ber_tag_t	tag;
570
571	assert( buf != NULL );
572
573	tag = ber_get_stringbv_null( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING );
574	*buf = bv.bv_val;
575
576	return tag;
577}
578
579ber_tag_t
580ber_get_stringal( BerElement *ber, struct berval **bv )
581{
582	ber_tag_t	tag;
583
584	assert( ber != NULL );
585	assert( bv != NULL );
586
587	*bv = (struct berval *) ber_memalloc_x( sizeof(struct berval),
588		ber->ber_memctx );
589	if ( *bv == NULL ) {
590		return LBER_DEFAULT;
591	}
592
593	tag = ber_get_stringbv( ber, *bv, LBER_BV_ALLOC );
594	if ( tag == LBER_DEFAULT ) {
595		ber_memfree_x( *bv, ber->ber_memctx );
596		*bv = NULL;
597	}
598	return tag;
599}
600
601ber_tag_t
602ber_get_bitstringa(
603	BerElement *ber,
604	char **buf,
605	ber_len_t *blen )
606{
607	ber_tag_t	tag;
608	struct berval	data;
609	unsigned char	unusedbits;
610
611	assert( buf != NULL );
612	assert( blen != NULL );
613
614	if ( (tag = ber_skip_element( ber, &data )) == LBER_DEFAULT ) {
615		goto fail;
616	}
617
618	if ( --data.bv_len > (ber_len_t)-1 / 8 ) {
619		goto fail;
620	}
621	unusedbits = *(unsigned char *) data.bv_val++;
622	if ( unusedbits > 7 ) {
623		goto fail;
624	}
625
626	if ( memchr( data.bv_val, 0, data.bv_len )) {
627		goto fail;
628	}
629
630	*buf = (char *) ber_memalloc_x( data.bv_len, ber->ber_memctx );
631	if ( *buf == NULL ) {
632		return LBER_DEFAULT;
633	}
634	memcpy( *buf, data.bv_val, data.bv_len );
635
636	*blen = data.bv_len * 8 - unusedbits;
637	return tag;
638
639 fail:
640	*buf = NULL;
641	return LBER_DEFAULT;
642}
643
644ber_tag_t
645ber_get_null( BerElement *ber )
646{
647	ber_len_t	len;
648	ber_tag_t	tag = ber_skip_tag( ber, &len );
649
650	return( len == 0 ? tag : LBER_DEFAULT );
651}
652
653ber_tag_t
654ber_get_boolean(
655	BerElement *ber,
656	ber_int_t *boolval )
657{
658	return ber_get_int( ber, boolval );
659}
660
661ber_tag_t
662ber_first_element(
663	BerElement *ber,
664	ber_len_t *len,
665	char **last )
666{
667	assert( last != NULL );
668
669	/* skip the sequence header, use the len to mark where to stop */
670	if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) {
671		*last = NULL;
672		return LBER_DEFAULT;
673	}
674
675	*last = ber->ber_ptr + *len;
676
677	if ( *len == 0 ) {
678		return LBER_DEFAULT;
679	}
680
681	return ber_peek_tag( ber, len );
682}
683
684ber_tag_t
685ber_next_element(
686	BerElement *ber,
687	ber_len_t *len,
688	LDAP_CONST char *last )
689{
690	assert( ber != NULL );
691	assert( last != NULL );
692	assert( LBER_VALID( ber ) );
693
694	if ( ber->ber_ptr >= last ) {
695		return LBER_DEFAULT;
696	}
697
698	return ber_peek_tag( ber, len );
699}
700
701/* VARARGS */
702ber_tag_t
703ber_scanf ( BerElement *ber,
704	LDAP_CONST char *fmt,
705	... )
706{
707	va_list		ap;
708	LDAP_CONST char		*fmt_reset;
709	char		*s, **ss, ***sss;
710	struct berval	data, *bval, **bvp, ***bvpp;
711	ber_int_t	*i;
712	ber_len_t	*l;
713	ber_tag_t	*t;
714	ber_tag_t	rc;
715	ber_len_t	len;
716
717	va_start( ap, fmt );
718
719	assert( ber != NULL );
720	assert( fmt != NULL );
721	assert( LBER_VALID( ber ) );
722
723	fmt_reset = fmt;
724
725	if ( ber->ber_debug & (LDAP_DEBUG_TRACE|LDAP_DEBUG_BER)) {
726		ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
727			"ber_scanf fmt (%s) ber:\n", fmt );
728		ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
729	}
730
731	for ( rc = 0; *fmt && rc != LBER_DEFAULT; fmt++ ) {
732		/* When this is modified, remember to update
733		 * the error-cleanup code below accordingly. */
734		switch ( *fmt ) {
735		case '!': { /* Hook */
736				BERDecodeCallback *f;
737				void *p;
738
739				f = va_arg( ap, BERDecodeCallback * );
740				p = va_arg( ap, void * );
741
742				rc = (*f)( ber, p, 0 );
743			} break;
744
745		case 'a':	/* octet string - allocate storage as needed */
746			ss = va_arg( ap, char ** );
747			rc = ber_get_stringa( ber, ss );
748			break;
749
750		case 'A':	/* octet string - allocate storage as needed,
751				 * but return NULL if len == 0 */
752			ss = va_arg( ap, char ** );
753			rc = ber_get_stringa_null( ber, ss );
754			break;
755
756		case 'b':	/* boolean */
757			i = va_arg( ap, ber_int_t * );
758			rc = ber_get_boolean( ber, i );
759			break;
760
761		case 'B':	/* bit string - allocate storage as needed */
762			ss = va_arg( ap, char ** );
763			l = va_arg( ap, ber_len_t * ); /* for length, in bits */
764			rc = ber_get_bitstringa( ber, ss, l );
765			break;
766
767		case 'e':	/* enumerated */
768		case 'i':	/* integer */
769			i = va_arg( ap, ber_int_t * );
770			rc = ber_get_int( ber, i );
771			break;
772
773		case 'l':	/* length of next item */
774			l = va_arg( ap, ber_len_t * );
775			rc = ber_peek_tag( ber, l );
776			break;
777
778		case 'm':	/* octet string in berval, in-place */
779			bval = va_arg( ap, struct berval * );
780			rc = ber_get_stringbv( ber, bval, 0 );
781			break;
782
783		case 'M':	/* bvoffarray - must include address of
784				 * a record len, and record offset.
785				 * number of records will be returned thru
786				 * len ptr on finish. parsed in-place.
787				 */
788		{
789			bgbvr cookie = { BvOff, 0 };
790			bvp = va_arg( ap, struct berval ** );
791			l = va_arg( ap, ber_len_t * );
792			cookie.siz = *l;
793			cookie.off = va_arg( ap, ber_len_t );
794			rc = ber_get_stringbvl( ber, &cookie );
795			*bvp = cookie.result;
796			*l = cookie.siz;
797			break;
798		}
799
800		case 'n':	/* null */
801			rc = ber_get_null( ber );
802			break;
803
804		case 'o':	/* octet string in a supplied berval */
805			bval = va_arg( ap, struct berval * );
806			rc = ber_get_stringbv( ber, bval, LBER_BV_ALLOC );
807			break;
808
809		case 'O':	/* octet string - allocate & include length */
810			bvp = va_arg( ap, struct berval ** );
811			rc = ber_get_stringal( ber, bvp );
812			break;
813
814		case 's':	/* octet string - in a buffer */
815			s = va_arg( ap, char * );
816			l = va_arg( ap, ber_len_t * );
817			rc = ber_get_stringb( ber, s, l );
818			break;
819
820		case 't':	/* tag of next item */
821			t = va_arg( ap, ber_tag_t * );
822			*t = rc = ber_peek_tag( ber, &len );
823			break;
824
825		case 'T':	/* skip tag of next item */
826			t = va_arg( ap, ber_tag_t * );
827			*t = rc = ber_skip_tag( ber, &len );
828			break;
829
830		case 'v':	/* sequence of strings */
831		{
832			bgbvr cookie = {
833				ChArray, LBER_BV_ALLOC | LBER_BV_STRING, sizeof( char * )
834			};
835			rc = ber_get_stringbvl( ber, &cookie );
836			*(va_arg( ap, char *** )) = cookie.result;
837			break;
838		}
839
840		case 'V':	/* sequence of strings + lengths */
841		{
842			bgbvr cookie = {
843				BvVec, LBER_BV_ALLOC, sizeof( struct berval * )
844			};
845			rc = ber_get_stringbvl( ber, &cookie );
846			*(va_arg( ap, struct berval *** )) = cookie.result;
847			break;
848		}
849
850		case 'W':	/* bvarray */
851		{
852			bgbvr cookie = {
853				BvArray, LBER_BV_ALLOC, sizeof( struct berval )
854			};
855			rc = ber_get_stringbvl( ber, &cookie );
856			*(va_arg( ap, struct berval ** )) = cookie.result;
857			break;
858		}
859
860		case 'x':	/* skip the next element - whatever it is */
861			rc = ber_skip_element( ber, &data );
862			break;
863
864		case '{':	/* begin sequence */
865		case '[':	/* begin set */
866			switch ( fmt[1] ) {
867			case 'v': case 'V': case 'W': case 'M':
868				break;
869			default:
870				rc = ber_skip_tag( ber, &len );
871				break;
872			}
873			break;
874
875		case '}':	/* end sequence */
876		case ']':	/* end set */
877			break;
878
879		default:
880			if( ber->ber_debug ) {
881				ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
882					"ber_scanf: unknown fmt %c\n", *fmt );
883			}
884			rc = LBER_DEFAULT;
885			break;
886		}
887	}
888
889	va_end( ap );
890
891	if ( rc == LBER_DEFAULT ) {
892		/*
893		 * Error.  Reclaim malloced memory that was given to the caller.
894		 * Set allocated pointers to NULL, "data length" outvalues to 0.
895		 */
896		va_start( ap, fmt );
897
898		for ( ; fmt_reset < fmt; fmt_reset++ ) {
899		switch ( *fmt_reset ) {
900		case '!': { /* Hook */
901				BERDecodeCallback *f;
902				void *p;
903
904				f = va_arg( ap, BERDecodeCallback * );
905				p = va_arg( ap, void * );
906
907				(void) (*f)( ber, p, 1 );
908			} break;
909
910		case 'a':	/* octet string - allocate storage as needed */
911		case 'A':
912			ss = va_arg( ap, char ** );
913			ber_memfree_x( *ss, ber->ber_memctx );
914			*ss = NULL;
915			break;
916
917		case 'b':	/* boolean */
918		case 'e':	/* enumerated */
919		case 'i':	/* integer */
920			(void) va_arg( ap, ber_int_t * );
921			break;
922
923		case 'l':	/* length of next item */
924			*(va_arg( ap, ber_len_t * )) = 0;
925			break;
926
927		case 'm':	/* berval in-place */
928			bval = va_arg( ap, struct berval * );
929			BER_BVZERO( bval );
930			break;
931
932		case 'M':	/* BVoff array in-place */
933			bvp = va_arg( ap, struct berval ** );
934			ber_memfree_x( *bvp, ber->ber_memctx );
935			*bvp = NULL;
936			*(va_arg( ap, ber_len_t * )) = 0;
937			(void) va_arg( ap, ber_len_t );
938			break;
939
940		case 'o':	/* octet string in a supplied berval */
941			bval = va_arg( ap, struct berval * );
942			ber_memfree_x( bval->bv_val, ber->ber_memctx );
943			BER_BVZERO( bval );
944			break;
945
946		case 'O':	/* octet string - allocate & include length */
947			bvp = va_arg( ap, struct berval ** );
948			ber_bvfree_x( *bvp, ber->ber_memctx );
949			*bvp = NULL;
950			break;
951
952		case 's':	/* octet string - in a buffer */
953			(void) va_arg( ap, char * );
954			*(va_arg( ap, ber_len_t * )) = 0;
955			break;
956
957		case 't':	/* tag of next item */
958		case 'T':	/* skip tag of next item */
959			(void) va_arg( ap, ber_tag_t * );
960			break;
961
962		case 'B':	/* bit string - allocate storage as needed */
963			ss = va_arg( ap, char ** );
964			ber_memfree_x( *ss, ber->ber_memctx );
965			*ss = NULL;
966			*(va_arg( ap, ber_len_t * )) = 0; /* for length, in bits */
967			break;
968
969		case 'v':	/* sequence of strings */
970			sss = va_arg( ap, char *** );
971			ber_memvfree_x( (void **) *sss, ber->ber_memctx );
972			*sss = NULL;
973			break;
974
975		case 'V':	/* sequence of strings + lengths */
976			bvpp = va_arg( ap, struct berval *** );
977			ber_bvecfree_x( *bvpp, ber->ber_memctx );
978			*bvpp = NULL;
979			break;
980
981		case 'W':	/* BerVarray */
982			bvp = va_arg( ap, struct berval ** );
983			ber_bvarray_free_x( *bvp, ber->ber_memctx );
984			*bvp = NULL;
985			break;
986
987		case 'n':	/* null */
988		case 'x':	/* skip the next element - whatever it is */
989		case '{':	/* begin sequence */
990		case '[':	/* begin set */
991		case '}':	/* end sequence */
992		case ']':	/* end set */
993			break;
994
995		default:
996			/* format should be good */
997			assert( 0 );
998		}
999		}
1000
1001		va_end( ap );
1002	}
1003
1004	return rc;
1005}
1006