From 64c7e39aa8130e4018039eb18bc6c3181c9ddcd4 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Mon, 29 Oct 2001 08:05:23 +0000 Subject: [PATCH] More compliant version of str2dn/dn2str; may need optimization; uncomment #define USE_LDAP_DN_PARSING in libraries/libldap/getdn.c to enable its use. The ava/rdn/dn handling routines (append, insert, free and so) must be made available thru declaration in some header. --- doc/man/man3/ldap_get_dn.3 | 130 ++++- doc/man/man3/ldap_get_dn.3.links | 5 + include/ldap.h | 11 +- libraries/libldap/dntest.c | 54 +- libraries/libldap/getdn.c | 830 ++++++++++++++++++++----------- 5 files changed, 721 insertions(+), 309 deletions(-) diff --git a/doc/man/man3/ldap_get_dn.3 b/doc/man/man3/ldap_get_dn.3 index b6be24c2a5..5cb7ef977b 100644 --- a/doc/man/man3/ldap_get_dn.3 +++ b/doc/man/man3/ldap_get_dn.3 @@ -20,6 +20,21 @@ char **ldap_explode_rdn( const char *rdn, int notypes ) .LP .ft B char *ldap_dn2ufn( const char * dn ) +.LP +.ft B +char *ldap_dn2dcedn( const char * dn ) +.LP +.ft B +char *ldap_dcedn2dn( const char * dn ) +.LP +.ft B +char *ldap_dn2ad_canonical( const char * dn ) +.LP +.ft B +int ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) +.LP +.ft B +int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) .SH DESCRIPTION These routines allow LDAP entry names (Distinguished Names, or DNs) to be obtained, parsed, converted to a user-friendly form, and tested. @@ -73,6 +88,116 @@ of the format, it is generally only used for display purposes. The space for the UFN returned is obtained dynamically and the user is responsible for freeing it via a call to .BR ldap_memfree (3). +.LP +.B ldap_dn2dcedn() +is used to turn a DN as returned by +.BR ldap_get_dn (3) +into a DCE-style DN, e.g. a string with most-significant to least +significant rdns separated by slashes ('/'); rdn components +are separated by commas (','). +Only printable chars (e.g. LDAPv2 printable string) are allowed, +at least in this implementation. +.B ldap_dcedn2dn() +performs the opposite operation. +.B ldap_dn2ad_canonical() +turns a DN into a AD canonical name, which is basically a DCE dn +with attribute types omitted. +The trailing domain, if present, is turned in a DNS-like domain. +The space for the returned value is obtained dynamically and the user +is responsible for freeing it via a call to +.BR ldap_memfree (3). +.LP +.B ldap_str2dn() +parses a string representation of a distinguished name contained in +.B str +into its components, +which are stored in +.B dn +as +.B ldap_ava +structures, arranged in +.B LDAPAVA, +.B LDAPRDN, +and +.B LDAPDN +terms, defined as: +.nf +.ft B + +typedef struct ldap_ava { + char *la_attr; + struct berval *la_value; + unsigned la_flags; +} LDAPAVA; + +typedef LDAPAVA** LDAPRDN; +typedef LDAPRDN** LDAPDN; + +.ft +.fi +The attribute types and the attribute values are not normalized. +The +.B la_flags +can be either +.B LDAP_AVA_STRING +or +.B LDAP_AVA_BINARY, +the latter meaning that the value is BER/DER encoded and thus must +be represented as, quoting from RFC 2253, " ... an +octothorpe character ('#' ASCII 35) followed by the hexadecimal +representation of each of the bytes of the BER encoding of the X.500 +AttributeValue." +The +.B flags +parameter to +.B ldap_str2dn() +can be +.LP +.nf + LDAP_DN_FORMAT_LDAPV3 + LDAP_DN_FORMAT_LDAPV2 + LDAP_DN_FORMAT_DCE + +.fi +which defines what DN syntax is expected (according to RFC 2253, +RFC 1779 and DCE, respectively). +The format can be \fIOR\fPed to the flags +.LP +.nf + LDAP_DN_P_NO_SPACES + LDAP_DN_P_NO_SPACE_AFTER_RDN + ... + LDAP_DN_PEDANTIC + +.fi +The latter is a shortcut for all the previous limitations. +.LP +.B LDAP_DN_P_NO_SPACES +does not allow extra spaces in the dn; the default is to silently +eliminate spaces around AVA separators ('='), RDN component separators +('+' for LDAPv3/LDAPv2 or ',' for DCE) and RDN separators +(',' LDAPv3/LDAPv2 or '/' for DCE). +.LP +.B LDAP_DN_P_NO_SPACE_AFTER_RDN +does not allow a single space after RDN separators. +.LP +.B ldap_dn2str() +performs the inverse operation, yielding in +.B str +a string representation of +.B dn. +It allows the same values for +.B flags +as +.B ldap_str2dn(), +plus +.LP +.nf + LDAP_DN_FORMAT_UFN + LDAP_DN_FORMAT_AD_CANONICAL + +.fi +for user-friendly naming (RFC 1781) and AD canonical. .SH ERRORS If an error occurs in .BR ldap_get_dn() , @@ -83,8 +208,11 @@ field in the \fIld\fP parameter is set to indicate the error. See for a description of possible error codes. .BR ldap_explode_dn() , .BR ldap_explode_rdn() , +.B ldap_dn2ufn(), +.B ldap_dn2dcedn(), +.B ldap_dcedn2dn(), and -.B ldap_dn2ufn() +.B ldap_dn2ad_canonical() will return NULL with .BR errno (3) set appropriately in case of trouble. diff --git a/doc/man/man3/ldap_get_dn.3.links b/doc/man/man3/ldap_get_dn.3.links index fe88d238cf..9bb38af6e8 100644 --- a/doc/man/man3/ldap_get_dn.3.links +++ b/doc/man/man3/ldap_get_dn.3.links @@ -1,3 +1,8 @@ ldap_explode_dn.3 ldap_explode_rdn.3 ldap_dn2ufn.3 +ldap_str2dn.3 +ldap_dn2str.3 +ldap_dn2dcedn.3 +ldap_dcedn2dn.3 +ldap_dn2ad_canonical.3 diff --git a/include/ldap.h b/include/ldap.h index 4163a3d4df..e219748ece 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -1179,7 +1179,7 @@ typedef struct ldap_ava { unsigned la_flags; #define LDAP_AVA_STRING 0x0000U #define LDAP_AVA_BINARY 0x0001U -#define LDAP_AVA_PRINTABLE 0x0002U +#define LDAP_AVA_NONPRINTABLE 0x0002U } LDAPAVA; typedef LDAPAVA** LDAPRDN; @@ -1194,7 +1194,8 @@ typedef LDAPRDN** LDAPDN; #define LDAP_DN_FORMAT_MASK 0x000FU /* str2dn flags */ -#define LDAP_DN_P_LEADTRAILSPACES 0x1000U +#define LDAP_DN_P_NOLEADTRAILSPACES 0x1000U +#define LDAP_DN_P_NOSPACEAFTERRDN 0x2000U #define LDAP_DN_PEDANTIC 0xF000U LDAP_F( int ) @@ -1209,6 +1210,9 @@ ldap_dn2str LDAP_P(( char **str, unsigned flags )); +LDAP_F( void ) +ldapava_free_dn LDAP_P(( LDAPDN *dn )); + LDAP_F( int ) ldap_dn_normalize LDAP_P(( LDAP_CONST char *in, unsigned iflags, @@ -1234,6 +1238,9 @@ ldap_dn2dcedn LDAP_P(( LDAP_CONST char *dn )); /* deprecated */ LDAP_F( char * ) ldap_dcedn2dn LDAP_P(( LDAP_CONST char *dce )); /* deprecated */ +LDAP_F( char * ) +ldap_dn2ad_canonical LDAP_P(( LDAP_CONST char *dn )); /* deprecated */ + /* * in getattr.c */ diff --git a/libraries/libldap/dntest.c b/libraries/libldap/dntest.c index 99e131f5e0..ec22bd9aab 100644 --- a/libraries/libldap/dntest.c +++ b/libraries/libldap/dntest.c @@ -35,7 +35,7 @@ main( int argc, char *argv[] ) int rc, i, debug = 0, f2 = 0; unsigned flags[ 2 ] = { 0U, 0U }; char *strin, *str, *str2, buf[ 1024 ]; - LDAPDN *dn = NULL; + LDAPDN *dn, *dn2 = NULL; while ( 1 ) { int opt = getopt( argc, argv, "d:" ); @@ -57,8 +57,9 @@ main( int argc, char *argv[] ) if ( argc < 2 ) { fprintf( stderr, "usage: dntest [flags-in[,...]] [flags-out[,...]]\n\n" ); - fprintf( stderr, "\tflags-in: V3,V2,DCE,PEDANTIC\n" ); - fprintf( stderr, "\tflags-out: V3,V2,UFN,DCE,AD,PEDANTIC\n\n" ); + fprintf( stderr, "\tflags-in: V3,V2,DCE,\n" ); + fprintf( stderr, "\tflags-out: V3,V2,UFN,DCE,AD,\n\n" ); + fprintf( stderr, "\t: PEDANTIC,NOSPACES,NOONESPACE\n\n" ); return( 0 ); } @@ -104,6 +105,10 @@ main( int argc, char *argv[] ) flags[ i ] |= LDAP_DN_FORMAT_AD_CANONICAL; } else if ( !strcasecmp( s, "PEDANTIC" ) ) { flags[ i ] |= LDAP_DN_PEDANTIC; + } else if ( !strcasecmp( s, "NOSPACES" ) ) { + flags[ i ] |= LDAP_DN_P_NOLEADTRAILSPACES; + } else if ( !strcasecmp( s, "NOONESPACE" ) ) { + flags[ i ] |= LDAP_DN_P_NOSPACEAFTERRDN; } } } @@ -118,6 +123,9 @@ main( int argc, char *argv[] ) char **values, *tmp, *tmp2; int n; + fprintf( stdout, "\nldap_dn2str(ldap_str2dn(\"%s\"))\n" + "\t= \"%s\"\n", strin, str ); + switch ( flags[ f2 ] & LDAP_DN_FORMAT_MASK ) { case LDAP_DN_FORMAT_UFN: case LDAP_DN_FORMAT_AD_CANONICAL: @@ -125,23 +133,21 @@ main( int argc, char *argv[] ) case LDAP_DN_FORMAT_LDAPV3: case LDAP_DN_FORMAT_LDAPV2: - fprintf( stdout, "\nldap_dn2str(ldap_str2dn(\"%s\"))\n" - "\t=\"%s\"\n", strin, str ); tmp = ldap_dn2ufn( strin ); - fprintf( stdout, "\nldap_dn2ufn(\"%s\")\n\t=\"%s\"\n", - strin, tmp ); + fprintf( stdout, "\nldap_dn2ufn(\"%s\")\n" + "\t= \"%s\"\n", strin, tmp ); ldap_memfree( tmp ); tmp = ldap_dn2dcedn( strin ); fprintf( stdout, "\nldap_dn2dcedn(\"%s\")\n" - "\t=\"%s\"\n", strin, tmp ); + "\t= \"%s\"\n", strin, tmp ); tmp2 = ldap_dcedn2dn( tmp ); fprintf( stdout, "\nldap_dcedn2dn(\"%s\")\n" - "\t=\"%s\"\n", tmp, tmp2 ); + "\t= \"%s\"\n", tmp, tmp2 ); ldap_memfree( tmp ); ldap_memfree( tmp2 ); tmp = ldap_dn2ad_canonical( strin ); fprintf( stdout, "\nldap_dn2ad_canonical(\"%s\")\n" - "\t=\"%s\"\n", strin, tmp ); + "\t= \"%s\"\n", strin, tmp ); ldap_memfree( tmp ); fprintf( stdout, "\nldap_explode_dn(\"%s\"):\n", str ); @@ -184,17 +190,41 @@ main( int argc, char *argv[] ) break; } - rc = ldap_str2dn( str, &dn, flags[ f2 ] ); + rc = ldap_str2dn( str, &dn2, flags[ f2 ] ); if ( rc == LDAP_SUCCESS && - ldap_dn2str( dn, &str2, flags[ f2 ] ) + ldap_dn2str( dn2, &str2, flags[ f2 ] ) == LDAP_SUCCESS ) { + int iRDN; + fprintf( stdout, "\n\"%s\"\n\t == \"%s\" ? %s\n", str, str2, strcmp( str, str2 ) == 0 ? "yes" : "no" ); + + for ( iRDN = 0; dn[ iRDN ] && dn2[ iRDN ]; iRDN++ ) { + LDAPRDN *r = dn[ iRDN ][ 0 ]; + LDAPRDN *r2 = dn2[ iRDN ][ 0 ]; + int iAVA; + + for ( iAVA = 0; r[ iAVA ] && r[ iAVA ]; iAVA++ ) { + LDAPAVA *a = r[ iAVA ][ 0 ]; + LDAPAVA *a2 = r2[ iAVA ][ 0 ]; + + if ( strcmp( a->la_attr, a2->la_attr ) + || a->la_flags != a2->la_flags + || a->la_value->bv_len != a2->la_value->bv_len + || memcmp( a->la_value->bv_val, a2->la_value->bv_val, a->la_value->bv_len ) ) { + fprintf( stdout, "mismatch\n" ); + + } + } + } + + ldapava_free_dn( dn2 ); ldap_memfree( str2 ); } ldap_memfree( str ); } + ldapava_free_dn( dn ); /* note: dn is not freed */ diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c index fa9cf29e88..10c5a2f274 100644 --- a/libraries/libldap/getdn.c +++ b/libraries/libldap/getdn.c @@ -23,11 +23,12 @@ #include "ldap-int.h" -#define NAME_TYPE_LDAP_RDN 0 -#define NAME_TYPE_LDAP_DN 1 -#define NAME_TYPE_DCE_DN 2 +/* forces the use of new dn parsing routines */ +/* #define USE_LDAP_DN_PARSING */ +/* 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 -static char **explode_name( const char *name, int notypes, int is_type ); static char *dn2dn( const char *dnin, unsigned fin, unsigned fout ); /* from libraries/libldap/schema.c */ @@ -47,39 +48,54 @@ static int hexstr2binval( const char *str, struct berval **val, 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 ber_len_t strval2strlen( struct berval *val, unsigned flags ); +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 ber_len_t strval2IA5strlen( struct berval *val, unsigned flags ); +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 ber_len_t strval2DCEstrlen( struct berval *val, unsigned flags ); +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 ber_len_t strval2ADstrlen( struct berval *val, unsigned flags ); +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, char **str, int *iRDN ); +static int dn2domain( LDAPDN *dn, char *str, int *iRDN ); /* AVA helpers */ -static LDAPAVA * ldapava_new( const char *attr, const struct berval *val, +LDAPAVA * ldapava_new( const char *attr, const struct berval *val, unsigned flags ); -static void ldapava_free( LDAPAVA *ava ); -static LDAPRDN * ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava ); -static void ldapava_free_rdn( LDAPRDN *rdn ); -static LDAPDN * ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn ); -static LDAPDN * ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn ); -static void ldapava_free_dn( LDAPDN *dn ); +void ldapava_free( LDAPAVA *ava ); +LDAPRDN * ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava ); +LDAPRDN * ldapava_insert_into_rdn( LDAPRDN *rdn, LDAPAVA *ava, unsigned where ); +void ldapava_free_rdn( LDAPRDN *rdn ); +LDAPDN * ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn ); +LDAPDN * ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn, unsigned where ); +void ldapava_free_dn( LDAPDN *dn ); /* Higher level helpers */ -static ber_len_t rdn2strlen( LDAPRDN *rdn ); -static int rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len ); -static ber_len_t rdn2UFNstrlen( LDAPRDN *rdn ); +static int rdn2strlen( LDAPRDN *rdn, ber_len_t *len, + int ( *s2l )( struct berval *, unsigned, ber_len_t * ) ); +static int rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len, + int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * )); +static int rdn2UFNstrlen( LDAPRDN *rdn, ber_len_t *len ); static int rdn2UFNstr( LDAPRDN *rdn, char *str, ber_len_t *len ); /* ? */ int ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags ); +#ifndef USE_LDAP_DN_PARSING /* deprecated */ +#define NAME_TYPE_LDAP_RDN 0 +#define NAME_TYPE_LDAP_DN 1 +#define NAME_TYPE_DCE_DN 2 + +static char **explode_name( const char *name, int notypes, int is_type ); +#endif /* !USE_LDAP_DN_PARSING */ + /* * RFC 1823 ldap_get_dn */ @@ -111,7 +127,7 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry ) char * ldap_dn2ufn( LDAP_CONST char *dn ) { -#ifndef USE_LDAP_DN_PARSING +#ifndef USE_LDAP_DN_PARSING /* deprecated */ char *ufn; char **vals; int i; @@ -154,10 +170,13 @@ ldap_dn2ufn( LDAP_CONST char *dn ) #endif /* USE_LDAP_DN_PARSING */ } +/* + * RFC 1823 ldap_explode_dn + */ char ** ldap_explode_dn( LDAP_CONST char *dn, int notypes ) { -#ifndef USE_LDAP_DN_PARSING +#ifndef USE_LDAP_DN_PARSING /* deprecated */ Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 ); return explode_name( dn, notypes, NAME_TYPE_LDAP_DN ); @@ -197,7 +216,7 @@ ldap_explode_dn( LDAP_CONST char *dn, int notypes ) char ** ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) { -#ifndef USE_LDAP_DN_PARSING +#ifndef USE_LDAP_DN_PARSING /* deprecated */ Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 ); return explode_name( rdn, notypes, NAME_TYPE_LDAP_RDN ); @@ -224,16 +243,20 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) v = LDAP_REALLOC( values, sizeof( char * ) * ( 2 + iAVA ) ); if ( v == NULL ) { - LBER_VFREE( values ); - ldapava_free_dn( tmpDN ); - return( NULL ); + goto error_return; } values = v; if ( ava->la_flags == LDAP_AVA_BINARY ) { vl = 1 + 2 * ava->la_value->bv_len; + } else { - vl = strval2strlen( ava->la_value, ava->la_flags ); + int rc; + + if ( strval2strlen( ava->la_value, + ava->la_flags, &vl ) ) { + goto error_return; + } } if ( !notypes ) { @@ -244,6 +267,7 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) str = LDAP_MALLOC( l + 1 ); AC_MEMCPY( str, ava->la_attr, al ); str[ al++ ] = '='; + } else { l = vl; str = LDAP_MALLOC( l + 1 ); @@ -251,10 +275,15 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) if ( ava->la_flags == LDAP_AVA_BINARY ) { str[ al++ ] = '#'; - binval2hexstr( ava->la_value, &str[ al ] ); + if ( binval2hexstr( ava->la_value, &str[ al ] ) ) { + goto error_return; + } + } else { - strval2str( ava->la_value, &str[ al ], - ava->la_flags, &vl ); + if ( strval2str( ava->la_value, &str[ al ], + ava->la_flags, &vl ) ) { + goto error_return; + } } str[ l ] = '\0'; @@ -262,45 +291,21 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) } values[ iAVA ] = NULL; - return( values ); -#endif /* USE_LDAP_DN_PARSING */ -} - -char * -ldap_dn2ad_canonical( LDAP_CONST char *dn ) -{ - Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 ); - - return dn2dn( dn, LDAP_DN_FORMAT_LDAPV3, LDAP_DN_FORMAT_AD_CANONICAL ); -} - -static char * -dn2dn( const char *dnin, unsigned fin, unsigned fout ) -{ - char *dnout = NULL; - LDAPDN *tmpDN = NULL; - - if( dnin == NULL ) { - return NULL; - } - - if ( ldap_str2dn( dnin , &tmpDN, fin ) != LDAP_SUCCESS ) { - return NULL; - } + ldapava_free_dn( tmpDN ); - /* don't care about the result ... */ - ldap_dn2str( tmpDN, &dnout, fout ); + return( values ); +error_return: + LBER_VFREE( values ); ldapava_free_dn( tmpDN ); - - return dnout; + return( NULL ); +#endif /* USE_LDAP_DN_PARSING */ } - char * ldap_dn2dcedn( LDAP_CONST char *dn ) { -#ifndef USE_LDAP_DN_PARSING +#ifndef USE_LDAP_DN_PARSING /* deprecated */ char *dce, *q, **rdns, **p; int len = 0; @@ -393,6 +398,15 @@ ldap_dcedn2dn( LDAP_CONST char *dce ) #endif /* USE_LDAP_DN_PARSING */ } +char * +ldap_dn2ad_canonical( LDAP_CONST char *dn ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 ); + + return dn2dn( dn, LDAP_DN_FORMAT_LDAPV3, LDAP_DN_FORMAT_AD_CANONICAL ); +} + +#ifndef USE_LDAP_DN_PARSING /* deprecated */ #define INQUOTE 1 #define OUTQUOTE 2 @@ -524,6 +538,42 @@ explode_name( const char *name, int notypes, int is_type ) return( parts ); } +#endif /* !USE_LDAP_DN_PARSING */ + +/* + * helper 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_LDAPV3 (rfc 2253 and ldapbis) + * LDAP_DN_FORMAT_LDAPV2 (rfc 1779) + * LDAP_DN_FORMAT_DCE (?) + * + * fout can be any of the above plus: + * LDAP_DN_FORMAT_UFN (rfc 1781, partial and with extensions) + * LDAP_DN_FORMAT_AD_CANONICAL (?) + */ +static char * +dn2dn( const char *dnin, unsigned fin, unsigned fout ) +{ + char *dnout = NULL; + LDAPDN *tmpDN = NULL; + + if( dnin == NULL ) { + return NULL; + } + + if ( ldap_str2dn( dnin , &tmpDN, fin ) != LDAP_SUCCESS ) { + return NULL; + } + + /* don't care about the result ... */ + ldap_dn2str( tmpDN, &dnout, fout ); + + ldapava_free_dn( tmpDN ); + + return dnout; +} /* States */ #define B4AVA 0x0000 @@ -613,6 +663,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. @@ -638,10 +692,22 @@ explode_name( const char *name, int notypes, int is_type ) #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? */ +/* FIXME: we do not allow binary values in domain, right? */ +#define LDAP_DN_IS_RDN_DC( rdn ) \ + ( ( rdn ) && ( rdn )[ 0 ][ 0 ] && !( rdn )[ 1 ] \ + && ( ( rdn )[ 0 ][ 0 ]->la_flags == LDAP_AVA_STRING ) \ + && ! strcasecmp( ( rdn )[ 0 ][ 0 ]->la_attr, LDAP_DC_ATTR ) ) + /* Composite rules */ +#define LDAP_DN_ALLOW_ONE_SPACE(f) \ + ( ( (f) & LDAP_DN_FORMAT_LDAPV2 ) \ + || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) ) #define LDAP_DN_ALLOW_SPACES(f) \ - ( ( (f) & LDAP_DN_FORMAT_LDAPV2 ) || !( (f) & LDAP_DN_PEDANTIC ) ) + ( ( (f) & LDAP_DN_FORMAT_LDAPV2 ) \ + || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) ) #define LDAP_DN_LDAPV3(f) \ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 ) #define LDAP_DN_LDAPV2(f) \ @@ -655,9 +721,10 @@ explode_name( const char *name, int notypes, int is_type ) #define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK ) /* - * LDAPAVA helpers + * LDAPAVA helpers (will become part of the API for operations + * on structural representations of DNs). */ -static LDAPAVA * +LDAPAVA * ldapava_new( const char *attr, const struct berval *val, unsigned flags ) { LDAPAVA *ava; @@ -679,7 +746,7 @@ ldapava_new( const char *attr, const struct berval *val, unsigned flags ) return( ava ); } -static void +void ldapava_free( LDAPAVA *ava ) { assert( ava ); @@ -690,7 +757,7 @@ ldapava_free( LDAPAVA *ava ) LDAP_FREE( ava ); } -static LDAPRDN * +LDAPRDN * ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava ) { LDAPRDN *newRDN; @@ -711,7 +778,39 @@ ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava ) return( newRDN ); } -static void +LDAPRDN * +ldapava_insert_into_rdn( LDAPRDN *rdn, LDAPAVA *ava, unsigned where ) +{ + LDAPRDN *newRDN; + unsigned i = 0U; + + assert( ava ); + + if ( rdn != NULL ) { + for ( i = 0U; rdn[ i ]; i++ ) { + /* no op */ + } + } + if ( where > i ) { + where = i; + /* assume "at end", which corresponds to + * ldapava_append_to_rdn */ + } + + newRDN = LDAP_REALLOC( rdn, ( i + 2 ) * sizeof( LDAPAVA ** ) ); + + /* data after insert point */ + AC_MEMCPY( &newRDN[ where + 1 ], &newRDN[ where ], + ( i - where ) * sizeof( LDAPRDN * ) ); + + newRDN[ where ] = LDAP_MALLOC( sizeof( LDAPAVA * ) ); + newRDN[ where ][ 0 ] = ava; + newRDN[ i + 1 ] = NULL; + + return( newRDN ); +} + +void ldapava_free_rdn( LDAPRDN *rdn ) { int iAVA; @@ -729,7 +828,7 @@ ldapava_free_rdn( LDAPRDN *rdn ) LDAP_VFREE( rdn ); } -static LDAPDN * +LDAPDN * ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn ) { LDAPDN *newDN; @@ -750,8 +849,8 @@ ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn ) return( newDN ); } -static LDAPDN * -ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn ) +LDAPDN * +ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn, unsigned where ) { LDAPDN *newDN; unsigned i = 0U; @@ -763,18 +862,26 @@ ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn ) /* no op */ } } - newDN = LDAP_MALLOC( ( i + 2 ) * sizeof( LDAPDN ) ); - AC_MEMCPY( &newDN[ 1 ], dn, i * sizeof( LDAPDN * ) ); - LDAP_FREE( dn ); + if ( where > i ) { + where = i; + /* assume "at end", which corresponds to + * ldapava_append_to_dn */ + } + + newDN = LDAP_REALLOC( dn, ( i + 2 ) * sizeof( LDAPRDN ** ) ); + + /* data after insert point */ + AC_MEMCPY( &newDN[ where + 1 ], &newDN[ where ], + ( i - where ) * sizeof( LDAPDN * ) ); - newDN[ 0 ] = LDAP_MALLOC( sizeof( LDAPRDN * ) ); - newDN[ 0 ][ 0 ] = rdn; + newDN[ where ] = LDAP_MALLOC( sizeof( LDAPRDN * ) ); + newDN[ where ][ 0 ] = rdn; newDN[ i + 1 ] = NULL; return( newDN ); } -static void +void ldapava_free_dn( LDAPDN *dn ) { int iRDN; @@ -805,6 +912,14 @@ ldapava_free_dn( LDAPDN *dn ) * experimental and will hopefully turn into something more simple * and readable as soon as it works as expected. */ +/* + * TODO: move parsing code to function + +int +ldap_str2rdn( const char *str, LDAPRDN **rdn, const char **n, unsigned flags ); + + * and make ldap_str2dn call it repeatedly + */ int ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) { @@ -881,6 +996,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 */ @@ -1190,7 +1313,7 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags ) if ( LDAP_DN_DCE( flags ) ) { /* add in reversed order */ dn = ldapava_insert_into_dn( newDN, - newRDN ); + newRDN, 0 ); } else { dn = ldapava_append_to_dn( newDN, newRDN ); @@ -1290,54 +1413,16 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl if ( LDAP_DN_HEXPAIR( p ) ) { char c; -#ifdef PARSE_UTF8 - int cl; - - /* - * here I guess I need to decode - * the byte; let's also check - * the resulting encoding is a legal - * UTF-8 char - */ -#endif /* PARSE_UTF8 */ hexstr2bin( p, &c ); -#ifdef PARSE_UTF8 - cl = ldap_utf8_charlen( &c ); - - for ( escapes += 2; --cl; escapes += 2 ) { - p += 2; - - /* - * there must be another escaped - * hexpair ... - */ - if ( !LDAP_DN_ESCAPE( p[ 0 ] ) ) { - return( 1 ); - } - p++; - if ( !LDAP_DN_HEXPAIR( p ) ) { - return( 1 ); - } - - /* - * that must be 10xxxxxx - */ - hexstr2bin( p, &c ); - if ( ( c & 0xc0 ) != 0x80 ) { - return( 1 ); - } - } -#else /* !PARSE_UTF8 */ escapes += 2; -#endif /* !PARSE_UTF8 */ if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) { /* * we assume the string is UTF-8 */ - *retFlags = LDAP_AVA_PRINTABLE; + *retFlags = LDAP_AVA_NONPRINTABLE; } p++; @@ -1398,6 +1483,7 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl if ( escapes == 0 && unescapes == 0 ) { ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; @@ -1410,12 +1496,14 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl || LDAP_DN_NEEDESCAPE( startPos[ s ] ) ) { ( *val )->bv_val[ d++ ] = startPos[ s++ ]; + } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) { char c; hexstr2bin( &startPos[ s ], &c ); ( *val )->bv_val[ d++ ] = c; s += 2; + } else { /* * we allow escaping of chars @@ -1458,9 +1546,11 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl p++; if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { escapes++; + } else { return( 1 ); } + } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { break; } @@ -1497,6 +1587,7 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl ( *val )->bv_len = len; if ( escapes == 0 ){ ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; @@ -1576,6 +1667,7 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl ( *val )->bv_len = len; if ( escapes == 0 ) { ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; @@ -1663,6 +1755,7 @@ quotedIA52strval( const char *str, struct berval **val, const char **next, unsig ( *val )->bv_len = len; if ( escapes == 0 ) { ( *val )->bv_val = LDAP_STRNDUP( startPos, len ); + } else { ber_len_t s, d; @@ -1697,6 +1790,7 @@ hexstr2bin( const char *str, char *c ) if ( LDAP_DN_ASCII_DIGIT( c1 ) ) { *c = c1 - '0'; + } else { c1 = tolower( c1 ); @@ -1709,6 +1803,7 @@ hexstr2bin( const char *str, char *c ) if ( LDAP_DN_ASCII_DIGIT( c2 ) ) { *c += c2 - '0'; + } else { c2 = tolower( c2 ); @@ -1865,39 +1960,50 @@ 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 = 1; char *p; assert( val ); + assert( len ); + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) { -#ifdef PARSE_UTF8 cl = ldap_utf8_charlen( p ); - if ( cl > 1 ) { + 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 ] & 0x80 ) == 0x00 ) { + return( -1 ); + } + } /* need to escape it */ l += 3 * cl; -#else /* !PARSE_UTF8 */ - if ( ( p[ 0 ] & 0x80 ) != 0x00 ) { - /* need to escape it */ - l += 3; -#endif /* !PARSE_UTF8 */ + } 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; + } else { l++; } } - return( l ); + *len = l; + + return( 0 ); } /* @@ -1923,21 +2029,16 @@ 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; ) { -#ifdef PARSE_UTF8 ber_len_t cl = ldap_utf8_charlen( &val->bv_val[ s ] ); if ( cl > 1 ) { for ( ; cl--; ) { -#else /* !PARSE_UTF8 */ - if ( ( val->bv_val[ s ] & 0x80 ) != 0x00 ) { -#endif /* !PARSE_UTF8 */ str[ d++ ] = '\\'; byte2hexpair( &val->bv_val[ s ], &str[ d ] ); s++; d += 2; -#ifdef PARSE_UTF8 } -#endif /* !PARSE_UTF8 */ + } else { if ( ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) @@ -1956,23 +2057,25 @@ 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 ); + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * Turn value into a binary encoded BER */ - return( 0 ); + return( -1 ); } else { for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { @@ -1980,13 +2083,16 @@ strval2IA5strlen( struct berval *val, unsigned flags ) || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { l += 2; + } else { l++; } } } - return( l ); + *len = l; + + return( 0 ); } /* @@ -2007,7 +2113,7 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * Turn value into a binary encoded BER */ @@ -2039,35 +2145,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 ); + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * 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 ); } /* @@ -2089,7 +2200,7 @@ strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * FIXME: Turn the value into a binary encoded BER? */ @@ -2120,35 +2231,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 ); + *len = 0; if ( val->bv_len == 0 ) { return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * 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 ); } /* @@ -2170,7 +2286,7 @@ strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) return( 0 ); } - if ( flags & LDAP_AVA_PRINTABLE ) { + if ( flags & LDAP_AVA_NONPRINTABLE ) { /* * FIXME: Turn the value into a binary encoded BER? */ @@ -2204,7 +2320,7 @@ strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) * by Luke Howard, http://www.padl.com/~lukeh) */ static int -dn2domain( LDAPDN *dn, char **str, int *iRDN ) +dn2domain( LDAPDN *dn, char *str, int *iRDN ) { int i; int domain = 0, first = 1; @@ -2215,7 +2331,6 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN ) /* sanity */ assert( dn ); assert( str ); - assert( *str ); assert( iRDN ); assert( *iRDN > 0 ); @@ -2229,10 +2344,7 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN ) assert( rdn[ 0 ][ 0 ] ); ava = rdn[ 0 ][ 0 ]; - /* FIXME: no composite rdn or non-"dc" types, right? */ - /* FIXME: we do not allow binary values in domain, right? */ - if ( rdn[ 1 ] || strcasecmp( ava->la_attr, LDAP_DC_ATTR ) - || ava->la_flags != LDAP_AVA_STRING ) { + if ( !LDAP_DN_IS_RDN_DC( rdn ) ) { break; } @@ -2240,14 +2352,15 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN ) if ( first ) { first = 0; - AC_MEMCPY( *str, ava->la_value->bv_val, + 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, + 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 ] = '.'; + str[ ava->la_value->bv_len ] = '.'; l += ava->la_value->bv_len + 1; } } @@ -2257,31 +2370,43 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN ) return( domain ); } -static ber_len_t -rdn2strlen( LDAPRDN *rdn ) +static int +rdn2strlen( LDAPRDN *rdn, ber_len_t *len, + int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) ) { int iAVA; - ber_len_t len = 0; + ber_len_t l = 0; + + *len = 0; for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { LDAPAVA *ava = rdn[ iAVA ][ 0 ]; /* len(type) + '=' + '+' | ',' */ - len += strlen( ava->la_attr ) + 2; + l += strlen( ava->la_attr ) + 2; if ( ava->la_flags & LDAP_AVA_BINARY ) { /* octothorpe + twice the length */ - len += 1 + 2 * ava->la_value->bv_len; + l += 1 + 2 * ava->la_value->bv_len; + } else { - len += strval2strlen( ava->la_value, ava->la_flags ); + ber_len_t vl; + + if ( ( *s2l )( ava->la_value, ava->la_flags, &vl ) ) { + return( -1 ); + } + l += vl; } } - return( len ); + *len = l; + + return( 0 ); } static int -rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len ) +rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len, + int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) ) { int iAVA; ber_len_t l = 0; @@ -2297,12 +2422,18 @@ rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len ) if ( ava->la_flags & LDAP_AVA_BINARY ) { str[ l++ ] = '#'; - binval2hexstr( ava->la_value, &str[ l ] ); + if ( binval2hexstr( ava->la_value, &str[ l ] ) ) { + return( -1 ); + } l += 2 * ava->la_value->bv_len; + } else { ber_len_t vl; - strval2str( ava->la_value, &str[ l ], - ava->la_flags, &vl ); + + if ( ( *s2s )( ava->la_value, &str[ l ], + ava->la_flags, &vl ) ) { + return( -1 ); + } l += vl; } str[ l++ ] = ( rdn[ iAVA + 1 ] ? '+' : ',' ); @@ -2313,28 +2444,42 @@ rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len ) return( 0 ); } -static ber_len_t -rdn2UFNstrlen( LDAPRDN *rdn ) +static int +rdn2UFNstrlen( LDAPRDN *rdn, ber_len_t *len ) { int iAVA; - ber_len_t len = 0; + ber_len_t l = 0; + + assert( rdn ); + assert( len ); + + *len = 0; for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { LDAPAVA *ava = rdn[ iAVA ][ 0 ]; /* ' + ' | ', ' */ - len += ( rdn[ iAVA + 1 ] ? 3 : 2 ); + l += ( rdn[ iAVA + 1 ] ? 3 : 2 ); /* 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; + l += 1 + 2 * ava->la_value->bv_len; + } else { - len += strval2strlen( ava->la_value, ava->la_flags ); + ber_len_t vl; + + if ( strval2strlen( ava->la_value, ava->la_flags, + &vl ) ) { + return( -1 ); + } + l += vl; } } - return( len ); + *len = l; + + return( 0 ); } static int @@ -2348,18 +2493,25 @@ rdn2UFNstr( LDAPRDN *rdn, char *str, ber_len_t *len ) if ( ava->la_flags & LDAP_AVA_BINARY ) { str[ l++ ] = '#'; - binval2hexstr( ava->la_value, &str[ l ] ); + if ( binval2hexstr( ava->la_value, &str[ l ] ) ) { + return( -1 ); + } l += 2 * ava->la_value->bv_len; + } else { ber_len_t vl; - strval2str( ava->la_value, &str[ l ], - ava->la_flags, &vl ); + + if ( strval2str( ava->la_value, &str[ l ], + ava->la_flags, &vl ) ) { + return( -1 ); + } l += vl; } if ( rdn[ iAVA + 1 ]) { AC_MEMCPY( &str[ l ], " + ", 3 ); l += 3; + } else { AC_MEMCPY( &str[ l ], ", ", 2 ); l += 2; @@ -2384,13 +2536,24 @@ ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags ) return( LDAP_SUCCESS ); } + *str = NULL; switch ( LDAP_DN_FORMAT( flags ) ) { case LDAP_DN_FORMAT_LDAPV3: - l = rdn2strlen( rdn ); + if ( rdn2strlen( rdn, &l, strval2strlen ) ) { + return( LDAP_OTHER ); + } + break; + + case LDAP_DN_FORMAT_LDAPV2: + if ( rdn2strlen( rdn, &l, strval2IA5strlen ) ) { + return( LDAP_OTHER ); + } break; case LDAP_DN_FORMAT_UFN: - l = rdn2UFNstrlen( rdn ); + if ( rdn2UFNstrlen( rdn, &l ) ) { + return( LDAP_OTHER ); + } break; default: @@ -2401,7 +2564,12 @@ ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags ) switch ( LDAP_DN_FORMAT( flags ) ) { case LDAP_DN_FORMAT_LDAPV3: - rc = rdn2str( rdn, *str, &l ); + rc = rdn2str( rdn, *str, &l, strval2str ); + back = 1; + break; + + case LDAP_DN_FORMAT_LDAPV2: + rc = rdn2str( rdn, *str, &l, strval2IA5str ); back = 1; break; @@ -2439,15 +2607,20 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) 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 * ); - + /* 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( str ); Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2str(%u)\n%s%s", flags, "", "" ); *str = NULL; + /* + * a null dn means an empty dn string + * FIXME: better raise an error? + */ if ( dn == NULL ) { *str = LDAP_STRDUP( "" ); return( LDAP_SUCCESS ); @@ -2455,62 +2628,24 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) switch ( LDAP_DN_FORMAT( flags ) ) { case LDAP_DN_FORMAT_LDAPV3: - s2l = strval2strlen; - s2s = strval2str; - - /* - * LDAPv3 uses helpers - */ - for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { - len += rdn2strlen( dn[ iRDN ][ 0 ] ); - } - - if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { - rc = LDAP_NO_MEMORY; - break; - } - - for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { - ber_len_t vl; - rdn2str( dn[ iRDN ][ 0 ], &( *str )[ l ], &vl ); - l += vl; - } - - assert( l == len ); - - /* - * trim the last ',' (the allocated memory - * is one byte longer than required) - */ - ( *str )[ len - 1 ] = '\0'; - - rc = LDAP_SUCCESS; - break; + sv2l = strval2strlen; + sv2s = strval2str; + goto got_funcs; case LDAP_DN_FORMAT_LDAPV2: - s2l = strval2IA5strlen; - s2s = strval2IA5str; - - /* - * LDAPv2 follows the regular path - */ + sv2l = strval2IA5strlen; + sv2s = strval2IA5str; +got_funcs: + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { - LDAPRDN *rdn = dn[ iRDN ][ 0 ]; - - for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { - LDAPAVA *ava = rdn[ iAVA ][ 0 ]; - - /* len(type) + '=' + '+' | ',' */ - len += strlen( ava->la_attr ) + 2; - - 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 ); - } + ber_len_t rdnl; + LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + + if ( rdn2strlen( rdn, &rdnl, sv2l ) ) { + goto return_results; } + + len += rdnl; } if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) { @@ -2519,34 +2654,19 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) } 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 ); - - 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; - ( *s2s )( ava->la_value, - &( *str )[ l ], - ava->la_flags, &vl ); - l += vl; - } - ( *str )[ l++ ] = - ( rdn[ iAVA + 1 ] ? '+' : ',' ); + ber_len_t rdnl; + LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + + if ( rdn2str( rdn, &( *str )[ l ], &rdnl, sv2s ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; } + l += rdnl; } + assert( l == len ); + /* * trim the last ',' (the allocated memory * is one byte longer than required) @@ -2556,7 +2676,7 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) rc = LDAP_SUCCESS; break; - case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_UFN: { /* * FIXME: quoting from RFC 1781: @@ -2578,15 +2698,43 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) 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[ iRDN ]; iRDN++ ) { - len += rdn2UFNstrlen( dn[ iRDN ][ 0 ] ); + ber_len_t rdnl; + LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + + if ( rdn2UFNstrlen( rdn, &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 ) { @@ -2594,20 +2742,55 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) break; } - for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { - ber_len_t vl; - rdn2UFNstr( dn[ iRDN ][ 0 ], &( *str )[ l ], &vl ); - l += vl; - } +#ifdef DC_IN_UFN + if ( leftmost_dc == -1 ) { +#endif /* DC_IN_UFN */ + for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t vl; + LDAPRDN *rdn = dn[ iRDN ][ 0 ]; + + if ( rdn2UFNstr( rdn, &( *str )[ l ], &vl ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } + l += vl; + } - /* - * trim the last ', ' (the allocated memory - * is two bytes longer than required) - */ - ( *str )[ len - 2 ] = '\0'; + /* + * trim the last ', ' (the allocated memory + * is two bytes longer than required) + */ + ( *str )[ len - 2 ] = '\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[ iRDN ][ 0 ]; + + if ( rdn2UFNstr( rdn, &( *str )[ l ], &vl ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } + l += vl; + } + + if ( !dn2domain( dn, &( *str )[ l ], &last_iRDN ) ) { + LDAP_FREE( *str ); + *str = 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[ iRDN ]; iRDN++ ) { @@ -2623,9 +2806,15 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) 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 ); + ber_len_t vl; + + if ( strval2DCEstrlen( ava->la_value, + ava->la_flags, &vl ) ) { + goto return_results; + } + len += vl; } } } @@ -2651,14 +2840,24 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) ( *str )[ l++ ] = '='; if ( ava->la_flags & LDAP_AVA_BINARY ) { ( *str )[ l++ ]= '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); + if ( binval2hexstr( ava->la_value, + &( *str )[ l ] ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += 2 * ava->la_value->bv_len; + } else { ber_len_t vl; - strval2DCEstr( ava->la_value, + + if ( strval2DCEstr( ava->la_value, &( *str )[ l ], - ava->la_flags, &vl ); + ava->la_flags, &vl ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += vl; } } @@ -2670,6 +2869,21 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) break; case LDAP_DN_FORMAT_AD_CANONICAL: { + + /* + * 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: + * + * "cn=Bill+sn=Gates,ou=People,dc=microsoft,dc=com" + * + * will read + * + * "microsoft.com/People/Bill,Gates" + */ for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) { LDAPRDN *rdn = dn[ iRDN ][ 0 ]; @@ -2683,9 +2897,15 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) 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 ); + ber_len_t vl; + + if ( strval2ADstrlen( ava->la_value, + ava->la_flags, &vl ) ) { + goto return_results; + } + len += vl; } } } @@ -2696,7 +2916,7 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) } iRDN--; - if ( iRDN && dn2domain( dn, str, &iRDN ) ) { + if ( iRDN && dn2domain( dn, *str, &iRDN ) ) { for ( l = strlen( *str ); iRDN >= 0 ; iRDN-- ) { LDAPRDN *rdn = dn[ iRDN ][ 0 ]; @@ -2706,19 +2926,30 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) ( *str)[ l++ ] = ( iAVA ? ',' : '/' ); if ( ava->la_flags & LDAP_AVA_BINARY ) { ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); + if ( binval2hexstr( ava->la_value, + &( *str )[ l ] ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += 2 * ava->la_value->bv_len; + } else { ber_len_t vl; - strval2ADstr( ava->la_value, + + if ( strval2ADstr( ava->la_value, &( *str )[ l ], ava->la_flags, - &vl ); + &vl ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += vl; } } } + } else { int first = 1; @@ -2742,21 +2973,32 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) if ( first ) { first = 0; + } else { ( *str )[ l++ ] = ( iAVA ? ',' : '/' ); } if ( ava->la_flags & LDAP_AVA_BINARY ) { ( *str )[ l++ ] = '#'; - binval2hexstr( ava->la_value, - &( *str )[ l ] ); + if ( binval2hexstr( ava->la_value, + &( *str )[ l ] ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += 2 * ava->la_value->bv_len; + } else { ber_len_t vl; - strval2ADstr( ava->la_value, + + if ( strval2ADstr( ava->la_value, &( *str )[ l ], ava->la_flags, - &vl ); + &vl ) ) { + LDAP_FREE( *str ); + *str = NULL; + goto return_results; + } l += vl; } } @@ -2775,7 +3017,7 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags ) } Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2str(%s,%u)=%d\n", *str, flags, rc ); - +return_results:; return( rc ); } -- 2.39.5