]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/aci.c
fix handling of bind-timeout
[openldap] / servers / slapd / aci.c
index 3d587cd9e661bb7d69ae376b534fb657fbcb8c86..70e749ef0a06b758d1b9fdbbf216593c632d138b 100644 (file)
 
 #define ACI_BUF_SIZE   1024    /* use most appropriate size */
 
+enum {
+       ACI_BV_ENTRY,
+       ACI_BV_CHILDREN,
+       ACI_BV_ONELEVEL,
+       ACI_BV_SUBTREE,
+
+       ACI_BV_BR_ENTRY,
+       ACI_BV_BR_ALL,
+
+       ACI_BV_ACCESS_ID,
+       ACI_BV_PUBLIC,
+       ACI_BV_USERS,
+       ACI_BV_SELF,
+       ACI_BV_DNATTR,
+       ACI_BV_GROUP,
+       ACI_BV_ROLE,
+       ACI_BV_SET,
+       ACI_BV_SET_REF,
+
+       ACI_BV_GRANT,
+       ACI_BV_DENY,
+
+       ACI_BV_GROUP_CLASS,
+       ACI_BV_GROUP_ATTR,
+       ACI_BV_ROLE_CLASS,
+       ACI_BV_ROLE_ATTR,
+
+       ACI_BV_SET_ATTR,
+
+       ACI_BV_LAST
+};
+
+static const struct berval     aci_bv[] = {
+       /* scope */
+       BER_BVC("entry"),
+       BER_BVC("children"),
+       BER_BVC("onelevel"),
+       BER_BVC("subtree"),
+
+       /* */
+       BER_BVC("[entry]"),
+       BER_BVC("[all]"),
+
+       /* type */
+       BER_BVC("access-id"),
+       BER_BVC("public"),
+       BER_BVC("users"),
+       BER_BVC("self"),
+       BER_BVC("dnattr"),
+       BER_BVC("group"),
+       BER_BVC("role"),
+       BER_BVC("set"),
+       BER_BVC("set-ref"),
+
+       /* actions */
+       BER_BVC("grant"),
+       BER_BVC("deny"),
+
+       /* schema */
+       BER_BVC(SLAPD_GROUP_CLASS),
+       BER_BVC(SLAPD_GROUP_ATTR),
+       BER_BVC(SLAPD_ROLE_CLASS),
+       BER_BVC(SLAPD_ROLE_ATTR),
+
+       BER_BVC(SLAPD_ACI_SET_ATTR),
+
+       BER_BVNULL
+};
+
+#ifdef SLAP_DYNACL
+static
+#endif /* SLAP_DYNACL */
+AttributeDescription *slap_ad_aci;
+
+static int
+OpenLDAPaciValidate(
+       Syntax          *syntax,
+       struct berval   *val );
+
+static int
+OpenLDAPaciPretty(
+       Syntax          *syntax,
+       struct berval   *val,
+       struct berval   *out,
+       void            *ctx );
+
+static int
+OpenLDAPaciNormalize(
+       slap_mask_t     use,
+       Syntax          *syntax,
+       MatchingRule    *mr,
+       struct berval   *val,
+       struct berval   *out,
+       void            *ctx );
+
+#define        OpenLDAPaciMatch                        octetStringMatch
+
 static int
 aci_list_map_rights(
        struct berval   *list )
@@ -57,6 +154,20 @@ aci_list_map_rights(
                }
 
                switch ( *bv.bv_val ) {
+               case 'x':
+                       /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
+                        * define any equivalent to the AUTH right, so I've just used
+                        * 'x' for now.
+                        */
+                       ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+                       break;
+               case 'd':
+                       /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+                        * the right 'd' to mean "delete"; we hijack it to mean
+                        * "disclose" for consistency wuith the rest of slapd.
+                        */
+                       ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
+                       break;
                case 'c':
                        ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
                        break;
@@ -74,13 +185,6 @@ aci_list_map_rights(
                case 'w':
                        ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
                        break;
-               case 'x':
-                       /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
-                        * define any equivalent to the AUTH right, so I've just used
-                        * 'x' for now.
-                        */
-                       ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
-                       break;
                default:
                        break;
                }
@@ -239,7 +343,7 @@ aci_group_member (
        const char              *text;
        int                     rc;
 
-       /* format of string is "group/objectClassValue/groupAttrName" */
+       /* format of string is "{group|role}/objectClassValue/groupAttrName" */
        if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
                return 0;
        }
@@ -299,7 +403,12 @@ aci_mask(
        slap_access_t           *deny,
        slap_aci_scope_t        asserted_scope )
 {
-       struct berval           bv, scope, perms, type, sdn;
+       struct berval           bv,
+                               scope,
+                               perms,
+                               type,
+                               opts,
+                               sdn;
        int                     rc;
                
 
@@ -348,8 +457,7 @@ aci_mask(
         * both match "subtree" */
        switch ( asserted_scope ) {
        case SLAP_ACI_SCOPE_ENTRY:
-               /* TODO: use ber_bvcmp */
-               if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
+               if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
                                && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
                {
                        return 0;
@@ -357,22 +465,21 @@ aci_mask(
                break;
 
        case SLAP_ACI_SCOPE_CHILDREN:
-               /* TODO: use ber_bvcmp */
-               if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
+               if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
                                && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
                {
                        return 0;
                }
                break;
 
-       default:
-               /* TODO: add assertion */
+       case SLAP_ACI_SCOPE_SUBTREE:
+               /* TODO: add assertion? */
                return 0;
        }
 
        /* get the list of permissions clauses, bail if empty */
        if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
-               /* TODO: add assertion */
+               assert( 0 );
                return 0;
        }
 
@@ -383,13 +490,12 @@ aci_mask(
 
        /* see if we have a DN match */
        if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
-               /* TODO: add assertion */
+               assert( 0 );
                return 0;
        }
 
        /* see if we have a public (i.e. anonymous) access */
-       /* TODO: use ber_bvcmp */
-       if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
+       if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
                return 1;
        }
        
@@ -399,8 +505,7 @@ aci_mask(
        }
 
        /* see if we have a users access */
-       /* TODO: use ber_bvcmp */
-       if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
+       if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
                return 1;
        }
        
@@ -417,95 +522,41 @@ aci_mask(
        sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
        sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
 
-       /* TODO: use ber_bvcmp */
-       if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
-               struct berval ndn;
-               
-               /* TODO: don't normalize */
-               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
-               if ( rc != LDAP_SUCCESS ) {
-                       return 0;
-               }
-
-               if ( dn_match( &op->o_ndn, &ndn ) ) {
-                       rc = 1;
-               }
-               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+       /* get the type options, if any */
+       if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
+               opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
+               type.bv_len = opts.bv_val - type.bv_val - 1;
 
-               return rc;
-
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
-               struct berval ndn;
-               
-               /* TODO: don't normalize */
-               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
-               if ( rc != LDAP_SUCCESS ) {
-                       return 0;
-               }
-
-               if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
-                       rc = 1;
-               }
-               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
-               return rc;
-
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
-               struct berval ndn, pndn;
-               
-               /* TODO: don't normalize */
-               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
-               if ( rc != LDAP_SUCCESS ) {
-                       return 0;
-               }
+       } else {
+               BER_BVZERO( &opts );
+       }
 
-               dnParent( &ndn, &pndn );
+       if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
+               return dn_match( &op->o_ndn, &sdn );
 
-               if ( dn_match( &op->o_ndn, &pndn ) ) {
-                       rc = 1;
-               }
-               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-
-               return rc;
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
+               return dnIsSuffix( &op->o_ndn, &sdn );
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
-               struct berval ndn;
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
+               struct berval pdn;
                
-               /* TODO: don't normalize */
-               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
-               if ( rc != LDAP_SUCCESS ) {
-                       return 0;
-               }
+               dnParent( &sdn, &pdn );
 
-               if ( !dn_match( &op->o_ndn, &ndn )
-                               && dnIsSuffix( &op->o_ndn, &ndn ) )
-               {
-                       rc = 1;
-               }
-               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+               return dn_match( &op->o_ndn, &pdn );
 
-               return rc;
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
+               return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
-               if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
-                       return 1;
-               }
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
+               return dn_match( &op->o_ndn, &e->e_nname );
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
                Attribute               *at;
                AttributeDescription    *ad = NULL;
                const char              *text;
 
                rc = slap_bv2ad( &sdn, &ad, &text );
-               if ( rc != LDAP_SUCCESS ) {
-                       /* TODO: add assertion */
-                       return 0;
-               }
+               assert( rc == LDAP_SUCCESS );
 
                rc = 0;
                for ( at = attrs_find( e->e_attrs, ad );
@@ -525,46 +576,166 @@ aci_mask(
 
                return rc;
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
-               if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
-                               &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
+               struct berval   oc,
+                               at;
+
+               if ( BER_BVISNULL( &opts ) ) {
+                       oc = aci_bv[ ACI_BV_GROUP_CLASS ];
+                       at = aci_bv[ ACI_BV_GROUP_ATTR ];
+
+               } else {
+                       if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+                               assert( 0 );
+                       }
+
+                       if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+                               at = aci_bv[ ACI_BV_GROUP_ATTR ];
+                       }
+               }
+
+               if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
                {
                        return 1;
                }
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
-               if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
-                               &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
+               struct berval   oc,
+                               at;
+
+               if ( BER_BVISNULL( &opts ) ) {
+                       oc = aci_bv[ ACI_BV_ROLE_CLASS ];
+                       at = aci_bv[ ACI_BV_ROLE_ATTR ];
+
+               } else {
+                       if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+                               assert( 0 );
+                       }
+
+                       if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+                               at = aci_bv[ ACI_BV_ROLE_ATTR ];
+                       }
+               }
+
+               if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
                {
                        return 1;
                }
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
-               if ( acl_match_set( &sdn, op, e, 0 ) ) {
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
+               if ( acl_match_set( &sdn, op, e, NULL ) ) {
                        return 1;
                }
 
-       /* TODO: use ber_bvcmp */
-       } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
-               if ( acl_match_set( &sdn, op, e, 1 ) ) {
+       } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
+               if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
                        return 1;
                }
+
+       } else {
+               /* it passed normalization! */
+               assert( 0 );
        }
 
        return 0;
 }
 
+int
+aci_init( void )
+{
+       /* OpenLDAP eXperimental Syntax */
+       static slap_syntax_defs_rec aci_syntax_def = {
+               "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
+                       SLAP_SYNTAX_HIDE,
+                       OpenLDAPaciValidate,
+                       OpenLDAPaciPretty
+       };
+       static slap_mrule_defs_rec aci_mr_def = {
+               "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
+                       "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
+                       SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+                       NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
+                       NULL, NULL,
+                       NULL
+       };
+       static struct {
+               char                    *name;
+               char                    *desc;
+               slap_mask_t             flags;
+               AttributeDescription    **ad;
+       }               aci_at = {
+               "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
+                       "NAME 'OpenLDAPaci' "
+                       "DESC 'OpenLDAP access control information (experimental)' "
+                       "EQUALITY OpenLDAPaciMatch "
+                       "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
+                       "USAGE directoryOperation )",
+               SLAP_AT_HIDE,
+               &slap_ad_aci
+       };
+
+       LDAPAttributeType       *at;
+       AttributeType           *sat;
+       int                     rc;
+       const char              *text;
+
+       /* ACI syntax */
+       rc = register_syntax( &aci_syntax_def );
+       if ( rc != 0 ) {
+               return rc;
+       }
+       
+       /* ACI equality rule */
+       rc = register_matching_rule( &aci_mr_def );
+       if ( rc != 0 ) {
+               return rc;
+       }
+
+       /* ACI attribute */
+       at = ldap_str2attributetype( aci_at.desc,
+               &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
+       if ( !at ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "aci_init: AttributeType \"%s\" parse failed: %s %s\n",
+                       aci_at.name, ldap_scherr2str( rc ), text );
+               return rc;
+       }
+
+       rc = at_add( at, 0, &sat, &text );
+       if ( rc != LDAP_SUCCESS ) {
+               ldap_attributetype_free( at );
+               Debug( LDAP_DEBUG_ANY,
+                       "aci_init: AttributeType \"%s\" load failed: %s %s\n",
+                       aci_at.name, scherr2str( rc ), text );
+               return rc;
+       }
+       ldap_memfree( at );
+
+       rc = slap_str2ad( aci_at.name,
+                       aci_at.ad, &text );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "aci_init: unable to find AttributeDescription "
+                       "\"%s\": %d (%s)\n",
+                       aci_at.name, rc, text );
+               return 1;
+       }
+
+       /* install flags */
+       sat->sat_flags |= aci_at.flags;
+
+       return rc;
+}
+
 #ifdef SLAP_DYNACL
-/*
- * FIXME: there is a silly dependence that makes it difficult
- * to move ACIs in a run-time loadable module under the "dynacl" 
- * umbrella, because sets share some helpers with ACIs.
- */
 static int
-dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
+dynacl_aci_parse(
+       const char *fname,
+       int lineno,
+       const char *opts,
+       slap_style_t sty,
+       const char *right,
+       void **privp )
 {
        AttributeDescription    *ad = NULL;
        const char              *text = NULL;
@@ -585,7 +756,7 @@ dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *r
                }
 
        } else {
-               ad = slap_schema.si_ad_aci;
+               ad = slap_ad_aci;
        }
 
        if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
@@ -637,6 +808,11 @@ dynacl_aci_mask(
        char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
 #endif /* LDAP_DEBUG */
 
+       if ( BER_BVISEMPTY( &e->e_nname ) ) {
+               /* no ACIs in the root DSE */
+               return -1;
+       }
+
        /* start out with nothing granted, nothing denied */
        ACL_INIT(tgrant);
        ACL_INIT(tdeny);
@@ -660,7 +836,7 @@ dynacl_aci_mask(
                        }
                }
                
-               Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+               Debug( LDAP_DEBUG_ACL, "        <= aci_mask grant %s deny %s\n",
                          accessmask2str( tgrant, accessmaskbuf, 1 ), 
                          accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
        }
@@ -672,35 +848,41 @@ dynacl_aci_mask(
        if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
                struct berval   parent_ndn;
 
-#if 1
-               /* to solve the chicken'n'egg problem of accessing
-                * the OpenLDAPaci attribute, the direct access
-                * to the entry's attribute is unchecked; however,
-                * further accesses to OpenLDAPaci values in the 
-                * ancestors occur through backend_attribute(), i.e.
-                * with the identity of the operation, requiring
-                * further access checking.  For uniformity, this
-                * makes further requests occur as the rootdn, if
-                * any, i.e. searching for the OpenLDAPaci attribute
-                * is considered an internal search.  If this is not
-                * acceptable, then the same check needs be performed
-                * when accessing the entry's attribute. */
-               Operation       op2 = *op;
-
-               if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
-                       op2.o_dn = op->o_bd->be_rootdn;
-                       op2.o_ndn = op->o_bd->be_rootndn;
-               }
-#endif
-
                dnParent( &e->e_nname, &parent_ndn );
                while ( !BER_BVISEMPTY( &parent_ndn ) ){
                        int             i;
                        BerVarray       bvals = NULL;
                        int             ret, stop;
 
-                       Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
-                       ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+                       /* to solve the chicken'n'egg problem of accessing
+                        * the OpenLDAPaci attribute, the direct access
+                        * to the entry's attribute is unchecked; however,
+                        * further accesses to OpenLDAPaci values in the 
+                        * ancestors occur through backend_attribute(), i.e.
+                        * with the identity of the operation, requiring
+                        * further access checking.  For uniformity, this
+                        * makes further requests occur as the rootdn, if
+                        * any, i.e. searching for the OpenLDAPaci attribute
+                        * is considered an internal search.  If this is not
+                        * acceptable, then the same check needs be performed
+                        * when accessing the entry's attribute. */
+                       struct berval   save_o_dn, save_o_ndn;
+       
+                       if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+                               save_o_dn = op->o_dn;
+                               save_o_ndn = op->o_ndn;
+
+                               op->o_dn = op->o_bd->be_rootdn;
+                               op->o_ndn = op->o_bd->be_rootndn;
+                       }
+
+                       Debug( LDAP_DEBUG_ACL, "        checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
+                       ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+
+                       if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+                               op->o_dn = save_o_dn;
+                               op->o_ndn = save_o_ndn;
+                       }
 
                        switch ( ret ) {
                        case LDAP_SUCCESS :
@@ -777,7 +959,15 @@ static slap_dynacl_t       dynacl_aci = {
 int
 dynacl_aci_init( void )
 {
-       return slap_dynacl_register( &dynacl_aci );
+       int     rc;
+
+       rc = aci_init();
+
+       if ( rc == 0 ) {
+               rc = slap_dynacl_register( &dynacl_aci );
+       }
+       
+       return rc;
 }
 
 #endif /* SLAP_DYNACL */
@@ -849,11 +1039,12 @@ OpenLDAPaciValidatePerms(
 
        for ( i = 0; i < perms->bv_len; ) {
                switch ( perms->bv_val[ i ] ) {
+               case 'x':
+               case 'd':
                case 'c':
                case 's':
                case 'r':
                case 'w':
-               case 'x':
                        break;
 
                default:
@@ -1079,6 +1270,7 @@ OpenLDAPaciNormalizeRights(
 static const struct berval *OpenLDAPaciscopes[] = {
        &aci_bv[ ACI_BV_ENTRY ],
        &aci_bv[ ACI_BV_CHILDREN ],
+       &aci_bv[ ACI_BV_SUBTREE ],
 
        NULL
 };
@@ -1110,7 +1302,7 @@ static const struct berval *OpenLDAPacitypes[] = {
        NULL
 };
 
-int
+static int
 OpenLDAPaciValidate(
        Syntax          *syntax,
        struct berval   *val )
@@ -1202,11 +1394,13 @@ OpenLDAPaciValidate(
                struct berval   ocbv = BER_BVNULL,
                                atbv = BER_BVNULL;
 
-               ocbv.bv_val = strchr( type.bv_val, '/' );
+               ocbv.bv_val = ber_bvchr( &type, '/' );
                if ( ocbv.bv_val != NULL ) {
                        ocbv.bv_val++;
+                       ocbv.bv_len = type.bv_len
+                                       - ( ocbv.bv_val - type.bv_val );
 
-                       atbv.bv_val = strchr( ocbv.bv_val, '/' );
+                       atbv.bv_val = ber_bvchr( &ocbv, '/' );
                        if ( atbv.bv_val != NULL ) {
                                AttributeDescription    *ad = NULL;
                                const char              *text = NULL;
@@ -1221,10 +1415,6 @@ OpenLDAPaciValidate(
                                if ( rc != LDAP_SUCCESS ) {
                                        return LDAP_INVALID_SYNTAX;
                                }
-                               
-                       } else {
-                               ocbv.bv_len = type.bv_len
-                                       - ( ocbv.bv_val - type.bv_val );
                        }
 
                        if ( oc_bvfind( &ocbv ) == NULL ) {
@@ -1261,7 +1451,7 @@ OpenLDAPaciPrettyNormal(
                        subject = BER_BVNULL,
                        nsubject = BER_BVNULL;
        int             idx,
-                       rc,
+                       rc = LDAP_SUCCESS,
                        freesubject = 0,
                        freetype = 0;
        char            *ptr;
@@ -1353,7 +1543,7 @@ OpenLDAPaciPrettyNormal(
                        struct berval   ocbv = BER_BVNULL,
                                        atbv = BER_BVNULL;
 
-                       ocbv.bv_val = strchr( type.bv_val, '/' );
+                       ocbv.bv_val = ber_bvchr( &type, '/' );
                        if ( ocbv.bv_val != NULL ) {
                                ObjectClass             *oc = NULL;
                                AttributeDescription    *ad = NULL;
@@ -1364,8 +1554,9 @@ OpenLDAPaciPrettyNormal(
                                bv.bv_len = ntype.bv_len;
 
                                ocbv.bv_val++;
+                               ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
 
-                               atbv.bv_val = strchr( ocbv.bv_val, '/' );
+                               atbv.bv_val = ber_bvchr( &ocbv, '/' );
                                if ( atbv.bv_val != NULL ) {
                                        atbv.bv_val++;
                                        atbv.bv_len = type.bv_len
@@ -1379,13 +1570,10 @@ OpenLDAPaciPrettyNormal(
                                        }
 
                                        bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
-                                       
-                               } else {
-                                       ocbv.bv_len = type.bv_len
-                                               - ( ocbv.bv_val - type.bv_val );
                                }
 
-                               if ( oc_bvfind( &ocbv ) == NULL ) {
+                               oc = oc_bvfind( &ocbv );
+                               if ( oc == NULL ) {
                                        rc = LDAP_INVALID_SYNTAX;
                                        goto cleanup;
                                }
@@ -1476,7 +1664,7 @@ cleanup:;
        return rc;
 }
 
-int
+static int
 OpenLDAPaciPretty(
        Syntax          *syntax,
        struct berval   *val,
@@ -1486,7 +1674,7 @@ OpenLDAPaciPretty(
        return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
 }
 
-int
+static int
 OpenLDAPaciNormalize(
        slap_mask_t     use,
        Syntax          *syntax,
@@ -1498,5 +1686,17 @@ OpenLDAPaciNormalize(
        return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
 }
 
+#if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
+/*
+ * FIXME: need config and Makefile.am code to ease building
+ * as dynamic module
+ */
+int
+init_module( int argc, char *argv[] )
+{
+       return slap_dynacl_register();
+}
+#endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
+
 #endif /* SLAPD_ACI_ENABLED */