]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-ldbm/alias.c
replace alias dereferencing code.
[openldap] / servers / slapd / back-ldbm / alias.c
index e93f6b5256b186ea122977727958f7bd7489efec..470ba0a7e1236c95a47bb371d957a1016d60e42b 100644 (file)
@@ -1,13 +1,6 @@
 /*
- * 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 "back-ldbm.h"
 #include "proto-back-ldbm.h"
 
-#ifdef SLAPD_ALIASES
 
-/*
- * dereference alias
- *     input origEntry is should be locked/unlocked by caller.
- *
- * returns origEntry if origEntry is not an alias
- * returns NULL if error
- * otherwise returns read locked alias
- */
-Entry *deref_alias_r (
-       Backend         *be,
-       Connection      *conn,
-       Operation       *op,
-       Entry           *origEntry,
-       int                     *err,
-       char            **matched_dn
-)
+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 *deref_internal_r(
+       Backend*        be,
+       Entry*          alias,
+       char*           dn,
+       int*            err,
+       Entry**         matched,
+       char**          text )
 {
        struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Entry *entry;
+       Entry *sup;
        unsigned depth;
-       Entry *e;
-       char **aliases = NULL;
-       char *newDN = NULL;
-       char *oldDN = NULL;
-       int rc = LDAP_SUCCESS;
-
-       /*
-        * Aliases are only deref'ed during search operations.
-        * if deref_alias_r (or deref_dn) is needed by other op,
-        * this will need to become argument
-        */
-       const int access = ACL_SEARCH;
-
-       /* be sure we have a starting entry */
-       if( origEntry != NULL ) {
-               return NULL;
-       }
+       char **dnlist;
 
-       Debug( LDAP_DEBUG_TRACE, "<= checking for alias for dn %s\n",
-               origEntry->e_dn, 0, 0 );
-
-       /*
-        * try to deref fully, up to a maximum depth.   If the max depth exceeded
-        * then send an error
-        */
-       e = origEntry;
-       for ( depth = 0; e != NULL; depth++ ) 
-       {
-               Attribute *a;
-               struct berval bv;
-
-               if ( ! access_allowed( be, conn, op, e,
-                       "entry", NULL, access ) )
-               {
-                       Debug( LDAP_DEBUG_ACL,
-                               "deref_alias_r: access to entry not allowed\n",
-                               0, 0, 0 );
-                       break;
-               }
+       assert( ( alias != NULL && dn == NULL ) || ( alias == NULL && dn != NULL ) );
 
-               /*
-                * aliased object names must be contained in an entry
-                * object class "alias".
-                */
-               a = attr_find(e->e_attrs, "objectclass");
+       *matched = NULL;
+       *err = LDAP_SUCCESS;
+       *text = NULL;
 
-               if( a == NULL ) {
-                       /* no objectclass attribute */
-                       break;
-               }
-
-               bv.bv_val = "REFERRAL";
-               bv.bv_len = sizeof("REFERRAL")-1;
-       
-               if (value_find(a->a_vals, &bv, a->a_syntax, 1) == 0) {
-                       /* is a referral */
-                       break;
-               }
-
-               bv.bv_val = "ALIAS";
-               bv.bv_len = sizeof("ALIAS")-1;
-       
-               if (value_find(a->a_vals, &bv, a->a_syntax, 1) != 0) {
-                       /* not an alias */
-                       break;
-               }
+       if( alias == NULL ) {
+               dn = ch_strdup( dn );
+               entry = dn2entry_r( be, dn, &sup );
 
-               if ( ! access_allowed( be, conn, op, e,
-                       "aliasedobjectname", NULL, access ) )
-               {
-                       Debug( LDAP_DEBUG_ACL,
-                               "deref_alias_r: access to reference not allowed\n",
-                               0, 0, 0 );
-                       break;
-               }
+       } else {
+               dn = ch_strdup( alias->e_ndn );
+               entry = alias;
+               sup = NULL;
+       }
 
-               a = attr_find( e->e_attrs, "aliasedobjectname" );
-
-               if( a == NULL ) {
-                       /*
-                        * there was an aliasedobjectname defined but no data.
-                        */
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= %s has no aliasedObjectName attribute\n", 
-                                e->e_dn, 0, 0 );
-                       send_ldap_result( conn, op, rc = LDAP_ALIAS_PROBLEM,
-                               NULL, "alias missing aliasedObjectName", NULL, 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 = dn2entry_r( be, aliasDN, &sup );
+
+                       if( newe != NULL ) {
+                               free( dn );
+                               cache_return_entry_r(&li->li_cache, entry );
+                               entry = newe;
+                               dn = ch_strdup( entry->e_ndn );
+                               charray_add( &dnlist, dn );
+                               continue;
+
+                       }
+                       
+                       if ( sup != NULL ) {
+                               cache_return_entry_r(&li->li_cache, entry );
+                               entry = NULL;
+                               continue;
+                       }
+
+                       /* no newe and no superior, we're done */
                        break;
-               }
 
-               /* 
-                * 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.
-                        */
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= %s has no value  aliasedObjectName attribute\n", 
-                                e->e_dn, 0, 0 );
-                       send_ldap_result( conn, op, rc = LDAP_ALIAS_PROBLEM,
-                               NULL, "alias missing aliasedObjectName value", NULL, NULL );
-                       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 = dn2entry_r( be, aliasDN, &newSup );
+
+                       if( newe != NULL ) {
+                               free(aliasDN);
+                               free( dn );
+                               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 );
+                               cache_return_entry_r(&li->li_cache, sup );
+                               sup = newSup;
+                               dn = aliasDN;
+                               continue;
+                       }
 
-               if( a->a_vals[1] != NULL ) {
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= %s alias has multiple values\n", 
-                                e->e_dn, 0, 0 );
-                       send_ldap_result( conn, op, rc= LDAP_ALIAS_PROBLEM,
-                               NULL, "multivalue aliasObjectName", NULL, NULL );
                        break;
-               }
 
-               if( depth >= be->be_max_deref_depth ) {
-                       /* depth limit exceeded */
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= deref(\"%s\") exceeded maximum deref depth (%d) at \"%s\"\n", 
-                                origEntry->e_dn, 
-                                be->be_max_deref_depth, 
-                                e->e_ndn );
-                       send_ldap_result( conn, op, rc = LDAP_ALIAS_DEREF_PROBLEM,
-                               NULL, "maximum deref depth exceeded", NULL, NULL );
+               } else {
+                       /* no newe and no superior, we're done */
                        break;
                }
+       }
 
-               charray_add( &aliases, e->e_ndn );
+       free( dn );
+       return entry;
+}
 
-               Debug( LDAP_DEBUG_TRACE, "<= %s is an alias for %s\n", 
-                       e->e_dn, a->a_vals[0]->bv_val, 0 );
 
-               if( oldDN != NULL ) free( oldDN );
-               oldDN = ch_strdup( e->e_ndn );
+static char* get_alias_dn(
+       Entry *e,
+       int *err,
+       char **errmsg )
+{      
+       Attribute *a = attr_find( e->e_attrs, "aliasedobjectname" );
 
-               /* 
-                * release past lock if not original
+       if( a == NULL ) {
+               /*
+                * there was an aliasedobjectname defined but no data.
                 */
-               if ( depth > 0 ) {
-                       cache_return_entry_r(&li->li_cache, e);
-               }
-               e = NULL;
-
-               if( newDN != NULL ) free( newDN );
-               newDN = ch_strdup( a->a_vals[0]->bv_val );
-               dn_normalize_case (newDN);
-
-               /* make sure new and old DN are not same to avoid loops */
-               if ( charray_inlist( aliases, newDN ) ) {
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= %s has circular alias %s\n", 
-                                origEntry->e_dn, newDN, 0 );
-                       send_ldap_result( conn, op, rc = LDAP_LOOP_DETECT,
-                               NULL, "circular alias", NULL, NULL );
-                       break;
-               }
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias missing aliasedObjectName attribute";
+               return NULL;
+       }
 
+       /* 
+        * aliasedObjectName should be SINGLE-VALUED with a single value. 
+        */                     
+       if ( a->a_vals[0] == NULL || a->a_vals[0]->bv_val != NULL ) {
                /*
-                * ok, so what happens if there is an alias in the DN of a dereferenced
-                * alias object?        
+                * there was an aliasedobjectname defined but no data.
                 */
-               if ( (e = dn2entry_r( be, newDN, NULL )) == NULL ) {
-                       /* could not deref return error */
-                       Debug( LDAP_DEBUG_TRACE, 
-                                "<= %s has dangling alias %s to %s\n", 
-                                origEntry->e_dn, oldDN, newDN );
-                       send_ldap_result( conn, op, rc = LDAP_ALIAS_DEREF_PROBLEM,
-                               NULL, "dangling alias", NULL, NULL );
-                       break;
-               }
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias missing aliasedObjectName value";
+               return NULL;
        }
 
-       if( e != NULL && origEntry != e && rc != LDAP_SUCCESS ) {
-               cache_return_entry_r(&li->li_cache, e);
-               e = NULL;
+       if( a->a_vals[1] != NULL ) {
+               *err = LDAP_ALIAS_PROBLEM;
+               *errmsg = "alias has multivalued aliasedObjectName";
+               return NULL;
        }
 
-       charray_free( aliases );
-       if( newDN ) free(newDN);
-       if( oldDN ) free(oldDN);
-
-       return e;
+       return a->a_vals[0]->bv_val;
 }
 
-
-/*
- * 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.
- *
- * Example:
- *
- * "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA" where
- *             "o=AliasedOrg,c=CA" is an alias for
- *                    "o=Org,c=CA"
- *     and
- *             "cn=AliasUser,ou=OU,o=Org,c=CA" is an alias for
- *                  "cn=User,ou=OU,o=Org,c=CA"
- *
- * 1) newDN = dn
- *             newDN is "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA"
- *
- * 2) loop: e = d2entry_r( newDN, matched )
- *             e is NULL
- *             matched is entry("o=AliasOrg,c=CA")
- *
- * 3) rmdr = remainder(newDN, matched)
- *             rmdr is "cn=AliasUser,ou=OU"
- *
- * 4) alias = deref(matched)
- *             alias is entry("o=Org,c=CA")
- *
- * 5) oldDN=newDN; newDN = rmdr + alias
- *             oldDN is "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA"
- *             newDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
- *
- * 6) compare(oldDN,newDN)
- *             goto loop (step 2)
- *
- * 7) e = d2entry_r( newDN, matched )
- *             e is NULL
- *             matched is entry("ou=OU,o=Org,c=CA")
- *
- * 8) rmdr = remainder(newDN, matched)
- *             rmdr is "cn=AliasUser"
- *
- * 9) alias = deref(matched)
- *             alias is entry("ou=OU,o=Org,c=CA")
- *
- *10) oldDN=newDN; newDN = rmdr + alias
- *             oldDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
- *             newDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
- *
- *11) compare(oldDN,newDN)
- *             break loop (step 2)
- *
- *12) return newDN
- *
- */
-char *deref_dn (
-       Backend         *be,
-       Connection      *conn,
-       Operation       *op,
-       char            *dn
-)
+char* new_superior(
+       char *dn,
+       char *oldSup,
+       char *newSup )
 {
-       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
-       unsigned        depth;
-       char*   remainder = NULL;
-       char*   newDN;
-
-       char    **dns;
-       
-       if (!dn) return NULL; 
-
-       Debug( LDAP_DEBUG_TRACE, 
-               "<= dereferencing dn: \"%s\"\n", 
-               dn, 0, 0 );
-
-       charray_add( &dns, "" );
-
-       newDN = ch_strdup( dn );
+       char *newDN;
+       size_t dnlen, olen, nlen;
+       assert( dn && oldSup && newSup );
 
-       for ( depth = 0; charray_inlist( dns, newDN ) != 0; depth++ )
-       {
-               Entry*  e = NULL;
-               Entry*  matched = NULL;
-               Entry*  alias = NULL;
-               int     rlen;
-
-               if( depth >= be->be_max_deref_depth ) {
-                       /* depth limit exceeded */
-                       break;
-               }
-
-               e = dn2entry_r( be, newDN, &matched );
-               
-               if( e != NULL ) {
-                       cache_return_entry_r(&li->li_cache, e);
-                       break;
-               }
-
-               if ( matched == NULL ) {
-                       /* nothing matched */
-                       break;
-               }
+       dnlen = strlen( dn );
+       olen = strlen( oldSup );
+       nlen = strlen( newSup );
 
-               charray_add( &dns, newDN );
+       newDN = ch_malloc( dnlen - olen + nlen + 1 );
 
-               Debug( LDAP_DEBUG_TRACE, "<= matched %s\n", matched->e_dn, 0, 0 );
+       memcpy( newDN, dn, dnlen - olen );
+       memcpy( &newDN[dnlen - olen], newSup, nlen );
+       newDN[dnlen - olen + nlen] = '\0';
 
-               rlen = strlen( newDN ) - strlen( matched->e_ndn );
-               remainder = ch_malloc( rlen + 1 );
-               strncpy( remainder, newDN, rlen );
-               remainder[rlen] = '\0';
-       
-               Debug( LDAP_DEBUG_TRACE, "<= remainder %s\n", remainder, 0, 0 );
-
-               alias = deref_alias_r( be, conn, op, matched );
-
-               cache_return_entry_r(&li->li_cache, matched);
+       return newDN;
+}
 
-               if( alias == matched ) {
-                       /* matched isn't an alias */
-                       break;
-               }
+static int dnlist_subordinate(
+       char** dnlist,
+       char *dn )
+{
+       int i;
+       assert( dnlist );
 
-               if( alias == NULL )  {
-                       /* alias error */
-                       break;
+       for( i = 0; dnlist[i] != NULL; i++ ) {
+               if( dn_issuffix( dnlist[i], dn ) ) {
+                       return 1;
                }
-       
-               Debug( LDAP_DEBUG_TRACE, "<= derefenced to %s\n", alias->e_dn, 0, 0 );
-
-               free( newDN );
-               newDN = ch_malloc( rlen + strlen( alias->e_ndn ) + 1 );
-               sprintf("%s%s", remainder, alias->e_ndn );
-
-               free( remainder );
-               remainder = NULL;
-
-               Debug( LDAP_DEBUG_TRACE, "<= expanded to %s\n", newDN, 0, 0 );
-
-               cache_return_entry_r( &li->li_cache, alias );
        }
 
-       charray_free( dns );
-
-       if( remainder != NULL ) {
-               free( remainder );
-       }
-
-       Debug( LDAP_DEBUG_TRACE, "<= %s\n", newDN, 0, 0 );
-
-       return newDN;
+       return 0;
 }
-#endif
\ No newline at end of file
+