From 8a943175138bbfec0b398ea4ffd24f3047df3951 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Fri, 27 Aug 2010 11:55:29 -0600 Subject: implement slapi_ldap_explode_dn and slapi_ldap_explode_rdn The DS has some problems with the openldap versions of these functions: 1) They are deprecated - should use the str2[r]dn and [r]dn2str and the bv versions of those functions instead 2) They escape utf-8 and other values in the strings - the mozldap functions do not do this 3) They handle double quoted strings, but they remove the quotes - our code expects the quotes to be left in place Until we fix our DN handling, and get rid of the double quoted DNs, we just use the mozldap versions of these functions. --- ldap/servers/slapd/back-ldbm/ancestorid.c | 4 +- ldap/servers/slapd/back-ldbm/import-threads.c | 2 +- ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 16 +- ldap/servers/slapd/back-ldif/modrdn.c | 12 +- ldap/servers/slapd/dse.c | 4 +- ldap/servers/slapd/entry.c | 8 +- ldap/servers/slapd/filterentry.c | 4 +- ldap/servers/slapd/ldaputil.c | 230 ++++++++++++++++++++++++++ ldap/servers/slapd/modrdn.c | 2 +- ldap/servers/slapd/rdn.c | 14 +- ldap/servers/slapd/slapi-plugin.h | 34 ++++ 11 files changed, 297 insertions(+), 33 deletions(-) (limited to 'ldap/servers/slapd') diff --git a/ldap/servers/slapd/back-ldbm/ancestorid.c b/ldap/servers/slapd/back-ldbm/ancestorid.c index 1337e037..43a30b8e 100644 --- a/ldap/servers/slapd/back-ldbm/ancestorid.c +++ b/ldap/servers/slapd/back-ldbm/ancestorid.c @@ -867,8 +867,8 @@ int slapi_sdn_suffix_cmp( size_t len = 0; char *p, *ndnstr; - rdns1 = ldap_explode_dn(slapi_sdn_get_ndn(left), 0); - rdns2 = ldap_explode_dn(slapi_sdn_get_ndn(right), 0); + rdns1 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(left), 0); + rdns2 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(right), 0); for(count1 = 0; rdns1[count1]!=NULL; count1++){ } diff --git a/ldap/servers/slapd/back-ldbm/import-threads.c b/ldap/servers/slapd/back-ldbm/import-threads.c index 73b40ec0..60a92b38 100644 --- a/ldap/servers/slapd/back-ldbm/import-threads.c +++ b/ldap/servers/slapd/back-ldbm/import-threads.c @@ -1497,7 +1497,7 @@ upgradedn_producer(void *param) workdn = slapi_ch_strdup(slapi_sdn_get_dn(sdn)); isentrydn = 1; } - rdns = ldap_explode_dn(workdn, 0); + rdns = slapi_ldap_explode_dn(workdn, 0); skipit = 0; for (rdnsp = rdns; rdnsp && *rdnsp; rdnsp++) { valueptr = PL_strchr(*rdnsp, '='); diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c index 4848c83e..70fdecab 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c @@ -701,7 +701,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb ) { char **rdns; int i; - if ( (rdns = ldap_explode_rdn( slapi_sdn_get_dn(&dn_newrdn), 0 )) != NULL ) + if ( (rdns = slapi_ldap_explode_rdn( slapi_sdn_get_dn(&dn_newrdn), 0 )) != NULL ) { for ( i = 0; rdns[i] != NULL; i++ ) { @@ -1202,10 +1202,10 @@ moddn_newrdn_mods(Slapi_PBlock *pb, const char *olddn, struct backentry *ec, Sla int baddn = 0; /* set to true if could not parse dn */ int badrdn = 0; /* set to true if could not parse rdn */ dn = slapi_ch_strdup(olddn); - dns = ldap_explode_dn( dn, 0 ); + dns = slapi_ldap_explode_dn( dn, 0 ); if ( dns != NULL ) { - rdns = ldap_explode_rdn( dns[0], 0 ); + rdns = slapi_ldap_explode_rdn( dns[0], 0 ); if ( rdns != NULL ) { for ( i = 0; rdns[i] != NULL; i++ ) @@ -1254,7 +1254,7 @@ moddn_newrdn_mods(Slapi_PBlock *pb, const char *olddn, struct backentry *ec, Sla /* * add new RDN values to the entry (non-normalized) */ - rdns = ldap_explode_rdn( newrdn, 0 ); + rdns = slapi_ldap_explode_rdn( newrdn, 0 ); if ( rdns != NULL ) { for ( i = 0; rdns[i] != NULL; i++ ) @@ -1434,7 +1434,7 @@ moddn_rename_child_entry( * excluding the old parent entry DN, and adding the new * superior entry DN. * - * ldap_explode_dn is probably a bit slow, but it knows about + * slapi_ldap_explode_dn is probably a bit slow, but it knows about * DN escaping which is pretty complicated, and we wouldn't * want to reimplement that here. * @@ -1449,7 +1449,7 @@ moddn_rename_child_entry( int i; olddn = slapi_entry_get_dn(ec->ep_entry); - olddns = ldap_explode_dn( olddn, 0 ); + olddns = slapi_ldap_explode_dn( olddn, 0 ); for(;olddns[olddncomps]!=NULL;olddncomps++); for(i=0;i #endif +#if defined(USE_OPENLDAP) +/* the server depends on the old, deprecated ldap_explode behavior which openldap + does not support - the use of the mozldap code should be seen as a temporary + measure which we should remove as we improve our internal DN handling */ +static char **mozldap_ldap_explode( const char *dn, const int notypes, const int nametype ); +static char **mozldap_ldap_explode_dn( const char *dn, const int notypes ); +static char **mozldap_ldap_explode_rdn( const char *rdn, const int notypes ); +#endif + #ifdef MEMPOOL_EXPERIMENTAL void _free_wrapper(void *ptr) { @@ -79,6 +129,7 @@ slapi_ldap_unbind( LDAP *ld ) } } +#if defined(USE_OPENLDAP) /* mozldap ldap_init and ldap_url_parse accept a hostname in the form host1[:port1]SPACEhost2[:port2]SPACEhostN[:portN] where SPACE is a single space (0x20) character @@ -151,6 +202,7 @@ end: slapi_ch_free_string(&my_copy); return retstr; } +#endif /* USE_OPENLDAP */ const char * slapi_urlparse_err2string( int err ) @@ -1034,6 +1086,26 @@ done: return rc; } +char ** +slapi_ldap_explode_rdn(const char *rdn, int notypes) +{ +#if defined(USE_OPENLDAP) + return mozldap_ldap_explode_rdn(rdn, notypes); +#else + return ldap_explode_dn(rdn, notypes); +#endif +} + +char ** +slapi_ldap_explode_dn(const char *dn, int notypes) +{ +#if defined(USE_OPENLDAP) + return mozldap_ldap_explode_dn(dn, notypes); +#else + return ldap_explode_dn(dn, notypes); +#endif +} + void slapi_add_auth_response_control( Slapi_PBlock *pb, const char *binddn ) { @@ -1871,3 +1943,161 @@ cleanup: } #endif /* HAVE_KRB5 */ + +#if defined(USE_OPENLDAP) + +#define LDAP_DN 1 +#define LDAP_RDN 2 + +#define INQUOTE 1 +#define OUTQUOTE 2 + +static char ** +mozldap_ldap_explode( const char *dn, const int notypes, const int nametype ) +{ + char *p, *q, *rdnstart, **rdns = NULL; + size_t plen = 0; + int state = 0; + int count = 0; + int startquote = 0; + int endquote = 0; + int len = 0; + int goteq = 0; + + if ( dn == NULL ) { + dn = ""; + } + + while ( ldap_utf8isspace( (char *)dn )) { /* ignore leading spaces */ + ++dn; + } + + p = rdnstart = (char *) dn; + state = OUTQUOTE; + + do { + p += plen; + plen = 1; + switch ( *p ) { + case '\\': + if ( *++p == '\0' ) + p--; + else + plen = LDAP_UTF8LEN(p); + break; + case '"': + if ( state == INQUOTE ) + state = OUTQUOTE; + else + state = INQUOTE; + break; + case '+': if ( nametype != LDAP_RDN ) break; + case ';': + case ',': + case '\0': + if ( state == OUTQUOTE ) { + /* + * semicolon and comma are not valid RDN + * separators. + */ + if ( nametype == LDAP_RDN && + ( *p == ';' || *p == ',' || !goteq)) { + charray_free( rdns ); + return NULL; + } + if ( (*p == ',' || *p == ';') && !goteq ) { + /* If we get here, we have a case similar + * to =,,= + * This is not a valid dn */ + charray_free( rdns ); + return NULL; + } + goteq = 0; + ++count; + if ( rdns == NULL ) { + if (( rdns = (char **)slapi_ch_malloc( 8 + * sizeof( char *))) == NULL ) + return( NULL ); + } else if ( count >= 8 ) { + if (( rdns = (char **)slapi_ch_realloc( + rdns, (count+1) * + sizeof( char *))) == NULL ) + return( NULL ); + } + rdns[ count ] = NULL; + endquote = 0; + if ( notypes ) { + for ( q = rdnstart; + q < p && *q != '='; ++q ) { + ; + } + if ( q < p ) { /* *q == '=' */ + rdnstart = ++q; + } + if ( *rdnstart == '"' ) { + startquote = 1; + ++rdnstart; + } + + if ( (*(p-1) == '"') && startquote ) { + endquote = 1; + --p; + } + } + + len = p - rdnstart; + if (( rdns[ count-1 ] = (char *)slapi_ch_calloc( + 1, len + 1 )) != NULL ) { + memcpy( rdns[ count-1 ], rdnstart, + len ); + if ( !endquote ) { + /* trim trailing spaces */ + while ( len > 0 && + ldap_utf8isspace( + &rdns[count-1][len-1] )) { + --len; + } + } + rdns[ count-1 ][ len ] = '\0'; + } + + /* + * 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++; + + rdnstart = *p ? p + 1 : p; + while ( ldap_utf8isspace( rdnstart )) + ++rdnstart; + } + break; + case '=': + if ( state == OUTQUOTE ) { + goteq = 1; + } + /* FALL */ + default: + plen = LDAP_UTF8LEN(p); + break; + } + } while ( *p ); + + return( rdns ); +} + +static char ** +mozldap_ldap_explode_dn( const char *dn, const int notypes ) +{ + return( mozldap_ldap_explode( dn, notypes, LDAP_DN ) ); +} + +static char ** +mozldap_ldap_explode_rdn( const char *rdn, const int notypes ) +{ + return( mozldap_ldap_explode( rdn, notypes, LDAP_RDN ) ); +} + +#endif /* USE_OPENLDAP */ diff --git a/ldap/servers/slapd/modrdn.c b/ldap/servers/slapd/modrdn.c index b721fc79..4cca3c97 100644 --- a/ldap/servers/slapd/modrdn.c +++ b/ldap/servers/slapd/modrdn.c @@ -465,7 +465,7 @@ op_shared_rename(Slapi_PBlock *pb, int passin_args) } /* check that the rdn is formatted correctly */ - if ((rdns = ldap_explode_rdn(newrdn, 0)) == NULL) + if ((rdns = slapi_ldap_explode_rdn(newrdn, 0)) == NULL) { if ( !internal_op ) { slapi_log_error(SLAPI_LOG_ARGS, NULL, diff --git a/ldap/servers/slapd/rdn.c b/ldap/servers/slapd/rdn.c index 1ef2b836..39b40bd2 100644 --- a/ldap/servers/slapd/rdn.c +++ b/ldap/servers/slapd/rdn.c @@ -101,7 +101,7 @@ slapi_rdn_init_dn(Slapi_RDN *rdn,const char *dn) slapi_rdn_init(rdn); if(dn!=NULL) { - char **dns= ldap_explode_dn(dn, 0); + char **dns= slapi_ldap_explode_dn(dn, 0); if(dns!=NULL) { rdn->rdn= slapi_ch_strdup(dns[0]); @@ -174,9 +174,9 @@ _slapi_rdn_init_all_dn_ext(Slapi_RDN *rdn, const Slapi_DN *sdn) if (q) { char bakup = *q; *q = '\0'; - dns = ldap_explode_dn(dn, 0); + dns = slapi_ldap_explode_dn(dn, 0); if (NULL == dns) { /* if dn contains NULL RDN (e.g., ",,"), - ldap_explode_dn returns NULL */ + slapi_ldap_explode_dn returns NULL */ *q = bakup; return -1; } @@ -187,16 +187,16 @@ _slapi_rdn_init_all_dn_ext(Slapi_RDN *rdn, const Slapi_DN *sdn) rc = 0; /* success */ } else { /* Given dn does not belong to this server. Just set it. */ - dns = ldap_explode_dn(dn, 0); + dns = slapi_ldap_explode_dn(dn, 0); } } } else { /* Given dn does not belong to this server. Just set it. */ - dns = ldap_explode_dn(dn, 0); + dns = slapi_ldap_explode_dn(dn, 0); } } else { /* Given dn does not belong to this server. Just set it. */ - dns = ldap_explode_dn(dn, 0); + dns = slapi_ldap_explode_dn(dn, 0); } /* Get the last matched position */ @@ -329,7 +329,7 @@ slapi_rdn_get_rdns(Slapi_RDN *rdn) } if(rdn->rdn!=NULL) { - rdn->rdns = ldap_explode_rdn( rdn->rdn, 0 ); + rdn->rdns = slapi_ldap_explode_rdn( rdn->rdn, 0 ); rdns= rdn->rdns; } slapi_setbit_uchar(rdn->flag,FLAG_RDNS); diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 948fe1c1..0aab49ca 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -5170,6 +5170,40 @@ int slapi_ldif_parse_line( int *freeval /* values will usually be returned in place as pointers into line - if the value is a url, the value will be malloced and must be freed by the caller */ ); +/** + * Parse an LDAP DN string. Return an array of RDN strings, terminated by a NULL. This + * function differs from the standard openldap ldap_explode_dn, which will escape utf-8 + * characters. In the directory server, we do not want to escape them. The caller + * should use slapi_ldap_value_free to free the returned memory when finished. + * + * \param dn The LDAP DN + * \param notypes set to true (1) to return only the attribute values with no attribute types + * \return \c An array of RDN strings - use slapi_ch_array_free to free + * + * \see slapi_ldap_value_free() + */ +char **slapi_ldap_explode_dn( + const char *dn, /* dn to explode */ + int notypes /* set to true to return only the values with no types */ +); + +/** + * Parse an LDAP RDN string. Return an array of AVA strings, terminated by a NULL. This + * function differs from the standard openldap ldap_explode_rdn, which will escape utf-8 + * characters. In the directory server, we do not want to escape them. The caller + * should use slapi_ldap_value_free to free the returned memory when finished. + * + * \param dn The LDAP RDN + * \param notypes set to true (1) to return only the attribute values with no attribute types + * \return \c An array of AVA strings - use slapi_ch_array_free to free + * + * \see slapi_ldap_value_free() + */ +char **slapi_ldap_explode_rdn( + const char *rdn, /* rdn to explode */ + int notypes /* set to true to return only the values with no types */ +); + /* * computed attributes */ -- cgit