]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-bdb2/alias.c
Add OpenLDAP RCSid to *.[ch] in clients, libraries, and servers.
[openldap] / servers / slapd / back-bdb2 / alias.c
index 1d66b27732d6aebda96201353fa4481f0a1421e6..3de54b9bf0b3eb1c07d3553d4c46cc29a7d153e3 100644 (file)
+/* $OpenLDAP$ */
 /*
- * Copyright (c) 1998 Will Ballantyne, ITSD, Government of BC
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to ITSD, Government of BC. The name of ITSD
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission. This software
- * is provided ``as is'' without express or implied warranty.
+ * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
  */
 
 #include "portable.h"
 
 #include <stdio.h>
-#include <string.h>
-#include <ac/socket.h>         /* Get struct sockaddr for slap.h */
+#include <ac/string.h>
+#include <ac/socket.h>
 #include "slap.h"
 #include "back-bdb2.h"
 #include "proto-back-bdb2.h"
 
-/*
- * given an alias object, dereference it to its end point.
- * Entry returned has reader lock or is NULL.  Starting entry is not released.
- */
-Entry *bdb2i_derefAlias_r ( BackendDB     *be,
-                   Connection  *conn,
-                   Operation   *op,
-                   Entry       *e)
+static char* get_alias_dn(
+       Entry *e,
+       int *err,
+       char **errmsg );
+
+static char* new_superior(
+       char *dn,
+       char *oldSup,
+       char *newSup );
+
+static int dnlist_subordinate(
+       char** dnlist,
+       char *dn );
+
+Entry *bdb2i_deref_r(
+       Backend*        be,
+       Entry*          alias,
+       char*           dn,
+       int*            err,
+       Entry**         matched,
+       char**          text )
 {
-  /* to free cache entries */
-  struct ldbminfo *li = (struct ldbminfo *) be->be_private;
-  Attribute *a;
-  int       depth;
-  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 );
-
-  /*
-   * try to deref fully, up to a maximum depth.  If the max depth exceeded
-   * then send an error
-   */
-  for ( depth = 0;
-       ( ( a = attr_find( e->e_attrs, "aliasedobjectname" ) ) != NULL) &&
-         ( depth < be->be_maxDerefDepth );
-       ++depth) 
-  {
-
-    /* 
-     * make sure there is a defined aliasedobjectname.  
-     * can only have one value so just use first value (0) in the attr list. 
-     */            
-    if (a->a_vals[0] && a->a_vals[0]->bv_val) {
-      char *newDN, *oldDN;
-
-      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_ndn);
-
-      /* 
-       * release past lock if not original
-       */
-      if ( (depth > 0) && e ) {
-          bdb2i_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
-       * alias object?  
-       */
-      if ( (e = bdb2i_dn2entry_r( be, newDN, &matched )) == NULL ) {
-
-               /* could not deref return error  */
-               Debug( LDAP_DEBUG_TRACE, 
-               "<= %s is a dangling alias to %s\n", 
-               oldDN, newDN, 0 );
-               send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "",
-                               "Dangling Alias" );
-
-                       if(matched != NULL) free(matched);
-       free (newDN);
-       free (oldDN);
-               break;
-      }
-
-      free (newDN);
-      free (oldDN);
-    }
-    else {
-      /*
-       * there was an aliasedobjectname defined but no data.
-       * this can't happen, right?
-       */
-               Debug( LDAP_DEBUG_TRACE, 
-               "<= %s has no data in aliasedobjectname attribute\n", 
-               (e && e->e_dn) ? e->e_dn : "(null)", 0, 0 );
-               send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "",
-                               "Alias missing aliasedobjectname" );
-               break;
-    }
-  }
-
-  /*
-   * warn if we pulled out due to exceeding the maximum deref depth
-   */
-  if ( depth >= be->be_maxDerefDepth ) {
-    Debug( LDAP_DEBUG_TRACE, 
-          "<= 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" );
-  }
-
-  return e;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Entry *entry;
+       Entry *sup;
+       unsigned depth;
+       char **dnlist;
+
+       assert( ( alias != NULL && dn == NULL ) || ( alias == NULL && dn != NULL ) );
+
+       *matched = NULL;
+       *err = LDAP_SUCCESS;
+       *text = NULL;
+
+       if( alias == NULL ) {
+               dn = ch_strdup( dn );
+               entry = bdb2i_dn2entry_r( be, dn, &sup );
+
+       } else {
+               dn = ch_strdup( alias->e_ndn );
+               entry = alias;
+               sup = NULL;
+       }
+
+       dnlist = NULL;
+       charray_add( &dnlist, dn );
+
+       for( depth=0 ; ; depth++ ) {
+               if( entry != NULL ) {
+                       Entry *newe;
+                       char *aliasDN;
+
+                       /* have entry, may be an alias */
+
+                       if( !is_entry_alias( entry ) ) {
+                               /* entry is not an alias */
+                               break;
+                       }
+
+                       /* entry is alias */
+                       if( depth > be->be_max_deref_depth ) {
+                               *matched = entry;
+                               entry = NULL;
+                               *err = LDAP_ALIAS_DEREF_PROBLEM;
+                               *text = "maximum deref depth exceeded";
+                               break;
+                       }
+
+                       /* deref entry */
+                       aliasDN = get_alias_dn( entry, err, text );
+
+                       if( aliasDN == NULL ) {
+                               *matched = entry;
+                               entry = NULL;
+                               break;
+                       }
+
+                       /* check if aliasDN is a subordinate of any DN in our list */
+                       if( dnlist_subordinate( dnlist, aliasDN ) ) {
+                               *matched = entry;
+                               entry = NULL;
+                               *err = LDAP_ALIAS_PROBLEM;
+                               *text = "circular alias";
+                               break;
+                       }
+
+                       /* attempt to dereference alias */
+
+                       newe = bdb2i_dn2entry_r( be, aliasDN, &sup );
+
+                       if( newe != NULL ) {
+                               free( dn );
+                               bdb2i_cache_return_entry_r(&li->li_cache, entry );
+                               entry = newe;
+                               dn = ch_strdup( entry->e_ndn );
+                               charray_add( &dnlist, dn );
+                               continue;
+
+                       }
+                       
+                       if ( sup != NULL ) {
+                               bdb2i_cache_return_entry_r(&li->li_cache, entry );
+                               entry = NULL;
+                               continue;
+                       }
+
+                       /* no newe and no superior, we're done */
+                       break;
+
+               } else if( sup != NULL ) {
+                       /* have superior, may be an alias */
+                       Entry *newe;
+                       Entry *newSup;
+                       char *supDN;
+                       char *aliasDN;
+
+                       if( !is_entry_alias( sup ) ) {
+                               /* entry is not an alias */
+                               *matched = sup;
+                               sup = NULL;
+                               break;
+                       }
+
+                       /* entry is alias */
+                       if( depth > be->be_max_deref_depth ) {
+                               *matched = sup;
+                               entry = NULL;
+                               *err = LDAP_ALIAS_DEREF_PROBLEM;
+                               *text = "maximum deref depth exceeded";
+                               break;
+                       }
+
+                       /* deref entry */
+                       supDN = get_alias_dn( sup, err, text );
+
+                       if( supDN == NULL ) {
+                               *matched = sup;
+                               break;
+                       }
+
+                       aliasDN = new_superior( dn, sup->e_ndn, supDN );
+
+                       if( aliasDN == NULL ) {
+                               free(aliasDN);
+                               *matched = sup;
+                               *err = LDAP_ALIAS_PROBLEM;
+                               *text = "superior alias problem";
+                               break;
+                       }
+
+                       /* check if aliasDN is a subordinate of any DN in our list */
+                       if( dnlist_subordinate( dnlist, aliasDN ) ) {
+                               free(aliasDN);
+                               *matched = entry;
+                               entry = NULL;
+                               *err = LDAP_ALIAS_PROBLEM;
+                               *text = "subordinate circular alias";
+                               break;
+                       }
+
+                       /* attempt to dereference alias */
+                       newe = bdb2i_dn2entry_r( be, aliasDN, &newSup );
+
+                       if( newe != NULL ) {
+                               free(aliasDN);
+                               free( dn );
+                               bdb2i_cache_return_entry_r(&li->li_cache, sup );
+                               entry = newe;
+                               dn = ch_strdup( entry->e_ndn );
+                               charray_add( &dnlist, dn );
+                               continue;
+
+                       }
+                       
+                       if ( newSup != NULL ) {
+                               free( dn );
+                               bdb2i_cache_return_entry_r(&li->li_cache, sup );
+                               sup = newSup;
+                               dn = aliasDN;
+                               continue;
+                       }
+
+                       break;
+
+               } else {
+                       /* no newe and no superior, we're done */
+                       break;
+               }
+       }
+
+       free( dn );
+       return entry;
 }
 
-/*
- * 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 *bdb2i_derefDN ( BackendDB     *be,
-                Connection  *conn,
-                Operation   *op,
-                char        *dn
-)
-{
-  struct ldbminfo *li = (struct ldbminfo *) be->be_private;
-  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", 
-        dn, 0, 0 );
-  
-  newDN = ch_strdup ( dn );
-
-  /* while we don't have a matched dn, deref the DN */
-  for ( depth = 0;
-       ( (eMatched = bdb2i_dn2entry_r( be, newDN, &matched )) == NULL) &&
-         (depth < be->be_maxDerefDepth);
-       ++depth ) {
-    
-    if ((matched != NULL) && *matched) {       
-      char *submatch;
-      
-      /* 
-       * make sure there actually is an entry for the matched part 
-       */
-      if ( (eMatched = bdb2i_dn2entry_r( be, matched, &submatch )) != NULL) {
-       char  *remainder; /* part before the aliased part */
-       int  rlen = strlen(newDN) - strlen(matched);
-       
-       Debug( LDAP_DEBUG_TRACE, "<= matched %s\n", matched, 0, 0 );
-       
-       remainder = ch_malloc (rlen + 1);
-       strncpy ( remainder, newDN, rlen );
-       remainder[rlen] = '\0';
-       
-       Debug( LDAP_DEBUG_TRACE, "<= remainder %s\n", remainder, 0, 0 );
-       
-       if ((eNew = bdb2i_derefAlias_r( be, conn, op, eMatched )) == NULL) {
-         free (matched);
-         matched = NULL;
-         free (newDN);
-         newDN = NULL;
-         free (remainder);
-         remainder = NULL;
-
-               bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
-               eMatched = NULL;
-         break; /*  no associated entry, dont deref */
+
+static char* get_alias_dn(
+       Entry *e,
+       int *err,
+       char **errmsg )
+{      
+       Attribute *a = attr_find( e->e_attrs, "aliasedobjectname" );
+
+       if( a == NULL ) {
+               /*
+                * there was an aliasedobjectname defined but no data.
+                */
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias missing aliasedObjectName attribute";
+               return NULL;
        }
-       else {
-
-         Debug( LDAP_DEBUG_TRACE, "<= l&g we have %s vs %s \n", matched, eNew->e_dn, 0 );
-
-         i = strcasecmp (matched, eNew->e_dn);
-          /* free reader lock */
-          bdb2i_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 (remainder);
-
-               bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
-               eMatched = NULL;
-           break;
-         }
-
-         /* 
-          * we have dereferenced the aliased part so put
-          * the new dn together
-          */
-         free (newDN);
-         newDN = ch_malloc (strlen(eMatched->e_dn) + rlen + 1);
-         strcpy (newDN, remainder);
-         strcat (newDN, eMatched->e_dn);
-         Debug( LDAP_DEBUG_TRACE, "<= expanded to %s\n", newDN, 0, 0 );
-
-         free (remainder);
+
+       /* 
+        * aliasedObjectName should be SINGLE-VALUED with a single value. 
+        */                     
+       if ( a->a_vals[0] == NULL || a->a_vals[0]->bv_val == NULL ) {
+               /*
+                * there was an aliasedobjectname defined but no data.
+                */
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias missing aliasedObjectName value";
+               return NULL;
        }
-        /* free reader lock */
-        bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
-      }
-      else {
-       if(submatch != NULL) free(submatch);
-       break; /* there was no entry for the matched part */
-      }
-    }
-    else {
-      break; /* there was no matched part */
-    }
-  }
-  
-       /* release lock if a match terminated the loop, there should be no
-        * outstanding locks at this point
-        */
-  if(eMatched != NULL) {
-    /* free reader lock */
-    bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
-  }
-
-  /*
-   * 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 = bdb2i_dn2entry_r( be, newDN, &matched )) != NULL) {
-    if ((eDeref = bdb2i_derefAlias_r( be, conn, op, eNew )) != NULL) {
-      free (newDN);
-      newDN = ch_strdup (eDeref->e_dn);
-      /* free reader lock */
-      bdb2i_cache_return_entry_r(&li->li_cache, eDeref);
-    }
-    /* free reader lock */
-    bdb2i_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) {
-       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 );
+
+       if( a->a_vals[1] != NULL ) {
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias has multivalued aliasedObjectName";
+               return NULL;
        }
-    send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "",
-                     "Maximum alias dereference depth exceeded for base" );
-  }
 
-  if (newDN == NULL) {
-    newDN = ch_strdup ( dn );
-  }
-  
-  Debug( LDAP_DEBUG_TRACE, "<= returning deref DN of \"%s\"\n", newDN, 0, 0 ); 
+       return a->a_vals[0]->bv_val;
+}
+
+static char* new_superior(
+       char *dn,
+       char *oldSup,
+       char *newSup )
+{
+       char *newDN;
+       size_t dnlen, olen, nlen;
+       assert( dn && oldSup && newSup );
+
+       dnlen = strlen( dn );
+       olen = strlen( oldSup );
+       nlen = strlen( newSup );
+
+       newDN = ch_malloc( dnlen - olen + nlen + 1 );
+
+       memcpy( newDN, dn, dnlen - olen );
+       memcpy( &newDN[dnlen - olen], newSup, nlen );
+       newDN[dnlen - olen + nlen] = '\0';
 
-  return newDN;
+       return newDN;
 }
+
+static int dnlist_subordinate(
+       char** dnlist,
+       char *dn )
+{
+       int i;
+       assert( dnlist );
+
+       for( i = 0; dnlist[i] != NULL; i++ ) {
+               if( dn_issuffix( dnlist[i], dn ) ) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+