X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-ldbm%2Falias.c;h=9c4f1adeacb014b5ce9855cd6964f824ab9eaeb8;hb=573e279d8769b0b74cdf63c21653f261cb741f25;hp=e9ca5200194816e82a5fbc223b9dfa24c66f8445;hpb=61476e7a3c9a4ecef2bcf1cbe9fdca3575db7161;p=openldap diff --git a/servers/slapd/back-ldbm/alias.c b/servers/slapd/back-ldbm/alias.c index e9ca520019..9c4f1adeac 100644 --- a/servers/slapd/back-ldbm/alias.c +++ b/servers/slapd/back-ldbm/alias.c @@ -21,17 +21,20 @@ /* * given an alias object, dereference it to its end point. - * entry returned has reader lock + * Entry returned has reader lock or is NULL. Starting entry is not released. */ Entry *derefAlias_r ( Backend *be, Connection *conn, Operation *op, Entry *e) { + struct ldbminfo *li = (struct ldbminfo *) be->be_private; /* to free cache entries */ Attribute *a; int depth; - char **pastAliases; char *matched; + Entry *origDN = e; + + if (!e) return NULL; /* be sure we have a starting entry */ Debug( LDAP_DEBUG_TRACE, "<= checking for alias for dn %s\n", e->e_dn, 0, 0 ); @@ -55,7 +58,40 @@ Entry *derefAlias_r ( Backend *be, Debug( LDAP_DEBUG_TRACE, "<= %s is an alias for %s\n", e->e_dn, a->a_vals[0]->bv_val, 0 ); newDN = ch_strdup (a->a_vals[0]->bv_val); - oldDN = ch_strdup (e->e_dn); + oldDN = ch_strdup (e->e_ndn); + + /* + * release past lock if not original + */ + if ( (depth > 0) && e ) { + cache_return_entry_r(&li->li_cache, e); + } + + /* make sure new and old DN are not same to avoid loops */ + dn_normalize_case (newDN); + if ( strcmp (newDN, oldDN) == 0 ) { + + Debug( LDAP_DEBUG_TRACE, + "<= %s alias is same as current %s\n", + oldDN, newDN, 0 ); + send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "", + "Circular alias" ); + free (newDN); + free (oldDN); + break; + } + + /* make sure new and original are not same to avoid deadlocks */ + if ( strcmp (newDN, origDN->e_ndn) == 0 ) { + Debug( LDAP_DEBUG_TRACE, + "<= %s alias is same as original %s\n", + oldDN, origDN->e_ndn, 0 ); + send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "", + "Circular alias" ); + free (newDN); + free (oldDN); + break; + } /* * ok, so what happens if there is an alias in the DN of a dereferenced @@ -67,11 +103,15 @@ Entry *derefAlias_r ( Backend *be, Debug( LDAP_DEBUG_TRACE, "<= %s is a dangling alias to %s\n", oldDN, newDN, 0 ); - send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "", + send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "", "Dangling Alias" ); - if(matched != NULL) free(matched); + if (matched != NULL) free(matched); + free (newDN); + free (oldDN); + break; } + free (newDN); free (oldDN); } @@ -82,9 +122,10 @@ Entry *derefAlias_r ( Backend *be, */ Debug( LDAP_DEBUG_TRACE, "<= %s has no data in aliasedobjectname attribute\n", - e->e_dn, 0, 0 ); + (e && e->e_dn) ? e->e_dn : "(null)", 0, 0 ); send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "", "Alias missing aliasedobjectname" ); + break; } } @@ -93,9 +134,11 @@ Entry *derefAlias_r ( Backend *be, */ if ( depth >= be->be_maxDerefDepth ) { Debug( LDAP_DEBUG_TRACE, - "<= %s exceeded maximum deref depth %d\n", - e->e_dn, be->be_maxDerefDepth, 0 ); - send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "", + "<= deref(\"%s\") exceeded maximum deref depth (%d) at \"%s\"\n", + origDN->e_dn ? origDN->e_dn : "(null)", + be->be_maxDerefDepth, + (e && e->e_ndn) ? e->e_ndn : "(null)"); + send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "", "Maximum alias dereference depth exceeded" ); } @@ -104,6 +147,17 @@ Entry *derefAlias_r ( Backend *be, /* * given a DN fully deref it and return the real DN or original DN if it fails + * This involves finding the last matched part then reconstructing forward + * e.g. + * ou=MyOU,o=MyAliasedOrg,c=MyCountry where o=MyAliasedOrg is an alias for o=MyOrg + * loop starts with newDN = ou=MyOU,o=MyAliasedOrg,c=MyCountry + * dn2entry_r on newDN gives null entry and o=MyAliasedOrg,c=MyCountry matched + * dn2entry_r on matched gives o=MyAliasedOrg,c=MyCountry entry + * remainder is ou=MyOU + * dereferencing o=MyAliasedOrg,c=MyCountry yields entry o=MyOrg,c=MyCountry + * release lock on o=MyAliasedOrg,c=MyCountry entry + * reconstructed dn is ou=MyOU,o=MyOrg,c=MyCountry + * release lock on o=MyOrg,c=MyCountry entry */ char *derefDN ( Backend *be, Connection *conn, @@ -112,16 +166,17 @@ char *derefDN ( Backend *be, ) { struct ldbminfo *li = (struct ldbminfo *) be->be_private; - char *matched; + char *matched = 0; char *newDN = NULL; int depth, i; Entry *eMatched; Entry *eDeref; Entry *eNew; + if (!dn) return NULL; Debug( LDAP_DEBUG_TRACE, - "<= dereferencing dn %s\n", + "<= dereferencing dn: \"%s\"\n", dn, 0, 0 ); newDN = ch_strdup ( dn ); @@ -134,7 +189,7 @@ char *derefDN ( Backend *be, if ((matched != NULL) && *matched) { char *submatch; - + /* * make sure there actually is an entry for the matched part */ @@ -156,6 +211,10 @@ char *derefDN ( Backend *be, free (newDN); newDN = NULL; free (remainder); + remainder = NULL; + + cache_return_entry_r(&li->li_cache, eMatched); + eMatched = NULL; break; /* no associated entry, dont deref */ } else { @@ -165,13 +224,18 @@ char *derefDN ( Backend *be, i = strcasecmp (matched, eNew->e_dn); /* free reader lock */ cache_return_entry_r(&li->li_cache, eNew); + + free (matched); + matched = NULL; + if (! i) { /* newDN same as old so not an alias, no need to go further */ free (newDN); newDN = NULL; - free (matched); - matched = NULL; free (remainder); + + cache_return_entry_r(&li->li_cache, eMatched); + eMatched = NULL; break; } @@ -185,12 +249,10 @@ char *derefDN ( Backend *be, strcat (newDN, eMatched->e_dn); Debug( LDAP_DEBUG_TRACE, "<= expanded to %s\n", newDN, 0, 0 ); - free (matched); - matched = NULL; free (remainder); } - /* free reader lock */ - cache_return_entry_r(&li->li_cache, eMatched); + /* free reader lock */ + cache_return_entry_r(&li->li_cache, eMatched); } else { if(submatch != NULL) free(submatch); @@ -202,32 +264,50 @@ char *derefDN ( Backend *be, } } - /* free reader lock */ - cache_return_entry_r(&li->li_cache, eMatched); + /* release lock if a match terminated the loop, there should be no + * outstanding locks at this point + */ + if(eMatched != NULL) { + /* free reader lock */ + cache_return_entry_r(&li->li_cache, eMatched); + } /* - * the final part of the DN might be an alias - * so try to dereference it. + * the final part of the DN might be an alias so try to dereference it. + * e.g. if we had started with dn = o=MyAliasedOrg,c=MyCountry the dn would match + * and the above loop complete but we would still be left with an aliased DN. */ - if ( (eNew = dn2entry_r( be, newDN, &matched )) != NULL) { - if ((eDeref = derefAlias_r( be, conn, op, eNew )) != NULL) { - free (newDN); - newDN = ch_strdup (eDeref->e_dn); + if (newDN != NULL) { + if ( (eNew = dn2entry_r( be, newDN, &matched )) != NULL) { + if ((eDeref = derefAlias_r( be, conn, op, eNew )) != NULL) { + free (newDN); + newDN = ch_strdup (eDeref->e_dn); + /* free reader lock */ + cache_return_entry_r(&li->li_cache, eDeref); + } /* free reader lock */ - cache_return_entry_r(&li->li_cache, eDeref); + cache_return_entry_r(&li->li_cache, eNew); } - /* free reader lock */ - cache_return_entry_r(&li->li_cache, eNew); } + if (matched != NULL) free(matched); /* * warn if we exceeded the max depth as the resulting DN may not be dereferenced */ if (depth >= be->be_maxDerefDepth) { - Debug( LDAP_DEBUG_TRACE, - "<= max deref depth exceeded in derefDN for %s, result %s\n", - dn, newDN, 0 ); - send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "", + if (newDN) { + Debug( LDAP_DEBUG_TRACE, + "<= max deref depth exceeded in derefDN for \"%s\", result \"%s\"\n", + dn, newDN, 0 ); + free (newDN); + newDN = NULL; + } + else { + Debug( LDAP_DEBUG_TRACE, + "<= max deref depth exceeded in derefDN for \"%s\", result NULL\n", + dn, 0, 0 ); + } + send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "", "Maximum alias dereference depth exceeded for base" ); } @@ -235,8 +315,7 @@ char *derefDN ( Backend *be, newDN = ch_strdup ( dn ); } - Debug( LDAP_DEBUG_TRACE, "<= returning deref DN of %s\n", newDN, 0, 0 ); - if (matched != NULL) free(matched); + Debug( LDAP_DEBUG_TRACE, "<= returning deref DN of \"%s\"\n", newDN, 0, 0 ); return newDN; }