xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/getdn.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: getdn.c,v 1.3 2021/08/14 16:14:56 christos Exp $	*/
24e6df137Slukem 
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem  *
6*549b59edSchristos  * Copyright 1998-2021 The OpenLDAP Foundation.
72de962bdSlukem  * All rights reserved.
82de962bdSlukem  *
92de962bdSlukem  * Redistribution and use in source and binary forms, with or without
102de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem  * Public License.
122de962bdSlukem  *
132de962bdSlukem  * A copy of this license is available in the file LICENSE in the
142de962bdSlukem  * top-level directory of the distribution or, alternatively, at
152de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem  */
172de962bdSlukem /* Portions Copyright (c) 1994 Regents of the University of Michigan.
182de962bdSlukem  * All rights reserved.
192de962bdSlukem  */
202de962bdSlukem 
21376af7d7Schristos #include <sys/cdefs.h>
22*549b59edSchristos __RCSID("$NetBSD: getdn.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
23376af7d7Schristos 
242de962bdSlukem #include "portable.h"
252de962bdSlukem 
262de962bdSlukem #include <stdio.h>
272de962bdSlukem 
282de962bdSlukem #include <ac/stdlib.h>
292de962bdSlukem #include <ac/socket.h>
302de962bdSlukem #include <ac/string.h>
312de962bdSlukem #include <ac/time.h>
322de962bdSlukem 
332de962bdSlukem #include "ldap-int.h"
342de962bdSlukem #include "ldap_schema.h"
354e27b3e8Schristos #include "ldif.h"
362de962bdSlukem 
372de962bdSlukem /* extension to UFN that turns trailing "dc=value" rdns in DNS style,
382de962bdSlukem  * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
392de962bdSlukem #define DC_IN_UFN
402de962bdSlukem 
412de962bdSlukem /* parsing/printing routines */
422de962bdSlukem static int str2strval( const char *str, ber_len_t stoplen, struct berval *val,
432de962bdSlukem 		const char **next, unsigned flags, int *retFlags, void *ctx );
442de962bdSlukem static int DCE2strval( const char *str, struct berval *val,
452de962bdSlukem 		const char **next, unsigned flags, void *ctx );
462de962bdSlukem static int IA52strval( const char *str, struct berval *val,
472de962bdSlukem 		const char **next, unsigned flags, void *ctx );
482de962bdSlukem static int quotedIA52strval( const char *str, struct berval *val,
492de962bdSlukem 		const char **next, unsigned flags, void *ctx );
502de962bdSlukem static int hexstr2binval( const char *str, struct berval *val,
512de962bdSlukem 		const char **next, unsigned flags, void *ctx );
522de962bdSlukem static int hexstr2bin( const char *str, char *c );
532de962bdSlukem static int byte2hexpair( const char *val, char *pair );
542de962bdSlukem static int binval2hexstr( struct berval *val, char *str );
552de962bdSlukem static int strval2strlen( struct berval *val, unsigned flags,
562de962bdSlukem 		ber_len_t *len );
572de962bdSlukem static int strval2str( struct berval *val, char *str, unsigned flags,
582de962bdSlukem 		ber_len_t *len );
592de962bdSlukem static int strval2IA5strlen( struct berval *val, unsigned flags,
602de962bdSlukem 		ber_len_t *len );
612de962bdSlukem static int strval2IA5str( struct berval *val, char *str, unsigned flags,
622de962bdSlukem 		ber_len_t *len );
632de962bdSlukem static int strval2DCEstrlen( struct berval *val, unsigned flags,
642de962bdSlukem 		ber_len_t *len );
652de962bdSlukem static int strval2DCEstr( struct berval *val, char *str, unsigned flags,
662de962bdSlukem 		ber_len_t *len );
672de962bdSlukem static int strval2ADstrlen( struct berval *val, unsigned flags,
682de962bdSlukem 		ber_len_t *len );
692de962bdSlukem static int strval2ADstr( struct berval *val, char *str, unsigned flags,
702de962bdSlukem 		ber_len_t *len );
712de962bdSlukem static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN );
722de962bdSlukem 
732de962bdSlukem /* AVA helpers */
742de962bdSlukem static LDAPAVA * ldapava_new(
752de962bdSlukem 	const struct berval *attr, const struct berval *val, unsigned flags, void *ctx );
762de962bdSlukem 
772de962bdSlukem /* Higher level helpers */
782de962bdSlukem static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
792de962bdSlukem 		int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
802de962bdSlukem static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
812de962bdSlukem 		int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
822de962bdSlukem static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len  );
832de962bdSlukem static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len );
842de962bdSlukem static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
852de962bdSlukem static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first );
862de962bdSlukem static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
872de962bdSlukem static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first );
882de962bdSlukem 
892de962bdSlukem /*
902de962bdSlukem  * RFC 1823 ldap_get_dn
912de962bdSlukem  */
922de962bdSlukem char *
ldap_get_dn(LDAP * ld,LDAPMessage * entry)932de962bdSlukem ldap_get_dn( LDAP *ld, LDAPMessage *entry )
942de962bdSlukem {
952de962bdSlukem 	char		*dn;
962de962bdSlukem 	BerElement	tmp;
972de962bdSlukem 
98*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn\n" );
992de962bdSlukem 
1002de962bdSlukem 	assert( ld != NULL );
1012de962bdSlukem 	assert( LDAP_VALID(ld) );
1022de962bdSlukem 	assert( entry != NULL );
1032de962bdSlukem 
1042de962bdSlukem 	tmp = *entry->lm_ber;	/* struct copy */
1052de962bdSlukem 	if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
1062de962bdSlukem 		ld->ld_errno = LDAP_DECODING_ERROR;
1072de962bdSlukem 		return( NULL );
1082de962bdSlukem 	}
1092de962bdSlukem 
1102de962bdSlukem 	return( dn );
1112de962bdSlukem }
1122de962bdSlukem 
1132de962bdSlukem int
ldap_get_dn_ber(LDAP * ld,LDAPMessage * entry,BerElement ** berout,BerValue * dn)1142de962bdSlukem ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout,
1152de962bdSlukem 	BerValue *dn )
1162de962bdSlukem {
1172de962bdSlukem 	BerElement	tmp, *ber;
1182de962bdSlukem 	ber_len_t	len = 0;
1192de962bdSlukem 	int rc = LDAP_SUCCESS;
1202de962bdSlukem 
121*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n" );
1222de962bdSlukem 
1232de962bdSlukem 	assert( ld != NULL );
1242de962bdSlukem 	assert( LDAP_VALID(ld) );
1252de962bdSlukem 	assert( entry != NULL );
1262de962bdSlukem 	assert( dn != NULL );
1272de962bdSlukem 
1282de962bdSlukem 	dn->bv_val = NULL;
1292de962bdSlukem 	dn->bv_len = 0;
1302de962bdSlukem 
1312de962bdSlukem 	if ( berout ) {
1322de962bdSlukem 		*berout = NULL;
1332de962bdSlukem 		ber = ldap_alloc_ber_with_options( ld );
1342de962bdSlukem 		if( ber == NULL ) {
1352de962bdSlukem 			return LDAP_NO_MEMORY;
1362de962bdSlukem 		}
1372de962bdSlukem 		*berout = ber;
1382de962bdSlukem 	} else {
1392de962bdSlukem 		ber = &tmp;
1402de962bdSlukem 	}
1412de962bdSlukem 
1422de962bdSlukem 	*ber = *entry->lm_ber;	/* struct copy */
1432de962bdSlukem 	if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) {
1442de962bdSlukem 		rc = ld->ld_errno = LDAP_DECODING_ERROR;
1452de962bdSlukem 	}
1462de962bdSlukem 	if ( rc == LDAP_SUCCESS ) {
1472de962bdSlukem 		/* set the length to avoid overrun */
1482de962bdSlukem 		rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
1492de962bdSlukem 		if( rc != LBER_OPT_SUCCESS ) {
1502de962bdSlukem 			rc = ld->ld_errno = LDAP_LOCAL_ERROR;
1512de962bdSlukem 		}
1522de962bdSlukem 	}
1532de962bdSlukem 	if ( rc != LDAP_SUCCESS && berout ) {
1542de962bdSlukem 		ber_free( ber, 0 );
1552de962bdSlukem 		*berout = NULL;
1562de962bdSlukem 	}
1572de962bdSlukem 	return rc;
1582de962bdSlukem }
1592de962bdSlukem 
1602de962bdSlukem /*
1612de962bdSlukem  * RFC 1823 ldap_dn2ufn
1622de962bdSlukem  */
1632de962bdSlukem char *
ldap_dn2ufn(LDAP_CONST char * dn)1642de962bdSlukem ldap_dn2ufn( LDAP_CONST char *dn )
1652de962bdSlukem {
1662de962bdSlukem 	char	*out = NULL;
1672de962bdSlukem 
168*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n" );
1692de962bdSlukem 
1702de962bdSlukem 	( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
1712de962bdSlukem 		&out, LDAP_DN_FORMAT_UFN );
1722de962bdSlukem 
1732de962bdSlukem 	return( out );
1742de962bdSlukem }
1752de962bdSlukem 
1762de962bdSlukem /*
1772de962bdSlukem  * RFC 1823 ldap_explode_dn
1782de962bdSlukem  */
1792de962bdSlukem char **
ldap_explode_dn(LDAP_CONST char * dn,int notypes)1802de962bdSlukem ldap_explode_dn( LDAP_CONST char *dn, int notypes )
1812de962bdSlukem {
1822de962bdSlukem 	LDAPDN	tmpDN;
1832de962bdSlukem 	char	**values = NULL;
1842de962bdSlukem 	int	iRDN;
1852de962bdSlukem 	unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
1862de962bdSlukem 
187*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_explode_dn\n" );
1882de962bdSlukem 
1892de962bdSlukem 	if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP )
1902de962bdSlukem 			!= LDAP_SUCCESS ) {
1912de962bdSlukem 		return NULL;
1922de962bdSlukem 	}
1932de962bdSlukem 
1942de962bdSlukem 	if( tmpDN == NULL ) {
1952de962bdSlukem 		values = LDAP_MALLOC( sizeof( char * ) );
1962de962bdSlukem 		if( values == NULL ) return NULL;
1972de962bdSlukem 
1982de962bdSlukem 		values[0] = NULL;
1992de962bdSlukem 		return values;
2002de962bdSlukem 	}
2012de962bdSlukem 
2022de962bdSlukem 	for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ );
2032de962bdSlukem 
2042de962bdSlukem 	values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
2052de962bdSlukem 	if ( values == NULL ) {
2062de962bdSlukem 		ldap_dnfree( tmpDN );
2072de962bdSlukem 		return NULL;
2082de962bdSlukem 	}
2092de962bdSlukem 
2102de962bdSlukem 	for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) {
2112de962bdSlukem 		ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag );
2122de962bdSlukem 	}
2132de962bdSlukem 	ldap_dnfree( tmpDN );
2142de962bdSlukem 	values[ iRDN ] = NULL;
2152de962bdSlukem 
2162de962bdSlukem 	return values;
2172de962bdSlukem }
2182de962bdSlukem 
2192de962bdSlukem char **
ldap_explode_rdn(LDAP_CONST char * rdn,int notypes)2202de962bdSlukem ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
2212de962bdSlukem {
2222de962bdSlukem 	LDAPRDN		tmpRDN;
2232de962bdSlukem 	char		**values = NULL;
2242de962bdSlukem 	const char 	*p;
2252de962bdSlukem 	int		iAVA;
2262de962bdSlukem 
227*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n" );
2282de962bdSlukem 
2292de962bdSlukem 	/*
2302de962bdSlukem 	 * we only parse the first rdn
2312de962bdSlukem 	 * FIXME: we prefer efficiency over checking if the _ENTIRE_
2322de962bdSlukem 	 * dn can be parsed
2332de962bdSlukem 	 */
2342de962bdSlukem 	if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP )
2352de962bdSlukem 			!= LDAP_SUCCESS ) {
2362de962bdSlukem 		return( NULL );
2372de962bdSlukem 	}
2382de962bdSlukem 
2392de962bdSlukem 	for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ;
2402de962bdSlukem 	values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
2412de962bdSlukem 	if ( values == NULL ) {
2422de962bdSlukem 		ldap_rdnfree( tmpRDN );
2432de962bdSlukem 		return( NULL );
2442de962bdSlukem 	}
2452de962bdSlukem 
2462de962bdSlukem 	for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) {
2472de962bdSlukem 		ber_len_t	l = 0, vl, al = 0;
2482de962bdSlukem 		char		*str;
2492de962bdSlukem 		LDAPAVA		*ava = tmpRDN[ iAVA ];
2502de962bdSlukem 
2512de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
2522de962bdSlukem 			vl = 1 + 2 * ava->la_value.bv_len;
2532de962bdSlukem 
2542de962bdSlukem 		} else {
2552de962bdSlukem 			if ( strval2strlen( &ava->la_value,
2562de962bdSlukem 						ava->la_flags, &vl ) ) {
2572de962bdSlukem 				goto error_return;
2582de962bdSlukem 			}
2592de962bdSlukem 		}
2602de962bdSlukem 
2612de962bdSlukem 		if ( !notypes ) {
2622de962bdSlukem 			al = ava->la_attr.bv_len;
2632de962bdSlukem 			l = vl + ava->la_attr.bv_len + 1;
2642de962bdSlukem 
2652de962bdSlukem 			str = LDAP_MALLOC( l + 1 );
266*549b59edSchristos 			if ( str == NULL ) {
267*549b59edSchristos 				goto error_return;
268*549b59edSchristos 			}
2692de962bdSlukem 			AC_MEMCPY( str, ava->la_attr.bv_val,
2702de962bdSlukem 					ava->la_attr.bv_len );
2712de962bdSlukem 			str[ al++ ] = '=';
2722de962bdSlukem 
2732de962bdSlukem 		} else {
2742de962bdSlukem 			l = vl;
2752de962bdSlukem 			str = LDAP_MALLOC( l + 1 );
276*549b59edSchristos 			if ( str == NULL ) {
277*549b59edSchristos 				goto error_return;
278*549b59edSchristos 			}
2792de962bdSlukem 		}
2802de962bdSlukem 
2812de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
2822de962bdSlukem 			str[ al++ ] = '#';
2832de962bdSlukem 			if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
2842de962bdSlukem 				goto error_return;
2852de962bdSlukem 			}
2862de962bdSlukem 
2872de962bdSlukem 		} else {
2882de962bdSlukem 			if ( strval2str( &ava->la_value, &str[ al ],
2892de962bdSlukem 					ava->la_flags, &vl ) ) {
2902de962bdSlukem 				goto error_return;
2912de962bdSlukem 			}
2922de962bdSlukem 		}
2932de962bdSlukem 
2942de962bdSlukem 		str[ l ] = '\0';
2952de962bdSlukem 		values[ iAVA ] = str;
2962de962bdSlukem 	}
2972de962bdSlukem 	values[ iAVA ] = NULL;
2982de962bdSlukem 
2992de962bdSlukem 	ldap_rdnfree( tmpRDN );
3002de962bdSlukem 
3012de962bdSlukem 	return( values );
3022de962bdSlukem 
3032de962bdSlukem error_return:;
3042de962bdSlukem 	LBER_VFREE( values );
3052de962bdSlukem 	ldap_rdnfree( tmpRDN );
3062de962bdSlukem 	return( NULL );
3072de962bdSlukem }
3082de962bdSlukem 
3092de962bdSlukem char *
ldap_dn2dcedn(LDAP_CONST char * dn)3102de962bdSlukem ldap_dn2dcedn( LDAP_CONST char *dn )
3112de962bdSlukem {
3122de962bdSlukem 	char	*out = NULL;
3132de962bdSlukem 
314*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n" );
3152de962bdSlukem 
3162de962bdSlukem 	( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
3172de962bdSlukem 				   &out, LDAP_DN_FORMAT_DCE );
3182de962bdSlukem 
3192de962bdSlukem 	return( out );
3202de962bdSlukem }
3212de962bdSlukem 
3222de962bdSlukem char *
ldap_dcedn2dn(LDAP_CONST char * dce)3232de962bdSlukem ldap_dcedn2dn( LDAP_CONST char *dce )
3242de962bdSlukem {
3252de962bdSlukem 	char	*out = NULL;
3262de962bdSlukem 
327*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n" );
3282de962bdSlukem 
3292de962bdSlukem 	( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
3302de962bdSlukem 
3312de962bdSlukem 	return( out );
3322de962bdSlukem }
3332de962bdSlukem 
3342de962bdSlukem char *
ldap_dn2ad_canonical(LDAP_CONST char * dn)3352de962bdSlukem ldap_dn2ad_canonical( LDAP_CONST char *dn )
3362de962bdSlukem {
3372de962bdSlukem 	char	*out = NULL;
3382de962bdSlukem 
339*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n" );
3402de962bdSlukem 
3412de962bdSlukem 	( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
3422de962bdSlukem 		       &out, LDAP_DN_FORMAT_AD_CANONICAL );
3432de962bdSlukem 
3442de962bdSlukem 	return( out );
3452de962bdSlukem }
3462de962bdSlukem 
3472de962bdSlukem /*
3482de962bdSlukem  * function that changes the string representation of dnin
3492de962bdSlukem  * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
3502de962bdSlukem  *
3512de962bdSlukem  * fin can be one of:
3522de962bdSlukem  * 	LDAP_DN_FORMAT_LDAP		(RFC 4514 liberal, plus some RFC 1779)
3532de962bdSlukem  * 	LDAP_DN_FORMAT_LDAPV3	(RFC 4514)
3542de962bdSlukem  * 	LDAP_DN_FORMAT_LDAPV2	(RFC 1779)
3552de962bdSlukem  * 	LDAP_DN_FORMAT_DCE		(?)
3562de962bdSlukem  *
3572de962bdSlukem  * fout can be any of the above except
3582de962bdSlukem  * 	LDAP_DN_FORMAT_LDAP
3592de962bdSlukem  * plus:
3602de962bdSlukem  * 	LDAP_DN_FORMAT_UFN		(RFC 1781, partial and with extensions)
3612de962bdSlukem  * 	LDAP_DN_FORMAT_AD_CANONICAL	(?)
3622de962bdSlukem  */
3632de962bdSlukem int
ldap_dn_normalize(LDAP_CONST char * dnin,unsigned fin,char ** dnout,unsigned fout)3642de962bdSlukem ldap_dn_normalize( LDAP_CONST char *dnin,
3652de962bdSlukem 	unsigned fin, char **dnout, unsigned fout )
3662de962bdSlukem {
3672de962bdSlukem 	int	rc;
3682de962bdSlukem 	LDAPDN	tmpDN = NULL;
3692de962bdSlukem 
370*549b59edSchristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n" );
3712de962bdSlukem 
3722de962bdSlukem 	assert( dnout != NULL );
3732de962bdSlukem 
3742de962bdSlukem 	*dnout = NULL;
3752de962bdSlukem 
3762de962bdSlukem 	if ( dnin == NULL ) {
3772de962bdSlukem 		return( LDAP_SUCCESS );
3782de962bdSlukem 	}
3792de962bdSlukem 
3802de962bdSlukem 	rc = ldap_str2dn( dnin , &tmpDN, fin );
3812de962bdSlukem 	if ( rc != LDAP_SUCCESS ) {
3822de962bdSlukem 		return( rc );
3832de962bdSlukem 	}
3842de962bdSlukem 
3852de962bdSlukem 	rc = ldap_dn2str( tmpDN, dnout, fout );
3862de962bdSlukem 
3872de962bdSlukem 	ldap_dnfree( tmpDN );
3882de962bdSlukem 
3892de962bdSlukem 	return( rc );
3902de962bdSlukem }
3912de962bdSlukem 
3922de962bdSlukem /* States */
3932de962bdSlukem #define B4AVA			0x0000
3942de962bdSlukem 
3952de962bdSlukem /* #define	B4ATTRTYPE		0x0001 */
3962de962bdSlukem #define B4OIDATTRTYPE		0x0002
3972de962bdSlukem #define B4STRINGATTRTYPE	0x0003
3982de962bdSlukem 
3992de962bdSlukem #define B4AVAEQUALS		0x0100
4002de962bdSlukem #define B4AVASEP		0x0200
4012de962bdSlukem #define B4RDNSEP		0x0300
4022de962bdSlukem #define GOTAVA			0x0400
4032de962bdSlukem 
4042de962bdSlukem #define B4ATTRVALUE		0x0010
4052de962bdSlukem #define B4STRINGVALUE		0x0020
4062de962bdSlukem #define B4IA5VALUEQUOTED	0x0030
4072de962bdSlukem #define B4IA5VALUE		0x0040
4082de962bdSlukem #define B4BINARYVALUE		0x0050
4092de962bdSlukem 
4102de962bdSlukem /*
4112de962bdSlukem  * Helpers (mostly from slap.h)
4122de962bdSlukem  * c is assumed to Unicode in an ASCII compatible format (UTF-8)
4132de962bdSlukem  * Macros assume "C" Locale (ASCII)
4142de962bdSlukem  */
4152de962bdSlukem #define LDAP_DN_ASCII_SPACE(c) \
4162de962bdSlukem 	( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
4172de962bdSlukem #define LDAP_DN_ASCII_LOWER(c)		LDAP_LOWER(c)
4182de962bdSlukem #define LDAP_DN_ASCII_UPPER(c)		LDAP_UPPER(c)
4192de962bdSlukem #define LDAP_DN_ASCII_ALPHA(c)		LDAP_ALPHA(c)
4202de962bdSlukem 
4212de962bdSlukem #define LDAP_DN_ASCII_DIGIT(c)		LDAP_DIGIT(c)
4222de962bdSlukem #define LDAP_DN_ASCII_LCASE_HEXALPHA(c)	LDAP_HEXLOWER(c)
4232de962bdSlukem #define LDAP_DN_ASCII_UCASE_HEXALPHA(c)	LDAP_HEXUPPER(c)
4242de962bdSlukem #define LDAP_DN_ASCII_HEXDIGIT(c)	LDAP_HEX(c)
4252de962bdSlukem #define LDAP_DN_ASCII_ALNUM(c)		LDAP_ALNUM(c)
4262de962bdSlukem #define LDAP_DN_ASCII_PRINTABLE(c)	( (c) >= ' ' && (c) <= '~' )
4272de962bdSlukem 
4282de962bdSlukem /* attribute type */
4292de962bdSlukem #define LDAP_DN_OID_LEADCHAR(c)		LDAP_DIGIT(c)
4302de962bdSlukem #define LDAP_DN_DESC_LEADCHAR(c)	LDAP_ALPHA(c)
4312de962bdSlukem #define LDAP_DN_DESC_CHAR(c)		LDAP_LDH(c)
4322de962bdSlukem #define LDAP_DN_LANG_SEP(c)		( (c) == ';' )
4332de962bdSlukem #define LDAP_DN_ATTRDESC_CHAR(c) \
4342de962bdSlukem 	( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
4352de962bdSlukem 
4362de962bdSlukem /* special symbols */
4372de962bdSlukem #define LDAP_DN_AVA_EQUALS(c)		( (c) == '=' )
4382de962bdSlukem #define LDAP_DN_AVA_SEP(c)		( (c) == '+' )
4392de962bdSlukem #define LDAP_DN_RDN_SEP(c)		( (c) == ',' )
4402de962bdSlukem #define LDAP_DN_RDN_SEP_V2(c)		( LDAP_DN_RDN_SEP(c) || (c) == ';' )
4412de962bdSlukem #define LDAP_DN_OCTOTHORPE(c)		( (c) == '#' )
4422de962bdSlukem #define LDAP_DN_QUOTES(c)		( (c) == '\"' )
4432de962bdSlukem #define LDAP_DN_ESCAPE(c)		( (c) == '\\' )
4442de962bdSlukem #define LDAP_DN_VALUE_END(c) \
4452de962bdSlukem 	( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
4462de962bdSlukem 
4472de962bdSlukem /* NOTE: according to RFC 4514, '=' can be escaped and treated as special,
4482de962bdSlukem  * i.e. escaped both as "\<hexpair>" and * as "\=", but it is treated as
4492de962bdSlukem  * a regular char, i.e. it can also appear as '='.
4502de962bdSlukem  *
4512de962bdSlukem  * As such, in 2.2 we used to allow reading unescaped '=', but we always
4522de962bdSlukem  * produced escaped '\3D'; this changes since 2.3, if compatibility issues
4532de962bdSlukem  * do not arise
4542de962bdSlukem  */
4552de962bdSlukem #define LDAP_DN_NE(c) \
4562de962bdSlukem 	( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
4572de962bdSlukem 	  || LDAP_DN_QUOTES(c) \
4582de962bdSlukem 	  || (c) == '<' || (c) == '>' )
4592de962bdSlukem #define LDAP_DN_MAYESCAPE(c) \
4602de962bdSlukem 	( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
4612de962bdSlukem 	  || LDAP_DN_AVA_EQUALS(c) \
4622de962bdSlukem 	  || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
4632de962bdSlukem #define LDAP_DN_SHOULDESCAPE(c)		( LDAP_DN_AVA_EQUALS(c) )
4642de962bdSlukem 
4652de962bdSlukem #define LDAP_DN_NEEDESCAPE(c) \
4662de962bdSlukem 	( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
4672de962bdSlukem #define LDAP_DN_NEEDESCAPE_LEAD(c) 	LDAP_DN_MAYESCAPE(c)
4682de962bdSlukem #define LDAP_DN_NEEDESCAPE_TRAIL(c) \
4692de962bdSlukem 	( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
4702de962bdSlukem #define LDAP_DN_WILLESCAPE_CHAR(c) \
4712de962bdSlukem 	( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
4722de962bdSlukem #define LDAP_DN_IS_PRETTY(f)		( (f) & LDAP_DN_PRETTY )
4732de962bdSlukem #define LDAP_DN_WILLESCAPE_HEX(f, c) \
4742de962bdSlukem 	( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
4752de962bdSlukem 
4762de962bdSlukem /* LDAPv2 */
4772de962bdSlukem #define	LDAP_DN_VALUE_END_V2(c) \
4782de962bdSlukem 	( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
4792de962bdSlukem /* RFC 1779 */
4802de962bdSlukem #define	LDAP_DN_V2_SPECIAL(c) \
4812de962bdSlukem 	  ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
4822de962bdSlukem 	    || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
4832de962bdSlukem 	    || LDAP_DN_OCTOTHORPE(c) )
4842de962bdSlukem #define LDAP_DN_V2_PAIR(c) \
4852de962bdSlukem 	  ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
4862de962bdSlukem 
4872de962bdSlukem /*
4882de962bdSlukem  * DCE (mostly from Luke Howard and IBM implementation for AIX)
4892de962bdSlukem  *
4902de962bdSlukem  * From: "Application Development Guide - Directory Services" (FIXME: add link?)
4912de962bdSlukem  * Here escapes and valid chars for GDS are considered; as soon as more
4922de962bdSlukem  * specific info is found, the macros will be updated.
4932de962bdSlukem  *
4942de962bdSlukem  * Chars:	'a'-'z', 'A'-'Z', '0'-'9',
4952de962bdSlukem  *		'.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
4962de962bdSlukem  *
4972de962bdSlukem  * Metachars:	'/', ',', '=', '\'.
4982de962bdSlukem  *
4992de962bdSlukem  * the '\' is used to escape other metachars.
5002de962bdSlukem  *
5012de962bdSlukem  * Assertion:		'='
5022de962bdSlukem  * RDN separator:	'/'
5032de962bdSlukem  * AVA separator:	','
5042de962bdSlukem  *
5052de962bdSlukem  * Attribute types must start with alphabetic chars and can contain
5062de962bdSlukem  * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
5072de962bdSlukem  */
5082de962bdSlukem #define LDAP_DN_RDN_SEP_DCE(c)		( (c) == '/' )
5092de962bdSlukem #define LDAP_DN_AVA_SEP_DCE(c)		( (c) == ',' )
5102de962bdSlukem #define LDAP_DN_ESCAPE_DCE(c)		( LDAP_DN_ESCAPE(c) )
5112de962bdSlukem #define	LDAP_DN_VALUE_END_DCE(c) \
5122de962bdSlukem 	( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
5132de962bdSlukem #define LDAP_DN_NEEDESCAPE_DCE(c) \
5142de962bdSlukem 	( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
5152de962bdSlukem 
5162de962bdSlukem /* AD Canonical */
5172de962bdSlukem #define LDAP_DN_RDN_SEP_AD(c)		( (c) == '/' )
5182de962bdSlukem #define LDAP_DN_ESCAPE_AD(c)		( LDAP_DN_ESCAPE(c) )
5192de962bdSlukem #define LDAP_DN_AVA_SEP_AD(c)		( (c) == ',' )	/* assume same as DCE */
5202de962bdSlukem #define	LDAP_DN_VALUE_END_AD(c) \
5212de962bdSlukem 	( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
5222de962bdSlukem #define LDAP_DN_NEEDESCAPE_AD(c) \
5232de962bdSlukem 	( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
5242de962bdSlukem 
5252de962bdSlukem /* generics */
5262de962bdSlukem #define LDAP_DN_HEXPAIR(s) \
5272de962bdSlukem 	( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
5282de962bdSlukem /* better look at the AttributeDescription? */
5292de962bdSlukem 
5302de962bdSlukem /* FIXME: no composite rdn or non-"dc" types, right?
5312de962bdSlukem  * (what about "dc" in OID form?) */
5322de962bdSlukem /* FIXME: we do not allow binary values in domain, right? */
5332de962bdSlukem /* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
5342de962bdSlukem /* NOTE: don't use strcasecmp() as it is locale specific! */
5352de962bdSlukem #define	LDAP_DC_ATTR	"dc"
5362de962bdSlukem #define	LDAP_DC_ATTRU	"DC"
5372de962bdSlukem #define LDAP_DN_IS_RDN_DC( r ) \
5382de962bdSlukem 	( (r) && (r)[0] && !(r)[1] \
5392de962bdSlukem 	  && ((r)[0]->la_flags & LDAP_AVA_STRING) \
5402de962bdSlukem 	  && ((r)[0]->la_attr.bv_len == 2) \
5412de962bdSlukem 	  && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
5422de962bdSlukem 		|| ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
5432de962bdSlukem 	  && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
5442de962bdSlukem 		|| ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
5452de962bdSlukem 
5462de962bdSlukem /* Composite rules */
5472de962bdSlukem #define LDAP_DN_ALLOW_ONE_SPACE(f) \
5482de962bdSlukem 	( LDAP_DN_LDAPV2(f) \
5492de962bdSlukem 	  || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
5502de962bdSlukem #define LDAP_DN_ALLOW_SPACES(f) \
5512de962bdSlukem 	( LDAP_DN_LDAPV2(f) \
5522de962bdSlukem 	  || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
5532de962bdSlukem #define LDAP_DN_LDAP(f) \
5542de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
5552de962bdSlukem #define LDAP_DN_LDAPV3(f) \
5562de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
5572de962bdSlukem #define LDAP_DN_LDAPV2(f) \
5582de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
5592de962bdSlukem #define LDAP_DN_DCE(f) \
5602de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
5612de962bdSlukem #define LDAP_DN_UFN(f) \
5622de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
5632de962bdSlukem #define LDAP_DN_ADC(f) \
5642de962bdSlukem 	( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
5652de962bdSlukem #define LDAP_DN_FORMAT(f)		( (f) & LDAP_DN_FORMAT_MASK )
5662de962bdSlukem 
5672de962bdSlukem /*
5682de962bdSlukem  * LDAPAVA helpers (will become part of the API for operations
5692de962bdSlukem  * on structural representations of DNs).
5702de962bdSlukem  */
5712de962bdSlukem static LDAPAVA *
ldapava_new(const struct berval * attr,const struct berval * val,unsigned flags,void * ctx)5722de962bdSlukem ldapava_new( const struct berval *attr, const struct berval *val,
5732de962bdSlukem 		unsigned flags, void *ctx )
5742de962bdSlukem {
5752de962bdSlukem 	LDAPAVA *ava;
5762de962bdSlukem 
5772de962bdSlukem 	assert( attr != NULL );
5782de962bdSlukem 	assert( val != NULL );
5792de962bdSlukem 
5802de962bdSlukem 	ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx );
5812de962bdSlukem 
5822de962bdSlukem 	if ( ava ) {
5832de962bdSlukem 		ava->la_attr.bv_len = attr->bv_len;
5842de962bdSlukem 		ava->la_attr.bv_val = (char *)(ava+1);
5852de962bdSlukem 		AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
5862de962bdSlukem 		ava->la_attr.bv_val[attr->bv_len] = '\0';
5872de962bdSlukem 
5882de962bdSlukem 		ava->la_value = *val;
5892de962bdSlukem 		ava->la_flags = flags | LDAP_AVA_FREE_VALUE;
5902de962bdSlukem 
5912de962bdSlukem 		ava->la_private = NULL;
5922de962bdSlukem 	}
5932de962bdSlukem 
5942de962bdSlukem 	return( ava );
5952de962bdSlukem }
5962de962bdSlukem 
597d11b170bStron static void
ldapava_free(LDAPAVA * ava,void * ctx)5982de962bdSlukem ldapava_free( LDAPAVA *ava, void *ctx )
5992de962bdSlukem {
6002de962bdSlukem 	assert( ava != NULL );
6012de962bdSlukem 
6022de962bdSlukem #if 0
6032de962bdSlukem 	/* ava's private must be freed by caller
6042de962bdSlukem 	 * (at present let's skip this check because la_private
6052de962bdSlukem 	 * basically holds static data) */
6062de962bdSlukem 	assert( ava->la_private == NULL );
6072de962bdSlukem #endif
6082de962bdSlukem 
6092de962bdSlukem 	if (ava->la_flags & LDAP_AVA_FREE_VALUE)
6102de962bdSlukem 		LDAP_FREEX( ava->la_value.bv_val, ctx );
6112de962bdSlukem 
6122de962bdSlukem 	LDAP_FREEX( ava, ctx );
6132de962bdSlukem }
6142de962bdSlukem 
6152de962bdSlukem void
ldap_rdnfree(LDAPRDN rdn)6162de962bdSlukem ldap_rdnfree( LDAPRDN rdn )
6172de962bdSlukem {
6182de962bdSlukem 	ldap_rdnfree_x( rdn, NULL );
6192de962bdSlukem }
6202de962bdSlukem 
6212de962bdSlukem void
ldap_rdnfree_x(LDAPRDN rdn,void * ctx)6222de962bdSlukem ldap_rdnfree_x( LDAPRDN rdn, void *ctx )
6232de962bdSlukem {
6242de962bdSlukem 	int iAVA;
6252de962bdSlukem 
6262de962bdSlukem 	if ( rdn == NULL ) {
6272de962bdSlukem 		return;
6282de962bdSlukem 	}
6292de962bdSlukem 
6302de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
6312de962bdSlukem 		ldapava_free( rdn[ iAVA ], ctx );
6322de962bdSlukem 	}
6332de962bdSlukem 
6342de962bdSlukem 	LDAP_FREEX( rdn, ctx );
6352de962bdSlukem }
6362de962bdSlukem 
6372de962bdSlukem void
ldap_dnfree(LDAPDN dn)6382de962bdSlukem ldap_dnfree( LDAPDN dn )
6392de962bdSlukem {
6402de962bdSlukem 	ldap_dnfree_x( dn, NULL );
6412de962bdSlukem }
6422de962bdSlukem 
6432de962bdSlukem void
ldap_dnfree_x(LDAPDN dn,void * ctx)6442de962bdSlukem ldap_dnfree_x( LDAPDN dn, void *ctx )
6452de962bdSlukem {
6462de962bdSlukem 	int iRDN;
6472de962bdSlukem 
6482de962bdSlukem 	if ( dn == NULL ) {
6492de962bdSlukem 		return;
6502de962bdSlukem 	}
6512de962bdSlukem 
6522de962bdSlukem 	for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
6532de962bdSlukem 		ldap_rdnfree_x( dn[ iRDN ], ctx );
6542de962bdSlukem 	}
6552de962bdSlukem 
6562de962bdSlukem 	LDAP_FREEX( dn, ctx );
6572de962bdSlukem }
6582de962bdSlukem 
6592de962bdSlukem /*
6602de962bdSlukem  * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
6612de962bdSlukem  * into a structural representation of the DN, by separating attribute
6622de962bdSlukem  * types and values encoded in the more appropriate form, which is
6632de962bdSlukem  * string or OID for attribute types and binary form of the BER encoded
6642de962bdSlukem  * value or Unicode string. Formats different from LDAPv3 are parsed
6652de962bdSlukem  * according to their own rules and turned into the more appropriate
6662de962bdSlukem  * form according to LDAPv3.
6672de962bdSlukem  *
6682de962bdSlukem  * NOTE: I realize the code is getting spaghettish; it is rather
6692de962bdSlukem  * experimental and will hopefully turn into something more simple
6702de962bdSlukem  * and readable as soon as it works as expected.
6712de962bdSlukem  */
6722de962bdSlukem 
6732de962bdSlukem /*
6742de962bdSlukem  * Default sizes of AVA and RDN static working arrays; if required
6752de962bdSlukem  * the are dynamically resized.  The values can be tuned in case
6762de962bdSlukem  * of special requirements (e.g. very deep DN trees or high number
6772de962bdSlukem  * of AVAs per RDN).
6782de962bdSlukem  */
6792de962bdSlukem #define	TMP_AVA_SLOTS	8
6802de962bdSlukem #define	TMP_RDN_SLOTS	32
6812de962bdSlukem 
6822de962bdSlukem int
ldap_str2dn(LDAP_CONST char * str,LDAPDN * dn,unsigned flags)6832de962bdSlukem ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags )
6842de962bdSlukem {
6852de962bdSlukem 	struct berval	bv;
6862de962bdSlukem 
6872de962bdSlukem 	assert( str != NULL );
6882de962bdSlukem 
6892de962bdSlukem 	bv.bv_len = strlen( str );
6902de962bdSlukem 	bv.bv_val = (char *) str;
6912de962bdSlukem 
6922de962bdSlukem 	return ldap_bv2dn_x( &bv, dn, flags, NULL );
6932de962bdSlukem }
6942de962bdSlukem 
6952de962bdSlukem int
ldap_bv2dn(struct berval * bv,LDAPDN * dn,unsigned flags)6962de962bdSlukem ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags )
6972de962bdSlukem {
6982de962bdSlukem 	return ldap_bv2dn_x( bv, dn, flags, NULL );
6992de962bdSlukem }
7002de962bdSlukem 
7012de962bdSlukem int
ldap_bv2dn_x(struct berval * bvin,LDAPDN * dn,unsigned flags,void * ctx)7022de962bdSlukem ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx )
7032de962bdSlukem {
7042de962bdSlukem 	const char 	*p;
7052de962bdSlukem 	int		rc = LDAP_DECODING_ERROR;
7062de962bdSlukem 	int		nrdns = 0;
7072de962bdSlukem 
7082de962bdSlukem 	LDAPDN		newDN = NULL;
7092de962bdSlukem 	LDAPRDN		newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_;
7102de962bdSlukem 	int		num_slots = TMP_RDN_SLOTS;
7112de962bdSlukem 	char		*str, *end;
7122de962bdSlukem 	struct berval	bvtmp, *bv = &bvtmp;
7132de962bdSlukem 
7142de962bdSlukem 	assert( bvin != NULL );
7152de962bdSlukem 	assert( bvin->bv_val != NULL );
7162de962bdSlukem 	assert( dn != NULL );
7172de962bdSlukem 
7182de962bdSlukem 	*bv = *bvin;
7192de962bdSlukem 	str = bv->bv_val;
7202de962bdSlukem 	end = str + bv->bv_len;
7212de962bdSlukem 
722*549b59edSchristos 	Debug2( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags );
7232de962bdSlukem 
7242de962bdSlukem 	*dn = NULL;
7252de962bdSlukem 
7262de962bdSlukem 	switch ( LDAP_DN_FORMAT( flags ) ) {
7272de962bdSlukem 	case LDAP_DN_FORMAT_LDAP:
7282de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV3:
7292de962bdSlukem 	case LDAP_DN_FORMAT_DCE:
7302de962bdSlukem 		break;
7312de962bdSlukem 
7322de962bdSlukem 		/* allow DN enclosed in brackets */
7332de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV2:
7342de962bdSlukem 		if ( str[0] == '<' ) {
7352de962bdSlukem 			if ( bv->bv_len < 2 || end[ -1 ] != '>' ) {
7362de962bdSlukem 				rc = LDAP_DECODING_ERROR;
7372de962bdSlukem 				goto parsing_error;
7382de962bdSlukem 			}
7392de962bdSlukem 			bv->bv_val++;
7402de962bdSlukem 			bv->bv_len -= 2;
7412de962bdSlukem 			str++;
7422de962bdSlukem 			end--;
7432de962bdSlukem 		}
7442de962bdSlukem 		break;
7452de962bdSlukem 
7462de962bdSlukem 	/* unsupported in str2dn */
7472de962bdSlukem 	case LDAP_DN_FORMAT_UFN:
7482de962bdSlukem 	case LDAP_DN_FORMAT_AD_CANONICAL:
7492de962bdSlukem 		return LDAP_PARAM_ERROR;
7502de962bdSlukem 
7512de962bdSlukem 	case LDAP_DN_FORMAT_LBER:
7522de962bdSlukem 	default:
7532de962bdSlukem 		return LDAP_PARAM_ERROR;
7542de962bdSlukem 	}
7552de962bdSlukem 
7562de962bdSlukem 	if ( bv->bv_len == 0 ) {
7572de962bdSlukem 		return LDAP_SUCCESS;
7582de962bdSlukem 	}
7592de962bdSlukem 
7602de962bdSlukem 	if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
7612de962bdSlukem 		/* value must have embedded NULs */
7622de962bdSlukem 		return LDAP_DECODING_ERROR;
7632de962bdSlukem 	}
7642de962bdSlukem 
7652de962bdSlukem 	p = str;
7662de962bdSlukem 	if ( LDAP_DN_DCE( flags ) ) {
7672de962bdSlukem 
7682de962bdSlukem 		/*
7692de962bdSlukem 		 * (from Luke Howard: thnx) A RDN separator is required
7702de962bdSlukem 		 * at the beginning of an (absolute) DN.
7712de962bdSlukem 		 */
7722de962bdSlukem 		if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
7732de962bdSlukem 			goto parsing_error;
7742de962bdSlukem 		}
7752de962bdSlukem 		p++;
7762de962bdSlukem 
7772de962bdSlukem 	/*
7782de962bdSlukem 	 * actually we do not want to accept by default the DCE form,
7792de962bdSlukem 	 * we do not want to auto-detect it
7802de962bdSlukem 	 */
7812de962bdSlukem #if 0
7822de962bdSlukem 	} else if ( LDAP_DN_LDAP( flags ) ) {
7832de962bdSlukem 		/*
7842de962bdSlukem 		 * if dn starts with '/' let's make it a DCE dn
7852de962bdSlukem 		 */
7862de962bdSlukem 		if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
7872de962bdSlukem 			flags |= LDAP_DN_FORMAT_DCE;
7882de962bdSlukem 			p++;
7892de962bdSlukem 		}
7902de962bdSlukem #endif
7912de962bdSlukem 	}
7922de962bdSlukem 
7932de962bdSlukem 	for ( ; p < end; p++ ) {
7942de962bdSlukem 		int		err;
7952de962bdSlukem 		struct berval 	tmpbv;
7962de962bdSlukem 		tmpbv.bv_len = bv->bv_len - ( p - str );
7972de962bdSlukem 		tmpbv.bv_val = (char *)p;
7982de962bdSlukem 
7992de962bdSlukem 		err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx);
8002de962bdSlukem 		if ( err != LDAP_SUCCESS ) {
8012de962bdSlukem 			goto parsing_error;
8022de962bdSlukem 		}
8032de962bdSlukem 
8042de962bdSlukem 		/*
8052de962bdSlukem 		 * We expect a rdn separator
8062de962bdSlukem 		 */
8072de962bdSlukem 		if ( p < end && p[ 0 ] ) {
8082de962bdSlukem 			switch ( LDAP_DN_FORMAT( flags ) ) {
8092de962bdSlukem 			case LDAP_DN_FORMAT_LDAPV3:
8102de962bdSlukem 				if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
8112de962bdSlukem 					rc = LDAP_DECODING_ERROR;
8122de962bdSlukem 					goto parsing_error;
8132de962bdSlukem 				}
8142de962bdSlukem 				break;
8152de962bdSlukem 
8162de962bdSlukem 			case LDAP_DN_FORMAT_LDAP:
8172de962bdSlukem 			case LDAP_DN_FORMAT_LDAPV2:
8182de962bdSlukem 				if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
8192de962bdSlukem 					rc = LDAP_DECODING_ERROR;
8202de962bdSlukem 					goto parsing_error;
8212de962bdSlukem 				}
8222de962bdSlukem 				break;
8232de962bdSlukem 
8242de962bdSlukem 			case LDAP_DN_FORMAT_DCE:
8252de962bdSlukem 				if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
8262de962bdSlukem 					rc = LDAP_DECODING_ERROR;
8272de962bdSlukem 					goto parsing_error;
8282de962bdSlukem 				}
8292de962bdSlukem 				break;
8302de962bdSlukem 			}
8312de962bdSlukem 		}
8322de962bdSlukem 
8332de962bdSlukem 
8342de962bdSlukem 		tmpDN[nrdns++] = newRDN;
8352de962bdSlukem 		newRDN = NULL;
8362de962bdSlukem 
8372de962bdSlukem 		/*
8382de962bdSlukem 		 * make the static RDN array dynamically rescalable
8392de962bdSlukem 		 */
8402de962bdSlukem 		if ( nrdns == num_slots ) {
8412de962bdSlukem 			LDAPRDN	*tmp;
8422de962bdSlukem 
8432de962bdSlukem 			if ( tmpDN == tmpDN_ ) {
8442de962bdSlukem 				tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx );
8452de962bdSlukem 				if ( tmp == NULL ) {
8462de962bdSlukem 					rc = LDAP_NO_MEMORY;
8472de962bdSlukem 					goto parsing_error;
8482de962bdSlukem 				}
8492de962bdSlukem 				AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
8502de962bdSlukem 
8512de962bdSlukem 			} else {
8522de962bdSlukem 				tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx );
8532de962bdSlukem 				if ( tmp == NULL ) {
8542de962bdSlukem 					rc = LDAP_NO_MEMORY;
8552de962bdSlukem 					goto parsing_error;
8562de962bdSlukem 				}
8572de962bdSlukem 			}
8582de962bdSlukem 
8592de962bdSlukem 			tmpDN = tmp;
8602de962bdSlukem 			num_slots *= 2;
8612de962bdSlukem 		}
8622de962bdSlukem 
8632de962bdSlukem 		if ( p >= end || p[ 0 ] == '\0' ) {
8642de962bdSlukem 			/*
8652de962bdSlukem 			 * the DN is over, phew
8662de962bdSlukem 			 */
8672de962bdSlukem 			newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx );
8682de962bdSlukem 			if ( newDN == NULL ) {
8692de962bdSlukem 				rc = LDAP_NO_MEMORY;
8702de962bdSlukem 				goto parsing_error;
8712de962bdSlukem 			} else {
8722de962bdSlukem 				int i;
8732de962bdSlukem 
8742de962bdSlukem 				if ( LDAP_DN_DCE( flags ) ) {
8752de962bdSlukem 					/* add in reversed order */
8762de962bdSlukem 					for ( i=0; i<nrdns; i++ )
8772de962bdSlukem 						newDN[i] = tmpDN[nrdns-1-i];
8782de962bdSlukem 				} else {
8792de962bdSlukem 					for ( i=0; i<nrdns; i++ )
8802de962bdSlukem 						newDN[i] = tmpDN[i];
8812de962bdSlukem 				}
8822de962bdSlukem 				newDN[nrdns] = NULL;
8832de962bdSlukem 				rc = LDAP_SUCCESS;
8842de962bdSlukem 			}
8852de962bdSlukem 			goto return_result;
8862de962bdSlukem 		}
8872de962bdSlukem 	}
8882de962bdSlukem 
8892de962bdSlukem parsing_error:;
8902de962bdSlukem 	if ( newRDN ) {
8912de962bdSlukem 		ldap_rdnfree_x( newRDN, ctx );
8922de962bdSlukem 	}
8932de962bdSlukem 
8942de962bdSlukem 	for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
8952de962bdSlukem 		ldap_rdnfree_x( tmpDN[nrdns], ctx );
8962de962bdSlukem 	}
8972de962bdSlukem 
8982de962bdSlukem return_result:;
8992de962bdSlukem 
9002de962bdSlukem 	if ( tmpDN != tmpDN_ ) {
9012de962bdSlukem 		LDAP_FREEX( tmpDN, ctx );
9022de962bdSlukem 	}
9032de962bdSlukem 
904*549b59edSchristos 	Debug3( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc,
9052de962bdSlukem 			rc ? ldap_err2string( rc ) : "" );
9062de962bdSlukem 	*dn = newDN;
9072de962bdSlukem 
9082de962bdSlukem 	return( rc );
9092de962bdSlukem }
9102de962bdSlukem 
9112de962bdSlukem /*
9122de962bdSlukem  * ldap_str2rdn
9132de962bdSlukem  *
9142de962bdSlukem  * Parses a relative DN according to flags up to a rdn separator
9152de962bdSlukem  * or to the end of str.
9162de962bdSlukem  * Returns the rdn and a pointer to the string continuation, which
9172de962bdSlukem  * corresponds to the rdn separator or to '\0' in case the string is over.
9182de962bdSlukem  */
9192de962bdSlukem int
ldap_str2rdn(LDAP_CONST char * str,LDAPRDN * rdn,char ** n_in,unsigned flags)9202de962bdSlukem ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn,
9212de962bdSlukem 	char **n_in, unsigned flags )
9222de962bdSlukem {
9232de962bdSlukem 	struct berval	bv;
9242de962bdSlukem 
9252de962bdSlukem 	assert( str != NULL );
9262de962bdSlukem 	assert( str[ 0 ] != '\0' );	/* FIXME: is this required? */
9272de962bdSlukem 
9282de962bdSlukem 	bv.bv_len = strlen( str );
9292de962bdSlukem 	bv.bv_val = (char *) str;
9302de962bdSlukem 
9312de962bdSlukem 	return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL );
9322de962bdSlukem }
9332de962bdSlukem 
9342de962bdSlukem int
ldap_bv2rdn(struct berval * bv,LDAPRDN * rdn,char ** n_in,unsigned flags)9352de962bdSlukem ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn,
9362de962bdSlukem 	char **n_in, unsigned flags )
9372de962bdSlukem {
9382de962bdSlukem 	return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL );
9392de962bdSlukem }
9402de962bdSlukem 
9412de962bdSlukem int
ldap_bv2rdn_x(struct berval * bv,LDAPRDN * rdn,char ** n_in,unsigned flags,void * ctx)9422de962bdSlukem ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn,
9432de962bdSlukem 	char **n_in, unsigned flags, void *ctx )
9442de962bdSlukem {
9452de962bdSlukem 	const char  	**n = (const char **) n_in;
9462de962bdSlukem 	const char 	*p;
9472de962bdSlukem 	int		navas = 0;
9482de962bdSlukem 	int 		state = B4AVA;
9492de962bdSlukem 	int		rc = LDAP_DECODING_ERROR;
9502de962bdSlukem 	int		attrTypeEncoding = LDAP_AVA_STRING,
9512de962bdSlukem 			attrValueEncoding = LDAP_AVA_STRING;
9522de962bdSlukem 
9532de962bdSlukem 	struct berval	attrType = BER_BVNULL;
9542de962bdSlukem 	struct berval 	attrValue = BER_BVNULL;
9552de962bdSlukem 
9562de962bdSlukem 	LDAPRDN		newRDN = NULL;
9572de962bdSlukem 	LDAPAVA		*tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
9582de962bdSlukem 	int		num_slots = TMP_AVA_SLOTS;
9592de962bdSlukem 
9602de962bdSlukem 	char		*str;
9612de962bdSlukem 	ber_len_t	stoplen;
9622de962bdSlukem 
9632de962bdSlukem 	assert( bv != NULL );
9642de962bdSlukem 	assert( bv->bv_len != 0 );
9652de962bdSlukem 	assert( bv->bv_val != NULL );
9662de962bdSlukem 	assert( rdn || flags & LDAP_DN_SKIP );
9672de962bdSlukem 	assert( n != NULL );
9682de962bdSlukem 
9692de962bdSlukem 	str = bv->bv_val;
9702de962bdSlukem 	stoplen = bv->bv_len;
9712de962bdSlukem 
9722de962bdSlukem 	if ( rdn ) {
9732de962bdSlukem 		*rdn = NULL;
9742de962bdSlukem 	}
9752de962bdSlukem 	*n = NULL;
9762de962bdSlukem 
9772de962bdSlukem 	switch ( LDAP_DN_FORMAT( flags ) ) {
9782de962bdSlukem 	case LDAP_DN_FORMAT_LDAP:
9792de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV3:
9802de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV2:
9812de962bdSlukem 	case LDAP_DN_FORMAT_DCE:
9822de962bdSlukem 		break;
9832de962bdSlukem 
9842de962bdSlukem 	/* unsupported in str2dn */
9852de962bdSlukem 	case LDAP_DN_FORMAT_UFN:
9862de962bdSlukem 	case LDAP_DN_FORMAT_AD_CANONICAL:
9872de962bdSlukem 		return LDAP_PARAM_ERROR;
9882de962bdSlukem 
9892de962bdSlukem 	case LDAP_DN_FORMAT_LBER:
9902de962bdSlukem 	default:
9912de962bdSlukem 		return LDAP_PARAM_ERROR;
9922de962bdSlukem 	}
9932de962bdSlukem 
9942de962bdSlukem 	if ( bv->bv_len == 0 ) {
9952de962bdSlukem 		return LDAP_SUCCESS;
9962de962bdSlukem 
9972de962bdSlukem 	}
9982de962bdSlukem 
9992de962bdSlukem 	if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
10002de962bdSlukem 		/* value must have embedded NULs */
10012de962bdSlukem 		return LDAP_DECODING_ERROR;
10022de962bdSlukem 	}
10032de962bdSlukem 
10042de962bdSlukem 	p = str;
10052de962bdSlukem 	for ( ; p[ 0 ] || state == GOTAVA; ) {
10062de962bdSlukem 
10072de962bdSlukem 		/*
10082de962bdSlukem 		 * The parser in principle advances one token a time,
10092de962bdSlukem 		 * or toggles state if preferable.
10102de962bdSlukem 		 */
10112de962bdSlukem 		switch (state) {
10122de962bdSlukem 
10132de962bdSlukem 		/*
10142de962bdSlukem 		 * an AttributeType can be encoded as:
10152de962bdSlukem 		 * - its string representation; in detail, implementations
10162de962bdSlukem 		 *   MUST recognize AttributeType string type names listed
10172de962bdSlukem 		 *   in Section 3 of RFC 4514, and MAY recognize other names.
10182de962bdSlukem 		 * - its numeric OID (a dotted decimal string)
10192de962bdSlukem 		 */
10202de962bdSlukem 		case B4AVA:
10212de962bdSlukem 			if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
10222de962bdSlukem 				if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
10232de962bdSlukem 					/* error */
10242de962bdSlukem 					goto parsing_error;
10252de962bdSlukem 				}
10262de962bdSlukem 				p++;
10272de962bdSlukem 			}
10282de962bdSlukem 
10292de962bdSlukem 			if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
10302de962bdSlukem 				if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
10312de962bdSlukem 					/* error */
10322de962bdSlukem 					goto parsing_error;
10332de962bdSlukem 				}
10342de962bdSlukem 
10352de962bdSlukem 				/* whitespace is allowed (and trimmed) */
10362de962bdSlukem 				p++;
10372de962bdSlukem 				while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
10382de962bdSlukem 					p++;
10392de962bdSlukem 				}
10402de962bdSlukem 
10412de962bdSlukem 				if ( !p[ 0 ] ) {
10422de962bdSlukem 					/* error: we expected an AVA */
10432de962bdSlukem 					goto parsing_error;
10442de962bdSlukem 				}
10452de962bdSlukem 			}
10462de962bdSlukem 
10472de962bdSlukem 			/* oid */
10482de962bdSlukem 			if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
10492de962bdSlukem 				state = B4OIDATTRTYPE;
10502de962bdSlukem 				break;
10512de962bdSlukem 			}
10522de962bdSlukem 
10532de962bdSlukem 			/* else must be alpha */
10542de962bdSlukem 			if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
10552de962bdSlukem 				goto parsing_error;
10562de962bdSlukem 			}
10572de962bdSlukem 
10582de962bdSlukem 			/* LDAPv2 "oid." prefix */
10592de962bdSlukem 			if ( LDAP_DN_LDAPV2( flags ) ) {
10602de962bdSlukem 				/*
10612de962bdSlukem 				 * to be overly pedantic, we only accept
10622de962bdSlukem 				 * "OID." or "oid."
10632de962bdSlukem 				 */
10642de962bdSlukem 				if ( flags & LDAP_DN_PEDANTIC ) {
10652de962bdSlukem 					if ( !strncmp( p, "OID.", 4 )
10662de962bdSlukem 						|| !strncmp( p, "oid.", 4 ) ) {
10672de962bdSlukem 						p += 4;
10682de962bdSlukem 						state = B4OIDATTRTYPE;
10692de962bdSlukem 						break;
10702de962bdSlukem 					}
10712de962bdSlukem 				} else {
10722de962bdSlukem 				       if ( !strncasecmp( p, "oid.", 4 ) ) {
10732de962bdSlukem 					       p += 4;
10742de962bdSlukem 					       state = B4OIDATTRTYPE;
10752de962bdSlukem 					       break;
10762de962bdSlukem 				       }
10772de962bdSlukem 				}
10782de962bdSlukem 			}
10792de962bdSlukem 
10802de962bdSlukem 			state = B4STRINGATTRTYPE;
10812de962bdSlukem 			break;
10822de962bdSlukem 
10832de962bdSlukem 		case B4OIDATTRTYPE: {
10842de962bdSlukem 			int 		err = LDAP_SUCCESS;
10852de962bdSlukem 
10862de962bdSlukem 			attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
10872de962bdSlukem 				LDAP_SCHEMA_SKIP);
10882de962bdSlukem 
10892de962bdSlukem 			if ( err != LDAP_SUCCESS ) {
10902de962bdSlukem 				goto parsing_error;
10912de962bdSlukem 			}
10922de962bdSlukem 			attrType.bv_len = p - attrType.bv_val;
10932de962bdSlukem 
10942de962bdSlukem 			attrTypeEncoding = LDAP_AVA_BINARY;
10952de962bdSlukem 
10962de962bdSlukem 			state = B4AVAEQUALS;
10972de962bdSlukem 			break;
10982de962bdSlukem 		}
10992de962bdSlukem 
11002de962bdSlukem 		case B4STRINGATTRTYPE: {
11012de962bdSlukem 			const char 	*startPos, *endPos = NULL;
11022de962bdSlukem 			ber_len_t 	len;
11032de962bdSlukem 
11042de962bdSlukem 			/*
11052de962bdSlukem 			 * the starting char has been found to be
11062de962bdSlukem 			 * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
11072de962bdSlukem 			 * FIXME: DCE attr types seem to have a more
11082de962bdSlukem 			 * restrictive syntax (no '-' ...)
11092de962bdSlukem 			 */
11102de962bdSlukem 			for ( startPos = p++; p[ 0 ]; p++ ) {
11112de962bdSlukem 				if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
11122de962bdSlukem 					continue;
11132de962bdSlukem 				}
11142de962bdSlukem 
11152de962bdSlukem 				if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
11162de962bdSlukem 
11172de962bdSlukem 					/*
11182de962bdSlukem 					 * RFC 4514 explicitly does not allow attribute
11192de962bdSlukem 					 * description options, such as language tags.
11202de962bdSlukem 					 */
11212de962bdSlukem 					if ( flags & LDAP_DN_PEDANTIC ) {
11222de962bdSlukem 						goto parsing_error;
11232de962bdSlukem 					}
11242de962bdSlukem 
11252de962bdSlukem 					/*
11262de962bdSlukem 					 * we trim ';' and following lang
11272de962bdSlukem 					 * and so from attribute types
11282de962bdSlukem 					 */
11292de962bdSlukem 					endPos = p;
11302de962bdSlukem 					for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
11312de962bdSlukem 							|| LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
11322de962bdSlukem 						/* no op */ ;
11332de962bdSlukem 					}
11342de962bdSlukem 					break;
11352de962bdSlukem 				}
11362de962bdSlukem 				break;
11372de962bdSlukem 			}
11382de962bdSlukem 
11392de962bdSlukem 			len = ( endPos ? endPos : p ) - startPos;
11402de962bdSlukem 			if ( len == 0 ) {
11412de962bdSlukem 				goto parsing_error;
11422de962bdSlukem 			}
11432de962bdSlukem 
11442de962bdSlukem 			attrTypeEncoding = LDAP_AVA_STRING;
11452de962bdSlukem 
11462de962bdSlukem 			/*
11472de962bdSlukem 			 * here we need to decide whether to use it as is
11482de962bdSlukem 			 * or turn it in OID form; as a consequence, we
11492de962bdSlukem 			 * need to decide whether to binary encode the value
11502de962bdSlukem 			 */
11512de962bdSlukem 
11522de962bdSlukem 			state = B4AVAEQUALS;
11532de962bdSlukem 
11542de962bdSlukem 			if ( flags & LDAP_DN_SKIP ) {
11552de962bdSlukem 				break;
11562de962bdSlukem 			}
11572de962bdSlukem 
11582de962bdSlukem 			attrType.bv_val = (char *)startPos;
11592de962bdSlukem 			attrType.bv_len = len;
11602de962bdSlukem 
11612de962bdSlukem 			break;
11622de962bdSlukem 		}
11632de962bdSlukem 
11642de962bdSlukem 		case B4AVAEQUALS:
11652de962bdSlukem 			/* spaces may not be allowed */
11662de962bdSlukem 			if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
11672de962bdSlukem 				if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
11682de962bdSlukem 					goto parsing_error;
11692de962bdSlukem 				}
11702de962bdSlukem 
11712de962bdSlukem 				/* trim spaces */
11722de962bdSlukem 				for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
11732de962bdSlukem 					/* no op */
11742de962bdSlukem 				}
11752de962bdSlukem 			}
11762de962bdSlukem 
11772de962bdSlukem 			/* need equal sign */
11782de962bdSlukem 			if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
11792de962bdSlukem 				goto parsing_error;
11802de962bdSlukem 			}
11812de962bdSlukem 			p++;
11822de962bdSlukem 
11832de962bdSlukem 			/* spaces may not be allowed */
11842de962bdSlukem 			if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
11852de962bdSlukem 				if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
11862de962bdSlukem 					goto parsing_error;
11872de962bdSlukem 				}
11882de962bdSlukem 
11892de962bdSlukem 				/* trim spaces */
11902de962bdSlukem 				for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
11912de962bdSlukem 					/* no op */
11922de962bdSlukem 				}
11932de962bdSlukem 			}
11942de962bdSlukem 
11952de962bdSlukem 			/*
11962de962bdSlukem 			 * octothorpe means a BER encoded value will follow
11972de962bdSlukem 			 * FIXME: I don't think DCE will allow it
11982de962bdSlukem 			 */
11992de962bdSlukem 			if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
12002de962bdSlukem 				p++;
12012de962bdSlukem 				attrValueEncoding = LDAP_AVA_BINARY;
12022de962bdSlukem 				state = B4BINARYVALUE;
12032de962bdSlukem 				break;
12042de962bdSlukem 			}
12052de962bdSlukem 
12062de962bdSlukem 			/* STRING value expected */
12072de962bdSlukem 
12082de962bdSlukem 			/*
12092de962bdSlukem 			 * if we're pedantic, an attribute type in OID form
12102de962bdSlukem 			 * SHOULD imply a BER encoded attribute value; we
12112de962bdSlukem 			 * should at least issue a warning
12122de962bdSlukem 			 */
12132de962bdSlukem 			if ( ( flags & LDAP_DN_PEDANTIC )
12142de962bdSlukem 				&& ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
12152de962bdSlukem 				/* OID attrType SHOULD use binary encoding */
12162de962bdSlukem 				goto parsing_error;
12172de962bdSlukem 			}
12182de962bdSlukem 
12192de962bdSlukem 			attrValueEncoding = LDAP_AVA_STRING;
12202de962bdSlukem 
12212de962bdSlukem 			/*
12222de962bdSlukem 			 * LDAPv2 allows the attribute value to be quoted;
12232de962bdSlukem 			 * also, IA5 values are expected, in principle
12242de962bdSlukem 			 */
12252de962bdSlukem 			if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
12262de962bdSlukem 				if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
12272de962bdSlukem 					p++;
12282de962bdSlukem 					state = B4IA5VALUEQUOTED;
12292de962bdSlukem 					break;
12302de962bdSlukem 				}
12312de962bdSlukem 
12322de962bdSlukem 				if ( LDAP_DN_LDAPV2( flags ) ) {
12332de962bdSlukem 					state = B4IA5VALUE;
12342de962bdSlukem 					break;
12352de962bdSlukem 				}
12362de962bdSlukem 			}
12372de962bdSlukem 
12382de962bdSlukem 			/*
12392de962bdSlukem 			 * here STRING means RFC 4514 string
12402de962bdSlukem 			 * FIXME: what about DCE strings?
12412de962bdSlukem 			 */
12422de962bdSlukem 			if ( !p[ 0 ] ) {
12432de962bdSlukem 				/* empty value */
12442de962bdSlukem 				state = GOTAVA;
12452de962bdSlukem 			} else {
12462de962bdSlukem 				state = B4STRINGVALUE;
12472de962bdSlukem 			}
12482de962bdSlukem 			break;
12492de962bdSlukem 
12502de962bdSlukem 		case B4BINARYVALUE:
12512de962bdSlukem 			if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) {
12522de962bdSlukem 				goto parsing_error;
12532de962bdSlukem 			}
12542de962bdSlukem 
12552de962bdSlukem 			state = GOTAVA;
12562de962bdSlukem 			break;
12572de962bdSlukem 
12582de962bdSlukem 		case B4STRINGVALUE:
12592de962bdSlukem 			switch ( LDAP_DN_FORMAT( flags ) ) {
12602de962bdSlukem 			case LDAP_DN_FORMAT_LDAP:
12612de962bdSlukem 			case LDAP_DN_FORMAT_LDAPV3:
12622de962bdSlukem 				if ( str2strval( p, stoplen - ( p - str ),
12632de962bdSlukem 							&attrValue, &p, flags,
12642de962bdSlukem 							&attrValueEncoding, ctx ) ) {
12652de962bdSlukem 					goto parsing_error;
12662de962bdSlukem 				}
12672de962bdSlukem 				break;
12682de962bdSlukem 
12692de962bdSlukem 			case LDAP_DN_FORMAT_DCE:
12702de962bdSlukem 				if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) {
12712de962bdSlukem 					goto parsing_error;
12722de962bdSlukem 				}
12732de962bdSlukem 				break;
12742de962bdSlukem 
12752de962bdSlukem 			default:
12762de962bdSlukem 				assert( 0 );
12772de962bdSlukem 			}
12782de962bdSlukem 
12792de962bdSlukem 			state = GOTAVA;
12802de962bdSlukem 			break;
12812de962bdSlukem 
12822de962bdSlukem 		case B4IA5VALUE:
12832de962bdSlukem 			if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) {
12842de962bdSlukem 				goto parsing_error;
12852de962bdSlukem 			}
12862de962bdSlukem 
12872de962bdSlukem 			state = GOTAVA;
12882de962bdSlukem 			break;
12892de962bdSlukem 
12902de962bdSlukem 		case B4IA5VALUEQUOTED:
12912de962bdSlukem 
12922de962bdSlukem 			/* lead quote already stripped */
12932de962bdSlukem 			if ( quotedIA52strval( p, &attrValue,
12942de962bdSlukem 						&p, flags, ctx ) ) {
12952de962bdSlukem 				goto parsing_error;
12962de962bdSlukem 			}
12972de962bdSlukem 
12982de962bdSlukem 			state = GOTAVA;
12992de962bdSlukem 			break;
13002de962bdSlukem 
13012de962bdSlukem 		case GOTAVA: {
13022de962bdSlukem 			int	rdnsep = 0;
13032de962bdSlukem 
13042de962bdSlukem 			if ( !( flags & LDAP_DN_SKIP ) ) {
13052de962bdSlukem 				LDAPAVA *ava;
13062de962bdSlukem 
13072de962bdSlukem 				/*
13082de962bdSlukem 				 * we accept empty values
13092de962bdSlukem 				 */
13102de962bdSlukem 				ava = ldapava_new( &attrType, &attrValue,
13112de962bdSlukem 						attrValueEncoding, ctx );
13122de962bdSlukem 				if ( ava == NULL ) {
13132de962bdSlukem 					rc = LDAP_NO_MEMORY;
13142de962bdSlukem 					goto parsing_error;
13152de962bdSlukem 				}
13162de962bdSlukem 				tmpRDN[navas++] = ava;
13172de962bdSlukem 
13182de962bdSlukem 				attrValue.bv_val = NULL;
13192de962bdSlukem 				attrValue.bv_len = 0;
13202de962bdSlukem 
13212de962bdSlukem 				/*
13222de962bdSlukem 				 * prepare room for new AVAs if needed
13232de962bdSlukem 				 */
13242de962bdSlukem 				if (navas == num_slots) {
13252de962bdSlukem 					LDAPAVA **tmp;
13262de962bdSlukem 
13272de962bdSlukem 					if ( tmpRDN == tmpRDN_ ) {
13282de962bdSlukem 						tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx );
13292de962bdSlukem 						if ( tmp == NULL ) {
13302de962bdSlukem 							rc = LDAP_NO_MEMORY;
13312de962bdSlukem 							goto parsing_error;
13322de962bdSlukem 						}
13332de962bdSlukem 						AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
13342de962bdSlukem 
13352de962bdSlukem 					} else {
13362de962bdSlukem 						tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx );
13372de962bdSlukem 						if ( tmp == NULL ) {
13382de962bdSlukem 							rc = LDAP_NO_MEMORY;
13392de962bdSlukem 							goto parsing_error;
13402de962bdSlukem 						}
13412de962bdSlukem 					}
13422de962bdSlukem 
13432de962bdSlukem 					tmpRDN = tmp;
13442de962bdSlukem 					num_slots *= 2;
13452de962bdSlukem 				}
13462de962bdSlukem 			}
13472de962bdSlukem 
13482de962bdSlukem 			/*
13492de962bdSlukem 			 * if we got an AVA separator ('+', or ',' for DCE )
13502de962bdSlukem 			 * we expect a new AVA for this RDN; otherwise
13512de962bdSlukem 			 * we add the RDN to the DN
13522de962bdSlukem 			 */
13532de962bdSlukem 			switch ( LDAP_DN_FORMAT( flags ) ) {
13542de962bdSlukem 			case LDAP_DN_FORMAT_LDAP:
13552de962bdSlukem 			case LDAP_DN_FORMAT_LDAPV3:
13562de962bdSlukem 			case LDAP_DN_FORMAT_LDAPV2:
13572de962bdSlukem 				if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
13582de962bdSlukem 					rdnsep = 1;
13592de962bdSlukem 				}
13602de962bdSlukem 				break;
13612de962bdSlukem 
13622de962bdSlukem 			case LDAP_DN_FORMAT_DCE:
13632de962bdSlukem 				if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
13642de962bdSlukem 					rdnsep = 1;
13652de962bdSlukem 				}
13662de962bdSlukem 				break;
13672de962bdSlukem 			}
13682de962bdSlukem 
13692de962bdSlukem 			if ( rdnsep ) {
13702de962bdSlukem 				/*
13712de962bdSlukem 				 * the RDN is over, phew
13722de962bdSlukem 				 */
13732de962bdSlukem 				*n = p;
13742de962bdSlukem 				if ( !( flags & LDAP_DN_SKIP ) ) {
13752de962bdSlukem 					newRDN = (LDAPRDN)LDAP_MALLOCX(
13762de962bdSlukem 						sizeof(LDAPAVA) * (navas+1), ctx );
13772de962bdSlukem 					if ( newRDN == NULL ) {
13782de962bdSlukem 						rc = LDAP_NO_MEMORY;
13792de962bdSlukem 						goto parsing_error;
13802de962bdSlukem 					} else {
13812de962bdSlukem 						AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas);
13822de962bdSlukem 						newRDN[navas] = NULL;
13832de962bdSlukem 					}
13842de962bdSlukem 
13852de962bdSlukem 				}
13862de962bdSlukem 				rc = LDAP_SUCCESS;
13872de962bdSlukem 				goto return_result;
13882de962bdSlukem 			}
13892de962bdSlukem 
13902de962bdSlukem 			/* they should have been used in an AVA */
13912de962bdSlukem 			attrType.bv_val = NULL;
13922de962bdSlukem 			attrValue.bv_val = NULL;
13932de962bdSlukem 
13942de962bdSlukem 			p++;
13952de962bdSlukem 			state = B4AVA;
13962de962bdSlukem 			break;
13972de962bdSlukem 		}
13982de962bdSlukem 
13992de962bdSlukem 		default:
14002de962bdSlukem 			assert( 0 );
14012de962bdSlukem 			goto parsing_error;
14022de962bdSlukem 		}
14032de962bdSlukem 	}
14042de962bdSlukem 	*n = p;
14052de962bdSlukem 
14062de962bdSlukem parsing_error:;
14072de962bdSlukem 	/* They are set to NULL after they're used in an AVA */
14082de962bdSlukem 
14092de962bdSlukem 	if ( attrValue.bv_val ) {
14102de962bdSlukem 		LDAP_FREEX( attrValue.bv_val, ctx );
14112de962bdSlukem 	}
14122de962bdSlukem 
14132de962bdSlukem 	for ( navas-- ; navas >= 0; navas-- ) {
14142de962bdSlukem 		ldapava_free( tmpRDN[navas], ctx );
14152de962bdSlukem 	}
14162de962bdSlukem 
14172de962bdSlukem return_result:;
14182de962bdSlukem 
14192de962bdSlukem 	if ( tmpRDN != tmpRDN_ ) {
14202de962bdSlukem 		LDAP_FREEX( tmpRDN, ctx );
14212de962bdSlukem 	}
14222de962bdSlukem 
14232de962bdSlukem 	if ( rdn ) {
14242de962bdSlukem 		*rdn = newRDN;
14252de962bdSlukem 	}
14262de962bdSlukem 
14272de962bdSlukem 	return( rc );
14282de962bdSlukem }
14292de962bdSlukem 
14302de962bdSlukem /*
14312de962bdSlukem  * reads in a UTF-8 string value, unescaping stuff:
14322de962bdSlukem  * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
14332de962bdSlukem  * '\' + HEXPAIR(p) -> unhex(p)
14342de962bdSlukem  */
14352de962bdSlukem static int
str2strval(const char * str,ber_len_t stoplen,struct berval * val,const char ** next,unsigned flags,int * retFlags,void * ctx)14362de962bdSlukem str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx )
14372de962bdSlukem {
14382de962bdSlukem 	const char 	*p, *end, *startPos, *endPos = NULL;
14392de962bdSlukem 	ber_len_t	len, escapes;
14402de962bdSlukem 
14412de962bdSlukem 	assert( str != NULL );
14422de962bdSlukem 	assert( val != NULL );
14432de962bdSlukem 	assert( next != NULL );
14442de962bdSlukem 
14452de962bdSlukem 	*next = NULL;
14462de962bdSlukem 	end = str + stoplen;
14472de962bdSlukem 	for ( startPos = p = str, escapes = 0; p < end; p++ ) {
14482de962bdSlukem 		if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
14492de962bdSlukem 			p++;
14502de962bdSlukem 			if ( p[ 0 ] == '\0' ) {
14512de962bdSlukem 				return( 1 );
14522de962bdSlukem 			}
14532de962bdSlukem 			if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
14542de962bdSlukem 				escapes++;
14552de962bdSlukem 				continue;
14562de962bdSlukem 			}
14572de962bdSlukem 
14582de962bdSlukem 			if ( LDAP_DN_HEXPAIR( p ) ) {
14592de962bdSlukem 				char c;
14602de962bdSlukem 
14612de962bdSlukem 				hexstr2bin( p, &c );
14622de962bdSlukem 				escapes += 2;
14632de962bdSlukem 
14642de962bdSlukem 				if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
14652de962bdSlukem 
14662de962bdSlukem 					/*
14672de962bdSlukem 					 * we assume the string is UTF-8
14682de962bdSlukem 					 */
14692de962bdSlukem 					*retFlags = LDAP_AVA_NONPRINTABLE;
14702de962bdSlukem 				}
14712de962bdSlukem 				p++;
14722de962bdSlukem 
14732de962bdSlukem 				continue;
14742de962bdSlukem 			}
14752de962bdSlukem 
14762de962bdSlukem 			if ( LDAP_DN_PEDANTIC & flags ) {
14772de962bdSlukem 				return( 1 );
14782de962bdSlukem 			}
14792de962bdSlukem 			/*
14802de962bdSlukem 			 * we do not allow escaping
14812de962bdSlukem 			 * of chars that don't need
14822de962bdSlukem 			 * to and do not belong to
14832de962bdSlukem 			 * HEXDIGITS
14842de962bdSlukem 			 */
14852de962bdSlukem 			return( 1 );
14862de962bdSlukem 
14872de962bdSlukem 		} else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
14882de962bdSlukem 			if ( p[ 0 ] == '\0' ) {
14892de962bdSlukem 				return( 1 );
14902de962bdSlukem 			}
14912de962bdSlukem 			*retFlags = LDAP_AVA_NONPRINTABLE;
14922de962bdSlukem 
14932de962bdSlukem 		} else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) )
14942de962bdSlukem 				|| ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
14952de962bdSlukem 			break;
14962de962bdSlukem 
14972de962bdSlukem 		} else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
14982de962bdSlukem 			/*
14992de962bdSlukem 			 * FIXME: maybe we can add
15002de962bdSlukem 			 * escapes if not pedantic?
15012de962bdSlukem 			 */
15022de962bdSlukem 			return( 1 );
15032de962bdSlukem 		}
15042de962bdSlukem 	}
15052de962bdSlukem 
15062de962bdSlukem 	/*
15072de962bdSlukem 	 * we do allow unescaped spaces at the end
15082de962bdSlukem 	 * of the value only in non-pedantic mode
15092de962bdSlukem 	 */
15102de962bdSlukem 	if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
15112de962bdSlukem 			!LDAP_DN_ESCAPE( p[ -2 ] ) ) {
15122de962bdSlukem 		if ( flags & LDAP_DN_PEDANTIC ) {
15132de962bdSlukem 			return( 1 );
15142de962bdSlukem 		}
15152de962bdSlukem 
15162de962bdSlukem 		/* strip trailing (unescaped) spaces */
15172de962bdSlukem 		for ( endPos = p - 1;
15182de962bdSlukem 				endPos > startPos + 1 &&
15192de962bdSlukem 				LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
15202de962bdSlukem 				!LDAP_DN_ESCAPE( endPos[ -2 ] );
15212de962bdSlukem 				endPos-- ) {
15222de962bdSlukem 			/* no op */
15232de962bdSlukem 		}
15242de962bdSlukem 	}
15252de962bdSlukem 
15262de962bdSlukem 	*next = p;
15272de962bdSlukem 	if ( flags & LDAP_DN_SKIP ) {
15282de962bdSlukem 		return( 0 );
15292de962bdSlukem 	}
15302de962bdSlukem 
15312de962bdSlukem 	/*
15322de962bdSlukem 	 * FIXME: test memory?
15332de962bdSlukem 	 */
15342de962bdSlukem 	len = ( endPos ? endPos : p ) - startPos - escapes;
15352de962bdSlukem 	val->bv_len = len;
15362de962bdSlukem 
15372de962bdSlukem 	if ( escapes == 0 ) {
15382de962bdSlukem 		if ( *retFlags & LDAP_AVA_NONPRINTABLE ) {
15392de962bdSlukem 			val->bv_val = LDAP_MALLOCX( len + 1, ctx );
1540*549b59edSchristos 			if ( val->bv_val == NULL ) {
1541*549b59edSchristos 				return( 1 );
1542*549b59edSchristos 			}
1543*549b59edSchristos 
15442de962bdSlukem 			AC_MEMCPY( val->bv_val, startPos, len );
15452de962bdSlukem 			val->bv_val[ len ] = '\0';
15462de962bdSlukem 		} else {
15472de962bdSlukem 			val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
15482de962bdSlukem 		}
15492de962bdSlukem 
15502de962bdSlukem 	} else {
15512de962bdSlukem 		ber_len_t	s, d;
15522de962bdSlukem 
15532de962bdSlukem 		val->bv_val = LDAP_MALLOCX( len + 1, ctx );
1554*549b59edSchristos 		if ( val->bv_val == NULL ) {
1555*549b59edSchristos 			return( 1 );
1556*549b59edSchristos 		}
1557*549b59edSchristos 
15582de962bdSlukem 		for ( s = 0, d = 0; d < len; ) {
15592de962bdSlukem 			if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
15602de962bdSlukem 				s++;
15612de962bdSlukem 				if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
15622de962bdSlukem 					val->bv_val[ d++ ] =
15632de962bdSlukem 						startPos[ s++ ];
15642de962bdSlukem 
15652de962bdSlukem 				} else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
15662de962bdSlukem 					char 	c;
15672de962bdSlukem 
15682de962bdSlukem 					hexstr2bin( &startPos[ s ], &c );
15692de962bdSlukem 					val->bv_val[ d++ ] = c;
15702de962bdSlukem 					s += 2;
15712de962bdSlukem 
15722de962bdSlukem 				} else {
15732de962bdSlukem 					/* we should never get here */
15742de962bdSlukem 					assert( 0 );
15752de962bdSlukem 				}
15762de962bdSlukem 
15772de962bdSlukem 			} else {
15782de962bdSlukem 				val->bv_val[ d++ ] = startPos[ s++ ];
15792de962bdSlukem 			}
15802de962bdSlukem 		}
15812de962bdSlukem 
15822de962bdSlukem 		val->bv_val[ d ] = '\0';
15832de962bdSlukem 		assert( d == len );
15842de962bdSlukem 	}
15852de962bdSlukem 
15862de962bdSlukem 	return( 0 );
15872de962bdSlukem }
15882de962bdSlukem 
15892de962bdSlukem static int
DCE2strval(const char * str,struct berval * val,const char ** next,unsigned flags,void * ctx)15902de962bdSlukem DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
15912de962bdSlukem {
15922de962bdSlukem 	const char 	*p, *startPos, *endPos = NULL;
15932de962bdSlukem 	ber_len_t	len, escapes;
15942de962bdSlukem 
15952de962bdSlukem 	assert( str != NULL );
15962de962bdSlukem 	assert( val != NULL );
15972de962bdSlukem 	assert( next != NULL );
15982de962bdSlukem 
15992de962bdSlukem 	*next = NULL;
16002de962bdSlukem 
16012de962bdSlukem 	for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
16022de962bdSlukem 		if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
16032de962bdSlukem 			p++;
16042de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
16052de962bdSlukem 				escapes++;
16062de962bdSlukem 
16072de962bdSlukem 			} else {
16082de962bdSlukem 				return( 1 );
16092de962bdSlukem 			}
16102de962bdSlukem 
16112de962bdSlukem 		} else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
16122de962bdSlukem 			break;
16132de962bdSlukem 		}
16142de962bdSlukem 
16152de962bdSlukem 		/*
16162de962bdSlukem 		 * FIXME: can we accept anything else? I guess we need
16172de962bdSlukem 		 * to stop if a value is not legal
16182de962bdSlukem 		 */
16192de962bdSlukem 	}
16202de962bdSlukem 
16212de962bdSlukem 	/*
16222de962bdSlukem 	 * (unescaped) trailing spaces are trimmed must be silently ignored;
16232de962bdSlukem 	 * so we eat them
16242de962bdSlukem 	 */
16252de962bdSlukem 	if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
16262de962bdSlukem 			!LDAP_DN_ESCAPE( p[ -2 ] ) ) {
16272de962bdSlukem 		if ( flags & LDAP_DN_PEDANTIC ) {
16282de962bdSlukem 			return( 1 );
16292de962bdSlukem 		}
16302de962bdSlukem 
16312de962bdSlukem 		/* strip trailing (unescaped) spaces */
16322de962bdSlukem 		for ( endPos = p - 1;
16332de962bdSlukem 				endPos > startPos + 1 &&
16342de962bdSlukem 				LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
16352de962bdSlukem 				!LDAP_DN_ESCAPE( endPos[ -2 ] );
16362de962bdSlukem 				endPos-- ) {
16372de962bdSlukem 			/* no op */
16382de962bdSlukem 		}
16392de962bdSlukem 	}
16402de962bdSlukem 
16412de962bdSlukem 	*next = p;
16422de962bdSlukem 	if ( flags & LDAP_DN_SKIP ) {
16432de962bdSlukem 		return( 0 );
16442de962bdSlukem 	}
16452de962bdSlukem 
16462de962bdSlukem 	len = ( endPos ? endPos : p ) - startPos - escapes;
16472de962bdSlukem 	val->bv_len = len;
16482de962bdSlukem 	if ( escapes == 0 ){
16492de962bdSlukem 		val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
16502de962bdSlukem 
16512de962bdSlukem 	} else {
16522de962bdSlukem 		ber_len_t	s, d;
16532de962bdSlukem 
16542de962bdSlukem 		val->bv_val = LDAP_MALLOCX( len + 1, ctx );
1655*549b59edSchristos 		if ( val->bv_val == NULL ) {
1656*549b59edSchristos 			return( 1 );
1657*549b59edSchristos 		}
1658*549b59edSchristos 
16592de962bdSlukem 		for ( s = 0, d = 0; d < len; ) {
16602de962bdSlukem 			/*
16612de962bdSlukem 			 * This point is reached only if escapes
16622de962bdSlukem 			 * are properly used, so all we need to
16632de962bdSlukem 			 * do is eat them
16642de962bdSlukem 			 */
16652de962bdSlukem 			if (  LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
16662de962bdSlukem 				s++;
16672de962bdSlukem 
16682de962bdSlukem 			}
16692de962bdSlukem 			val->bv_val[ d++ ] = startPos[ s++ ];
16702de962bdSlukem 		}
16712de962bdSlukem 		val->bv_val[ d ] = '\0';
16722de962bdSlukem 		assert( strlen( val->bv_val ) == len );
16732de962bdSlukem 	}
16742de962bdSlukem 
16752de962bdSlukem 	return( 0 );
16762de962bdSlukem }
16772de962bdSlukem 
16782de962bdSlukem static int
IA52strval(const char * str,struct berval * val,const char ** next,unsigned flags,void * ctx)16792de962bdSlukem IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
16802de962bdSlukem {
16812de962bdSlukem 	const char 	*p, *startPos, *endPos = NULL;
16822de962bdSlukem 	ber_len_t	len, escapes;
16832de962bdSlukem 
16842de962bdSlukem 	assert( str != NULL );
16852de962bdSlukem 	assert( val != NULL );
16862de962bdSlukem 	assert( next != NULL );
16872de962bdSlukem 
16882de962bdSlukem 	*next = NULL;
16892de962bdSlukem 
16902de962bdSlukem 	/*
16912de962bdSlukem 	 * LDAPv2 (RFC 1779)
16922de962bdSlukem 	 */
16932de962bdSlukem 
16942de962bdSlukem 	for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
16952de962bdSlukem 		if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
16962de962bdSlukem 			p++;
16972de962bdSlukem 			if ( p[ 0 ] == '\0' ) {
16982de962bdSlukem 				return( 1 );
16992de962bdSlukem 			}
17002de962bdSlukem 
17012de962bdSlukem 			if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
17022de962bdSlukem 					&& ( LDAP_DN_PEDANTIC & flags ) ) {
17032de962bdSlukem 				return( 1 );
17042de962bdSlukem 			}
17052de962bdSlukem 			escapes++;
17062de962bdSlukem 
17072de962bdSlukem 		} else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
17082de962bdSlukem 			break;
17092de962bdSlukem 		}
17102de962bdSlukem 
17112de962bdSlukem 		/*
17122de962bdSlukem 		 * FIXME: can we accept anything else? I guess we need
17132de962bdSlukem 		 * to stop if a value is not legal
17142de962bdSlukem 		 */
17152de962bdSlukem 	}
17162de962bdSlukem 
17172de962bdSlukem 	/* strip trailing (unescaped) spaces */
17182de962bdSlukem 	for ( endPos = p;
17192de962bdSlukem 			endPos > startPos + 1 &&
17202de962bdSlukem 			LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
17212de962bdSlukem 			!LDAP_DN_ESCAPE( endPos[ -2 ] );
17222de962bdSlukem 			endPos-- ) {
17232de962bdSlukem 		/* no op */
17242de962bdSlukem 	}
17252de962bdSlukem 
17262de962bdSlukem 	*next = p;
17272de962bdSlukem 	if ( flags & LDAP_DN_SKIP ) {
17282de962bdSlukem 		return( 0 );
17292de962bdSlukem 	}
17302de962bdSlukem 
17312de962bdSlukem 	len = ( endPos ? endPos : p ) - startPos - escapes;
17322de962bdSlukem 	val->bv_len = len;
17332de962bdSlukem 	if ( escapes == 0 ) {
17342de962bdSlukem 		val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
17352de962bdSlukem 
17362de962bdSlukem 	} else {
17372de962bdSlukem 		ber_len_t	s, d;
17382de962bdSlukem 
17392de962bdSlukem 		val->bv_val = LDAP_MALLOCX( len + 1, ctx );
1740*549b59edSchristos 		if ( val->bv_val == NULL ) {
1741*549b59edSchristos 			return( 1 );
1742*549b59edSchristos 		}
1743*549b59edSchristos 
17442de962bdSlukem 		for ( s = 0, d = 0; d < len; ) {
17452de962bdSlukem 			if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
17462de962bdSlukem 				s++;
17472de962bdSlukem 			}
17482de962bdSlukem 			val->bv_val[ d++ ] = startPos[ s++ ];
17492de962bdSlukem 		}
17502de962bdSlukem 		val->bv_val[ d ] = '\0';
17512de962bdSlukem 		assert( strlen( val->bv_val ) == len );
17522de962bdSlukem 	}
17532de962bdSlukem 
17542de962bdSlukem 	return( 0 );
17552de962bdSlukem }
17562de962bdSlukem 
17572de962bdSlukem static int
quotedIA52strval(const char * str,struct berval * val,const char ** next,unsigned flags,void * ctx)17582de962bdSlukem quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
17592de962bdSlukem {
17602de962bdSlukem 	const char 	*p, *startPos, *endPos = NULL;
17612de962bdSlukem 	ber_len_t	len;
17622de962bdSlukem 	unsigned	escapes = 0;
17632de962bdSlukem 
17642de962bdSlukem 	assert( str != NULL );
17652de962bdSlukem 	assert( val != NULL );
17662de962bdSlukem 	assert( next != NULL );
17672de962bdSlukem 
17682de962bdSlukem 	*next = NULL;
17692de962bdSlukem 
17702de962bdSlukem 	/* initial quote already eaten */
17712de962bdSlukem 	for ( startPos = p = str; p[ 0 ]; p++ ) {
17722de962bdSlukem 		/*
17732de962bdSlukem 		 * According to RFC 1779, the quoted value can
17742de962bdSlukem 		 * contain escaped as well as unescaped special values;
17752de962bdSlukem 		 * as a consequence we tolerate escaped values
17762de962bdSlukem 		 * (e.g. '"\,"' -> '\,') and escape unescaped specials
17772de962bdSlukem 		 * (e.g. '","' -> '\,').
17782de962bdSlukem 		 */
17792de962bdSlukem 		if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
17802de962bdSlukem 			if ( p[ 1 ] == '\0' ) {
17812de962bdSlukem 				return( 1 );
17822de962bdSlukem 			}
17832de962bdSlukem 			p++;
17842de962bdSlukem 
17852de962bdSlukem 			if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
17862de962bdSlukem 					&& ( LDAP_DN_PEDANTIC & flags ) ) {
17872de962bdSlukem 				/*
17882de962bdSlukem 				 * do we allow to escape normal chars?
17892de962bdSlukem 				 * LDAPv2 does not allow any mechanism
17902de962bdSlukem 				 * for escaping chars with '\' and hex
17912de962bdSlukem 				 * pair
17922de962bdSlukem 				 */
17932de962bdSlukem 				return( 1 );
17942de962bdSlukem 			}
17952de962bdSlukem 			escapes++;
17962de962bdSlukem 
17972de962bdSlukem 		} else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
17982de962bdSlukem 			endPos = p;
17992de962bdSlukem 			/* eat closing quotes */
18002de962bdSlukem 			p++;
18012de962bdSlukem 			break;
18022de962bdSlukem 		}
18032de962bdSlukem 
18042de962bdSlukem 		/*
18052de962bdSlukem 		 * FIXME: can we accept anything else? I guess we need
18062de962bdSlukem 		 * to stop if a value is not legal
18072de962bdSlukem 		 */
18082de962bdSlukem 	}
18092de962bdSlukem 
18102de962bdSlukem 	if ( endPos == NULL ) {
18112de962bdSlukem 		return( 1 );
18122de962bdSlukem 	}
18132de962bdSlukem 
18142de962bdSlukem 	/* Strip trailing (unescaped) spaces */
18152de962bdSlukem 	for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
18162de962bdSlukem 		/* no op */
18172de962bdSlukem 	}
18182de962bdSlukem 
18192de962bdSlukem 	*next = p;
18202de962bdSlukem 	if ( flags & LDAP_DN_SKIP ) {
18212de962bdSlukem 		return( 0 );
18222de962bdSlukem 	}
18232de962bdSlukem 
18242de962bdSlukem 	len = endPos - startPos - escapes;
18252de962bdSlukem 	assert( endPos >= startPos + escapes );
18262de962bdSlukem 	val->bv_len = len;
18272de962bdSlukem 	if ( escapes == 0 ) {
18282de962bdSlukem 		val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
18292de962bdSlukem 
18302de962bdSlukem 	} else {
18312de962bdSlukem 		ber_len_t	s, d;
18322de962bdSlukem 
18332de962bdSlukem 		val->bv_val = LDAP_MALLOCX( len + 1, ctx );
1834*549b59edSchristos 		if ( val->bv_val == NULL ) {
1835*549b59edSchristos 			return( 1 );
1836*549b59edSchristos 		}
1837*549b59edSchristos 
18382de962bdSlukem 		val->bv_len = len;
18392de962bdSlukem 
18402de962bdSlukem 		for ( s = d = 0; d < len; ) {
18412de962bdSlukem 			if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
18422de962bdSlukem 				s++;
18432de962bdSlukem 			}
18442de962bdSlukem 			val->bv_val[ d++ ] = str[ s++ ];
18452de962bdSlukem 		}
18462de962bdSlukem 		val->bv_val[ d ] = '\0';
18472de962bdSlukem 		assert( strlen( val->bv_val ) == len );
18482de962bdSlukem 	}
18492de962bdSlukem 
18502de962bdSlukem 	return( 0 );
18512de962bdSlukem }
18522de962bdSlukem 
18532de962bdSlukem static int
hexstr2bin(const char * str,char * c)18542de962bdSlukem hexstr2bin( const char *str, char *c )
18552de962bdSlukem {
18562de962bdSlukem 	char	c1, c2;
18572de962bdSlukem 
18582de962bdSlukem 	assert( str != NULL );
18592de962bdSlukem 	assert( c != NULL );
18602de962bdSlukem 
18612de962bdSlukem 	c1 = str[ 0 ];
18622de962bdSlukem 	c2 = str[ 1 ];
18632de962bdSlukem 
18642de962bdSlukem 	if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
18652de962bdSlukem 		*c = c1 - '0';
18662de962bdSlukem 
18672de962bdSlukem 	} else {
18682de962bdSlukem 		if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
18692de962bdSlukem 			*c = c1 - 'A' + 10;
18702de962bdSlukem 		} else {
18712de962bdSlukem 			assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
18722de962bdSlukem 			*c = c1 - 'a' + 10;
18732de962bdSlukem 		}
18742de962bdSlukem 	}
18752de962bdSlukem 
18762de962bdSlukem 	*c <<= 4;
18772de962bdSlukem 
18782de962bdSlukem 	if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
18792de962bdSlukem 		*c += c2 - '0';
18802de962bdSlukem 
18812de962bdSlukem 	} else {
18822de962bdSlukem 		if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
18832de962bdSlukem 			*c += c2 - 'A' + 10;
18842de962bdSlukem 		} else {
18852de962bdSlukem 			assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
18862de962bdSlukem 			*c += c2 - 'a' + 10;
18872de962bdSlukem 		}
18882de962bdSlukem 	}
18892de962bdSlukem 
18902de962bdSlukem 	return( 0 );
18912de962bdSlukem }
18922de962bdSlukem 
18932de962bdSlukem static int
hexstr2binval(const char * str,struct berval * val,const char ** next,unsigned flags,void * ctx)18942de962bdSlukem hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
18952de962bdSlukem {
18962de962bdSlukem 	const char 	*p, *startPos, *endPos = NULL;
18972de962bdSlukem 	ber_len_t	len;
18982de962bdSlukem 	ber_len_t	s, d;
18992de962bdSlukem 
19002de962bdSlukem 	assert( str != NULL );
19012de962bdSlukem 	assert( val != NULL );
19022de962bdSlukem 	assert( next != NULL );
19032de962bdSlukem 
19042de962bdSlukem 	*next = NULL;
19052de962bdSlukem 
19062de962bdSlukem 	for ( startPos = p = str; p[ 0 ]; p += 2 ) {
19072de962bdSlukem 		switch ( LDAP_DN_FORMAT( flags ) ) {
19082de962bdSlukem 		case LDAP_DN_FORMAT_LDAPV3:
19092de962bdSlukem 			if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
19102de962bdSlukem 				goto end_of_value;
19112de962bdSlukem 			}
19122de962bdSlukem 			break;
19132de962bdSlukem 
19142de962bdSlukem 		case LDAP_DN_FORMAT_LDAP:
19152de962bdSlukem 		case LDAP_DN_FORMAT_LDAPV2:
19162de962bdSlukem 			if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
19172de962bdSlukem 				goto end_of_value;
19182de962bdSlukem 			}
19192de962bdSlukem 			break;
19202de962bdSlukem 
19212de962bdSlukem 		case LDAP_DN_FORMAT_DCE:
19222de962bdSlukem 			if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
19232de962bdSlukem 				goto end_of_value;
19242de962bdSlukem 			}
19252de962bdSlukem 			break;
19262de962bdSlukem 		}
19272de962bdSlukem 
19282de962bdSlukem 		if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
19292de962bdSlukem 			if ( flags & LDAP_DN_PEDANTIC ) {
19302de962bdSlukem 				return( 1 );
19312de962bdSlukem 			}
19322de962bdSlukem 			endPos = p;
19332de962bdSlukem 
19342de962bdSlukem 			for ( ; p[ 0 ]; p++ ) {
19352de962bdSlukem 				switch ( LDAP_DN_FORMAT( flags ) ) {
19362de962bdSlukem 				case LDAP_DN_FORMAT_LDAPV3:
19372de962bdSlukem 					if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
19382de962bdSlukem 						goto end_of_value;
19392de962bdSlukem 					}
19402de962bdSlukem 					break;
19412de962bdSlukem 
19422de962bdSlukem 				case LDAP_DN_FORMAT_LDAP:
19432de962bdSlukem 				case LDAP_DN_FORMAT_LDAPV2:
19442de962bdSlukem 					if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
19452de962bdSlukem 						goto end_of_value;
19462de962bdSlukem 					}
19472de962bdSlukem 					break;
19482de962bdSlukem 
19492de962bdSlukem 				case LDAP_DN_FORMAT_DCE:
19502de962bdSlukem 					if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
19512de962bdSlukem 						goto end_of_value;
19522de962bdSlukem 					}
19532de962bdSlukem 					break;
19542de962bdSlukem 				}
19552de962bdSlukem 			}
19562de962bdSlukem 			break;
19572de962bdSlukem 		}
19582de962bdSlukem 
19592de962bdSlukem 		if ( !LDAP_DN_HEXPAIR( p ) ) {
19602de962bdSlukem 			return( 1 );
19612de962bdSlukem 		}
19622de962bdSlukem 	}
19632de962bdSlukem 
19642de962bdSlukem end_of_value:;
19652de962bdSlukem 
19662de962bdSlukem 	*next = p;
19672de962bdSlukem 	if ( flags & LDAP_DN_SKIP ) {
19682de962bdSlukem 		return( 0 );
19692de962bdSlukem 	}
19702de962bdSlukem 
19712de962bdSlukem 	len = ( ( endPos ? endPos : p ) - startPos ) / 2;
19722de962bdSlukem 	/* must be even! */
19732de962bdSlukem 	assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
19742de962bdSlukem 
19752de962bdSlukem 	val->bv_len = len;
19762de962bdSlukem 	val->bv_val = LDAP_MALLOCX( len + 1, ctx );
19772de962bdSlukem 	if ( val->bv_val == NULL ) {
19782de962bdSlukem 		return( LDAP_NO_MEMORY );
19792de962bdSlukem 	}
19802de962bdSlukem 
19812de962bdSlukem 	for ( s = 0, d = 0; d < len; s += 2, d++ ) {
19822de962bdSlukem 		char 	c;
19832de962bdSlukem 
19842de962bdSlukem 		hexstr2bin( &startPos[ s ], &c );
19852de962bdSlukem 
19862de962bdSlukem 		val->bv_val[ d ] = c;
19872de962bdSlukem 	}
19882de962bdSlukem 
19892de962bdSlukem 	val->bv_val[ d ] = '\0';
19902de962bdSlukem 
19912de962bdSlukem 	return( 0 );
19922de962bdSlukem }
19932de962bdSlukem 
19942de962bdSlukem /*
19952de962bdSlukem  * convert a byte in a hexadecimal pair
19962de962bdSlukem  */
19972de962bdSlukem static int
byte2hexpair(const char * val,char * pair)19982de962bdSlukem byte2hexpair( const char *val, char *pair )
19992de962bdSlukem {
20002de962bdSlukem 	static const char	hexdig[] = "0123456789ABCDEF";
20012de962bdSlukem 
20022de962bdSlukem 	assert( val != NULL );
20032de962bdSlukem 	assert( pair != NULL );
20042de962bdSlukem 
20052de962bdSlukem 	/*
20062de962bdSlukem 	 * we assume the string has enough room for the hex encoding
20072de962bdSlukem 	 * of the value
20082de962bdSlukem 	 */
20092de962bdSlukem 
20102de962bdSlukem 	pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
20112de962bdSlukem 	pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
20122de962bdSlukem 
20132de962bdSlukem 	return( 0 );
20142de962bdSlukem }
20152de962bdSlukem 
20162de962bdSlukem /*
20172de962bdSlukem  * convert a binary value in hexadecimal pairs
20182de962bdSlukem  */
20192de962bdSlukem static int
binval2hexstr(struct berval * val,char * str)20202de962bdSlukem binval2hexstr( struct berval *val, char *str )
20212de962bdSlukem {
20222de962bdSlukem 	ber_len_t	s, d;
20232de962bdSlukem 
20242de962bdSlukem 	assert( val != NULL );
20252de962bdSlukem 	assert( str != NULL );
20262de962bdSlukem 
20272de962bdSlukem 	if ( val->bv_len == 0 ) {
20282de962bdSlukem 		return( 0 );
20292de962bdSlukem 	}
20302de962bdSlukem 
20312de962bdSlukem 	/*
20322de962bdSlukem 	 * we assume the string has enough room for the hex encoding
20332de962bdSlukem 	 * of the value
20342de962bdSlukem 	 */
20352de962bdSlukem 
20362de962bdSlukem 	for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
20372de962bdSlukem 		byte2hexpair( &val->bv_val[ s ], &str[ d ] );
20382de962bdSlukem 	}
20392de962bdSlukem 
20402de962bdSlukem 	return( 0 );
20412de962bdSlukem }
20422de962bdSlukem 
20432de962bdSlukem /*
20442de962bdSlukem  * Length of the string representation, accounting for escaped hex
20452de962bdSlukem  * of UTF-8 chars
20462de962bdSlukem  */
20472de962bdSlukem static int
strval2strlen(struct berval * val,unsigned flags,ber_len_t * len)20482de962bdSlukem strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
20492de962bdSlukem {
20502de962bdSlukem 	ber_len_t	l, cl = 1;
20512de962bdSlukem 	char		*p, *end;
20522de962bdSlukem 	int		escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
20532de962bdSlukem #ifdef PRETTY_ESCAPE
20542de962bdSlukem 	int		escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
20552de962bdSlukem #endif /* PRETTY_ESCAPE */
20562de962bdSlukem 
20572de962bdSlukem 	assert( val != NULL );
20582de962bdSlukem 	assert( len != NULL );
20592de962bdSlukem 
20602de962bdSlukem 	*len = 0;
20612de962bdSlukem 	if ( val->bv_len == 0 ) {
20622de962bdSlukem 		return( 0 );
20632de962bdSlukem 	}
20642de962bdSlukem 
20652de962bdSlukem 	end = val->bv_val + val->bv_len - 1;
20662de962bdSlukem 	for ( l = 0, p = val->bv_val; p <= end; p += cl ) {
20672de962bdSlukem 
20682de962bdSlukem 		/*
20692de962bdSlukem 		 * escape '%x00'
20702de962bdSlukem 		 */
20712de962bdSlukem 		if ( p[ 0 ] == '\0' ) {
20722de962bdSlukem 			cl = 1;
20732de962bdSlukem 			l += 3;
20742de962bdSlukem 			continue;
20752de962bdSlukem 		}
20762de962bdSlukem 
20772de962bdSlukem 		cl = LDAP_UTF8_CHARLEN2( p, cl );
20782de962bdSlukem 		if ( cl == 0 ) {
20792de962bdSlukem 			/* illegal utf-8 char! */
20802de962bdSlukem 			return( -1 );
20812de962bdSlukem 
20822de962bdSlukem 		} else if ( cl > 1 ) {
20832de962bdSlukem 			ber_len_t cnt;
20842de962bdSlukem 
20852de962bdSlukem 			for ( cnt = 1; cnt < cl; cnt++ ) {
20862de962bdSlukem 				if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
20872de962bdSlukem 					return( -1 );
20882de962bdSlukem 				}
20892de962bdSlukem 			}
20902de962bdSlukem 			l += escaped_byte_len * cl;
20912de962bdSlukem 
20922de962bdSlukem 		} else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
20932de962bdSlukem 				|| LDAP_DN_SHOULDESCAPE( p[ 0 ] )
20942de962bdSlukem 				|| ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
20952de962bdSlukem 				|| ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
20962de962bdSlukem #ifdef PRETTY_ESCAPE
20972de962bdSlukem #if 0
20982de962bdSlukem 			if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
20992de962bdSlukem #else
21002de962bdSlukem 			if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
21012de962bdSlukem #endif
21022de962bdSlukem 
21032de962bdSlukem 				/*
21042de962bdSlukem 				 * there might be some chars we want
21052de962bdSlukem 				 * to escape in form of a couple
21062de962bdSlukem 				 * of hexdigits for optimization purposes
21072de962bdSlukem 				 */
21082de962bdSlukem 				l += 3;
21092de962bdSlukem 
21102de962bdSlukem 			} else {
21112de962bdSlukem 				l += escaped_ascii_len;
21122de962bdSlukem 			}
21132de962bdSlukem #else /* ! PRETTY_ESCAPE */
21142de962bdSlukem 			l += 3;
21152de962bdSlukem #endif /* ! PRETTY_ESCAPE */
21162de962bdSlukem 
21172de962bdSlukem 		} else {
21182de962bdSlukem 			l++;
21192de962bdSlukem 		}
21202de962bdSlukem 	}
21212de962bdSlukem 
21222de962bdSlukem 	*len = l;
21232de962bdSlukem 
21242de962bdSlukem 	return( 0 );
21252de962bdSlukem }
21262de962bdSlukem 
21272de962bdSlukem /*
21282de962bdSlukem  * convert to string representation, escaping with hex the UTF-8 stuff;
21292de962bdSlukem  * assume the destination has enough room for escaping
21302de962bdSlukem  */
21312de962bdSlukem static int
21322de962bdSlukem strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
21332de962bdSlukem {
21342de962bdSlukem 	ber_len_t	s, d, end;
21352de962bdSlukem 
21362de962bdSlukem 	assert( val != NULL );
21372de962bdSlukem 	assert( str != NULL );
21382de962bdSlukem 	assert( len != NULL );
21392de962bdSlukem 
21402de962bdSlukem 	if ( val->bv_len == 0 ) {
21412de962bdSlukem 		*len = 0;
21422de962bdSlukem 		return( 0 );
21432de962bdSlukem 	}
21442de962bdSlukem 
21452de962bdSlukem 	/*
21462de962bdSlukem 	 * we assume the string has enough room for the hex encoding
21472de962bdSlukem 	 * of the value
21482de962bdSlukem 	 */
21492de962bdSlukem 	for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
21502de962bdSlukem 		ber_len_t	cl;
21512de962bdSlukem 
21522de962bdSlukem 		/*
21532de962bdSlukem 		 * escape '%x00'
21542de962bdSlukem 		 */
21552de962bdSlukem 		if ( val->bv_val[ s ] == '\0' ) {
21562de962bdSlukem 			cl = 1;
21572de962bdSlukem 			str[ d++ ] = '\\';
21582de962bdSlukem 			str[ d++ ] = '0';
21592de962bdSlukem 			str[ d++ ] = '0';
21602de962bdSlukem 			s++;
21612de962bdSlukem 			continue;
21622de962bdSlukem 		}
21632de962bdSlukem 
21642de962bdSlukem 		/*
21652de962bdSlukem 		 * The length was checked in strval2strlen();
21662de962bdSlukem 		 */
21674e6df137Slukem 		cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] );
21682de962bdSlukem 
21692de962bdSlukem 		/*
21702de962bdSlukem 		 * there might be some chars we want to escape in form
21712de962bdSlukem 		 * of a couple of hexdigits for optimization purposes
21722de962bdSlukem 		 */
21732de962bdSlukem 		if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) )
21742de962bdSlukem #ifdef PRETTY_ESCAPE
21752de962bdSlukem #if 0
21762de962bdSlukem 				|| LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] )
21772de962bdSlukem #else
21782de962bdSlukem 				|| LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] )
21792de962bdSlukem #endif
21802de962bdSlukem #else /* ! PRETTY_ESCAPE */
21812de962bdSlukem 				|| LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
21822de962bdSlukem 				|| LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
21832de962bdSlukem 				|| ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
21842de962bdSlukem 				|| ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
21852de962bdSlukem 
21862de962bdSlukem #endif /* ! PRETTY_ESCAPE */
21872de962bdSlukem 				) {
21882de962bdSlukem 			for ( ; cl--; ) {
21892de962bdSlukem 				str[ d++ ] = '\\';
21902de962bdSlukem 				byte2hexpair( &val->bv_val[ s ], &str[ d ] );
21912de962bdSlukem 				s++;
21922de962bdSlukem 				d += 2;
21932de962bdSlukem 			}
21942de962bdSlukem 
21952de962bdSlukem 		} else if ( cl > 1 ) {
21962de962bdSlukem 			for ( ; cl--; ) {
21972de962bdSlukem 				str[ d++ ] = val->bv_val[ s++ ];
21982de962bdSlukem 			}
21992de962bdSlukem 
22002de962bdSlukem 		} else {
22012de962bdSlukem #ifdef PRETTY_ESCAPE
22022de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
22032de962bdSlukem 					|| LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
22042de962bdSlukem 					|| ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
22052de962bdSlukem 					|| ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
22062de962bdSlukem 				str[ d++ ] = '\\';
22072de962bdSlukem 				if ( !LDAP_DN_IS_PRETTY( flags ) ) {
22082de962bdSlukem 					byte2hexpair( &val->bv_val[ s ], &str[ d ] );
22092de962bdSlukem 					s++;
22102de962bdSlukem 					d += 2;
22112de962bdSlukem 					continue;
22122de962bdSlukem 				}
22132de962bdSlukem 			}
22142de962bdSlukem #endif /* PRETTY_ESCAPE */
22152de962bdSlukem 			str[ d++ ] = val->bv_val[ s++ ];
22162de962bdSlukem 		}
22172de962bdSlukem 	}
22182de962bdSlukem 
22192de962bdSlukem 	*len = d;
22202de962bdSlukem 
22212de962bdSlukem 	return( 0 );
22222de962bdSlukem }
22232de962bdSlukem 
22242de962bdSlukem /*
22252de962bdSlukem  * Length of the IA5 string representation (no UTF-8 allowed)
22262de962bdSlukem  */
22272de962bdSlukem static int
22282de962bdSlukem strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
22292de962bdSlukem {
22302de962bdSlukem 	ber_len_t	l;
22312de962bdSlukem 	char		*p;
22322de962bdSlukem 
22332de962bdSlukem 	assert( val != NULL );
22342de962bdSlukem 	assert( len != NULL );
22352de962bdSlukem 
22362de962bdSlukem 	*len = 0;
22372de962bdSlukem 	if ( val->bv_len == 0 ) {
22382de962bdSlukem 		return( 0 );
22392de962bdSlukem 	}
22402de962bdSlukem 
22412de962bdSlukem 	if ( flags & LDAP_AVA_NONPRINTABLE ) {
22422de962bdSlukem 		/*
22432de962bdSlukem 		 * Turn value into a binary encoded BER
22442de962bdSlukem 		 */
22452de962bdSlukem 		return( -1 );
22462de962bdSlukem 
22472de962bdSlukem 	} else {
22482de962bdSlukem 		for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
22492de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
22502de962bdSlukem 					|| LDAP_DN_SHOULDESCAPE( p[ 0 ] )
22512de962bdSlukem 					|| ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
22522de962bdSlukem 					|| ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
22532de962bdSlukem 				l += 2;
22542de962bdSlukem 
22552de962bdSlukem 			} else {
22562de962bdSlukem 				l++;
22572de962bdSlukem 			}
22582de962bdSlukem 		}
22592de962bdSlukem 	}
22602de962bdSlukem 
22612de962bdSlukem 	*len = l;
22622de962bdSlukem 
22632de962bdSlukem 	return( 0 );
22642de962bdSlukem }
22652de962bdSlukem 
22662de962bdSlukem /*
22672de962bdSlukem  * convert to string representation (np UTF-8)
22682de962bdSlukem  * assume the destination has enough room for escaping
22692de962bdSlukem  */
22702de962bdSlukem static int
22712de962bdSlukem strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
22722de962bdSlukem {
22732de962bdSlukem 	ber_len_t	s, d, end;
22742de962bdSlukem 
22752de962bdSlukem 	assert( val != NULL );
22762de962bdSlukem 	assert( str != NULL );
22772de962bdSlukem 	assert( len != NULL );
22782de962bdSlukem 
22792de962bdSlukem 	if ( val->bv_len == 0 ) {
22802de962bdSlukem 		*len = 0;
22812de962bdSlukem 		return( 0 );
22822de962bdSlukem 	}
22832de962bdSlukem 
22842de962bdSlukem 	if ( flags & LDAP_AVA_NONPRINTABLE ) {
22852de962bdSlukem 		/*
22862de962bdSlukem 		 * Turn value into a binary encoded BER
22872de962bdSlukem 		 */
22882de962bdSlukem 		*len = 0;
22892de962bdSlukem 		return( -1 );
22902de962bdSlukem 
22912de962bdSlukem 	} else {
22922de962bdSlukem 		/*
22932de962bdSlukem 		 * we assume the string has enough room for the hex encoding
22942de962bdSlukem 		 * of the value
22952de962bdSlukem 		 */
22962de962bdSlukem 
22972de962bdSlukem 		for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
22982de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
22992de962bdSlukem 					|| LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
23002de962bdSlukem 					|| ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
23012de962bdSlukem 					|| ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
23022de962bdSlukem 				str[ d++ ] = '\\';
23032de962bdSlukem 			}
23042de962bdSlukem 			str[ d++ ] = val->bv_val[ s++ ];
23052de962bdSlukem 		}
23062de962bdSlukem 	}
23072de962bdSlukem 
23082de962bdSlukem 	*len = d;
23092de962bdSlukem 
23102de962bdSlukem 	return( 0 );
23112de962bdSlukem }
23122de962bdSlukem 
23132de962bdSlukem /*
23142de962bdSlukem  * Length of the (supposedly) DCE string representation,
23152de962bdSlukem  * accounting for escaped hex of UTF-8 chars
23162de962bdSlukem  */
23172de962bdSlukem static int
23182de962bdSlukem strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
23192de962bdSlukem {
23202de962bdSlukem 	ber_len_t	l;
23212de962bdSlukem 	char		*p;
23222de962bdSlukem 
23232de962bdSlukem 	assert( val != NULL );
23242de962bdSlukem 	assert( len != NULL );
23252de962bdSlukem 
23262de962bdSlukem 	*len = 0;
23272de962bdSlukem 	if ( val->bv_len == 0 ) {
23282de962bdSlukem 		return( 0 );
23292de962bdSlukem 	}
23302de962bdSlukem 
23312de962bdSlukem 	if ( flags & LDAP_AVA_NONPRINTABLE ) {
23322de962bdSlukem 		/*
23332de962bdSlukem 		 * FIXME: Turn the value into a binary encoded BER?
23342de962bdSlukem 		 */
23352de962bdSlukem 		return( -1 );
23362de962bdSlukem 
23372de962bdSlukem 	} else {
23382de962bdSlukem 		for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
23392de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
23402de962bdSlukem 				l += 2;
23412de962bdSlukem 
23422de962bdSlukem 			} else {
23432de962bdSlukem 				l++;
23442de962bdSlukem 			}
23452de962bdSlukem 		}
23462de962bdSlukem 	}
23472de962bdSlukem 
23482de962bdSlukem 	*len = l;
23492de962bdSlukem 
23502de962bdSlukem 	return( 0 );
23512de962bdSlukem }
23522de962bdSlukem 
23532de962bdSlukem /*
23542de962bdSlukem  * convert to (supposedly) DCE string representation,
23552de962bdSlukem  * escaping with hex the UTF-8 stuff;
23562de962bdSlukem  * assume the destination has enough room for escaping
23572de962bdSlukem  */
23582de962bdSlukem static int
23592de962bdSlukem strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
23602de962bdSlukem {
23612de962bdSlukem 	ber_len_t	s, d;
23622de962bdSlukem 
23632de962bdSlukem 	assert( val != NULL );
23642de962bdSlukem 	assert( str != NULL );
23652de962bdSlukem 	assert( len != NULL );
23662de962bdSlukem 
23672de962bdSlukem 	if ( val->bv_len == 0 ) {
23682de962bdSlukem 		*len = 0;
23692de962bdSlukem 		return( 0 );
23702de962bdSlukem 	}
23712de962bdSlukem 
23722de962bdSlukem 	if ( flags & LDAP_AVA_NONPRINTABLE ) {
23732de962bdSlukem 		/*
23742de962bdSlukem 		 * FIXME: Turn the value into a binary encoded BER?
23752de962bdSlukem 		 */
23762de962bdSlukem 		*len = 0;
23772de962bdSlukem 		return( -1 );
23782de962bdSlukem 
23792de962bdSlukem 	} else {
23802de962bdSlukem 
23812de962bdSlukem 		/*
23822de962bdSlukem 		 * we assume the string has enough room for the hex encoding
23832de962bdSlukem 		 * of the value
23842de962bdSlukem 		 */
23852de962bdSlukem 
23862de962bdSlukem 		for ( s = 0, d = 0; s < val->bv_len; ) {
23872de962bdSlukem 			if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
23882de962bdSlukem 				str[ d++ ] = '\\';
23892de962bdSlukem 			}
23902de962bdSlukem 			str[ d++ ] = val->bv_val[ s++ ];
23912de962bdSlukem 		}
23922de962bdSlukem 	}
23932de962bdSlukem 
23942de962bdSlukem 	*len = d;
23952de962bdSlukem 
23962de962bdSlukem 	return( 0 );
23972de962bdSlukem }
23982de962bdSlukem 
23992de962bdSlukem /*
24002de962bdSlukem  * Length of the (supposedly) AD canonical string representation,
24012de962bdSlukem  * accounting for chars that need to be escaped
24022de962bdSlukem  */
24032de962bdSlukem static int
24042de962bdSlukem strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
24052de962bdSlukem {
24062de962bdSlukem 	ber_len_t	l, cl;
24072de962bdSlukem 	char		*p;
24082de962bdSlukem 
24092de962bdSlukem 	assert( val != NULL );
24102de962bdSlukem 	assert( len != NULL );
24112de962bdSlukem 
24122de962bdSlukem 	*len = 0;
24132de962bdSlukem 	if ( val->bv_len == 0 ) {
24142de962bdSlukem 		return( 0 );
24152de962bdSlukem 	}
24162de962bdSlukem 
24172de962bdSlukem 	for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) {
24182de962bdSlukem 		cl = LDAP_UTF8_CHARLEN2( p, cl );
24192de962bdSlukem 		if ( cl == 0 ) {
24202de962bdSlukem 			/* illegal utf-8 char */
24212de962bdSlukem 			return -1;
24222de962bdSlukem 		} else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
24232de962bdSlukem 			l += 2;
24242de962bdSlukem 		} else {
24252de962bdSlukem 			l += cl;
24262de962bdSlukem 		}
24272de962bdSlukem 	}
24282de962bdSlukem 
24292de962bdSlukem 	*len = l;
24302de962bdSlukem 
24312de962bdSlukem 	return( 0 );
24322de962bdSlukem }
24332de962bdSlukem 
24342de962bdSlukem /*
24352de962bdSlukem  * convert to (supposedly) AD string representation,
24362de962bdSlukem  * assume the destination has enough room for escaping
24372de962bdSlukem  */
24382de962bdSlukem static int
24392de962bdSlukem strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
24402de962bdSlukem {
24412de962bdSlukem 	ber_len_t	s, d, cl;
24422de962bdSlukem 
24432de962bdSlukem 	assert( val != NULL );
24442de962bdSlukem 	assert( str != NULL );
24452de962bdSlukem 	assert( len != NULL );
24462de962bdSlukem 
24472de962bdSlukem 	if ( val->bv_len == 0 ) {
24482de962bdSlukem 		*len = 0;
24492de962bdSlukem 		return( 0 );
24502de962bdSlukem 	}
24512de962bdSlukem 
24522de962bdSlukem 	/*
24532de962bdSlukem 	 * we assume the string has enough room for the escaping
24542de962bdSlukem 	 * of the value
24552de962bdSlukem 	 */
24562de962bdSlukem 
24572de962bdSlukem 	for ( s = 0, d = 0; s < val->bv_len; ) {
24582de962bdSlukem 		cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl );
24592de962bdSlukem 		if ( cl == 0 ) {
24602de962bdSlukem 			/* illegal utf-8 char */
24612de962bdSlukem 			return -1;
24622de962bdSlukem 		} else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) {
24632de962bdSlukem 			str[ d++ ] = '\\';
24642de962bdSlukem 		}
24652de962bdSlukem 		for (; cl--;) {
24662de962bdSlukem 			str[ d++ ] = val->bv_val[ s++ ];
24672de962bdSlukem 		}
24682de962bdSlukem 	}
24692de962bdSlukem 
24702de962bdSlukem 	*len = d;
24712de962bdSlukem 
24722de962bdSlukem 	return( 0 );
24732de962bdSlukem }
24742de962bdSlukem 
24752de962bdSlukem /*
24762de962bdSlukem  * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
24772de962bdSlukem  * the first part of the AD representation of the DN is written in DNS
24782de962bdSlukem  * form, i.e. dot separated domain name components (as suggested
24792de962bdSlukem  * by Luke Howard, http://www.padl.com/~lukeh)
24802de962bdSlukem  */
24812de962bdSlukem static int
24822de962bdSlukem dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN )
24832de962bdSlukem {
24842de962bdSlukem 	int 		i;
24852de962bdSlukem 	int		domain = 0, first = 1;
24862de962bdSlukem 	ber_len_t	l = 1; /* we move the null also */
24872de962bdSlukem 	char		*str;
24882de962bdSlukem 
24892de962bdSlukem 	/* we are guaranteed there's enough memory in str */
24902de962bdSlukem 
24912de962bdSlukem 	/* sanity */
24922de962bdSlukem 	assert( dn != NULL );
24932de962bdSlukem 	assert( bv != NULL );
24942de962bdSlukem 	assert( iRDN != NULL );
24952de962bdSlukem 	assert( *iRDN >= 0 );
24962de962bdSlukem 
24972de962bdSlukem 	str = bv->bv_val + pos;
24982de962bdSlukem 
24992de962bdSlukem 	for ( i = *iRDN; i >= 0; i-- ) {
25002de962bdSlukem 		LDAPRDN		rdn;
25012de962bdSlukem 		LDAPAVA		*ava;
25022de962bdSlukem 
25032de962bdSlukem 		assert( dn[ i ] != NULL );
25042de962bdSlukem 		rdn = dn[ i ];
25052de962bdSlukem 
25062de962bdSlukem 		assert( rdn[ 0 ] != NULL );
25072de962bdSlukem 		ava = rdn[ 0 ];
25082de962bdSlukem 
25092de962bdSlukem 		if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
25102de962bdSlukem 			break;
25112de962bdSlukem 		}
25122de962bdSlukem 
25134e27b3e8Schristos 		if ( ldif_is_not_printable( ava->la_value.bv_val, ava->la_value.bv_len ) ) {
25144e27b3e8Schristos 			domain = 0;
25154e27b3e8Schristos 			break;
25164e27b3e8Schristos 		}
25174e27b3e8Schristos 
25182de962bdSlukem 		domain = 1;
25192de962bdSlukem 
25202de962bdSlukem 		if ( first ) {
25212de962bdSlukem 			first = 0;
25222de962bdSlukem 			AC_MEMCPY( str, ava->la_value.bv_val,
25232de962bdSlukem 					ava->la_value.bv_len + 1);
25242de962bdSlukem 			l += ava->la_value.bv_len;
25252de962bdSlukem 
25262de962bdSlukem 		} else {
25272de962bdSlukem 			AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
25282de962bdSlukem 			AC_MEMCPY( str, ava->la_value.bv_val,
25292de962bdSlukem 					ava->la_value.bv_len );
25302de962bdSlukem 			str[ ava->la_value.bv_len ] = '.';
25312de962bdSlukem 			l += ava->la_value.bv_len + 1;
25322de962bdSlukem 		}
25332de962bdSlukem 	}
25342de962bdSlukem 
25352de962bdSlukem 	*iRDN = i;
25362de962bdSlukem 	bv->bv_len = pos + l - 1;
25372de962bdSlukem 
25382de962bdSlukem 	return( domain );
25392de962bdSlukem }
25402de962bdSlukem 
25412de962bdSlukem static int
25422de962bdSlukem rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
25432de962bdSlukem 	 int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
25442de962bdSlukem {
25452de962bdSlukem 	int		iAVA;
25462de962bdSlukem 	ber_len_t	l = 0;
25472de962bdSlukem 
25482de962bdSlukem 	*len = 0;
25492de962bdSlukem 
25502de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
25512de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
25522de962bdSlukem 
25532de962bdSlukem 		/* len(type) + '=' + '+' | ',' */
25542de962bdSlukem 		l += ava->la_attr.bv_len + 2;
25552de962bdSlukem 
25562de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
25572de962bdSlukem 			/* octothorpe + twice the length */
25582de962bdSlukem 			l += 1 + 2 * ava->la_value.bv_len;
25592de962bdSlukem 
25602de962bdSlukem 		} else {
25612de962bdSlukem 			ber_len_t	vl;
25622de962bdSlukem 			unsigned	f = flags | ava->la_flags;
25632de962bdSlukem 
25642de962bdSlukem 			if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
25652de962bdSlukem 				return( -1 );
25662de962bdSlukem 			}
25672de962bdSlukem 			l += vl;
25682de962bdSlukem 		}
25692de962bdSlukem 	}
25702de962bdSlukem 
25712de962bdSlukem 	*len = l;
25722de962bdSlukem 
25732de962bdSlukem 	return( 0 );
25742de962bdSlukem }
25752de962bdSlukem 
25762de962bdSlukem static int
25772de962bdSlukem rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
25782de962bdSlukem 	int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
25792de962bdSlukem {
25802de962bdSlukem 	int		iAVA;
25812de962bdSlukem 	ber_len_t	l = 0;
25822de962bdSlukem 
25832de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
25842de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
25852de962bdSlukem 
25862de962bdSlukem 		AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
25872de962bdSlukem 				ava->la_attr.bv_len );
25882de962bdSlukem 		l += ava->la_attr.bv_len;
25892de962bdSlukem 
25902de962bdSlukem 		str[ l++ ] = '=';
25912de962bdSlukem 
25922de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
25932de962bdSlukem 			str[ l++ ] = '#';
25942de962bdSlukem 			if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
25952de962bdSlukem 				return( -1 );
25962de962bdSlukem 			}
25972de962bdSlukem 			l += 2 * ava->la_value.bv_len;
25982de962bdSlukem 
25992de962bdSlukem 		} else {
26002de962bdSlukem 			ber_len_t	vl;
26012de962bdSlukem 			unsigned	f = flags | ava->la_flags;
26022de962bdSlukem 
26032de962bdSlukem 			if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
26042de962bdSlukem 				return( -1 );
26052de962bdSlukem 			}
26062de962bdSlukem 			l += vl;
26072de962bdSlukem 		}
26082de962bdSlukem 		str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' );
26092de962bdSlukem 	}
26102de962bdSlukem 
26112de962bdSlukem 	*len = l;
26122de962bdSlukem 
26132de962bdSlukem 	return( 0 );
26142de962bdSlukem }
26152de962bdSlukem 
26162de962bdSlukem static int
26172de962bdSlukem rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
26182de962bdSlukem {
26192de962bdSlukem 	int		iAVA;
26202de962bdSlukem 	ber_len_t	l = 0;
26212de962bdSlukem 
26222de962bdSlukem 	*len = 0;
26232de962bdSlukem 
26242de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
26252de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
26262de962bdSlukem 
26272de962bdSlukem 		/* len(type) + '=' + ',' | '/' */
26282de962bdSlukem 		l += ava->la_attr.bv_len + 2;
26292de962bdSlukem 
26302de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
26312de962bdSlukem 			/* octothorpe + twice the length */
26322de962bdSlukem 			l += 1 + 2 * ava->la_value.bv_len;
26332de962bdSlukem 		} else {
26342de962bdSlukem 			ber_len_t	vl;
26352de962bdSlukem 			unsigned	f = flags | ava->la_flags;
26362de962bdSlukem 
26372de962bdSlukem 			if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
26382de962bdSlukem 				return( -1 );
26392de962bdSlukem 			}
26402de962bdSlukem 			l += vl;
26412de962bdSlukem 		}
26422de962bdSlukem 	}
26432de962bdSlukem 
26442de962bdSlukem 	*len = l;
26452de962bdSlukem 
26462de962bdSlukem 	return( 0 );
26472de962bdSlukem }
26482de962bdSlukem 
26492de962bdSlukem static int
26502de962bdSlukem rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
26512de962bdSlukem {
26522de962bdSlukem 	int		iAVA;
26532de962bdSlukem 	ber_len_t	l = 0;
26542de962bdSlukem 
26552de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
26562de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
26572de962bdSlukem 
26582de962bdSlukem 		if ( first ) {
26592de962bdSlukem 			first = 0;
26602de962bdSlukem 		} else {
26612de962bdSlukem 			str[ l++ ] = ( iAVA ? ',' : '/' );
26622de962bdSlukem 		}
26632de962bdSlukem 
26642de962bdSlukem 		AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
26652de962bdSlukem 				ava->la_attr.bv_len );
26662de962bdSlukem 		l += ava->la_attr.bv_len;
26672de962bdSlukem 
26682de962bdSlukem 		str[ l++ ] = '=';
26692de962bdSlukem 
26702de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
26712de962bdSlukem 			str[ l++ ] = '#';
26722de962bdSlukem 			if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
26732de962bdSlukem 				return( -1 );
26742de962bdSlukem 			}
26752de962bdSlukem 			l += 2 * ava->la_value.bv_len;
26762de962bdSlukem 		} else {
26772de962bdSlukem 			ber_len_t	vl;
26782de962bdSlukem 			unsigned	f = flags | ava->la_flags;
26792de962bdSlukem 
26802de962bdSlukem 			if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
26812de962bdSlukem 				return( -1 );
26822de962bdSlukem 			}
26832de962bdSlukem 			l += vl;
26842de962bdSlukem 		}
26852de962bdSlukem 	}
26862de962bdSlukem 
26872de962bdSlukem 	*len = l;
26882de962bdSlukem 
26892de962bdSlukem 	return( 0 );
26902de962bdSlukem }
26912de962bdSlukem 
26922de962bdSlukem static int
26932de962bdSlukem rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
26942de962bdSlukem {
26952de962bdSlukem 	int		iAVA;
26962de962bdSlukem 	ber_len_t	l = 0;
26972de962bdSlukem 
26982de962bdSlukem 	assert( rdn != NULL );
26992de962bdSlukem 	assert( len != NULL );
27002de962bdSlukem 
27012de962bdSlukem 	*len = 0;
27022de962bdSlukem 
27032de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
27042de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
27052de962bdSlukem 
27062de962bdSlukem 		/* ' + ' | ', ' */
27072de962bdSlukem 		l += ( rdn[ iAVA + 1 ] ? 3 : 2 );
27082de962bdSlukem 
27092de962bdSlukem 		/* FIXME: are binary values allowed in UFN? */
27102de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
27112de962bdSlukem 			/* octothorpe + twice the value */
27122de962bdSlukem 			l += 1 + 2 * ava->la_value.bv_len;
27132de962bdSlukem 
27142de962bdSlukem 		} else {
27152de962bdSlukem 			ber_len_t	vl;
27162de962bdSlukem 			unsigned	f = flags | ava->la_flags;
27172de962bdSlukem 
27182de962bdSlukem 			if ( strval2strlen( &ava->la_value, f, &vl ) ) {
27192de962bdSlukem 				return( -1 );
27202de962bdSlukem 			}
27212de962bdSlukem 			l += vl;
27222de962bdSlukem 		}
27232de962bdSlukem 	}
27242de962bdSlukem 
27252de962bdSlukem 	*len = l;
27262de962bdSlukem 
27272de962bdSlukem 	return( 0 );
27282de962bdSlukem }
27292de962bdSlukem 
27302de962bdSlukem static int
27312de962bdSlukem rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len )
27322de962bdSlukem {
27332de962bdSlukem 	int		iAVA;
27342de962bdSlukem 	ber_len_t	l = 0;
27352de962bdSlukem 
27362de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
27372de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
27382de962bdSlukem 
27392de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
27402de962bdSlukem 			str[ l++ ] = '#';
27412de962bdSlukem 			if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
27422de962bdSlukem 				return( -1 );
27432de962bdSlukem 			}
27442de962bdSlukem 			l += 2 * ava->la_value.bv_len;
27452de962bdSlukem 
27462de962bdSlukem 		} else {
27472de962bdSlukem 			ber_len_t	vl;
27482de962bdSlukem 			unsigned	f = flags | ava->la_flags;
27492de962bdSlukem 
27502de962bdSlukem 			if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
27512de962bdSlukem 				return( -1 );
27522de962bdSlukem 			}
27532de962bdSlukem 			l += vl;
27542de962bdSlukem 		}
27552de962bdSlukem 
27562de962bdSlukem 		if ( rdn[ iAVA + 1 ] ) {
27572de962bdSlukem 			AC_MEMCPY( &str[ l ], " + ", 3 );
27582de962bdSlukem 			l += 3;
27592de962bdSlukem 
27602de962bdSlukem 		} else {
27612de962bdSlukem 			AC_MEMCPY( &str[ l ], ", ", 2 );
27622de962bdSlukem 			l += 2;
27632de962bdSlukem 		}
27642de962bdSlukem 	}
27652de962bdSlukem 
27662de962bdSlukem 	*len = l;
27672de962bdSlukem 
27682de962bdSlukem 	return( 0 );
27692de962bdSlukem }
27702de962bdSlukem 
27712de962bdSlukem static int
27722de962bdSlukem rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
27732de962bdSlukem {
27742de962bdSlukem 	int		iAVA;
27752de962bdSlukem 	ber_len_t	l = 0;
27762de962bdSlukem 
27772de962bdSlukem 	assert( rdn != NULL );
27782de962bdSlukem 	assert( len != NULL );
27792de962bdSlukem 
27802de962bdSlukem 	*len = 0;
27812de962bdSlukem 
27822de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
27832de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
27842de962bdSlukem 
27852de962bdSlukem 		/* ',' | '/' */
27862de962bdSlukem 		l++;
27872de962bdSlukem 
27882de962bdSlukem 		/* FIXME: are binary values allowed in UFN? */
27892de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
27902de962bdSlukem 			/* octothorpe + twice the value */
27912de962bdSlukem 			l += 1 + 2 * ava->la_value.bv_len;
27922de962bdSlukem 		} else {
27932de962bdSlukem 			ber_len_t	vl;
27942de962bdSlukem 			unsigned	f = flags | ava->la_flags;
27952de962bdSlukem 
27962de962bdSlukem 			if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
27972de962bdSlukem 				return( -1 );
27982de962bdSlukem 			}
27992de962bdSlukem 			l += vl;
28002de962bdSlukem 		}
28012de962bdSlukem 	}
28022de962bdSlukem 
28032de962bdSlukem 	*len = l;
28042de962bdSlukem 
28052de962bdSlukem 	return( 0 );
28062de962bdSlukem }
28072de962bdSlukem 
28082de962bdSlukem static int
28092de962bdSlukem rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
28102de962bdSlukem {
28112de962bdSlukem 	int		iAVA;
28122de962bdSlukem 	ber_len_t	l = 0;
28132de962bdSlukem 
28142de962bdSlukem 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
28152de962bdSlukem 		LDAPAVA		*ava = rdn[ iAVA ];
28162de962bdSlukem 
28172de962bdSlukem 		if ( first ) {
28182de962bdSlukem 			first = 0;
28192de962bdSlukem 		} else {
28202de962bdSlukem 			str[ l++ ] = ( iAVA ? ',' : '/' );
28212de962bdSlukem 		}
28222de962bdSlukem 
28232de962bdSlukem 		if ( ava->la_flags & LDAP_AVA_BINARY ) {
28242de962bdSlukem 			str[ l++ ] = '#';
28252de962bdSlukem 			if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
28262de962bdSlukem 				return( -1 );
28272de962bdSlukem 			}
28282de962bdSlukem 			l += 2 * ava->la_value.bv_len;
28292de962bdSlukem 		} else {
28302de962bdSlukem 			ber_len_t	vl;
28312de962bdSlukem 			unsigned	f = flags | ava->la_flags;
28322de962bdSlukem 
28332de962bdSlukem 			if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
28342de962bdSlukem 				return( -1 );
28352de962bdSlukem 			}
28362de962bdSlukem 			l += vl;
28372de962bdSlukem 		}
28382de962bdSlukem 	}
28392de962bdSlukem 
28402de962bdSlukem 	*len = l;
28412de962bdSlukem 
28422de962bdSlukem 	return( 0 );
28432de962bdSlukem }
28442de962bdSlukem 
28452de962bdSlukem /*
28462de962bdSlukem  * ldap_rdn2str
28472de962bdSlukem  *
28482de962bdSlukem  * Returns in str a string representation of rdn based on flags.
28492de962bdSlukem  * There is some duplication of code between this and ldap_dn2str;
28502de962bdSlukem  * this is wanted to reduce the allocation of temporary buffers.
28512de962bdSlukem  */
28522de962bdSlukem int
28532de962bdSlukem ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags )
28542de962bdSlukem {
28552de962bdSlukem 	struct berval bv;
28562de962bdSlukem 	int rc;
28572de962bdSlukem 
28582de962bdSlukem 	assert( str != NULL );
28592de962bdSlukem 
28602de962bdSlukem 	if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
28612de962bdSlukem 		return LDAP_PARAM_ERROR;
28622de962bdSlukem 	}
28632de962bdSlukem 
28642de962bdSlukem 	rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL );
28652de962bdSlukem 	*str = bv.bv_val;
28662de962bdSlukem 	return rc;
28672de962bdSlukem }
28682de962bdSlukem 
28692de962bdSlukem int
28702de962bdSlukem ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags )
28712de962bdSlukem {
28722de962bdSlukem 	return ldap_rdn2bv_x( rdn, bv, flags, NULL );
28732de962bdSlukem }
28742de962bdSlukem 
28752de962bdSlukem int
28762de962bdSlukem ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx )
28772de962bdSlukem {
28782de962bdSlukem 	int		rc, back;
28792de962bdSlukem 	ber_len_t	l;
28802de962bdSlukem 
28812de962bdSlukem 	assert( bv != NULL );
28822de962bdSlukem 
28832de962bdSlukem 	bv->bv_len = 0;
28842de962bdSlukem 	bv->bv_val = NULL;
28852de962bdSlukem 
28862de962bdSlukem 	if ( rdn == NULL ) {
28872de962bdSlukem 		bv->bv_val = LDAP_STRDUPX( "", ctx );
28882de962bdSlukem 		return( LDAP_SUCCESS );
28892de962bdSlukem 	}
28902de962bdSlukem 
28912de962bdSlukem 	/*
28922de962bdSlukem 	 * This routine wastes "back" bytes at the end of the string
28932de962bdSlukem 	 */
28942de962bdSlukem 
28952de962bdSlukem 	switch ( LDAP_DN_FORMAT( flags ) ) {
28962de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV3:
28972de962bdSlukem 		if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
28982de962bdSlukem 			return LDAP_DECODING_ERROR;
28992de962bdSlukem 		}
29002de962bdSlukem 		break;
29012de962bdSlukem 
29022de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV2:
29032de962bdSlukem 		if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
29042de962bdSlukem 			return LDAP_DECODING_ERROR;
29052de962bdSlukem 		}
29062de962bdSlukem 		break;
29072de962bdSlukem 
29082de962bdSlukem 	case LDAP_DN_FORMAT_UFN:
29092de962bdSlukem 		if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
29102de962bdSlukem 			return LDAP_DECODING_ERROR;
29112de962bdSlukem 		}
29122de962bdSlukem 		break;
29132de962bdSlukem 
29142de962bdSlukem 	case LDAP_DN_FORMAT_DCE:
29152de962bdSlukem 		if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
29162de962bdSlukem 			return LDAP_DECODING_ERROR;
29172de962bdSlukem 		}
29182de962bdSlukem 		break;
29192de962bdSlukem 
29202de962bdSlukem 	case LDAP_DN_FORMAT_AD_CANONICAL:
29212de962bdSlukem 		if ( rdn2ADstrlen( rdn, flags, &l ) ) {
29222de962bdSlukem 			return LDAP_DECODING_ERROR;
29232de962bdSlukem 		}
29242de962bdSlukem 		break;
29252de962bdSlukem 
29262de962bdSlukem 	default:
29272de962bdSlukem 		return LDAP_PARAM_ERROR;
29282de962bdSlukem 	}
29292de962bdSlukem 
29302de962bdSlukem 	bv->bv_val = LDAP_MALLOCX( l + 1, ctx );
2931*549b59edSchristos 	if ( bv->bv_val == NULL ) {
2932*549b59edSchristos 		return LDAP_NO_MEMORY;
2933*549b59edSchristos 	}
29342de962bdSlukem 
29352de962bdSlukem 	switch ( LDAP_DN_FORMAT( flags ) ) {
29362de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV3:
29372de962bdSlukem 		rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
29382de962bdSlukem 		back = 1;
29392de962bdSlukem 		break;
29402de962bdSlukem 
29412de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV2:
29422de962bdSlukem 		rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
29432de962bdSlukem 		back = 1;
29442de962bdSlukem 		break;
29452de962bdSlukem 
29462de962bdSlukem 	case LDAP_DN_FORMAT_UFN:
29472de962bdSlukem 		rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
29482de962bdSlukem 		back = 2;
29492de962bdSlukem 		break;
29502de962bdSlukem 
29512de962bdSlukem 	case LDAP_DN_FORMAT_DCE:
29522de962bdSlukem 		rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
29532de962bdSlukem 		back = 0;
29542de962bdSlukem 		break;
29552de962bdSlukem 
29562de962bdSlukem 	case LDAP_DN_FORMAT_AD_CANONICAL:
29572de962bdSlukem 		rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
29582de962bdSlukem 		back = 0;
29592de962bdSlukem 		break;
29602de962bdSlukem 
29612de962bdSlukem 	default:
29622de962bdSlukem 		/* need at least one of the previous */
29632de962bdSlukem 		return LDAP_PARAM_ERROR;
29642de962bdSlukem 	}
29652de962bdSlukem 
29662de962bdSlukem 	if ( rc ) {
29672de962bdSlukem 		LDAP_FREEX( bv->bv_val, ctx );
29682de962bdSlukem 		return rc;
29692de962bdSlukem 	}
29702de962bdSlukem 
29712de962bdSlukem 	bv->bv_len = l - back;
29722de962bdSlukem 	bv->bv_val[ bv->bv_len ] = '\0';
29732de962bdSlukem 
29742de962bdSlukem 	return LDAP_SUCCESS;
29752de962bdSlukem }
29762de962bdSlukem 
29772de962bdSlukem /*
29782de962bdSlukem  * Very bulk implementation; many optimizations can be performed
29792de962bdSlukem  *   - a NULL dn results in an empty string ""
29802de962bdSlukem  *
29812de962bdSlukem  * FIXME: doubts
29822de962bdSlukem  *   a) what do we do if a UTF-8 string must be converted in LDAPv2?
29832de962bdSlukem  *      we must encode it in binary form ('#' + HEXPAIRs)
29842de962bdSlukem  *   b) does DCE/AD support UTF-8?
29852de962bdSlukem  *      no clue; don't think so.
29862de962bdSlukem  *   c) what do we do when binary values must be converted in UTF/DCE/AD?
29872de962bdSlukem  *      use binary encoded BER
29882de962bdSlukem  */
29892de962bdSlukem int ldap_dn2str( LDAPDN dn, char **str, unsigned flags )
29902de962bdSlukem {
29912de962bdSlukem 	struct berval bv;
29922de962bdSlukem 	int rc;
29932de962bdSlukem 
29942de962bdSlukem 	assert( str != NULL );
29952de962bdSlukem 
29962de962bdSlukem 	if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
29972de962bdSlukem 		return LDAP_PARAM_ERROR;
29982de962bdSlukem 	}
29992de962bdSlukem 
30002de962bdSlukem 	rc = ldap_dn2bv_x( dn, &bv, flags, NULL );
30012de962bdSlukem 	*str = bv.bv_val;
30022de962bdSlukem 	return rc;
30032de962bdSlukem }
30042de962bdSlukem 
30052de962bdSlukem int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags )
30062de962bdSlukem {
30072de962bdSlukem 	return ldap_dn2bv_x( dn, bv, flags, NULL );
30082de962bdSlukem }
30092de962bdSlukem 
30102de962bdSlukem int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx )
30112de962bdSlukem {
30122de962bdSlukem 	int		iRDN;
30132de962bdSlukem 	int		rc = LDAP_ENCODING_ERROR;
30142de962bdSlukem 	ber_len_t	len, l;
30152de962bdSlukem 
30162de962bdSlukem 	/* stringifying helpers for LDAPv3/LDAPv2 */
30172de962bdSlukem 	int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
30182de962bdSlukem 	int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
30192de962bdSlukem 
30202de962bdSlukem 	assert( bv != NULL );
30212de962bdSlukem 	bv->bv_len = 0;
30222de962bdSlukem 	bv->bv_val = NULL;
30232de962bdSlukem 
3024*549b59edSchristos 	Debug1( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags );
30252de962bdSlukem 
30262de962bdSlukem 	/*
30272de962bdSlukem 	 * a null dn means an empty dn string
30282de962bdSlukem 	 * FIXME: better raise an error?
30292de962bdSlukem 	 */
3030d11b170bStron 	if ( dn == NULL || dn[0] == NULL ) {
30312de962bdSlukem 		bv->bv_val = LDAP_STRDUPX( "", ctx );
30322de962bdSlukem 		return( LDAP_SUCCESS );
30332de962bdSlukem 	}
30342de962bdSlukem 
30352de962bdSlukem 	switch ( LDAP_DN_FORMAT( flags ) ) {
30362de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV3:
30372de962bdSlukem 		sv2l = strval2strlen;
30382de962bdSlukem 		sv2s = strval2str;
30392de962bdSlukem 
30402de962bdSlukem 		if( 0 ) {
30412de962bdSlukem 	case LDAP_DN_FORMAT_LDAPV2:
30422de962bdSlukem 			sv2l = strval2IA5strlen;
30432de962bdSlukem 			sv2s = strval2IA5str;
30442de962bdSlukem 		}
30452de962bdSlukem 
30462de962bdSlukem 		for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
30472de962bdSlukem 			ber_len_t	rdnl;
30482de962bdSlukem 			if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) {
30492de962bdSlukem 				goto return_results;
30502de962bdSlukem 			}
30512de962bdSlukem 
30522de962bdSlukem 			len += rdnl;
30532de962bdSlukem 		}
30542de962bdSlukem 
30552de962bdSlukem 		if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
30562de962bdSlukem 			rc = LDAP_NO_MEMORY;
30572de962bdSlukem 			break;
30582de962bdSlukem 		}
30592de962bdSlukem 
30602de962bdSlukem 		for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
30612de962bdSlukem 			ber_len_t	rdnl;
30622de962bdSlukem 
30632de962bdSlukem 			if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags,
30642de962bdSlukem 					&rdnl, sv2s ) ) {
30652de962bdSlukem 				LDAP_FREEX( bv->bv_val, ctx );
30662de962bdSlukem 				bv->bv_val = NULL;
30672de962bdSlukem 				goto return_results;
30682de962bdSlukem 			}
30692de962bdSlukem 			l += rdnl;
30702de962bdSlukem 		}
30712de962bdSlukem 
30722de962bdSlukem 		assert( l == len );
30732de962bdSlukem 
30742de962bdSlukem 		/*
30752de962bdSlukem 		 * trim the last ',' (the allocated memory
30762de962bdSlukem 		 * is one byte longer than required)
30772de962bdSlukem 		 */
30782de962bdSlukem 		bv->bv_len = len - 1;
30792de962bdSlukem 		bv->bv_val[ bv->bv_len ] = '\0';
30802de962bdSlukem 
30812de962bdSlukem 		rc = LDAP_SUCCESS;
30822de962bdSlukem 		break;
30832de962bdSlukem 
30842de962bdSlukem 	case LDAP_DN_FORMAT_UFN: {
30852de962bdSlukem 		/*
30862de962bdSlukem 		 * FIXME: quoting from RFC 1781:
30872de962bdSlukem 		 *
30882de962bdSlukem    To take a distinguished name, and generate a name of this format with
30892de962bdSlukem    attribute types omitted, the following steps are followed.
30902de962bdSlukem 
30912de962bdSlukem     1.  If the first attribute is of type CommonName, the type may be
30922de962bdSlukem 	omitted.
30932de962bdSlukem 
30942de962bdSlukem     2.  If the last attribute is of type Country, the type may be
30952de962bdSlukem         omitted.
30962de962bdSlukem 
30972de962bdSlukem     3.  If the last attribute is of type Country, the last
30982de962bdSlukem         Organisation attribute may have the type omitted.
30992de962bdSlukem 
31002de962bdSlukem     4.  All attributes of type OrganisationalUnit may have the type
31012de962bdSlukem         omitted, unless they are after an Organisation attribute or
31022de962bdSlukem         the first attribute is of type OrganisationalUnit.
31032de962bdSlukem 
31042de962bdSlukem          * this should be the pedantic implementation.
31052de962bdSlukem 		 *
31062de962bdSlukem 		 * Here the standard implementation reflects
31072de962bdSlukem 		 * the one historically provided by OpenLDAP
31082de962bdSlukem 		 * (and UMIch, I presume), with the variant
31092de962bdSlukem 		 * of spaces and plusses (' + ') separating
31102de962bdSlukem 		 * rdn components.
31112de962bdSlukem 		 *
31122de962bdSlukem 		 * A non-standard but nice implementation could
31132de962bdSlukem 		 * be to turn the  final "dc" attributes into a
31142de962bdSlukem 		 * dot-separated domain.
31152de962bdSlukem 		 *
31162de962bdSlukem 		 * Other improvements could involve the use of
31172de962bdSlukem 		 * friendly country names and so.
31182de962bdSlukem 		 */
31192de962bdSlukem #ifdef DC_IN_UFN
31202de962bdSlukem 		int	leftmost_dc = -1;
31212de962bdSlukem 		int	last_iRDN = -1;
31222de962bdSlukem #endif /* DC_IN_UFN */
31232de962bdSlukem 
31242de962bdSlukem 		for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
31252de962bdSlukem 			ber_len_t	rdnl;
31262de962bdSlukem 
31272de962bdSlukem 			if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) {
31282de962bdSlukem 				goto return_results;
31292de962bdSlukem 			}
31302de962bdSlukem 			len += rdnl;
31312de962bdSlukem 
31322de962bdSlukem #ifdef DC_IN_UFN
31332de962bdSlukem 			if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) {
31342de962bdSlukem 				if ( leftmost_dc == -1 ) {
31352de962bdSlukem 					leftmost_dc = iRDN;
31362de962bdSlukem 				}
31372de962bdSlukem 			} else {
31382de962bdSlukem 				leftmost_dc = -1;
31392de962bdSlukem 			}
31402de962bdSlukem #endif /* DC_IN_UFN */
31412de962bdSlukem 		}
31422de962bdSlukem 
31432de962bdSlukem 		if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
31442de962bdSlukem 			rc = LDAP_NO_MEMORY;
31452de962bdSlukem 			break;
31462de962bdSlukem 		}
31472de962bdSlukem 
31482de962bdSlukem #ifdef DC_IN_UFN
31492de962bdSlukem 		if ( leftmost_dc == -1 ) {
31502de962bdSlukem #endif /* DC_IN_UFN */
31512de962bdSlukem 			for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
31522de962bdSlukem 				ber_len_t	vl;
31532de962bdSlukem 
31542de962bdSlukem 				if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
31552de962bdSlukem 						flags, &vl ) ) {
31562de962bdSlukem 					LDAP_FREEX( bv->bv_val, ctx );
31572de962bdSlukem 					bv->bv_val = NULL;
31582de962bdSlukem 					goto return_results;
31592de962bdSlukem 				}
31602de962bdSlukem 				l += vl;
31612de962bdSlukem 			}
31622de962bdSlukem 
31632de962bdSlukem 			/*
31642de962bdSlukem 			 * trim the last ', ' (the allocated memory
31652de962bdSlukem 			 * is two bytes longer than required)
31662de962bdSlukem 			 */
31672de962bdSlukem 			bv->bv_len = len - 2;
31682de962bdSlukem 			bv->bv_val[ bv->bv_len ] = '\0';
31692de962bdSlukem #ifdef DC_IN_UFN
31702de962bdSlukem 		} else {
31712de962bdSlukem 			last_iRDN = iRDN - 1;
31722de962bdSlukem 
31732de962bdSlukem 			for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
31742de962bdSlukem 				ber_len_t	vl;
31752de962bdSlukem 
31762de962bdSlukem 				if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
31772de962bdSlukem 						flags, &vl ) ) {
31782de962bdSlukem 					LDAP_FREEX( bv->bv_val, ctx );
31792de962bdSlukem 					bv->bv_val = NULL;
31802de962bdSlukem 					goto return_results;
31812de962bdSlukem 				}
31822de962bdSlukem 				l += vl;
31832de962bdSlukem 			}
31842de962bdSlukem 
31852de962bdSlukem 			if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
31862de962bdSlukem 				LDAP_FREEX( bv->bv_val, ctx );
31872de962bdSlukem 				bv->bv_val = NULL;
31882de962bdSlukem 				goto return_results;
31892de962bdSlukem 			}
31902de962bdSlukem 
31912de962bdSlukem 			/* the string is correctly terminated by dn2domain */
31922de962bdSlukem 		}
31932de962bdSlukem #endif /* DC_IN_UFN */
31942de962bdSlukem 
31952de962bdSlukem 		rc = LDAP_SUCCESS;
31962de962bdSlukem 
31972de962bdSlukem 	} break;
31982de962bdSlukem 
31992de962bdSlukem 	case LDAP_DN_FORMAT_DCE:
32002de962bdSlukem 		for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
32012de962bdSlukem 			ber_len_t	rdnl;
32022de962bdSlukem 			if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) {
32032de962bdSlukem 				goto return_results;
32042de962bdSlukem 			}
32052de962bdSlukem 
32062de962bdSlukem 			len += rdnl;
32072de962bdSlukem 		}
32082de962bdSlukem 
32092de962bdSlukem 		if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
32102de962bdSlukem 			rc = LDAP_NO_MEMORY;
32112de962bdSlukem 			break;
32122de962bdSlukem 		}
32132de962bdSlukem 
32142de962bdSlukem 		for ( l = 0; iRDN--; ) {
32152de962bdSlukem 			ber_len_t	rdnl;
32162de962bdSlukem 
32172de962bdSlukem 			if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags,
32182de962bdSlukem 					&rdnl, 0 ) ) {
32192de962bdSlukem 				LDAP_FREEX( bv->bv_val, ctx );
32202de962bdSlukem 				bv->bv_val = NULL;
32212de962bdSlukem 				goto return_results;
32222de962bdSlukem 			}
32232de962bdSlukem 			l += rdnl;
32242de962bdSlukem 		}
32252de962bdSlukem 
32262de962bdSlukem 		assert( l == len );
32272de962bdSlukem 
32282de962bdSlukem 		bv->bv_len = len;
32292de962bdSlukem 		bv->bv_val[ bv->bv_len ] = '\0';
32302de962bdSlukem 
32312de962bdSlukem 		rc = LDAP_SUCCESS;
32322de962bdSlukem 		break;
32332de962bdSlukem 
32342de962bdSlukem 	case LDAP_DN_FORMAT_AD_CANONICAL: {
32352de962bdSlukem 		int	trailing_slash = 1;
32362de962bdSlukem 
32372de962bdSlukem 		/*
32382de962bdSlukem 		 * Sort of UFN for DCE DNs: a slash ('/') separated
32392de962bdSlukem 		 * global->local DN with no types; strictly speaking,
32402de962bdSlukem 		 * the naming context should be a domain, which is
3241*549b59edSchristos 		 * written in DNS-style, e.g. dot-separated.
32422de962bdSlukem 		 *
32432de962bdSlukem 		 * Example:
32442de962bdSlukem 		 *
32452de962bdSlukem 		 * 	"givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
32462de962bdSlukem 		 *
32472de962bdSlukem 		 * will read
32482de962bdSlukem 		 *
32492de962bdSlukem 		 * 	"microsoft.com/People/Bill,Gates"
32502de962bdSlukem 		 */
32512de962bdSlukem 		for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) {
32522de962bdSlukem 			ber_len_t	rdnl;
32532de962bdSlukem 
32542de962bdSlukem 			if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) {
32552de962bdSlukem 				goto return_results;
32562de962bdSlukem 			}
32572de962bdSlukem 
32582de962bdSlukem 			len += rdnl;
32592de962bdSlukem 		}
32602de962bdSlukem 
32612de962bdSlukem 		/* reserve room for trailing '/' in case the DN
32622de962bdSlukem 		 * is exactly a domain */
32632de962bdSlukem 		if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL )
32642de962bdSlukem 		{
32652de962bdSlukem 			rc = LDAP_NO_MEMORY;
32662de962bdSlukem 			break;
32672de962bdSlukem 		}
32682de962bdSlukem 
32692de962bdSlukem 		iRDN--;
32702de962bdSlukem 		if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) {
32712de962bdSlukem 			for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
32722de962bdSlukem 				ber_len_t	rdnl;
32732de962bdSlukem 
32742de962bdSlukem 				trailing_slash = 0;
32752de962bdSlukem 
32762de962bdSlukem 				if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
32772de962bdSlukem 						flags, &rdnl, 0 ) ) {
32782de962bdSlukem 					LDAP_FREEX( bv->bv_val, ctx );
32792de962bdSlukem 					bv->bv_val = NULL;
32802de962bdSlukem 					goto return_results;
32812de962bdSlukem 				}
32822de962bdSlukem 				l += rdnl;
32832de962bdSlukem 			}
32842de962bdSlukem 
32852de962bdSlukem 		} else {
32862de962bdSlukem 			int		first = 1;
32872de962bdSlukem 
32882de962bdSlukem 			/*
32892de962bdSlukem 			 * Strictly speaking, AD canonical requires
32902de962bdSlukem 			 * a DN to be in the form "..., dc=smtg",
32912de962bdSlukem 			 * i.e. terminated by a domain component
32922de962bdSlukem 			 */
32932de962bdSlukem 			if ( flags & LDAP_DN_PEDANTIC ) {
32942de962bdSlukem 				LDAP_FREEX( bv->bv_val, ctx );
32952de962bdSlukem 				bv->bv_val = NULL;
32962de962bdSlukem 				rc = LDAP_ENCODING_ERROR;
32972de962bdSlukem 				break;
32982de962bdSlukem 			}
32992de962bdSlukem 
33002de962bdSlukem 			for ( l = 0; iRDN >= 0 ; iRDN-- ) {
33012de962bdSlukem 				ber_len_t	rdnl;
33022de962bdSlukem 
33032de962bdSlukem 				if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
33042de962bdSlukem 						flags, &rdnl, first ) ) {
33052de962bdSlukem 					LDAP_FREEX( bv->bv_val, ctx );
33062de962bdSlukem 					bv->bv_val = NULL;
33072de962bdSlukem 					goto return_results;
33082de962bdSlukem 				}
33092de962bdSlukem 				if ( first ) {
33102de962bdSlukem 					first = 0;
33112de962bdSlukem 				}
33122de962bdSlukem 				l += rdnl;
33132de962bdSlukem 			}
33142de962bdSlukem 		}
33152de962bdSlukem 
33162de962bdSlukem 		if ( trailing_slash ) {
33172de962bdSlukem 			/* the DN is exactly a domain -- need a trailing
33182de962bdSlukem 			 * slash; room was reserved in advance */
33192de962bdSlukem 			bv->bv_val[ len ] = '/';
33202de962bdSlukem 			len++;
33212de962bdSlukem 		}
33222de962bdSlukem 
33232de962bdSlukem 		bv->bv_len = len;
33242de962bdSlukem 		bv->bv_val[ bv->bv_len ] = '\0';
33252de962bdSlukem 
33262de962bdSlukem 		rc = LDAP_SUCCESS;
33272de962bdSlukem 	} break;
33282de962bdSlukem 
33292de962bdSlukem 	default:
33302de962bdSlukem 		return LDAP_PARAM_ERROR;
33312de962bdSlukem 	}
33322de962bdSlukem 
3333*549b59edSchristos 	Debug3( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n",
33342de962bdSlukem 		bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" );
33352de962bdSlukem 
33362de962bdSlukem return_results:;
33372de962bdSlukem 	return( rc );
33382de962bdSlukem }
33392de962bdSlukem 
3340