X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Fgetdn.c;h=781dca07afe6450849247111e1a12ed1e7caf525;hb=8c30114d847ff034dd9e9382d88e276fa7f82833;hp=0108e39a848fb3b4067f211b56b5d5c0df56ac42;hpb=5b4b53561717d7b054b6f5b1eaff1a227b16c13a;p=openldap diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c index 0108e39a84..781dca07af 100644 --- a/libraries/libldap/getdn.c +++ b/libraries/libldap/getdn.c @@ -1,6 +1,6 @@ /* $OpenLDAP$ */ /* - * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved. + * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved. * COPYING RESTRICTIONS APPLY, see COPYRIGHT file */ /* Portions @@ -15,32 +15,84 @@ #include #include - -#include #include #include #include #include "ldap-int.h" +#include "ldap_schema.h" -#define NAME_TYPE_LDAP_RDN 0 -#define NAME_TYPE_LDAP_DN 1 -#define NAME_TYPE_DCE_DN 2 +/* extension to UFN that turns trailing "dc=value" rdns in DNS style, + * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */ +#define DC_IN_UFN +#define PRETTY_ESCAPE -static char **explode_name( const char *name, int notypes, int is_type ); +/* parsing/printing routines */ +static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, + const char **next, unsigned flags, unsigned *retFlags ); +static int DCE2strval( const char *str, struct berval *val, + const char **next, unsigned flags ); +static int IA52strval( const char *str, struct berval *val, + const char **next, unsigned flags ); +static int quotedIA52strval( const char *str, struct berval *val, + const char **next, unsigned flags ); +static int hexstr2binval( const char *str, struct berval *val, + const char **next, unsigned flags ); +static int hexstr2bin( const char *str, char *c ); +static int byte2hexpair( const char *val, char *pair ); +static int binval2hexstr( struct berval *val, char *str ); +static int strval2strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2IA5strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2IA5str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2DCEstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2DCEstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2ADstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2ADstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN ); + +/* AVA helpers */ +static LDAPAVA * ldapava_new( + const struct berval *attr, const struct berval *val, unsigned flags ); + +/* Higher level helpers */ +static int rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *, unsigned, ber_len_t * ) ); +static int rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * )); +static int rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ); +static int rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len ); +static int rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ); +static int rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flag, ber_len_t *len, int first ); +static int rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ); +static int rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first ); +/* + * RFC 1823 ldap_get_dn + */ char * ldap_get_dn( LDAP *ld, LDAPMessage *entry ) { char *dn; BerElement tmp; +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_get_dn\n", 0, 0, 0 ); +#else Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 ); +#endif - if ( entry == NULL ) { - ld->ld_errno = LDAP_PARAM_ERROR; - return( NULL ); - } + assert( ld != NULL ); + assert( LDAP_VALID(ld) ); + assert( entry != NULL ); tmp = *entry->lm_ber; /* struct copy */ if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) { @@ -51,281 +103,265 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry ) return( dn ); } +/* + * RFC 1823 ldap_dn2ufn + */ char * ldap_dn2ufn( LDAP_CONST char *dn ) { - char *ufn; - char **vals; - int i; + char *out = NULL; +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_dn2ufn\n", 0, 0, 0 ); +#else Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 ); +#endif - /* produces completely untyped UFNs */ + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_UFN ); + + return( out ); +} - if( dn == NULL ) { - return NULL; - } +/* + * RFC 1823 ldap_explode_dn + */ +char ** +ldap_explode_dn( LDAP_CONST char *dn, int notypes ) +{ + LDAPDN *tmpDN; + char **values = NULL; + int iRDN; + unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_explode_dn\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 ); +#endif - vals = ldap_explode_dn( dn , 0 ); - if( vals == NULL ) { + if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { return NULL; } - for ( i = 0; vals[i]; i++ ) { - char **rvals; - - rvals = ldap_explode_rdn( vals[i] , 1 ); - if ( rvals == NULL ) { - LDAP_VFREE( vals ); - return NULL; - } + if( tmpDN == NULL ) { + values = LDAP_MALLOC( sizeof( char * ) ); + if( values == NULL ) return NULL; - LDAP_FREE( vals[i] ); - vals[i] = ldap_charray2str( rvals, " + " ); - LDAP_VFREE( rvals ); + values[0] = NULL; + return values; } - ufn = ldap_charray2str( vals, ", " ); + for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ ); - LDAP_VFREE( vals ); - return ufn; -} + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) ); + if ( values == NULL ) { + ldap_dnfree( tmpDN ); + return NULL; + } -char ** -ldap_explode_dn( LDAP_CONST char *dn, int notypes ) -{ - Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 ); + for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ ) { + ldap_rdn2str( tmpDN[ 0 ][ iRDN ], &values[ iRDN ], flag ); + } + ldap_dnfree( tmpDN ); + values[ iRDN ] = NULL; - return explode_name( dn, notypes, NAME_TYPE_LDAP_DN ); + return values; } char ** ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) { + LDAPRDN *tmpRDN; + char **values = NULL; + const char *p; + int iAVA; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_explode_rdn\n", 0, 0, 0 ); +#else Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 ); - return explode_name( rdn, notypes, NAME_TYPE_LDAP_RDN ); -} - -char * -ldap_dn2dcedn( LDAP_CONST char *dn ) -{ - char *dce, *q, **rdns, **p; - int len = 0; - - Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 ); +#endif - rdns = explode_name( dn, 0, NAME_TYPE_LDAP_DN ); - if ( rdns == NULL ) { - return NULL; - } - - for ( p = rdns; *p != NULL; p++ ) { - len += strlen( *p ) + 1; + /* + * we only parse the first rdn + * FIXME: we prefer efficiency over checking if the _ENTIRE_ + * dn can be parsed + */ + if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { + return( NULL ); } - q = dce = LDAP_MALLOC( len + 1 ); - if ( dce == NULL ) { - return NULL; + for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) ; + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) ); + if ( values == NULL ) { + ldap_rdnfree( tmpRDN ); + return( NULL ); } - p--; /* get back past NULL */ + for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) { + ber_len_t l = 0, vl, al = 0; + char *str; + LDAPAVA *ava = tmpRDN[ 0 ][ iAVA ]; + + if ( ava->la_flags == LDAP_AVA_BINARY ) { + vl = 1 + 2 * ava->la_value.bv_len; - for ( ; p != rdns; p-- ) { - strcpy( q, "/" ); - q++; - strcpy( q, *p ); - q += strlen( *p ); - } + } else { + if ( strval2strlen( &ava->la_value, + ava->la_flags, &vl ) ) { + goto error_return; + } + } + + if ( !notypes ) { + al = ava->la_attr.bv_len; + l = vl + ava->la_attr.bv_len + 1; - strcpy( q, "/" ); - q++; - strcpy( q, *p ); - - return dce; -} + str = LDAP_MALLOC( l + 1 ); + AC_MEMCPY( str, ava->la_attr.bv_val, + ava->la_attr.bv_len ); + str[ al++ ] = '='; -char * -ldap_dcedn2dn( LDAP_CONST char *dce ) -{ - char *dn, *q, **rdns, **p; - int len; + } else { + l = vl; + str = LDAP_MALLOC( l + 1 ); + } + + if ( ava->la_flags == LDAP_AVA_BINARY ) { + str[ al++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) { + goto error_return; + } - Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 ); + } else { + if ( strval2str( &ava->la_value, &str[ al ], + ava->la_flags, &vl ) ) { + goto error_return; + } + } - rdns = explode_name( dce, 0, NAME_TYPE_DCE_DN ); - if ( rdns == NULL ) { - return NULL; + str[ l ] = '\0'; + values[ iAVA ] = str; } + values[ iAVA ] = NULL; - len = 0; + ldap_rdnfree( tmpRDN ); - for ( p = rdns; *p != NULL; p++ ) { - len += strlen( *p ) + 1; - } + return( values ); - q = dn = LDAP_MALLOC( len ); - if ( dn == NULL ) { - return NULL; - } +error_return:; + LBER_VFREE( values ); + ldap_rdnfree( tmpRDN ); + return( NULL ); +} - p--; +char * +ldap_dn2dcedn( LDAP_CONST char *dn ) +{ + char *out = NULL; - for ( ; p != rdns; p-- ) { - strcpy( q, *p ); - q += strlen( *p ); - strcpy( q, "," ); - q++; - } +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_dn2dcedn\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 ); +#endif - if ( *dce == '/' ) { - /* the name was fully qualified, thus the most-significant - * RDN was empty. trash the last comma */ - q--; - *q = '\0'; - } else { - /* the name was relative. copy the most significant RDN */ - strcpy( q, *p ); - } + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_DCE ); - return dn; + return( out ); } -#define INQUOTE 1 -#define OUTQUOTE 2 - -static char ** -explode_name( const char *name, int notypes, int is_type ) +char * +ldap_dcedn2dn( LDAP_CONST char *dce ) { - const char *p, *q, *rdn; - char **parts = NULL; - int offset, state, have_equals, count = 0, endquote, len; + char *out = NULL; - /* safe guard */ - if(name == NULL) name = ""; +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_dcedn2dn\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 ); +#endif - /* skip leading whitespace */ - while( ldap_utf8_isspace( name )) { - LDAP_UTF8_INCR( name ); - } + ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 ); - p = rdn = name; - offset = 0; - state = OUTQUOTE; - have_equals=0; + return( out ); +} - do { - /* step forward */ - p += offset; - offset = 1; +char * +ldap_dn2ad_canonical( LDAP_CONST char *dn ) +{ + char *out = NULL; - switch ( *p ) { - case '\\': - if ( p[1] != '\0' ) { - offset = LDAP_UTF8_OFFSET(++p); - } - break; - case '"': - if ( state == INQUOTE ) - state = OUTQUOTE; - else - state = INQUOTE; - break; - case '=': - if( state == OUTQUOTE ) have_equals++; - break; - case '+': - if (is_type == NAME_TYPE_LDAP_RDN) - goto end_part; - break; - case '/': - if (is_type == NAME_TYPE_DCE_DN) - goto end_part; - break; - case ';': - case ',': - if (is_type == NAME_TYPE_LDAP_DN) - goto end_part; - break; - case '\0': - end_part: - if ( state == OUTQUOTE ) { - ++count; - have_equals=0; - - if ( parts == NULL ) { - if (( parts = (char **)LDAP_MALLOC( 8 - * sizeof( char *))) == NULL ) - return( NULL ); - } else if ( count >= 8 ) { - if (( parts = (char **)LDAP_REALLOC( parts, - (count+1) * sizeof( char *))) - == NULL ) - return( NULL ); - } +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_dn2ad_canonical\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 ); +#endif - parts[ count ] = NULL; - endquote = 0; + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_AD_CANONICAL ); - if ( notypes ) { - for ( q = rdn; q < p && *q != '='; ++q ) { - /* EMPTY */; - } + return( out ); +} - if ( q < p ) { - rdn = ++q; - } +/* + * function that changes the string representation of dnin + * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK ) + * + * fin can be one of: + * LDAP_DN_FORMAT_LDAP (rfc 2253 and ldapbis liberal, + * plus some rfc 1779) + * LDAP_DN_FORMAT_LDAPV3 (rfc 2253 and ldapbis) + * LDAP_DN_FORMAT_LDAPV2 (rfc 1779) + * LDAP_DN_FORMAT_DCE (?) + * + * fout can be any of the above except + * LDAP_DN_FORMAT_LDAP + * plus: + * LDAP_DN_FORMAT_UFN (rfc 1781, partial and with extensions) + * LDAP_DN_FORMAT_AD_CANONICAL (?) + */ +int +ldap_dn_normalize( LDAP_CONST char *dnin, + unsigned fin, char **dnout, unsigned fout ) +{ + int rc; + LDAPDN *tmpDN = NULL; - if ( *rdn == '"' ) { - ++rdn; - } - - if ( p[-1] == '"' ) { - endquote = 1; - --p; - } - } +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_dn_normalize\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 ); +#endif - len = p - rdn; + assert( dnout ); - if (( parts[ count-1 ] = (char *)LDAP_CALLOC( 1, - len + 1 )) != NULL ) - { - AC_MEMCPY( parts[ count-1 ], rdn, len ); + *dnout = NULL; - if( !endquote ) { - /* skip trailing spaces */ - while( len > 0 && ldap_utf8_isspace( - &parts[count-1][len-1] ) ) - { - --len; - } - } + if ( dnin == NULL ) { + return( LDAP_SUCCESS ); + } - parts[ count-1 ][ len ] = '\0'; - } + rc = ldap_str2dn( dnin , &tmpDN, fin ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } - /* - * Don't forget to increment 'p' back to where - * it should be. If we don't, then we will - * never get past an "end quote." - */ - if ( endquote == 1 ) - p++; + rc = ldap_dn2str( tmpDN, dnout, fout ); - rdn = *p ? &p[1] : p; - while ( ldap_utf8_isspace( rdn ) ) - ++rdn; - } break; - } - } while ( *p ); + ldap_dnfree( tmpDN ); - return( parts ); + return( rc ); } /* States */ #define B4AVA 0x0000 -#define B4ATTRTYPE 0x0001 +/* #define B4ATTRTYPE 0x0001 */ #define B4OIDATTRTYPE 0x0002 #define B4STRINGATTRTYPE 0x0003 @@ -340,28 +376,28 @@ explode_name( const char *name, int notypes, int is_type ) #define B4IA5VALUE 0x0040 #define B4BINARYVALUE 0x0050 -/* Helpers (mostly from slapd.h; maybe it should be rewritten from this) */ +/* + * Helpers (mostly from slap.h) + * c is assumed to Unicode in an ASCII compatible format (UTF-8) + * Macros assume "C" Locale (ASCII) + */ #define LDAP_DN_ASCII_SPACE(c) \ ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' ) -#define LDAP_DN_ASCII_LOWER(c) ( (c) >= 'a' && (c) <= 'z' ) -#define LDAP_DN_ASCII_UPPER(c) ( (c) >= 'A' && (c) <= 'Z' ) -#define LDAP_DN_ASCII_ALPHA(c) \ - ( LDAP_DN_ASCII_LOWER(c) || LDAP_DN_ASCII_UPPER(c) ) -#define LDAP_DN_ASCII_DIGIT(c) ( (c) >= '0' && (c) <= '9' ) -#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) ( (c) >= 'a' && (c) <= 'f' ) -#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) ( (c) >= 'A' && (c) <= 'F' ) -#define LDAP_DN_ASCII_HEXDIGIT(c) \ - ( LDAP_DN_ASCII_DIGIT(c) \ - || LDAP_DN_ASCII_LCASE_HEXALPHA(c) \ - || LDAP_DN_ASCII_UCASE_HEXALPHA(c) ) -#define LDAP_DN_ASCII_ALNUM(c) \ - ( LDAP_DN_ASCII_ALPHA(c) || LDAP_DN_ASCII_DIGIT(c) ) +#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c) +#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c) +#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c) + +#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c) +#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c) +#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c) +#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c) +#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c) #define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' ) /* attribute type */ -#define LDAP_DN_OID_LEADCHAR(c) ( LDAP_DN_ASCII_DIGIT(c) ) -#define LDAP_DN_DESC_LEADCHAR(c) ( LDAP_DN_ASCII_ALPHA(c) ) -#define LDAP_DN_DESC_CHAR(c) ( LDAP_DN_ASCII_ALNUM(c) || (c) == '-' ) +#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c) +#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c) +#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c) #define LDAP_DN_LANG_SEP(c) ( (c) == ';' ) #define LDAP_DN_ATTRDESC_CHAR(c) \ ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) ) @@ -379,12 +415,19 @@ explode_name( const char *name, int notypes, int is_type ) #define LDAP_DN_NE(c) \ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \ || LDAP_DN_QUOTES(c) || (c) == '<' || (c) == '>' ) +#define LDAP_DN_MAYESCAPE(c) \ + ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \ + || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) ) #define LDAP_DN_NEEDESCAPE(c) \ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) ) -#define LDAP_DN_NEEDESCAPE_LEAD(c) \ - ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) || LDAP_DN_NE(c) ) +#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c) #define LDAP_DN_NEEDESCAPE_TRAIL(c) \ ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) ) +#define LDAP_DN_WILLESCAPE_CHAR(c) \ + ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY ) +#define LDAP_DN_WILLESCAPE_HEX(f, c) \ + ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) ) /* LDAPv2 */ #define LDAP_DN_VALUE_END_V2(c) \ @@ -410,6 +453,10 @@ explode_name( const char *name, int notypes, int is_type ) * Metachars: '/', ',', '=', '\'. * * the '\' is used to escape other metachars. + * + * Assertion: '=' + * RDN separator: '/' + * AVA separator: ',' * * Attribute types must start with alphabetic chars and can contain * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed. @@ -434,11 +481,33 @@ explode_name( const char *name, int notypes, int is_type ) /* generics */ #define LDAP_DN_HEXPAIR(s) \ ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) ) -#define LDAP_DC_ATTR "dc" - +/* better look at the AttributeDescription? */ + +/* FIXME: no composite rdn or non-"dc" types, right? + * (what about "dc" in OID form?) */ +/* FIXME: we do not allow binary values in domain, right? */ +/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */ +/* NOTE: don't use strcasecmp() as it is locale specific! */ +#define LDAP_DC_ATTR "dc" +#define LDAP_DC_ATTRU "DC" +#define LDAP_DN_IS_RDN_DC( r ) \ + ( (r) && (r)[0][0] && !(r)[0][1] \ + && ((r)[0][0]->la_flags == LDAP_AVA_STRING) \ + && ((r)[0][0]->la_attr.bv_len == 2) \ + && (((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \ + || ((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \ + && (((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \ + || ((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1]))) + /* Composite rules */ +#define LDAP_DN_ALLOW_ONE_SPACE(f) \ + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) ) #define LDAP_DN_ALLOW_SPACES(f) \ - ( ( (f) & LDAP_DN_FORMAT_LDAPV2 ) || !( (f) & LDAP_DN_PEDANTIC ) ) + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) ) +#define LDAP_DN_LDAP(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP ) #define LDAP_DN_LDAPV3(f) \ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 ) #define LDAP_DN_LDAPV2(f) \ @@ -451,95 +520,62 @@ explode_name( const char *name, int notypes, int is_type ) ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL ) #define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK ) -/* from libraries/libldap/schema.c */ -extern char * parse_numericoid(const char **sp, int *code, const int flags); - -static int str2strval( const char *str, struct berval **val, - const char **next, unsigned flags, unsigned *retFlags ); -static int DCE2strval( const char *str, struct berval **val, - const char **next, unsigned flags ); -static int IA52strval( const char *str, struct berval **val, - const char **next, unsigned flags ); -static int quotedIA52strval( const char *str, struct berval **val, - const char **next, unsigned flags ); -static int hexstr2binval( const char *str, struct berval **val, - const char **next, unsigned flags ); -static int hexstr2bin( const char *str, unsigned *c ); -static int byte2hexpair( const char *val, char *pair ); -static int binval2hexstr( struct berval *val, char *str ); -static ber_len_t strval2strlen( struct berval *val, unsigned flags ); -static int strval2str( struct berval *val, char *str, unsigned flags, - ber_len_t *len ); -static ber_len_t strval2IA5strlen( struct berval *val, unsigned flags ); -static int strval2IA5str( struct berval *val, char *str, unsigned flags, - ber_len_t *len ); -static ber_len_t strval2DCEstrlen( struct berval *val, unsigned flags ); -static int strval2DCEstr( struct berval *val, char *str, unsigned flags, - ber_len_t *len ); -static ber_len_t strval2ADstrlen( struct berval *val, unsigned flags ); -static int strval2ADstr( struct berval *val, char *str, unsigned flags, - ber_len_t *len ); -static int dn2domain( LDAPDN *dn, char **str, int *iRDN ); - /* - * LDAPAVA helpers + * LDAPAVA helpers (will become part of the API for operations + * on structural representations of DNs). */ -static LDAPAVA * -ldapava_new( const char *attr, const struct berval *val, unsigned flags ) +LDAPAVA * +ldapava_new( const struct berval *attr, const struct berval *val, + unsigned flags ) { LDAPAVA *ava; assert( attr ); assert( val ); - ava = LDAP_MALLOC( sizeof( LDAPAVA ) ); + ava = LDAP_MALLOC( sizeof( LDAPAVA ) + attr->bv_len + 1 ); /* should we test it? */ if ( ava == NULL ) { - return NULL; + return( NULL ); } - ava->la_attr = ( char * )attr; - ava->la_value = ( struct berval * )val; - ava->la_flags = flags; - - return ava; -} + ava->la_attr.bv_len = attr->bv_len; + ava->la_attr.bv_val = (char *)(ava+1); + AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len ); + ava->la_attr.bv_val[attr->bv_len] = '\0'; -static void -ldapava_free( LDAPAVA *ava ) -{ - assert( ava ); + ava->la_value = *val; + ava->la_flags = flags; - LDAP_FREE( ava->la_attr ); - ber_bvfree( ava->la_value ); + ava->la_private = NULL; - LDAP_FREE( ava ); + return( ava ); } -static LDAPRDN * -ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava ) +void +ldap_avafree( LDAPAVA *ava ) { - LDAPRDN *newRDN; - unsigned i = 0U; - assert( ava ); - if ( rdn != NULL ) { - for ( i = 0U; rdn[ i ]; i++ ) { - /* no op */ - } - } - newRDN = LDAP_REALLOC( rdn, ( i + 2 ) * sizeof( LDAPAVA ** ) ); - newRDN[ i ] = LDAP_MALLOC( sizeof( LDAPAVA * ) ); - newRDN[ i ][ 0 ] = ava; - newRDN[ i + 1 ] = NULL; +#if 0 + /* ava's private must be freed by caller + * (at present let's skip this check because la_private + * basically holds static data) */ + assert( ava->la_private == NULL ); +#endif + +#if 0 + /* la_attr is now contiguous with ava, not freed separately */ + LDAP_FREE( ava->la_attr.bv_val ); +#endif + LDAP_FREE( ava->la_value.bv_val ); - return newRDN; + LDAP_FREE( ava ); } -static void -ldapava_free_rdn( LDAPRDN *rdn ) +void +ldap_rdnfree( LDAPRDN *rdn ) { int iAVA; @@ -547,62 +583,15 @@ ldapava_free_rdn( LDAPRDN *rdn ) return; } - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - assert( rdn[ iAVA ][ 0 ] ); - - ldapava_free( rdn[ iAVA ][ 0 ] ); - } - - LDAP_VFREE( rdn ); -} - -static LDAPDN * -ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn ) -{ - LDAPDN *newDN; - unsigned i = 0U; - - assert( rdn ); - - if ( dn != NULL ) { - for ( i = 0U; dn[ i ]; i++ ) { - /* no op */ - } - } - newDN = LDAP_REALLOC( dn, ( i + 2 ) * sizeof( LDAPRDN ** ) ); - newDN[ i ] = LDAP_MALLOC( sizeof( LDAPRDN * ) ); - newDN[ i ][ 0 ] = rdn; - newDN[ i + 1 ] = NULL; - - return newDN; -} - -static LDAPDN * -ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn ) -{ - LDAPDN *newDN; - unsigned i = 0U; - - assert( rdn ); - - if ( dn != NULL ) { - for ( i = 0U; dn[ i ]; i++ ) { - /* no op */ - } + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + ldap_avafree( rdn[ 0 ][ iAVA ] ); } - newDN = LDAP_MALLOC( ( i + 2 ) * sizeof( LDAPDN ) ); - AC_MEMCPY( &newDN[ 1 ], dn, i * sizeof( LDAPDN * ) ); - LDAP_FREE( dn ); - - newDN[ 0 ] = LDAP_MALLOC( sizeof( LDAPRDN * ) ); - newDN[ 0 ][ 0 ] = rdn; - newDN[ i + 1 ] = NULL; - return newDN; + LDAP_FREE( rdn ); } -static void -ldapava_free_dn( LDAPDN *dn ) +void +ldap_dnfree( LDAPDN *dn ) { int iRDN; @@ -610,18 +599,16 @@ ldapava_free_dn( LDAPDN *dn ) return; } - for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) { - assert( dn[ iRDN ][ 0 ] ); - - ldapava_free_rdn( dn[ iRDN ][ 0 ] ); + for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ldap_rdnfree( dn[ 0 ][ iRDN ] ); } - LDAP_VFREE( dn ); + LDAP_FREE( dn ); } /* * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE) - * into a structured representation of the DN, by separating attribute + * into a structural representation of the DN, by separating attribute * types and values encoded in the more appropriate form, which is * string or OID for attribute types and binary form of the BER encoded * value or Unicode string. Formats different from LDAPv3 are parsed @@ -632,28 +619,56 @@ ldapava_free_dn( LDAPDN *dn ) * experimental and will hopefully turn into something more simple * and readable as soon as it works as expected. */ + +/* + * Default sizes of AVA and RDN static working arrays; if required + * the are dynamically resized. The values can be tuned in case + * of special requirements (e.g. very deep DN trees or high number + * of AVAs per RDN). + */ +#define TMP_AVA_SLOTS 8 +#define TMP_RDN_SLOTS 32 + int -ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) +ldap_str2dn( LDAP_CONST char *str, LDAPDN **dn, unsigned flags ) { - const char *p; - int state = B4AVA; - int rc = LDAP_INVALID_DN_SYNTAX; - int attrTypeEncoding, attrValueEncoding; + struct berval bv; - char *attrType = NULL; - struct berval *attrValue = NULL; + assert( str ); + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2dn( &bv, dn, flags ); +} + +int +ldap_bv2dn( struct berval *bv, LDAPDN **dn, unsigned flags ) +{ + const char *p; + int rc = LDAP_DECODING_ERROR; + int nrdns = 0; LDAPDN *newDN = NULL; - LDAPRDN *newRDN = NULL; + LDAPRDN *newRDN = NULL, *tmpDN_[TMP_RDN_SLOTS], **tmpDN = tmpDN_; + int num_slots = TMP_RDN_SLOTS; + char *str = bv->bv_val; + char *end = str + bv->bv_len; - assert( str ); + assert( bv ); + assert( bv->bv_val ); assert( dn ); - Debug( LDAP_DEBUG_TRACE, "=> ldap_str2dn(%s,%u)\n%s", str, flags, "" ); +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ARGS, "ldap_bv2dn(%s,%u)\n%s", str, flags, "" ); +#else + Debug( LDAP_DEBUG_TRACE, "=> ldap_bv2dn(%s,%u)\n%s", str, flags, "" ); +#endif *dn = NULL; switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: case LDAP_DN_FORMAT_LDAPV3: case LDAP_DN_FORMAT_LDAPV2: case LDAP_DN_FORMAT_DCE: @@ -662,14 +677,20 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) /* unsupported in str2dn */ case LDAP_DN_FORMAT_UFN: case LDAP_DN_FORMAT_AD_CANONICAL: - return( LDAP_INVALID_DN_SYNTAX ); + return LDAP_PARAM_ERROR; + case LDAP_DN_FORMAT_LBER: default: - return( LDAP_OTHER ); + return LDAP_PARAM_ERROR; } - if ( str[ 0 ] == '\0' ) { - return( LDAP_SUCCESS ); + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; } p = str; @@ -683,20 +704,247 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) goto parsing_error; } p++; - } - for ( ; p[ 0 ] || state == GOTAVA; ) { - + /* + * actually we do not want to accept by default the DCE form, + * we do not want to auto-detect it + */ +#if 0 + } else if ( LDAP_DN_LDAP( flags ) ) { /* - * The parser in principle advances one token a time, - * or toggles state if preferable. + * if dn starts with '/' let's make it a DCE dn */ - switch (state) { + if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + flags |= LDAP_DN_FORMAT_DCE; + p++; + } +#endif + } - /* - * an AttributeType can be encoded as: - * - its string representation; in detail, implementations - * MUST recognize AttributeType string type names listed + for ( ; p < end; p++ ) { + int err; + struct berval tmpbv; + tmpbv.bv_len = bv->bv_len - ( p - str ); + tmpbv.bv_val = (char *)p; + + err = ldap_bv2rdn( &tmpbv, &newRDN, (char **) &p, flags ); + if ( err != LDAP_SUCCESS ) { + goto parsing_error; + } + + /* + * We expect a rdn separator + */ + if ( p < end && p[ 0 ] ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + } + } + + + tmpDN[nrdns++] = newRDN; + newRDN = NULL; + + /* + * make the static RDN array dynamically rescalable + */ + if ( nrdns == num_slots ) { + LDAPRDN **tmp; + + if ( tmpDN == tmpDN_ ) { + tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPRDN * ) ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) ); + + } else { + tmp = LDAP_REALLOC( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ) ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpDN = tmp; + num_slots *= 2; + } + + if ( p >= end || p[ 0 ] == '\0' ) { + /* + * the DN is over, phew + */ + newDN = (LDAPDN *)LDAP_MALLOC( sizeof(LDAPDN) + + sizeof(LDAPRDN *) * (nrdns+1)); + if ( newDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + int i; + + newDN[0] = (LDAPRDN **)(newDN+1); + + if ( LDAP_DN_DCE( flags ) ) { + /* add in reversed order */ + for ( i=0; i= 0; nrdns-- ) { + ldap_rdnfree( tmpDN[nrdns] ); + } + +return_result:; + + if ( tmpDN != tmpDN_ ) { + LDAP_FREE( tmpDN ); + } + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, RESULTS, "<= ldap_bv2dn(%s,%u)=%d\n", + str, flags, rc ); +#else + Debug( LDAP_DEBUG_TRACE, "<= ldap_bv2dn(%s,%u)=%d\n", str, flags, rc ); +#endif + *dn = newDN; + + return( rc ); +} + +/* + * ldap_str2rdn + * + * Parses a relative DN according to flags up to a rdn separator + * or to the end of str. + * Returns the rdn and a pointer to the string continuation, which + * corresponds to the rdn separator or to '\0' in case the string is over. + */ +int +ldap_str2rdn( LDAP_CONST char *str, LDAPRDN **rdn, + char **n_in, unsigned flags ) +{ + struct berval bv; + + assert( str ); + assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */ + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2rdn( &bv, rdn, n_in, flags ); +} + +int +ldap_bv2rdn( struct berval *bv, LDAPRDN **rdn, + char **n_in, unsigned flags ) +{ + const char **n = (const char **) n_in; + const char *p; + int navas = 0; + int state = B4AVA; + int rc = LDAP_DECODING_ERROR; + int attrTypeEncoding = LDAP_AVA_STRING, + attrValueEncoding = LDAP_AVA_STRING; + + struct berval attrType = { 0, NULL }; + struct berval attrValue = { 0, NULL }; + + LDAPRDN *newRDN = NULL; + LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_; + int num_slots = TMP_AVA_SLOTS; + + char *str; + ber_len_t stoplen; + + assert( bv ); + assert( bv->bv_len ); + assert( bv->bv_val ); + assert( rdn || flags & LDAP_DN_SKIP ); + assert( n ); + + str = bv->bv_val; + stoplen = bv->bv_len; + + if ( rdn ) { + *rdn = NULL; + } + *n = NULL; + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + case LDAP_DN_FORMAT_DCE: + break; + + /* unsupported in str2dn */ + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return LDAP_PARAM_ERROR; + + case LDAP_DN_FORMAT_LBER: + default: + return LDAP_PARAM_ERROR; + } + + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; + } + + p = str; + for ( ; p[ 0 ] || state == GOTAVA; ) { + + /* + * The parser in principle advances one token a time, + * or toggles state if preferable. + */ + switch (state) { + + /* + * an AttributeType can be encoded as: + * - its string representation; in detail, implementations + * MUST recognize AttributeType string type names listed * in section 2.3 of draft-ietf-ldapbis-dn-XX.txt, and * MAY recognize other names. * - its numeric OID (a dotted decimal string); in detail @@ -708,6 +956,14 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) * longer; to be liberal, we still implement it. */ case B4AVA: + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) { + /* error */ + goto parsing_error; + } + p++; + } + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { /* error */ @@ -726,10 +982,6 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) } } - state = B4ATTRTYPE; - break; - - case B4ATTRTYPE: /* oid */ if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) { state = B4OIDATTRTYPE; @@ -748,8 +1000,8 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) * "OID." or "oid." */ if ( flags & LDAP_DN_PEDANTIC ) { - if ( !strncmp( p, "oid.", 4 ) - || !strncmp( p, "OID.", 4 ) ) { + if ( !strncmp( p, "OID.", 4 ) + || !strncmp( p, "oid.", 4 ) ) { p += 4; state = B4OIDATTRTYPE; break; @@ -769,10 +1021,14 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) case B4OIDATTRTYPE: { int err = LDAP_SUCCESS; - attrType = parse_numericoid( &p, &err, 0 ); - if ( attrType == NULL ) { + attrType.bv_val = ldap_int_parse_numericoid( &p, &err, + LDAP_SCHEMA_SKIP); + + if ( err != LDAP_SUCCESS ) { goto parsing_error; } + attrType.bv_len = p - attrType.bv_val; + attrTypeEncoding = LDAP_AVA_BINARY; state = B4AVAEQUALS; @@ -787,7 +1043,7 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) * the starting char has been found to be * a LDAP_DN_DESC_LEADCHAR so we don't re-check it * FIXME: DCE attr types seem to have a more - * restrictive syntax + * restrictive syntax (no '-' ...) */ for ( startPos = p++; p[ 0 ]; p++ ) { if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) { @@ -797,7 +1053,7 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) { /* - * FIXME: RFC 2253 does not explicitly + * RFC 2253 does not explicitly * allow lang extensions to attribute * types in DNs ... */ @@ -806,8 +1062,8 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) } /* - * should we rather trim ';' from - * attribute types? + * we trim ';' and following lang + * and so from attribute types */ endPos = p; for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] ) @@ -824,8 +1080,6 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) goto parsing_error; } - assert( attrType == NULL ); - attrType = LDAP_STRNDUP( startPos, len ); attrTypeEncoding = LDAP_AVA_STRING; /* @@ -835,6 +1089,14 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) */ state = B4AVAEQUALS; + + if ( flags & LDAP_DN_SKIP ) { + break; + } + + attrType.bv_val = (char *)startPos; + attrType.bv_len = len; + break; } @@ -899,19 +1161,29 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) * LDAPv2 allows the attribute value to be quoted; * also, IA5 values are expected, in principle */ - if ( LDAP_DN_LDAPV2( flags ) ) { + if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) { if ( LDAP_DN_QUOTES( p[ 0 ] ) ) { p++; state = B4IA5VALUEQUOTED; break; } - state = B4IA5VALUE; - break; + if ( LDAP_DN_LDAPV2( flags ) ) { + state = B4IA5VALUE; + break; + } } - /* FIXME: here STRING means UTF-8 string, right? */ - state = B4STRINGVALUE; + /* + * here STRING means RFC 2253 string + * FIXME: what about DCE strings? + */ + if ( !p[ 0 ] ) { + /* empty value */ + state = GOTAVA; + } else { + state = B4STRINGVALUE; + } break; case B4BINARYVALUE: @@ -924,18 +1196,17 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) case B4STRINGVALUE: switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: case LDAP_DN_FORMAT_LDAPV3: - if ( str2strval( p, &attrValue, - &p, flags, + if ( str2strval( p, stoplen - ( p - str ), + &attrValue, &p, flags, &attrValueEncoding ) ) { goto parsing_error; } break; case LDAP_DN_FORMAT_DCE: - /* FIXME: does DCE use UTF-8? */ - if ( DCE2strval( p, &attrValue, - &p, flags ) ) { + if ( DCE2strval( p, &attrValue, &p, flags ) ) { goto parsing_error; } break; @@ -967,54 +1238,60 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) break; case GOTAVA: { - LDAPAVA *ava; - LDAPRDN *rdn; int rdnsep = 0; - /* - * FIXME: should we accept empty values? - */ + if ( !( flags & LDAP_DN_SKIP ) ) { + LDAPAVA *ava; - ava = ldapava_new( attrType, attrValue, - attrValueEncoding ); - if ( ava == NULL ) { - rc = LDAP_NO_MEMORY; - goto parsing_error; - } + /* + * we accept empty values + */ + ava = ldapava_new( &attrType, &attrValue, + attrValueEncoding ); + + if ( ava == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + tmpRDN[navas++] = ava; - rdn = ldapava_append_to_rdn( newRDN, ava ); - if ( rdn == NULL ) { - rc = LDAP_NO_MEMORY; - goto parsing_error; + attrValue.bv_val = NULL; + attrValue.bv_len = 0; + + /* + * prepare room for new AVAs if needed + */ + if (navas == num_slots) { + LDAPAVA **tmp; + + if ( tmpRDN == tmpRDN_ ) { + tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPAVA * ) ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) ); + + } else { + tmp = LDAP_REALLOC( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ) ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpRDN = tmp; + num_slots *= 2; + } } - newRDN = rdn; - /* add the AVA to this RDN */ -#if 1 -#if 0 - { - wchar_t buf[1024]; - - ldap_x_utf8s_to_wcs( buf, attrType, - sizeof( buf ) ); - fprintf( stderr, "***< %ls", buf ); - ldap_x_utf8s_to_wcs( buf, attrValue->bv_val, - sizeof( buf ) ); - fprintf( stderr, " = %ls >***\n", buf ); - } -#else - fprintf( stderr, "***< %s = %s >***\n", - attrType, attrValue->bv_val ); -#endif -#endif - /* - * if we got an AVA separator ('+', | ',' * for DCE ) + * if we got an AVA separator ('+', or ',' for DCE ) * we expect a new AVA for this RDN; otherwise * we add the RDN to the DN */ - switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: case LDAP_DN_FORMAT_LDAPV3: case LDAP_DN_FORMAT_LDAPV2: if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) { @@ -1030,39 +1307,34 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) } if ( rdnsep ) { - LDAPDN *dn; + /* + * the RDN is over, phew + */ + *n = p; + if ( !( flags & LDAP_DN_SKIP ) ) { + newRDN = (LDAPRDN *)LDAP_MALLOC( sizeof(LDAPRDN) + + sizeof(LDAPAVA *) * (navas+1) ); + if ( newRDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + int i; - if ( LDAP_DN_DCE( flags ) ) { - /* add in reversed order */ - dn = ldapava_insert_into_dn( newDN, - newRDN ); - } else { - dn = ldapava_append_to_dn( newDN, - newRDN ); - } + newRDN[0] = (LDAPAVA**)(newRDN+1); - if ( dn == NULL ) { - rc = LDAP_NO_MEMORY; - goto parsing_error; - } - newDN = dn; - - if ( p[ 0 ] == '\0' ) { - - /* - * the DN is over, phew - */ - rc = LDAP_SUCCESS; - goto return_result; - } + for (i=0; i= 0; navas-- ) { + ldap_avafree( tmpRDN[navas] ); } return_result:; - Debug( LDAP_DEBUG_TRACE, "<= ldap_str2dn(%s,%u)=%d\n", str, flags, rc ); - *dn = newDN; + if ( tmpRDN != tmpRDN_ ) { + LDAP_FREE( tmpRDN ); + } + + if ( rdn ) { + *rdn = newRDN; + } return( rc ); } @@ -1108,45 +1378,43 @@ return_result:; * '\' + HEXPAIR(p) -> unhex(p) */ static int -str2strval( const char *str, struct berval **val, const char **next, unsigned flags, unsigned *retFlags ) +str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, unsigned *retFlags ) { - const char *p, *startPos, *endPos = NULL; + const char *p, *end, *startPos, *endPos = NULL; ber_len_t len, escapes; assert( str ); assert( val ); assert( next ); - *val = NULL; *next = NULL; - - for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { + end = str + stoplen; + for ( startPos = p = str, escapes = 0; p < end; p++ ) { if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { p++; if ( p[ 0 ] == '\0' ) { return( 1 ); } - if ( ( p == startPos + 1 && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) - || ( LDAP_DN_VALUE_END( p[ 1 ] ) && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) - || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { + if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) { escapes++; continue; } if ( LDAP_DN_HEXPAIR( p ) ) { - /* - * FIXME: here I guess I need to decode - * the byte; it would be nice to check - * the resulting encoding is a legal - * UTF-8 char - */ - p++; + char c; + + hexstr2bin( p, &c ); escapes += 2; - /* - * we assume the string is UTF-8 - */ - *retFlags = LDAP_AVA_PRINTABLE; + if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) { + + /* + * we assume the string is UTF-8 + */ + *retFlags = LDAP_AVA_NONPRINTABLE; + } + p++; + continue; } @@ -1154,21 +1422,27 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl return( 1 ); } /* - * FIXME: we allow escaping + * we do not allow escaping * of chars that don't need * to and do not belong to - * HEXDIGITS (we also allow - * single hexdigit; maybe we - * shouldn't). + * HEXDIGITS */ + return( 1 ); + + } else if (!LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) { + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + *retFlags = LDAP_AVA_NONPRINTABLE; - } else if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) + || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) { break; } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { /* * FIXME: maybe we can add - * escapes? + * escapes if not pedantic? */ return( 1 ); } @@ -1187,66 +1461,70 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl /* strip trailing (unescaped) spaces */ for ( endPos = p - 1; endPos > startPos + 1 && - LDAP_DN_ASCII_SPACE( endPos[ 0 ] ) && - !LDAP_DN_ESCAPE( endPos[ -1 ] ); + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); endPos-- ) { /* no op */ } } + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + /* * FIXME: test memory? */ len = ( endPos ? endPos : p ) - startPos - escapes; - *val = LDAP_MALLOC( sizeof( struct berval ) ); - ( *val )->bv_len = len; + val->bv_len = len; if ( escapes == 0 ) { - ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + if ( *retFlags == LDAP_AVA_NONPRINTABLE ) { + val->bv_val = LDAP_MALLOC( len + 1 ); + AC_MEMCPY( val->bv_val, startPos, len ); + val->bv_val[ len ] = '\0'; + } else { + val->bv_val = LDAP_STRNDUP( startPos, len ); + } + } else { ber_len_t s, d; - char *utfStart = NULL; - ( *val )->bv_val = LDAP_MALLOC( len + 1 ); + val->bv_val = LDAP_MALLOC( len + 1 ); for ( s = 0, d = 0; d < len; ) { if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { s++; - if ( ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( startPos[ s ] ) ) - || ( s == len - 1 && LDAP_DN_NEEDESCAPE_TRAIL( startPos[ s ] ) ) - || LDAP_DN_NEEDESCAPE( startPos[ s ] ) ) { - ( *val )->bv_val[ d++ ] = + if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) { + val->bv_val[ d++ ] = startPos[ s++ ]; + } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) { - unsigned c; + char c; hexstr2bin( &startPos[ s ], &c ); - ( *val )->bv_val[ d++ ] = c; + val->bv_val[ d++ ] = c; s += 2; + } else { - /* - * we allow escaping of chars - * that do not need to - */ - ( *val )->bv_val[ d++ ] = - startPos[ s++ ]; + /* we should never get here */ + assert( 0 ); } } else { - ( *val )->bv_val[ d++ ] = startPos[ s++ ]; + val->bv_val[ d++ ] = startPos[ s++ ]; } } - ( *val )->bv_val[ d ] = '\0'; - assert( strlen( ( *val )->bv_val ) == len ); - } - - *next = p; + val->bv_val[ d ] = '\0'; + assert( d == len ); + } return( 0 ); } static int -DCE2strval( const char *str, struct berval **val, const char **next, unsigned flags ) +DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags ) { const char *p, *startPos, *endPos = NULL; ber_len_t len, escapes; @@ -1255,23 +1533,26 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl assert( val ); assert( next ); - *val = NULL; *next = NULL; for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { - /* - * FIXME: is '\' the escape char for DCE? - */ if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) { p++; if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { escapes++; + } else { return( 1 ); } + } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { break; } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ } /* @@ -1287,23 +1568,27 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl /* strip trailing (unescaped) spaces */ for ( endPos = p - 1; endPos > startPos + 1 && - LDAP_DN_ASCII_SPACE( endPos[ 0 ] ) && - !LDAP_DN_ESCAPE( endPos[ -1 ] ); + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); endPos-- ) { /* no op */ } } - - len = ( endPos ? endPos : p ) - startPos - escapes; - *val = LDAP_MALLOC( sizeof( struct berval ) ); - ( *val )->bv_len = len; - if ( escapes == 0 ){ - ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); - } else { + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + if ( escapes == 0 ){ + val->bv_val = LDAP_STRNDUP( startPos, len ); + + } else { ber_len_t s, d; - ( *val )->bv_val = LDAP_MALLOC( len + 1 ); + val->bv_val = LDAP_MALLOC( len + 1 ); for ( s = 0, d = 0; d < len; ) { /* * This point is reached only if escapes @@ -1314,19 +1599,17 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl s++; } - ( *val )->bv_val[ d++ ] = startPos[ s++ ]; + val->bv_val[ d++ ] = startPos[ s++ ]; } - ( *val )->bv_val[ d ] = '\0'; - assert( strlen( ( *val )->bv_val ) == len ); + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); } - *next = p; - return( 0 ); } static int -IA52strval( const char *str, struct berval **val, const char **next, unsigned flags ) +IA52strval( const char *str, struct berval *val, const char **next, unsigned flags ) { const char *p, *startPos, *endPos = NULL; ber_len_t len, escapes; @@ -1335,12 +1618,10 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl assert( val ); assert( next ); - *val = NULL; *next = NULL; /* - * FIXME: need to check how escape stuff works - * with LDAPv2 (RFC 1779, right?) + * LDAPv2 (RFC 1779) */ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { @@ -1359,6 +1640,11 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { break; } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ } /* strip trailing (unescaped) spaces */ @@ -1370,31 +1656,35 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl /* no op */ } - *val = LDAP_MALLOC( sizeof( struct berval ) ); + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + len = ( endPos ? endPos : p ) - startPos - escapes; - ( *val )->bv_len = len; + val->bv_len = len; if ( escapes == 0 ) { - ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + val->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; - ( *val )->bv_val = LDAP_MALLOC( len + 1 ); + val->bv_val = LDAP_MALLOC( len + 1 ); for ( s = 0, d = 0; d < len; ) { if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { s++; } - ( *val )->bv_val[ d++ ] = startPos[ s++ ]; + val->bv_val[ d++ ] = startPos[ s++ ]; } - ( *val )->bv_val[ d ] = '\0'; - assert( strlen( ( *val )->bv_val ) == len ); + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); } - *next = p; return( 0 ); } static int -quotedIA52strval( const char *str, struct berval **val, const char **next, unsigned flags ) +quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags ) { const char *p, *startPos, *endPos = NULL; ber_len_t len; @@ -1404,18 +1694,12 @@ quotedIA52strval( const char *str, struct berval **val, const char **next, unsig assert( val ); assert( next ); - *val = NULL; *next = NULL; - /* - * FIXME: of course, as long as we remove the quotes, - * we need to escape chars as required ... - */ - /* initial quote already eaten */ for ( startPos = p = str; p[ 0 ]; p++ ) { /* - * FIXME: According to RFC 1779, the quoted value can + * According to RFC 1779, the quoted value can * contain escaped as well as unescaped special values; * as a consequence we tolerate escaped values * (e.g. '"\,"' -> '\,') and escape unescaped specials @@ -1445,48 +1729,56 @@ quotedIA52strval( const char *str, struct berval **val, const char **next, unsig p++; break; } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ } if ( endPos == NULL ) { return( 1 ); } - /* FIXME: strip trailing (unescaped) spaces? */ + /* Strip trailing (unescaped) spaces */ for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { /* no op */ } + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + len = endPos - startPos - escapes; - assert( len >= 0 ); - *val = LDAP_MALLOC( sizeof( struct berval ) ); - ( *val )->bv_len = len; + assert( endPos >= startPos + escapes ); + val->bv_len = len; if ( escapes == 0 ) { - ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + val->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; - ( *val )->bv_val = LDAP_MALLOC( len + 1 ); - ( *val )->bv_len = len; + val->bv_val = LDAP_MALLOC( len + 1 ); + val->bv_len = len; for ( s = d = 0; d < len; ) { if ( LDAP_DN_ESCAPE( str[ s ] ) ) { s++; } - ( *val )->bv_val[ d++ ] = str[ s++ ]; + val->bv_val[ d++ ] = str[ s++ ]; } - ( *val )->bv_val[ d ] = '\0'; - assert( strlen( ( *val )->bv_val ) == len ); + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); } - *next = p; - return( 0 ); } static int -hexstr2bin( const char *str, unsigned *c ) +hexstr2bin( const char *str, char *c ) { - unsigned c1, c2; + char c1, c2; assert( str ); assert( c ); @@ -1496,10 +1788,12 @@ hexstr2bin( const char *str, unsigned *c ) if ( LDAP_DN_ASCII_DIGIT( c1 ) ) { *c = c1 - '0'; - } else { - c1 = tolower( c1 ); - if ( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) ) { + } else { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) { + *c = c1 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) ); *c = c1 - 'a' + 10; } } @@ -1508,10 +1802,12 @@ hexstr2bin( const char *str, unsigned *c ) if ( LDAP_DN_ASCII_DIGIT( c2 ) ) { *c += c2 - '0'; + } else { - c2 = tolower( c2 ); - - if ( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) ) { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) { + *c += c2 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) ); *c += c2 - 'a' + 10; } } @@ -1520,7 +1816,7 @@ hexstr2bin( const char *str, unsigned *c ) } static int -hexstr2binval( const char *str, struct berval **val, const char **next, unsigned flags ) +hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags ) { const char *p, *startPos, *endPos = NULL; ber_len_t len; @@ -1530,11 +1826,27 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned assert( val ); assert( next ); - *val = NULL; *next = NULL; for ( startPos = p = str; p[ 0 ]; p += 2 ) { - if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } break; } @@ -1545,7 +1857,24 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned endPos = p; for ( ; p[ 0 ]; p++ ) { - if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } break; } } @@ -1557,32 +1886,32 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned } } - len = ( ( endPos ? endPos : p ) - startPos ) / 2; - /* must be even! */ - assert( 2 * len == ( endPos ? endPos : p ) - startPos ); +end_of_value:; - *val = LDAP_MALLOC( sizeof( struct berval ) ); - if ( *val == NULL ) { - return( LDAP_NO_MEMORY ); + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); } - ( *val )->bv_len = len; - ( *val )->bv_val = LDAP_MALLOC( len + 1 ); - if ( ( *val )->bv_val == NULL ) { - LDAP_FREE( *val ); + len = ( ( endPos ? endPos : p ) - startPos ) / 2; + /* must be even! */ + assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos )); + + val->bv_len = len; + val->bv_val = LDAP_MALLOC( len + 1 ); + if ( val->bv_val == NULL ) { return( LDAP_NO_MEMORY ); } for ( s = 0, d = 0; d < len; s += 2, d++ ) { - unsigned c; + char c; hexstr2bin( &startPos[ s ], &c ); - ( *val )->bv_val[ d ] = c; + val->bv_val[ d ] = c; } - ( *val )->bv_val[ d ] = '\0'; - *next = p; + val->bv_val[ d ] = '\0'; return( 0 ); } @@ -1593,7 +1922,7 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned static int byte2hexpair( const char *val, char *pair ) { - static const char hexdig[] = "0123456789abcdef"; + static const char hexdig[] = "0123456789ABCDEF"; assert( val ); assert( pair ); @@ -1620,7 +1949,6 @@ binval2hexstr( struct berval *val, char *str ) assert( val ); assert( str ); - /* FIXME: what should I do with a null value? */ if ( val->bv_len == 0 ) { return( 0 ); } @@ -1641,34 +1969,82 @@ binval2hexstr( struct berval *val, char *str ) * Length of the string representation, accounting for escaped hex * of UTF-8 chars */ -static ber_len_t -strval2strlen( struct berval *val, unsigned flags ) +static int +strval2strlen( struct berval *val, unsigned flags, ber_len_t *len ) { - ber_len_t l, cl; + ber_len_t l, cl = 1; char *p; - + int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3; +#ifdef PRETTY_ESCAPE + int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3; +#endif /* PRETTY_ESCAPE */ + assert( val ); + assert( len ); - /* FIXME: what should I do with a null value? */ + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } - for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) { - cl = ldap_utf8_charlen( p ); - if ( cl > 1 ) { - /* need to escape it */ - l += 3 * cl; - } else if ( ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) - || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) - || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { - l += 2; + for ( l = 0, p = val->bv_val; p < val->bv_val + val->bv_len; p += cl ) { + + /* + * escape '%x00' + */ + if ( p[ 0 ] == '\0' ) { + cl = 1; + l += 3; + continue; + } + + cl = LDAP_UTF8_CHARLEN2( p, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char! */ + return( -1 ); + + } else if ( cl > 1 ) { + ber_len_t cnt; + + for ( cnt = 1; cnt < cl; cnt++ ) { + if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) { + return( -1 ); + } + } + l += escaped_byte_len * cl; + + } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { +#ifdef PRETTY_ESCAPE +#if 0 + if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) { +#else + if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) { +#endif + + /* + * there might be some chars we want + * to escape in form of a couple + * of hexdigits for optimization purposes + */ + l += 3; + + } else { + l += escaped_ascii_len; + } +#else /* ! PRETTY_ESCAPE */ + l += 3; +#endif /* ! PRETTY_ESCAPE */ + } else { l++; } } - return( l ); + *len = l; + + return( 0 ); } /* @@ -1678,13 +2054,12 @@ strval2strlen( struct berval *val, unsigned flags ) static int strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) { - ber_len_t s, d, cl, end; + ber_len_t s, d, end; assert( val ); assert( str ); assert( len ); - /* FIXME: what should I do with a null value? */ if ( val->bv_len == 0 ) { *len = 0; return( 0 ); @@ -1695,21 +2070,71 @@ strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) * of the value */ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { - cl = ldap_utf8_charlen( &val->bv_val[ s ] ); + ber_len_t cl; + + /* + * escape '%x00' + */ + if ( val->bv_val[ s ] == '\0' ) { + cl = 1; + str[ d++ ] = '\\'; + str[ d++ ] = '0'; + str[ d++ ] = '0'; + s++; + continue; + } - if ( cl > 1 ) { + /* + * The length was checked in strval2strlen(); + * LDAP_UTF8_CHARLEN() should suffice + */ + cl = LDAP_UTF8_CHARLEN2( &val->bv_val[ s ], cl ); + assert( cl > 0 ); + + /* + * there might be some chars we want to escape in form + * of a couple of hexdigits for optimization purposes + */ + if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) +#ifdef PRETTY_ESCAPE +#if 0 + || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) +#else + || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) +#endif +#else /* ! PRETTY_ESCAPE */ + || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) + +#endif /* ! PRETTY_ESCAPE */ + ) { for ( ; cl--; ) { str[ d++ ] = '\\'; byte2hexpair( &val->bv_val[ s ], &str[ d ] ); s++; d += 2; } + + } else if ( cl > 1 ) { + for ( ; cl--; ) { + str[ d++ ] = val->bv_val[ s++ ]; + } + } else { - if ( ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) - || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) - || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) ) { +#ifdef PRETTY_ESCAPE + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { str[ d++ ] = '\\'; + if ( !LDAP_DN_IS_PRETTY( flags ) ) { + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + s++; + d += 2; + continue; + } } +#endif /* PRETTY_ESCAPE */ str[ d++ ] = val->bv_val[ s++ ]; } } @@ -1722,38 +2147,42 @@ strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) /* * Length of the IA5 string representation (no UTF-8 allowed) */ -static ber_len_t -strval2IA5strlen( struct berval *val, unsigned flags ) +static int +strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len ) { ber_len_t l; char *p; assert( val ); + assert( len ); - /* FIXME: what should I do with a null value? */ + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * Turn value into a binary encoded BER */ - return( 0 ); + return( -1 ); } else { for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { - if ( ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) - || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) - || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { + if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { l += 2; + } else { l++; } } } - return l; + *len = l; + + return( 0 ); } /* @@ -1769,16 +2198,16 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) assert( str ); assert( len ); - /* FIXME: what should I do with a null value? */ if ( val->bv_len == 0 ) { *len = 0; - return ( 0 ); + return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * Turn value into a binary encoded BER */ + *len = 0; return( -1 ); } else { @@ -1788,9 +2217,9 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) */ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { - if ( ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) - || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) - || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) ) { + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { str[ d++ ] = '\\'; } str[ d++ ] = val->bv_val[ s++ ]; @@ -1806,36 +2235,40 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) * Length of the (supposedly) DCE string representation, * accounting for escaped hex of UTF-8 chars */ -static ber_len_t -strval2DCEstrlen( struct berval *val, unsigned flags ) +static int +strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len ) { ber_len_t l; char *p; assert( val ); + assert( len ); - /* FIXME: what should I do with a null value? */ + *len = 0; if ( val->bv_len == 0 ) { - return ( 0 ); + return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * FIXME: Turn the value into a binary encoded BER? */ - return( 0 ); + return( -1 ); } else { for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { l += 2; + } else { l++; } } } - return l; + *len = l; + + return( 0 ); } /* @@ -1852,16 +2285,16 @@ strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) assert( str ); assert( len ); - /* FIXME: what should I do with a null value? */ if ( val->bv_len == 0 ) { *len = 0; - return ( 0 ); + return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * FIXME: Turn the value into a binary encoded BER? */ + *len = 0; return( -1 ); } else { @@ -1888,36 +2321,40 @@ strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) * Length of the (supposedly) AD canonical string representation, * accounting for escaped hex of UTF-8 chars */ -static ber_len_t -strval2ADstrlen( struct berval *val, unsigned flags ) +static int +strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len ) { ber_len_t l; char *p; assert( val ); + assert( len ); - /* FIXME: what should I do with a null value? */ + *len = 0; if ( val->bv_len == 0 ) { - return ( 0 ); + return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * FIXME: Turn the value into a binary encoded BER? */ - return( 0 ); + return( -1 ); } else { for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { if ( LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) { l += 2; + } else { l++; } } } - return l; + *len = l; + + return( 0 ); } /* @@ -1928,22 +2365,22 @@ strval2ADstrlen( struct berval *val, unsigned flags ) static int strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) { - ber_len_t s, d, cl; + ber_len_t s, d; assert( val ); assert( str ); assert( len ); - /* FIXME: what should I do with a null value? */ if ( val->bv_len == 0 ) { *len = 0; - return ( 0 ); + return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* - * FIXME: binary encoded BER + * FIXME: Turn the value into a binary encoded BER? */ + *len = 0; return( -1 ); } else { @@ -1968,40 +2405,39 @@ strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) /* * If the DN is terminated by single-AVA RDNs with attribute type of "dc", - * the forst part of the AD representation of the DN is written in DNS + * the first part of the AD representation of the DN is written in DNS * form, i.e. dot separated domain name components (as suggested * by Luke Howard, http://www.padl.com/~lukeh) */ static int -dn2domain( LDAPDN *dn, char **str, int *iRDN ) +dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN ) { int i; int domain = 0, first = 1; ber_len_t l = 1; /* we move the null also */ + char *str; /* we are guaranteed there's enough memory in str */ /* sanity */ assert( dn ); - assert( str ); - assert( *str ); + assert( bv ); assert( iRDN ); - assert( *iRDN > 0 ); + assert( *iRDN >= 0 ); + + str = bv->bv_val + pos; for ( i = *iRDN; i >= 0; i-- ) { LDAPRDN *rdn; LDAPAVA *ava; - assert( dn[ i ][ 0 ] ); - rdn = dn[ i ][ 0 ]; + assert( dn[ 0 ][ i ] ); + rdn = dn[ 0 ][ i ]; assert( rdn[ 0 ][ 0 ] ); ava = rdn[ 0 ][ 0 ]; - /* FIXME: no composite rdn or non-"dc" types */ - /* FIXME: we do not allow binary values in domain */ - if ( rdn[ 1 ] || strcasecmp( ava->la_attr, LDAP_DC_ATTR ) - || ava->la_flags & LDAP_AVA_BINARY ) { + if ( !LDAP_DN_IS_RDN_DC( rdn ) ) { break; } @@ -2009,394 +2445,1036 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN ) if ( first ) { first = 0; - AC_MEMCPY( *str, ava->la_value->bv_val, - ava->la_value->bv_len + 1); - l += ava->la_value->bv_len; + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len + 1); + l += ava->la_value.bv_len; + } else { - AC_MEMCPY( *str + ava->la_value->bv_len + 1, *str, l); - AC_MEMCPY( *str, ava->la_value->bv_val, - ava->la_value->bv_len ); - ( *str )[ ava->la_value->bv_len ] = '.'; - l += ava->la_value->bv_len + 1; + AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l); + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len ); + str[ ava->la_value.bv_len ] = '.'; + l += ava->la_value.bv_len + 1; } } *iRDN = i; + bv->bv_len = pos + l - 1; return( domain ); } -/* - * Very bulk implementation; many optimizations can be performed - * - a NULL dn results in an empty string "" - * - * FIXME: doubts - * a) what do we do if a UTF-8 string must be converted in LDAPv2? - * we must encode it in binary form ('#' + HEXPAIRs) - * b) does DCE/AD support UTF-8? - * no clue; don't think so. - * c) what do we do when binary values must be converted in UTF/DCE/AD? - * use binary encode - */ -int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) +static int +rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) ) { - int iRDN, iAVA; - int rc = LDAP_OTHER; - ber_len_t len, l; - - ber_len_t ( *s2l )( struct berval *, unsigned ); - int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ); - - assert( str ); + int iAVA; + ber_len_t l = 0; - Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2str(%u)\n%s%s", flags, "", "" ); + *len = 0; - *str = NULL; + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; - if ( dn == NULL ) { - *str = LDAP_STRDUP( "" ); - return( LDAP_SUCCESS ); - } + /* len(type) + '=' + '+' | ',' */ + l += ava->la_attr.bv_len + 2; - switch ( LDAP_DN_FORMAT( flags ) ) { - case LDAP_DN_FORMAT_LDAPV3: - s2l = strval2strlen; - s2s = strval2str; - goto v2_v3; - - case LDAP_DN_FORMAT_LDAPV2: - s2l = strval2IA5strlen; - s2s = strval2IA5str; -v2_v3: + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; - /* - * FIXME: we're treating LDAPv3 and LDAPv2 the same way; - * is it correct? No. LDAPv2 need to use binary encode - * ( '#' + hex form of BER) in case of non-IA5 chars. - */ - for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( ( *s2l )( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; + return( 0 ); +} - /* len(type) + '=' + '+' | ',' */ - len += strlen( ava->la_attr ) + 2; +static int +rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) ) +{ + int iAVA; + ber_len_t l = 0; - /* FIXME: are binary values allowed - * in LDAPv2? */ - if ( ava->la_flags & LDAP_AVA_BINARY ) { - /* octothorpe + twice the length */ - len += 1 + 2 * ava->la_value->bv_len; - } else { - len += ( *s2l )( ava->la_value, - ava->la_flags ); - } - } - } + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; - if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { - rc = LDAP_NO_MEMORY; - break; - } + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; - for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - ber_len_t al = strlen( ava->la_attr ); + str[ l++ ] = '='; - AC_MEMCPY( &( *str )[ l ], ava->la_attr, al ); - l += al; + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; - ( *str )[ l++ ] = '='; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; - if ( ava->la_flags & LDAP_AVA_BINARY ) { - ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); - l += 2 * ava->la_value->bv_len; - } else { - ber_len_t vl; - ( *s2s )( ava->la_value, - &( *str )[ l ], - ava->la_flags, &vl ); - l += vl; - } - ( *str )[ l++ ] = - ( rdn[ iAVA + 1 ] ? '+' : ',' ); + if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); } + l += vl; } + str[ l++ ] = ( rdn[ 0 ][ iAVA + 1 ] ? '+' : ',' ); + } - /* - * trim the last ',' (the allocated memory - * is one byte longer than required) - */ - ( *str )[ len - 1 ] = '\0'; - - rc = LDAP_SUCCESS; - break; + *len = l; - case LDAP_DN_FORMAT_UFN: + return( 0 ); +} - /* - * FIXME: quoting from RFC 1781: - * - To take a distinguished name, and generate a name of this format with - attribute types omitted, the following steps are followed. +static int +rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; - 1. If the first attribute is of type CommonName, the type may be - omitted. + *len = 0; - 2. If the last attribute is of type Country, the type may be - omitted. + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; - 3. If the last attribute is of type Country, the last - Organisation attribute may have the type omitted. + /* len(type) + '=' + ',' | '/' */ + l += ava->la_attr.bv_len + 2; - 4. All attributes of type OrganisationalUnit may have the type - omitted, unless they are after an Organisation attribute or - the first attribute is of type OrganisationalUnit. + switch ( ava->la_flags ) { + case LDAP_AVA_BINARY: + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; + break; - * this should be the pedantic implementation. - * A non-standard but nice implementation could - * be to turn the final "dc" attributes into a - * dot-separated domain. - * Other improvements could involve the use of - * friendly country names and so. - */ + case LDAP_AVA_STRING: { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + break; + } - for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + default: + return( -1 ); + } + } + + *len = l; - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; + return( 0 ); +} - /* ' + ' | ', ' */ - len += ( rdn[ iAVA + 1 ] ? 3 : 2 ); +static int +rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; - /* FIXME: are binary values allowed in UFN? */ - if ( ava->la_flags & LDAP_AVA_BINARY ) { - /* octothorpe + twice the value */ - len += 1 + 2 * ava->la_value->bv_len; - } else { - len += strval2strlen( ava->la_value, - ava->la_flags ); - } - } + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); } - if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { - rc = LDAP_NO_MEMORY; + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; + + str[ l++ ] = '='; + + switch ( ava->la_flags ) { + case LDAP_AVA_BINARY: + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; break; - } - for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - - if ( ava->la_flags & LDAP_AVA_BINARY ) { - ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); - l += 2 * ava->la_value->bv_len; - } else { - ber_len_t vl; - strval2str( ava->la_value, - &( *str )[ l ], - ava->la_flags, &vl ); - l += vl; - } + case LDAP_AVA_STRING: { + ber_len_t vl; + unsigned f = flags | ava->la_flags; - if ( rdn[ iAVA + 1 ]) { - AC_MEMCPY( &( *str )[ l ], " + ", 3 ); - l += 3; - } else { - AC_MEMCPY( &( *str )[ l ], ", ", 2 ); - l += 2; - } + if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); } + l += vl; + break; } + + default: + return( -1 ); + } + } - /* - * trim the last ', ' (the allocated memory - * is two bytes longer than required) - */ - ( *str )[ len - 2 ] = '\0'; + *len = l; - rc = LDAP_SUCCESS; + return( 0 ); +} + +static int +rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn ); + assert( len ); + + *len = 0; + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + + /* ' + ' | ', ' */ + l += ( rdn[ 0 ][ iAVA + 1 ] ? 3 : 2 ); + + /* FIXME: are binary values allowed in UFN? */ + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2strlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + + if ( rdn[ 0 ][ iAVA + 1 ]) { + AC_MEMCPY( &str[ l ], " + ", 3 ); + l += 3; + + } else { + AC_MEMCPY( &str[ l ], ", ", 2 ); + l += 2; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn ); + assert( len ); + + *len = 0; + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + + /* ',' | '/' */ + l++; + + /* FIXME: are binary values allowed in UFN? */ + switch ( ava->la_flags ) { + case LDAP_AVA_BINARY: + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + break; + + case LDAP_AVA_STRING: { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + break; + } + + default: + return( -1 ); + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); + } + + switch ( ava->la_flags ) { + case LDAP_AVA_BINARY: + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + break; + + case LDAP_AVA_STRING: { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + break; + } + + default: + return( -1 ); + } + } + + *len = l; + + return( 0 ); +} + +/* + * ldap_rdn2str + * + * Returns in str a string representation of rdn based on flags. + * There is some duplication of code between this and ldap_dn2str; + * this is wanted to reduce the allocation of temporary buffers. + */ +int +ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } + + rc = ldap_rdn2bv( rdn, &bv, flags ); + *str = bv.bv_val; + return rc; +} + +int +ldap_rdn2bv( LDAPRDN *rdn, struct berval *bv, unsigned flags ) +{ + int rc, back; + ber_len_t l; + + assert( bv ); + + bv->bv_len = 0; + bv->bv_val = NULL; + + if ( rdn == NULL ) { + bv->bv_val = LDAP_STRDUP( "" ); + return( LDAP_SUCCESS ); + } + + /* + * This routine wastes "back" bytes at the end of the string + */ + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_LDAPV2: + if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_UFN: + if ( rdn2UFNstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } break; case LDAP_DN_FORMAT_DCE: - for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + if ( rdn2DCEstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + if ( rdn2ADstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + default: + return LDAP_PARAM_ERROR; + } + + bv->bv_val = LDAP_MALLOC( l + 1 ); + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str ); + back = 1; + break; + + case LDAP_DN_FORMAT_LDAPV2: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str ); + back = 1; + break; + + case LDAP_DN_FORMAT_UFN: + rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l ); + back = 2; + break; + + case LDAP_DN_FORMAT_DCE: + rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + default: + /* need at least one of the previous */ + return LDAP_PARAM_ERROR; + } + + if ( rc ) { + ldap_memfree( bv->bv_val ); + return rc; + } + + bv->bv_len = l - back; + bv->bv_val[ bv->bv_len ] = '\0'; + + return LDAP_SUCCESS; +} + +/* + * Very bulk implementation; many optimizations can be performed + * - a NULL dn results in an empty string "" + * + * FIXME: doubts + * a) what do we do if a UTF-8 string must be converted in LDAPv2? + * we must encode it in binary form ('#' + HEXPAIRs) + * b) does DCE/AD support UTF-8? + * no clue; don't think so. + * c) what do we do when binary values must be converted in UTF/DCE/AD? + * use binary encoded BER + */ +int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; + rc = ldap_dn2bv( dn, &bv, flags ); + *str = bv.bv_val; + return rc; +} - /* len(type) + '=' + ( ',' || '/' ) */ - len += strlen( ava->la_attr ) + 2; +int ldap_dn2bv( LDAPDN *dn, struct berval *bv, unsigned flags ) +{ + int iRDN; + int rc = LDAP_ENCODING_ERROR; + ber_len_t len, l; - /* FIXME: are binary values allowed in DCE? */ - if ( ava->la_flags & LDAP_AVA_BINARY ) { - /* octothorpe + twice the value */ - len += 1 + 2 * ava->la_value->bv_len; - } else { - len += strval2DCEstrlen( ava->la_value, - ava->la_flags ); + /* stringifying helpers for LDAPv3/LDAPv2 */ + int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l ); + int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l ); + + assert( bv ); + bv->bv_len = 0; + bv->bv_val = NULL; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ARGS, "=> ldap_dn2bv(%u)\n%s%s", + flags, "", "" ); +#else + Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2bv(%u)\n%s%s", flags, "", "" ); +#endif + + /* + * a null dn means an empty dn string + * FIXME: better raise an error? + */ + if ( dn == NULL ) { + bv->bv_val = LDAP_STRDUP( "" ); + return( LDAP_SUCCESS ); + } + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + sv2l = strval2strlen; + sv2s = strval2str; + + if( 0 ) { + case LDAP_DN_FORMAT_LDAPV2: + sv2l = strval2IA5strlen; + sv2s = strval2IA5str; + } + + for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2strlen( rdn, flags, &rdnl, sv2l ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2str( rdn, &bv->bv_val[ l ], flags, + &rdnl, sv2s ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + assert( l == len ); + + /* + * trim the last ',' (the allocated memory + * is one byte longer than required) + */ + bv->bv_len = len - 1; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + break; + + case LDAP_DN_FORMAT_UFN: { + /* + * FIXME: quoting from RFC 1781: + * + To take a distinguished name, and generate a name of this format with + attribute types omitted, the following steps are followed. + + 1. If the first attribute is of type CommonName, the type may be + omitted. + + 2. If the last attribute is of type Country, the type may be + omitted. + + 3. If the last attribute is of type Country, the last + Organisation attribute may have the type omitted. + + 4. All attributes of type OrganisationalUnit may have the type + omitted, unless they are after an Organisation attribute or + the first attribute is of type OrganisationalUnit. + + * this should be the pedantic implementation. + * + * Here the standard implementation reflects + * the one historically provided by OpenLDAP + * (and UMIch, I presume), with the variant + * of spaces and plusses (' + ') separating + * rdn components. + * + * A non-standard but nice implementation could + * be to turn the final "dc" attributes into a + * dot-separated domain. + * + * Other improvements could involve the use of + * friendly country names and so. + */ +#ifdef DC_IN_UFN + int leftmost_dc = -1; + int last_iRDN = -1; +#endif /* DC_IN_UFN */ + + for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2UFNstrlen( rdn, flags, &rdnl ) ) { + goto return_results; + } + len += rdnl; + +#ifdef DC_IN_UFN + if ( LDAP_DN_IS_RDN_DC( rdn ) ) { + if ( leftmost_dc == -1 ) { + leftmost_dc = iRDN; } + } else { + leftmost_dc = -1; } +#endif /* DC_IN_UFN */ } - if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { + if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) { rc = LDAP_NO_MEMORY; break; } - for ( l = 0; iRDN--; ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - /* - * FIXME: does DCE allow '+'? +#ifdef DC_IN_UFN + if ( leftmost_dc == -1 ) { +#endif /* DC_IN_UFN */ + for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t vl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; + } + l += vl; + } + + /* + * trim the last ', ' (the allocated memory + * is two bytes longer than required) */ - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - ber_len_t al = strlen( ava->la_attr ); - - ( *str )[ l++ ] = ( iAVA ? ',' : '/' ); - AC_MEMCPY( &( *str )[ l ], ava->la_attr, al ); - l += al; - ( *str )[ l++ ] = '='; - if ( ava->la_flags & LDAP_AVA_BINARY ) { - ( *str )[ l++ ]= '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); - l += 2 * ava->la_value->bv_len; - } else { - ber_len_t vl; - strval2DCEstr( ava->la_value, - &( *str )[ l ], - ava->la_flags, &vl ); - l += vl; + bv->bv_len = len - 2; + bv->bv_val[ bv->bv_len ] = '\0'; +#ifdef DC_IN_UFN + } else { + last_iRDN = iRDN - 1; + + for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) { + ber_len_t vl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; } + l += vl; + } + + if ( !dn2domain( dn, bv, l, &last_iRDN ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; + } + + /* the string is correctly terminated by dn2domain */ + } +#endif /* DC_IN_UFN */ + + rc = LDAP_SUCCESS; + + } break; + + case LDAP_DN_FORMAT_DCE: + for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2DCEstrlen( rdn, flags, &rdnl ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0; iRDN--; ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2DCEstr( rdn, &bv->bv_val[ l ], flags, + &rdnl, 0 ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; } + l += rdnl; } - ( *str )[ len ] = '\0'; + assert( l == len ); + + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; rc = LDAP_SUCCESS; break; case LDAP_DN_FORMAT_AD_CANONICAL: { - for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - - /* ',' || '/' || '.' */ - len += 1; - - /* FIXME: are binary values allowed in AD? */ - if ( ava->la_flags & LDAP_AVA_BINARY ) { - /* octothorpe + twice the value */ - len += 1 + 2 * ava->la_value->bv_len; - } else { - len += strval2ADstrlen( ava->la_value, - ava->la_flags ); - } + /* + * Sort of UFN for DCE DNs: a slash ('/') separated + * global->local DN with no types; strictly speaking, + * the naming context should be a domain, which is + * written in DNS-style, e.g. dot-deparated. + * + * Example: + * + * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com" + * + * will read + * + * "microsoft.com/People/Bill,Gates" + */ + for ( iRDN = 0, len = -1; dn[ 0 ][ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2ADstrlen( rdn, flags, &rdnl ) ) { + goto return_results; } + + len += rdnl; } - if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { + if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) { rc = LDAP_NO_MEMORY; break; } iRDN--; - if ( iRDN && dn2domain( dn, str, &iRDN ) ) { - for ( l = strlen( *str ); iRDN >= 0 ; iRDN-- ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - - ( *str)[ l++ ] = ( iAVA ? ',' : '/' ); - if ( ava->la_flags & LDAP_AVA_BINARY ) { - ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); - l += 2 * ava->la_value->bv_len; - } else { - ber_len_t vl; - strval2ADstr( ava->la_value, - &( *str )[ l ], - ava->la_flags, - &vl ); - l += vl; - } + if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) ) { + for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) { + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2ADstr( rdn, &bv->bv_val[ l ], + flags, &rdnl, 0 ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; } + l += rdnl; } + } else { int first = 1; /* - * FIXME: strictly speaking, AD canonical requires - * a DN to be in the form "..., dc=smtg" + * Strictly speaking, AD canonical requires + * a DN to be in the form "..., dc=smtg", + * i.e. terminated by a domain component */ if ( flags & LDAP_DN_PEDANTIC ) { - LDAP_FREE( *str ); - *str = NULL; - rc = LDAP_INVALID_DN_SYNTAX; + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + rc = LDAP_ENCODING_ERROR; break; } for ( l = 0; iRDN >= 0 ; iRDN-- ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - - if ( first ) { - first = 0; - } else { - ( *str )[ l++ ] = - ( iAVA ? ',' : '/' ); - } - if ( ava->la_flags & LDAP_AVA_BINARY ) { - ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); - l += 2 * ava->la_value->bv_len; - } else { - ber_len_t vl; - strval2ADstr( ava->la_value, - &( *str )[ l ], - ava->la_flags, - &vl ); - l += vl; - } + ber_len_t rdnl; + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + + if ( rdn2ADstr( rdn, &bv->bv_val[ l ], + flags, &rdnl, first ) ) { + LDAP_FREE( bv->bv_val ); + bv->bv_val = NULL; + goto return_results; + } + if ( first ) { + first = 0; } + l += rdnl; } } - ( *str )[ len ] = '\0'; + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; rc = LDAP_SUCCESS; - break; - } + } break; default: - assert( 0 ); - + return LDAP_PARAM_ERROR; } - Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2str(%s,%u)=%d\n", *str, flags, rc ); - +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, RESULTS, "<= ldap_dn2bv(%s,%u)=%d\n", + bv->bv_val, flags, rc ); +#else + Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2bv(%s,%u)=%d\n", + bv->bv_val, flags, rc ); +#endif + +return_results:; return( rc ); } +#ifdef HAVE_TLS +#include +#include + +/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN. + * x509_name must be an (X509_NAME *). If func is non-NULL, the + * constructed DN will use numeric OIDs to identify attributeTypes, + * and the func() will be invoked to rewrite the DN with the given + * flags. + * + * Otherwise the DN will use shortNames as defined in the OpenSSL + * library. + * + * It's preferable to let slapd do the OID to attributeType mapping, + * because the OpenSSL tables are known to have many typos in versions + * up to (at least) 0.9.6c. However, the LDAP client has no schema tables, + * so we're forced to use OpenSSL's mapping there. + * -- Howard Chu 2002-04-18 + */ + +int +ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, + unsigned flags ) +{ + LDAPDN *newDN; + LDAPRDN *newRDN; + LDAPAVA *newAVA, *baseAVA; + X509_NAME_ENTRY *ne; + ASN1_OBJECT *obj; + ASN1_STRING *str; + char oids[8192], *oidptr = oids, *oidbuf = NULL; + void *ptrs[2048]; + int i, j, k = 0, navas, nrdns, rc = LDAP_SUCCESS; + int set = -1; + size_t dnsize, oidrem = sizeof(oids), oidsize = 0; + int csize; + + struct berval Val; + + assert( bv ); + bv->bv_len = 0; + bv->bv_val = NULL; + + /* Get the number of AVAs. This is not necessarily the same as + * the number of RDNs. + */ + navas = X509_NAME_entry_count( x509_name ); + + /* Get the last element, to see how many RDNs there are */ + ne = X509_NAME_get_entry( x509_name, navas - 1 ); + nrdns = ne->set + 1; + + /* Allocate the DN/RDN/AVA stuff as a single block */ + dnsize = sizeof(LDAPDN) + sizeof(LDAPRDN *) * (nrdns+1); + dnsize += sizeof(LDAPRDN) * nrdns + sizeof(LDAPAVA *) * (navas+nrdns); + dnsize += sizeof(LDAPAVA) * navas; + if (dnsize > sizeof(ptrs)) { + newDN = (LDAPDN *)LDAP_MALLOC( dnsize ); + if ( newDN == NULL ) + return LDAP_NO_MEMORY; + } else { + newDN = (LDAPDN *)ptrs; + } + + newDN[0] = (LDAPRDN**)(newDN+1); + newDN[0][nrdns] = NULL; + newRDN = (LDAPRDN*)(newDN[0] + nrdns+1); + newAVA = (LDAPAVA*)(newRDN + navas + nrdns*2); + baseAVA = newAVA; + + /* Retrieve RDNs in reverse order; LDAP is backwards from X.500. */ + for ( i = nrdns - 1, j = 0; i >= 0; i-- ) { + ne = X509_NAME_get_entry( x509_name, i ); + obj = X509_NAME_ENTRY_get_object( ne ); + str = X509_NAME_ENTRY_get_data( ne ); + + /* If set changed, move to next RDN */ + if ( set != ne->set ) { + /* If this is not the first time, end the + * previous RDN and advance. + */ + if ( j > 0 ) { + newRDN[0][k] = NULL; + newRDN = (LDAPRDN*)(newRDN[0]+k+1); + } + newDN[0][j++] = newRDN; + + newRDN[0] = (LDAPAVA**)(newRDN+1); + k = 0; + set = ne->set; + } + newAVA->la_private = NULL; + newAVA->la_flags = LDAP_AVA_STRING; + + if ( !func ) { + int n = OBJ_obj2nid( obj ); + + if (n == NID_undef) + goto get_oid; + newAVA->la_attr.bv_val = (char *)OBJ_nid2sn( n ); + newAVA->la_attr.bv_len = strlen( newAVA->la_attr.bv_val ); + } else { +get_oid: newAVA->la_attr.bv_val = oidptr; + newAVA->la_attr.bv_len = OBJ_obj2txt( oidptr, oidrem, obj, 1 ); + oidptr += newAVA->la_attr.bv_len + 1; + oidrem -= newAVA->la_attr.bv_len + 1; + + /* Running out of OID buffer space? */ + if (oidrem < 128) { + if ( oidsize == 0 ) { + oidsize = sizeof(oids) * 2; + oidrem = oidsize; + oidbuf = LDAP_MALLOC( oidsize ); + if ( oidbuf == NULL ) goto nomem; + oidptr = oidbuf; + } else { + char *old = oidbuf; + oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 ); + if ( oidbuf == NULL ) goto nomem; + /* Buffer moved! Fix AVA pointers */ + if ( old != oidbuf ) { + LDAPAVA *a; + long dif = oidbuf - old; + + for (a=baseAVA; a<=newAVA; a++){ + if (a->la_attr.bv_val >= old && + a->la_attr.bv_val <= (old + oidsize)) + a->la_attr.bv_val += dif; + } + } + oidptr = oidbuf + oidsize - oidrem; + oidrem += oidsize; + oidsize *= 2; + } + } + } + Val.bv_val = str->data; + Val.bv_len = str->length; + switch( str->type ) { + case V_ASN1_UNIVERSALSTRING: + /* This uses 32-bit ISO 10646-1 */ + csize = 4; goto to_utf8; + case V_ASN1_BMPSTRING: + /* This uses 16-bit ISO 10646-1 */ + csize = 2; goto to_utf8; + case V_ASN1_T61STRING: + /* This uses 8-bit, assume ISO 8859-1 */ + csize = 1; +to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value ); + if (rc != LDAP_SUCCESS) goto nomem; + newAVA->la_flags = LDAP_AVA_NONPRINTABLE; + break; + case V_ASN1_UTF8STRING: + newAVA->la_flags = LDAP_AVA_NONPRINTABLE; + /* This is already in UTF-8 encoding */ + case V_ASN1_IA5STRING: + case V_ASN1_PRINTABLESTRING: + /* These are always 7-bit strings */ + ber_dupbv( &newAVA->la_value, &Val ); + default: + ; + } + newRDN[0][k] = newAVA; + newAVA++; + k++; + } + newRDN[0][k] = NULL; + + if ( func ) { + rc = func( newDN, flags ); + if ( rc != LDAP_SUCCESS ) + goto nomem; + } + + rc = ldap_dn2bv( newDN, bv, LDAP_DN_FORMAT_LDAPV3 ); + +nomem: + for (;baseAVA < newAVA; baseAVA++) { + LDAP_FREE( baseAVA->la_value.bv_val ); + } + + if ( oidsize != 0 ) + LDAP_FREE( oidbuf ); + if ( newDN != (LDAPDN*) ptrs ) + LDAP_FREE( newDN ); + return rc; +} +#endif /* HAVE_TLS */