From bb75d74d5ea1695e64cc63b1e328cc5ebeb5cdd5 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/plugins/acl/acl.c | 4 +- ldap/servers/plugins/acl/aclutil.c | 4 +- ldap/servers/plugins/referint/referint.c | 4 +- ldap/servers/plugins/replication/winsync-plugin.h | 2 +- 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 | 228 ++++++++++++++++++++++ ldap/servers/slapd/modrdn.c | 2 +- ldap/servers/slapd/rdn.c | 14 +- ldap/servers/slapd/slapi-plugin.h | 34 ++++ lib/ldaputil/cert.c | 4 +- 16 files changed, 304 insertions(+), 42 deletions(-) diff --git a/ldap/servers/plugins/acl/acl.c b/ldap/servers/plugins/acl/acl.c index cb20dbeb..4fcdf744 100644 --- a/ldap/servers/plugins/acl/acl.c +++ b/ldap/servers/plugins/acl/acl.c @@ -172,9 +172,9 @@ static int check_rdn_access( Slapi_PBlock *pb, Slapi_Entry *e, char *dn, int retCode = LDAP_INSUFFICIENT_ACCESS; int i; - if ( (dns = ldap_explode_dn( dn, 0 )) != NULL ) { + if ( (dns = slapi_ldap_explode_dn( dn, 0 )) != NULL ) { - if ( (rdns = ldap_explode_rdn( dns[0], 0 )) != NULL ) { + if ( (rdns = slapi_ldap_explode_rdn( dns[0], 0 )) != NULL ) { for ( i = 0; rdns[i] != NULL; i++ ) { char *type; diff --git a/ldap/servers/plugins/acl/aclutil.c b/ldap/servers/plugins/acl/aclutil.c index e1eb0ab1..d5729110 100644 --- a/ldap/servers/plugins/acl/aclutil.c +++ b/ldap/servers/plugins/acl/aclutil.c @@ -556,8 +556,8 @@ aclutil_expand_paramString ( char *str, Slapi_Entry *e ) char *buf = NULL; - e_dns = ldap_explode_dn ( slapi_entry_get_ndn ( e ), 0 ); - a_dns = ldap_explode_dn ( str, 0 ); + e_dns = slapi_ldap_explode_dn ( slapi_entry_get_ndn ( e ), 0 ); + a_dns = slapi_ldap_explode_dn ( str, 0 ); i = 0; ncomponents = 0; diff --git a/ldap/servers/plugins/referint/referint.c b/ldap/servers/plugins/referint/referint.c index 454c5162..3207e5b3 100644 --- a/ldap/servers/plugins/referint/referint.c +++ b/ldap/servers/plugins/referint/referint.c @@ -375,7 +375,7 @@ _update_one_per_mod(const char *entryDN, /* DN of the searched entry */ Slapi_Value *v = NULL; /* need to put together rdn into a dn */ - dnParts = ldap_explode_dn( origDN, 0 ); + dnParts = slapi_ldap_explode_dn( origDN, 0 ); if (NULL == newRDN) { newRDN = dnParts[0]; } @@ -553,7 +553,7 @@ _update_all_per_mod(const char *entryDN, /* DN of the searched entry */ Slapi_Value *v = NULL; /* need to put together rdn into a dn */ - dnParts = ldap_explode_dn( origDN, 0 ); + dnParts = slapi_ldap_explode_dn( origDN, 0 ); if (NULL == newRDN) { newRDN = dnParts[0]; } diff --git a/ldap/servers/plugins/replication/winsync-plugin.h b/ldap/servers/plugins/replication/winsync-plugin.h index 5a0f65d5..e70c4a94 100644 --- a/ldap/servers/plugins/replication/winsync-plugin.h +++ b/ldap/servers/plugins/replication/winsync-plugin.h @@ -371,7 +371,7 @@ test_winsync_get_new_ds_user_dn_cb(void *cbdata, const Slapi_Entry *rawentry, "--> test_winsync_get_new_ds_user_dn_cb -- old dn [%s] -- begin\n", *new_dn_string); - rdns = ldap_explode_dn(*new_dn_string, 0); + rdns = slapi_ldap_explode_dn(*new_dn_string, 0); if (!rdns || !rdns[0]) { ldap_value_free(rdns); return; 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 dcb442ec..425e8546 100644 --- a/ldap/servers/slapd/back-ldbm/import-threads.c +++ b/ldap/servers/slapd/back-ldbm/import-threads.c @@ -1478,7 +1478,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 dbe53e5b..645fea92 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++ ) { @@ -1199,10 +1199,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++ ) @@ -1251,7 +1251,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++ ) @@ -1431,7 +1431,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. * @@ -1446,7 +1446,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) { @@ -1034,6 +1084,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(dn, 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 +1941,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 766136db..5bb9859d 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 */ diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c index 30fd0f4b..c26ff41f 100644 --- a/lib/ldaputil/cert.c +++ b/lib/ldaputil/cert.c @@ -221,12 +221,12 @@ _explode_dn (const char* dn) { auto char*** exp = NULL; if (dn && *dn) { - auto char** rdns = ldap_explode_dn (dn, 0); + auto char** rdns = slapi_ldap_explode_dn (dn, 0); if (rdns) { auto size_t expLen = 0; auto char** rdn; for (rdn = rdns; *rdn; ++rdn) { - auto char** avas = ldap_explode_rdn (*rdn, 0); + auto char** avas = slapi_ldap_explode_rdn (*rdn, 0); if (avas && *avas) { exp = (char***) ldapu_realloc (exp, sizeof(char**) * (expLen + 2)); if (exp) { -- cgit