]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/acl.c
Read config tree from back-ldif
[openldap] / servers / slapd / acl.c
index ca88758241078fb25162c6ddbc8a1b6186083410..6babdba3390e2b99a823f28f490c83677cf7fec6 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2004 The OpenLDAP Foundation.
+ * Copyright 1998-2005 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,6 +48,8 @@
 static struct berval 
        aci_bv_entry            = BER_BVC("entry"),
        aci_bv_children         = BER_BVC("children"),
+       aci_bv_onelevel         = BER_BVC("onelevel"),
+       aci_bv_subtree          = BER_BVC("subtree"),
        aci_bv_br_entry         = BER_BVC("[entry]"),
        aci_bv_br_all           = BER_BVC("[all]"),
        aci_bv_access_id        = BER_BVC("access-id"),
@@ -75,6 +77,11 @@ static struct berval
        aci_bv_role_attr        = BER_BVC(SLAPD_ROLE_ATTR),
        aci_bv_set_attr         = BER_BVC(SLAPD_ACI_SET_ATTR);
 
+typedef enum slap_aci_scope_t {
+       SLAP_ACI_SCOPE_ENTRY            = 0x1,
+       SLAP_ACI_SCOPE_CHILDREN         = 0x2,
+       SLAP_ACI_SCOPE_SUBTREE          = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
+} slap_aci_scope_t;
 
 static AccessControl * acl_get(
        AccessControl *ac, int *count,
@@ -104,7 +111,7 @@ static int aci_mask(
        regmatch_t *matches,
        slap_access_t *grant,
        slap_access_t *deny,
-       struct berval *scope);
+       slap_aci_scope_t scope);
 #endif
 
 static int     regex_matches(
@@ -246,7 +253,7 @@ access_allowed_mask(
                    "<= root access granted\n",
                        0, 0, 0 );
                if ( maskp ) {
-                       mask = ACL_LVL_WRITE;
+                       mask = ACL_LVL_MANAGE;
                }
 
                goto done;
@@ -387,7 +394,7 @@ vd_access:
                "=> access_allowed: %s access %s by %s\n",
                access2str( access ),
                ACL_GRANT(mask, access) ? "granted" : "denied",
-               accessmask2str( mask, accessmaskbuf ) );
+               accessmask2str( mask, accessmaskbuf, 1 ) );
 
        ret = ACL_GRANT(mask, access);
 
@@ -534,8 +541,11 @@ acl_get(
                                Debug( LDAP_DEBUG_ACL,
                                        "acl_get: valpat %s\n",
                                        a->acl_attrval.bv_val, 0, 0 );
-                               if (regexec(&a->acl_attrval_re, val->bv_val, 0, NULL, 0))
+                               if ( regexec( &a->acl_attrval_re, val->bv_val, 0, NULL, 0 ) )
+                               {
                                        continue;
+                               }
+
                        } else {
                                int match = 0;
                                const char *text;
@@ -668,7 +678,7 @@ acl_mask(
                "=> acl_mask: to %s by \"%s\", (%s) \n",
                val ? "value" : "all values",
                op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
-               accessmask2str( *mask, accessmaskbuf ) );
+               accessmask2str( *mask, accessmaskbuf, 1) );
 
 
        if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
@@ -688,7 +698,7 @@ acl_mask(
                ACL_INVALIDATE( modmask );
 
                /* AND <who> clauses */
-               if ( b->a_dn_pat.bv_len != 0 ) {
+               if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
                        Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
                                b->a_dn_pat.bv_val, 0, 0);
                        /*
@@ -696,17 +706,23 @@ acl_mask(
                         * user is bound as somebody in the same namespace as
                         * the entry, OR the given dn matches the dn pattern
                         */
-                       if ( bvmatch( &b->a_dn_pat, &aci_bv_anonymous ) ) {
+                       /*
+                        * NOTE: styles "anonymous", "users" and "self" 
+                        * have been moved to enum slap_style_t, whose 
+                        * value is set in a_dn_style; however, the string
+                        * is maintaned in a_dn_pat.
+                        */
+                       if ( b->a_dn_style == ACL_STYLE_ANONYMOUS ) {
                                if ( op->o_ndn.bv_len != 0 ) {
                                        continue;
                                }
 
-                       } else if ( bvmatch( &b->a_dn_pat, &aci_bv_users ) ) {
+                       } else if ( b->a_dn_style == ACL_STYLE_USERS ) {
                                if ( op->o_ndn.bv_len == 0 ) {
                                        continue;
                                }
 
-                       } else if ( bvmatch( &b->a_dn_pat, &aci_bv_self ) ) {
+                       } else if ( b->a_dn_style == ACL_STYLE_SELF ) {
                                if ( op->o_ndn.bv_len == 0 ) {
                                        continue;
                                }
@@ -897,7 +913,7 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_sockurl_pat.bv_len ) {
+               if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
                        if ( ! op->o_conn->c_listener ) {
                                continue;
                        }
@@ -938,7 +954,7 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_domain_pat.bv_len ) {
+               if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
                        if ( !op->o_conn->c_peer_domain.bv_val ) {
                                continue;
                        }
@@ -993,7 +1009,7 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_peername_pat.bv_len ) {
+               if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
                        if ( !op->o_conn->c_peer_name.bv_val ) {
                                continue;
                        }
@@ -1101,8 +1117,8 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_sockname_pat.bv_len ) {
-                       if ( !op->o_conn->c_sock_name.bv_val ) {
+               if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
+                       if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
                                continue;
                        }
                        Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
@@ -1217,7 +1233,7 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_group_pat.bv_len ) {
+               if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
                        struct berval bv;
                        struct berval ndn = BER_BVNULL;
                        int rc;
@@ -1309,9 +1325,9 @@ dn_match_cleanup:;
                        }
                }
 
-               if ( b->a_set_pat.bv_len != 0 ) {
-                       struct berval bv;
-                       char buf[ACL_BUF_SIZE];
+               if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+                       struct berval   bv;
+                       char            buf[ACL_BUF_SIZE];
 
                        if ( b->a_set_style == ACL_STYLE_EXPAND ) {
                                int             tmp_nmatch;
@@ -1319,7 +1335,7 @@ dn_match_cleanup:;
                                                *tmp_matchesp = tmp_matches;
                                int             rc = 0;
 
-                               bv.bv_len = sizeof(buf) - 1;
+                               bv.bv_len = sizeof( buf ) - 1;
                                bv.bv_val = buf;
 
                                rc = 0;
@@ -1366,10 +1382,11 @@ dn_match_cleanup:;
                                        continue;
                                }
 
-                       }else{
+                       } else {
                                bv = b->a_set_pat;
                        }
-                       if (aci_match_set( &bv, op, e, 0 ) == 0) {
+                       
+                       if ( aci_match_set( &bv, op, e, 0 ) == 0 ) {
                                continue;
                        }
                }
@@ -1409,20 +1426,85 @@ dn_match_cleanup:;
                        }
                }
 
+#ifdef SLAP_DYNACL
+               if ( b->a_dynacl ) {
+                       slap_dynacl_t   *da;
+                       slap_access_t   tgrant, tdeny;
+
+                       /* this case works different from the others above.
+                        * since aci's themselves give permissions, we need
+                        * to first check b->a_access_mask, the ACL's access level.
+                        */
+                       if ( BER_BVISEMPTY( &e->e_nname ) ) {
+                               /* no ACIs in the root DSE */
+                               continue;
+                       }
+
+                       /* first check if the right being requested
+                        * is allowed by the ACL clause.
+                        */
+                       if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
+                               continue;
+                       }
+
+                       /* start out with nothing granted, nothing denied */
+                       ACL_INIT(tgrant);
+                       ACL_INIT(tdeny);
+
+                       for ( da = b->a_dynacl; da; da = da->da_next ) {
+                               slap_access_t   grant, deny;
+
+                               (void)( *da->da_mask )( da->da_private, op, e, desc, val, nmatch, matches, &grant, &deny );
+
+                               tgrant |= grant;
+                               tdeny |= deny;
+                       }
+
+                       /* remove anything that the ACL clause does not allow */
+                       tgrant &= b->a_access_mask & ACL_PRIV_MASK;
+                       tdeny &= ACL_PRIV_MASK;
+
+                       /* see if we have anything to contribute */
+                       if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
+                               continue;
+                       }
+
+                       /* this could be improved by changing acl_mask so that it can deal with
+                        * by clauses that return grant/deny pairs.  Right now, it does either
+                        * additive or subtractive rights, but not both at the same time.  So,
+                        * we need to combine the grant/deny pair into a single rights mask in
+                        * a smart way:  if either grant or deny is "empty", then we use the
+                        * opposite as is, otherwise we remove any denied rights from the grant
+                        * rights mask and construct an additive mask.
+                        */
+                       if (ACL_IS_INVALID(tdeny)) {
+                               modmask = tgrant | ACL_PRIV_ADDITIVE;
+
+                       } else if (ACL_IS_INVALID(tgrant)) {
+                               modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
+
+                       } else {
+                               modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
+                       }
+
+               } else
+#else /* !SLAP_DYNACL */
+
 #ifdef SLAPD_ACI_ENABLED
                if ( b->a_aci_at != NULL ) {
                        Attribute       *at;
-                       slap_access_t grant, deny, tgrant, tdeny;
-                       struct berval parent_ndn, old_parent_ndn;
-                       BerVarray bvals = NULL;
-                       int ret,stop;
+                       slap_access_t   grant, deny, tgrant, tdeny;
+                       struct berval   parent_ndn,
+                                       old_parent_ndn = BER_BVNULL;
+                       BerVarray       bvals = NULL;
+                       int             ret, stop;
 
                        /* this case works different from the others above.
                         * since aci's themselves give permissions, we need
                         * to first check b->a_access_mask, the ACL's access level.
                         */
 
-                       if ( e->e_nname.bv_len == 0 ) {
+                       if ( BER_BVISEMPTY( &e->e_nname ) ) {
                                /* no ACIs in the root DSE */
                                continue;
                        }
@@ -1454,24 +1536,24 @@ dn_match_cleanup:;
                                                e, desc, val,
                                                &at->a_nvals[i],
                                                nmatch, matches,
-                                               &grant, &deny,  &aci_bv_entry ) != 0)
+                                               &grant, &deny, SLAP_ACI_SCOPE_ENTRY ) != 0)
                                        {
                                                tgrant |= grant;
                                                tdeny |= deny;
                                        }
                                }
                                Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
-                                         accessmask2str(tgrant,accessmaskbuf), 
-                                         accessmask2str(tdeny, accessmaskbuf1), 0);
+                                         accessmask2str(tgrant,accessmaskbuf, 1), 
+                                         accessmask2str(tdeny, accessmaskbuf1, 1), 0);
 
                        }
                        /* If the entry level aci didn't contain anything valid for the 
                         * current operation, climb up the tree and evaluate the
                         * acis with scope set to subtree
                         */
-                       if( (tgrant == ACL_PRIV_NONE) && (tdeny == ACL_PRIV_NONE) ){
+                       if ( (tgrant == ACL_PRIV_NONE) && (tdeny == ACL_PRIV_NONE) ) {
                                dnParent(&(e->e_nname), &parent_ndn);
-                               while ( parent_ndn.bv_val != old_parent_ndn.bv_val ){
+                               while ( parent_ndn.bv_val != old_parent_ndn.bv_val ) {
                                        old_parent_ndn = parent_ndn;
                                        Debug(LDAP_DEBUG_ACL, "checking ACI of %s\n", parent_ndn.bv_val, 0, 0);
                                        ret = backend_attribute(op, NULL, &parent_ndn, b->a_aci_at, &bvals, ACL_AUTH);
@@ -1490,7 +1572,7 @@ dn_match_cleanup:;
 #endif
                                                        if (aci_mask(op, e, desc, val, &bvals[i],
                                                                        nmatch, matches,
-                                                                       &grant, &deny, &aci_bv_children) != 0)
+                                                                       &grant, &deny, SLAP_ACI_SCOPE_CHILDREN ) != 0 )
                                                        {
                                                                tgrant |= grant;
                                                                tdeny |= deny;
@@ -1502,8 +1584,8 @@ dn_match_cleanup:;
                                                                }
                                                        }
                                                        Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
-                                                               accessmask2str(tgrant,accessmaskbuf),
-                                                               accessmask2str(tdeny, accessmaskbuf1), 0);
+                                                               accessmask2str(tgrant,accessmaskbuf, 1),
+                                                               accessmask2str(tdeny, accessmaskbuf1, 1), 0);
                                                }
                                                break;
 
@@ -1561,14 +1643,15 @@ dn_match_cleanup:;
                        }
 
                } else
-#endif
+#endif /* SLAPD_ACI_ENABLED */
+#endif /* !SLAP_DYNACL */
                {
                        modmask = b->a_access_mask;
                }
 
                Debug( LDAP_DEBUG_ACL,
                        "<= acl_mask: [%d] applying %s (%s)\n",
-                       i, accessmask2str( modmask, accessmaskbuf ), 
+                       i, accessmask2str( modmask, accessmaskbuf, 1 ), 
                        b->a_type == ACL_CONTINUE
                                ? "continue"
                                : b->a_type == ACL_BREAK
@@ -1598,7 +1681,7 @@ dn_match_cleanup:;
 
                Debug( LDAP_DEBUG_ACL,
                        "<= acl_mask: [%d] mask: %s\n",
-                       i, accessmask2str(*mask, accessmaskbuf), 0 );
+                       i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
 
                if( b->a_type == ACL_CONTINUE ) {
                        continue;
@@ -1616,7 +1699,7 @@ dn_match_cleanup:;
 
        Debug( LDAP_DEBUG_ACL,
                "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
-               accessmask2str(*mask, accessmaskbuf), 0, 0 );
+               accessmask2str(*mask, accessmaskbuf, 1), 0, 0 );
        return ACL_STOP;
 }
 
@@ -1661,7 +1744,9 @@ acl_check_modlist(
                Debug( LDAP_DEBUG_ACL,
                        "=> access_allowed: backend default %s access %s to \"%s\"\n",
                        access2str( ACL_WRITE ),
-                       op->o_bd->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val );
+                       op->o_bd->be_dfltaccess >= ACL_WRITE
+                               ? "granted" : "denied",
+                       op->o_dn.bv_val );
                ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
                goto done;
        }
@@ -1755,40 +1840,45 @@ done:
 
 static int
 aci_get_part(
-       struct berval *list,
-       int ix,
-       char sep,
-       struct berval *bv )
+       struct berval   *list,
+       int             ix,
+       char            sep,
+       struct berval   *bv )
 {
-       int len;
-       char *p;
+       int     len;
+       char    *p;
 
-       if (bv) {
+       if ( bv ) {
                BER_BVZERO( bv );
        }
        len = list->bv_len;
        p = list->bv_val;
-       while (len >= 0 && --ix >= 0) {
-               while (--len >= 0 && *p++ != sep) ;
+       while ( len >= 0 && --ix >= 0 ) {
+               while ( --len >= 0 && *p++ != sep )
+                       ;
        }
-       while (len >= 0 && *p == ' ') {
+       while ( len >= 0 && *p == ' ' ) {
                len--;
                p++;
        }
-       if (len < 0)
-               return(-1);
+       if ( len < 0 ) {
+               return -1;
+       }
 
-       if (!bv)
-               return(0);
+       if ( !bv ) {
+               return 0;
+       }
 
        bv->bv_val = p;
-       while (--len >= 0 && *p != sep) {
+       while ( --len >= 0 && *p != sep ) {
                bv->bv_len++;
                p++;
        }
-       while (bv->bv_len > 0 && *--p == ' ')
+       while ( bv->bv_len > 0 && *--p == ' ' ) {
                bv->bv_len--;
-       return(bv->bv_len);
+       }
+       
+       return bv->bv_len;
 }
 
 typedef struct aci_set_gather_t {
@@ -1940,27 +2030,18 @@ aci_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *de
        
        p.cookie = cookie;
        
+       op2.o_hdr = cp->op->o_hdr;
        op2.o_tag = LDAP_REQ_SEARCH;
-       op2.o_protocol = LDAP_VERSION3;
        op2.o_ndn = op2.o_bd->be_rootndn;
        op2.o_callback = &cb;
        op2.o_time = slap_get_time();
        op2.o_do_not_cache = 1;
        op2.o_is_auth_check = 0;
-       op2.o_threadctx = cp->op->o_threadctx;
-       op2.o_tmpmemctx = cp->op->o_tmpmemctx;
-       op2.o_tmpmfuncs = cp->op->o_tmpmfuncs;
-#ifdef LDAP_SLAPI
-       op2.o_pb = cp->op->o_pb;
-#endif
-       op2.o_conn = cp->op->o_conn;
-       op2.o_connid = cp->op->o_connid;
        ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->op->o_tmpmemctx );
        op2.ors_slimit = SLAP_NO_LIMIT;
        op2.ors_tlimit = SLAP_NO_LIMIT;
        op2.ors_attrs = anlistp;
        op2.ors_attrsonly = 0;
-       op2.o_sync_slog_size = -1;
 
        cb.sc_private = &p;
 
@@ -2035,8 +2116,9 @@ aci_match_set (
        int             rc = 0;
        AciSetCookie    cookie;
 
-       if (setref == 0) {
+       if ( setref == 0 ) {
                ber_dupbv_x( &set, subj, op->o_tmpmemctx );
+
        } else {
                struct berval           subjdn, ndn = BER_BVNULL;
                struct berval           setat;
@@ -2046,7 +2128,7 @@ aci_match_set (
 
                /* format of string is "entry/setAttrName" */
                if ( aci_get_part( subj, 0, '/', &subjdn ) < 0 ) {
-                       return(0);
+                       return 0;
                }
 
                if ( aci_get_part( subj, 1, '/', &setat ) < 0 ) {
@@ -2318,77 +2400,192 @@ aci_mask(
        regmatch_t              *matches,
        slap_access_t           *grant,
        slap_access_t           *deny,
-       struct berval           *scope
+       slap_aci_scope_t        asserted_scope
 )
 {
-    struct berval bv, perms, sdn;
-       int rc;
+       struct berval           bv, scope, perms, type, sdn;
+       int                     rc;
                
 
-       assert( desc->ad_cname.bv_val != NULL );
+       assert( !BER_BVISNULL( &desc->ad_cname ) );
 
        /* parse an aci of the form:
-               oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
+               oid # scope # action;rights;attr;rights;attr 
+                       $ action;rights;attr;rights;attr # type # subject
+
+          [NOTE: the following comment is very outdated,
+          as the draft version it refers to (Ando, 2004-11-20)].
 
           See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
           a full description of the format for this attribute.
           Differences: "this" in the draft is "self" here, and
-          "self" and "public" is in the position of dnType.
+          "self" and "public" is in the position of type.
+
+          <scope> = {entry|children|subtree}
+          <type> = {public|users|access-id|subtree|onelevel|children|
+                    self|dnattr|group|role|set|set-ref}
 
-          For now, this routine only supports scope=entry.
+          This routine now supports scope={ENTRY,CHILDREN}
+          with the semantics:
+            - ENTRY applies to "entry" and "subtree";
+            - CHILDREN aplies to "children" and "subtree"
         */
+
        /* check that the aci has all 5 components */
-       if (aci_get_part(aci, 4, '#', NULL) < 0)
-               return(0);
+       if ( aci_get_part( aci, 4, '#', NULL ) < 0 ) {
+               return 0;
+       }
 
        /* check that the aci family is supported */
-       if (aci_get_part(aci, 0, '#', &bv) < 0)
-               return(0);
+       if ( aci_get_part( aci, 0, '#', &bv ) < 0 ) {
+               return 0;
+       }
 
        /* check that the scope matches */
-       if (aci_get_part(aci, 1, '#', &bv) < 0
-               || ber_bvstrcasecmp( scope, &bv ) != 0)
-       {
-               return(0);
+       if ( aci_get_part( aci, 1, '#', &scope ) < 0 ) {
+               return 0;
+       }
+
+       /* note: scope can be either ENTRY or CHILDREN;
+        * they respectively match "entry" and "children" in bv
+        * both match "subtree" */
+       switch ( asserted_scope ) {
+       case SLAP_ACI_SCOPE_ENTRY:
+               if ( ber_bvstrcasecmp( &scope, &aci_bv_entry ) != 0
+                               && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
+               {
+                       return 0;
+               }
+               break;
+
+       case SLAP_ACI_SCOPE_CHILDREN:
+               if ( ber_bvstrcasecmp( &scope, &aci_bv_children ) != 0
+                               && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
+               {
+                       return 0;
+               }
+               break;
+
+       default:
+               return 0;
        }
 
        /* get the list of permissions clauses, bail if empty */
-       if (aci_get_part(aci, 2, '#', &perms) <= 0)
-               return(0);
+       if ( aci_get_part( aci, 2, '#', &perms ) <= 0 ) {
+               return 0;
+       }
 
        /* check if any permissions allow desired access */
-       if (aci_list_get_rights(&perms, &desc->ad_cname, val, grant, deny) == 0)
-               return(0);
+       if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
+               return 0;
+       }
 
        /* see if we have a DN match */
-       if (aci_get_part(aci, 3, '#', &bv) < 0)
-               return(0);
+       if ( aci_get_part( aci, 3, '#', &type ) < 0 ) {
+               return 0;
+       }
 
-       if (aci_get_part(aci, 4, '#', &sdn) < 0)
-               return(0);
+       /* see if we have a public (i.e. anonymous) access */
+       if ( ber_bvstrcasecmp( &aci_bv_public, &type ) == 0 ) {
+               return 1;
+       }
+       
+       /* otherwise require an identity */
+       if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
+               return 0;
+       }
 
-       if (ber_bvstrcasecmp( &aci_bv_access_id, &bv ) == 0) {
+       /* see if we have a users access */
+       if ( ber_bvstrcasecmp( &aci_bv_users, &type ) == 0 ) {
+               return 1;
+       }
+       
+       /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
+        * just grab all the berval up to its end (ITS#3303).
+        * NOTE: the problem could be solved by providing the DN with
+        * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 
+        * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
+#if 0
+       if ( aci_get_part( aci, 4, '#', &sdn ) < 0 ) {
+               return 0;
+       }
+#endif
+       sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
+       sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
+
+       if ( ber_bvstrcasecmp( &aci_bv_access_id, &type ) == 0 ) {
                struct berval ndn;
-               rc = 0;
-               if ( dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
-                       if ( dn_match( &op->o_ndn, &ndn ) ) {
-                               rc = 1;
-                       }
-                       slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+               
+               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 );
+
+               return rc;
+
+       } else if ( ber_bvstrcasecmp( &aci_bv_subtree, &type ) == 0 ) {
+               struct berval ndn;
+               
+               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+               if ( rc != LDAP_SUCCESS ) {
+                       return 0;
                }
-               return (rc);
 
-       } else if (ber_bvstrcasecmp( &aci_bv_public, &bv ) == 0) {
-               return(1);
+               if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
+                       rc = 1;
+               }
+               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
 
-       } else if (ber_bvstrcasecmp( &aci_bv_self, &bv ) == 0) {
-               if (dn_match(&op->o_ndn, &e->e_nname))
-                       return(1);
+               return rc;
 
-       } else if (ber_bvstrcasecmp( &aci_bv_dnattr, &bv ) == 0) {
-               Attribute *at;
-               AttributeDescription *ad = NULL;
-               const char *text;
+       } else if ( ber_bvstrcasecmp( &aci_bv_onelevel, &type ) == 0 ) {
+               struct berval ndn, pndn;
+               
+               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+               if ( rc != LDAP_SUCCESS ) {
+                       return 0;
+               }
+
+               dnParent( &ndn, &pndn );
+
+               if ( dn_match( &op->o_ndn, &pndn ) ) {
+                       rc = 1;
+               }
+               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+               return rc;
+
+       } else if ( ber_bvstrcasecmp( &aci_bv_children, &type ) == 0 ) {
+               struct berval ndn;
+               
+               rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
+               if ( rc != LDAP_SUCCESS ) {
+                       return 0;
+               }
+
+               if ( !dn_match( &op->o_ndn, &ndn )
+                               && dnIsSuffix( &op->o_ndn, &ndn ) )
+               {
+                       rc = 1;
+               }
+               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+
+               return rc;
+
+       } else if ( ber_bvstrcasecmp( &aci_bv_self, &type ) == 0 ) {
+               if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
+                       return 1;
+               }
+
+       } else if ( ber_bvstrcasecmp( &aci_bv_dnattr, &type ) == 0 ) {
+               Attribute               *at;
+               AttributeDescription    *ad = NULL;
+               const char              *text;
 
                rc = slap_bv2ad( &sdn, &ad, &text );
 
@@ -2398,17 +2595,15 @@ aci_mask(
 
                rc = 0;
 
-               bv = op->o_ndn;
-
-               for(at = attrs_find( e->e_attrs, ad );
-                       at != NULL;
-                       at = attrs_find( at->a_next, ad ) )
+               for ( at = attrs_find( e->e_attrs, ad );
+                               at != NULL;
+                               at = attrs_find( at->a_next, ad ) )
                {
-                       if (value_find_ex( ad,
+                       if ( value_find_ex( ad,
                                SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
                                        SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
                                at->a_nvals,
-                               &bv, op->o_tmpmemctx) == 0 )
+                               &op->o_ndn, op->o_tmpmemctx ) == 0 )
                        {
                                rc = 1;
                                break;
@@ -2417,32 +2612,333 @@ aci_mask(
 
                return rc;
 
+       } else if ( ber_bvstrcasecmp( &aci_bv_group, &type ) == 0 ) {
+               if ( aci_group_member( &sdn, &aci_bv_group_class,
+                               &aci_bv_group_attr, op, e, nmatch, matches ) )
+               {
+                       return 1;
+               }
+
+       } else if ( ber_bvstrcasecmp( &aci_bv_role, &type ) == 0 ) {
+               if ( aci_group_member( &sdn, &aci_bv_role_class,
+                               &aci_bv_role_attr, op, e, nmatch, matches ) )
+               {
+                       return 1;
+               }
 
-       } else if (ber_bvstrcasecmp( &aci_bv_group, &bv ) == 0) {
-               if (aci_group_member(&sdn, &aci_bv_group_class,
-                               &aci_bv_group_attr, op, e, nmatch, matches))
-                       return(1);
+       } else if ( ber_bvstrcasecmp( &aci_bv_set, &type ) == 0 ) {
+               if ( aci_match_set( &sdn, op, e, 0 ) ) {
+                       return 1;
+               }
 
-       } else if (ber_bvstrcasecmp( &aci_bv_role, &bv ) == 0) {
-               if (aci_group_member(&sdn, &aci_bv_role_class,
-                               &aci_bv_role_attr, op, e, nmatch, matches))
-                       return(1);
+       } else if ( ber_bvstrcasecmp( &aci_bv_set_ref, &type ) == 0 ) {
+               if ( aci_match_set( &sdn, op, e, 1 ) ) {
+                       return 1;
+               }
+       }
 
-       } else if (ber_bvstrcasecmp( &aci_bv_set, &bv ) == 0) {
-               if (aci_match_set(&sdn, op, e, 0))
-                       return(1);
+       return 0;
+}
 
-       } else if (ber_bvstrcasecmp( &aci_bv_set_ref, &bv ) == 0) {
-               if (aci_match_set(&sdn, op, e, 1))
-                       return(1);
+#ifdef SLAP_DYNACL
+static int
+dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
+{
+       AttributeDescription    *ad = NULL;
+       const char              *text = NULL;
 
+       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+               fprintf( stderr, "%s: line %d: "
+                       "inappropriate style \"%s\" in \"aci\" by clause\n",
+                       fname, lineno, sty );
+               return -1;
        }
 
-       return(0);
+       if ( right != NULL && *right != '\0' ) {
+               if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
+                       fprintf( stderr,
+                               "%s: line %d: aci \"%s\": %s\n",
+                               fname, lineno, right, text );
+                       return -1;
+               }
+
+       } else {
+               ad = slap_schema.si_ad_aci;
+       }
+
+       if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
+               fprintf( stderr, "%s: line %d: "
+                       "aci \"%s\": inappropriate syntax: %s\n",
+                       fname, lineno, right,
+                       ad->ad_type->sat_syntax_oid );
+               return -1;
+       }
+
+       *privp = (void *)ad;
+
+       return 0;
+}
+
+static int
+dynacl_aci_unparse( void *priv, struct berval *bv )
+{
+       AttributeDescription    *ad = ( AttributeDescription * )priv;
+       char *ptr;
+
+       assert( ad );
+
+       bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
+       ptr = lutil_strcopy( bv->bv_val, " aci=" );
+       ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
+       bv->bv_len = ptr - bv->bv_val;
+
+       return 0;
+}
+
+
+static int
+dynacl_aci_mask(
+               void                    *priv,
+               Operation               *op,
+               Entry                   *e,
+               AttributeDescription    *desc,
+               struct berval           *val,
+               int                     nmatch,
+               regmatch_t              *matches,
+               slap_access_t           *grantp,
+               slap_access_t           *denyp )
+{
+       AttributeDescription    *ad = ( AttributeDescription * )priv;
+       Attribute               *at;
+       slap_access_t           tgrant, tdeny, grant, deny;
+#ifdef LDAP_DEBUG
+       char                    accessmaskbuf[ACCESSMASK_MAXLEN];
+       char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
+#endif /* LDAP_DEBUG */
+
+       /* start out with nothing granted, nothing denied */
+       ACL_INIT(tgrant);
+       ACL_INIT(tdeny);
+
+       /* get the aci attribute */
+       at = attr_find( e->e_attrs, ad );
+       if ( at != NULL ) {
+               int             i;
+
+               /* the aci is an multi-valued attribute.  The
+                * rights are determined by OR'ing the individual
+                * rights given by the acis.
+                */
+               for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
+                       if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
+                                       nmatch, matches, &grant, &deny,
+                                       SLAP_ACI_SCOPE_ENTRY ) != 0 )
+                       {
+                               tgrant |= grant;
+                               tdeny |= deny;
+                       }
+               }
+               
+               Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+                         accessmask2str( tgrant, accessmaskbuf, 1 ), 
+                         accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
+       }
+
+       /* If the entry level aci didn't contain anything valid for the 
+        * current operation, climb up the tree and evaluate the
+        * acis with scope set to subtree
+        */
+       if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
+               struct berval   parent_ndn;
+               struct berval   old_parent_ndn = BER_BVNULL;
+
+#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 ( parent_ndn.bv_val != old_parent_ndn.bv_val ){
+                       int             i;
+                       BerVarray       bvals = NULL;
+                       int             ret, stop;
+
+                       old_parent_ndn = parent_ndn;
+                       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 );
+
+                       switch ( ret ) {
+                       case LDAP_SUCCESS :
+                               stop = 0;
+                               if ( !bvals ) {
+                                       break;
+                               }
+
+                               for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++) {
+                                       if ( aci_mask( op, e, desc, val,
+                                                       &bvals[i],
+                                                       nmatch, matches,
+                                                       &grant, &deny,
+                                                       SLAP_ACI_SCOPE_CHILDREN ) != 0 )
+                                       {
+                                               tgrant |= grant;
+                                               tdeny |= deny;
+                                               /* evaluation stops as soon as either a "deny" or a 
+                                                * "grant" directive matches.
+                                                */
+                                               if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
+                                                       stop = 1;
+                                               }
+                                       }
+                                       Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
+                                               accessmask2str( tgrant, accessmaskbuf, 1 ),
+                                               accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
+                               }
+                               break;
+
+                       case LDAP_NO_SUCH_ATTRIBUTE:
+                               /* just go on if the aci-Attribute is not present in
+                                * the current entry 
+                                */
+                               Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
+                               stop = 0;
+                               break;
+
+                       case LDAP_NO_SUCH_OBJECT:
+                               /* We have reached the base object */
+                               Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
+                               stop = 1;
+                               break;
+
+                       default:
+                               stop = 1;
+                               break;
+                       }
+
+                       if ( stop ) {
+                               break;
+                       }
+                       dnParent( &old_parent_ndn, &parent_ndn );
+               }
+       }
+
+       *grantp = tgrant;
+       *denyp = tdeny;
+
+       return 0;
 }
 
+/* need to register this at some point */
+static slap_dynacl_t   dynacl_aci = {
+       "aci",
+       dynacl_aci_parse,
+       dynacl_aci_unparse,
+       dynacl_aci_mask,
+       NULL,
+       NULL,
+       NULL
+};
+
+#endif /* SLAP_DYNACL */
+
 #endif /* SLAPD_ACI_ENABLED */
 
+#ifdef SLAP_DYNACL
+
+/*
+ * dynamic ACL infrastructure
+ */
+static slap_dynacl_t   *da_list = NULL;
+
+int
+slap_dynacl_register( slap_dynacl_t *da )
+{
+       slap_dynacl_t   *tmp;
+
+       for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
+               if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
+                       break;
+               }
+       }
+
+       if ( tmp != NULL ) {
+               return -1;
+       }
+       
+       if ( da->da_mask == NULL ) {
+               return -1;
+       }
+       
+       da->da_private = NULL;
+       da->da_next = da_list;
+       da_list = da;
+
+       return 0;
+}
+
+static slap_dynacl_t *
+slap_dynacl_next( slap_dynacl_t *da )
+{
+       if ( da ) {
+               return da->da_next;
+       }
+       return da_list;
+}
+
+slap_dynacl_t *
+slap_dynacl_get( const char *name )
+{
+       slap_dynacl_t   *da;
+
+       for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
+               if ( strcasecmp( da->da_name, name ) == 0 ) {
+                       break;
+               }
+       }
+
+       return da;
+}
+#endif /* SLAP_DYNACL */
+
+int
+acl_init( void )
+{
+       int             i, rc;
+#ifdef SLAP_DYNACL
+       slap_dynacl_t   *known_dynacl[] = {
+#ifdef SLAPD_ACI_ENABLED
+               &dynacl_aci,
+#endif  /* SLAPD_ACI_ENABLED */
+               NULL
+       };
+
+       for ( i = 0; known_dynacl[ i ]; i++ ) {
+               rc = slap_dynacl_register( known_dynacl[ i ] ); 
+               if ( rc ) {
+                       return rc;
+               }
+       }
+#endif /* SLAP_DYNACL */
+
+       return 0;
+}
+
 static int
 string_expand(
        struct berval   *bv,