]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#6198 ACL: separate <who> checking
authorOndřej Kuzník <ondra@mistotebe.net>
Mon, 27 Apr 2026 15:24:09 +0000 (16:24 +0100)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 30 Apr 2026 16:41:28 +0000 (16:41 +0000)
servers/slapd/acl.c
servers/slapd/aclparse.c
servers/slapd/proto-slap.h

index 863a7c81cf611e6488251489048199e254344365..36d526f6d4922001a8b404efd27e6cec9c208a50 100644 (file)
@@ -800,7 +800,7 @@ acl_mask_dn(
                        MATCHES_MEMSET( &tmp_matches );
                        tmp_data = &tmp_matches.dn_data[0];
 
-                       if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+                       if ( !a || a->acl_attrval_style == ACL_STYLE_REGEX )
                                tmp_matchesp = matches;
                        else switch ( a->acl_dn_style ) {
                        case ACL_STYLE_REGEX:
@@ -808,7 +808,8 @@ acl_mask_dn(
                                        tmp_matchesp = matches; 
                                        break;
                                }
-                       /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+                       /* FALLTHRU */
+                       /* applies also to ACL_STYLE_REGEX when pattern is "*" */
 
                        case ACL_STYLE_BASE:
                                tmp_data[0].rm_so = 0;
@@ -867,7 +868,7 @@ acl_mask_dn(
                        bv.bv_val = buf;
 
                        /* Expand value regex */
-                       if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+                       if ( !a || a->acl_attrval_style == ACL_STYLE_REGEX )
                                tmp_matchesp = matches;
                        else switch ( a->acl_dn_style ) {
                        case ACL_STYLE_REGEX:
@@ -875,7 +876,8 @@ acl_mask_dn(
                                        tmp_matchesp = matches;
                                        break;
                                }
-                       /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+                       /* FALLTHRU */
+                       /* applies also to ACL_STYLE_REGEX when pattern is "*" */
 
                        case ACL_STYLE_BASE:
                                tmp_data[0].rm_so = 0;
@@ -1014,10 +1016,7 @@ acl_mask_dnattr(
        Operation               *op,
        Entry                   *e,
        struct berval           *val,
-       AccessControl           *a,
        int                     count,
-       AccessControlState      *state,
-       slap_mask_t                     *mask,
        slap_dn_access          *bdn,
        struct berval           *opndn )
 {
@@ -1097,502 +1096,417 @@ acl_mask_dnattr(
        return 0;
 }
 
-
-/*
- * slap_acl_mask - modifies mask based upon the given acl and the
- * requested access to entry e, attribute attr, value val.  if val
- * is null, access to the whole attribute is assumed (all values).
- *
- * returns     0       access NOT allowed
- *             1       access allowed
- */
-
-static slap_control_t
-slap_acl_mask(
-       AccessControl           *a,
-       AccessControl           *prev,
-       slap_mask_t             *mask,
-       Operation               *op,
-       Entry                   *e,
-       AttributeDescription    *desc,
-       struct berval           *val,
-       AclRegexMatches         *matches,
-       int                     count,
-       AccessControlState      *state,
-       slap_access_t   access )
+int
+acl_check_who(
+       Operation *op,
+       Entry *e,
+       struct berval *val,
+       AccessControl *a,
+       Access *b,
+       AclRegexMatches *matches,
+       int count )
 {
-       int             i;
-       Access          *b;
-#ifdef LDAP_DEBUG
-       char            accessmaskbuf[ACCESSMASK_MAXLEN];
-#endif /* DEBUG */
-       const char      *attr;
-#ifdef SLAP_DYNACL
-       slap_mask_t     a2pmask = ACL_ACCESS2PRIV( access );
-#endif /* SLAP_DYNACL */
-
-       assert( a != NULL );
-       assert( mask != NULL );
-       assert( desc != NULL );
-
-       attr = desc->ad_cname.bv_val;
-
-       assert( attr != NULL );
+       if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
+               Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
+                               b->a_dn_pat.bv_val );
+               /*
+                * if access applies to the entry itself, and the
+                * user is bound as somebody in the same namespace as
+                * the entry, OR the given dn matches the dn pattern
+                */
+               /*
+                * 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 maintained in a_dn_pat.
+                */
 
-       Debug( LDAP_DEBUG_ACL,
-               "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
-               e->e_dn, attr );
+               if ( acl_mask_dn( op, e, val, a, matches,
+                                       &b->a_dn, &op->o_ndn ) )
+               {
+                       return 1;
+               }
+       }
 
-       Debug( LDAP_DEBUG_ACL,
-               "=> 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, 1 ) );
+       if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
+               struct berval   ndn;
 
+               Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
+                               b->a_realdn_pat.bv_val );
+               /*
+                * if access applies to the entry itself, and the
+                * user is bound as somebody in the same namespace as
+                * the entry, OR the given dn matches the dn pattern
+                */
+               /*
+                * 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 maintained in a_dn_pat.
+                */
 
-       b = a->acl_access;
-       i = 1;
+               if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
+               {
+                       ndn = op->o_conn->c_ndn;
+               } else {
+                       ndn = op->o_ndn;
+               }
 
-       for ( ; b != NULL; b = b->a_next, i++ ) {
-               slap_mask_t oldmask, modmask;
+               if ( acl_mask_dn( op, e, val, a, matches,
+                                       &b->a_realdn, &ndn ) )
+               {
+                       return 1;
+               }
+       }
 
-               ACL_INVALIDATE( modmask );
+       if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+               if ( ! op->o_conn->c_listener ) {
+                       return 1;
+               }
+               Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
+                               b->a_sockurl_pat.bv_val );
 
-               /* check for the "self" modifier in the <access> field */
-               if ( b->a_dn.a_self ) {
-                       const char *dummy;
-                       int rc, match = 0;
+               if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
+                       if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
+                               if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
+                                                       &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
 
-                       ACL_RECORD_VALUE_STATE;
+                       } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
+                               struct berval   bv;
+                               char buf[ACL_BUF_SIZE];
 
-                       /* must have DN syntax */
-                       if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName &&
-                               !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue;
+                               bv.bv_len = sizeof( buf ) - 1;
+                               bv.bv_val = buf;
+                               if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
 
-                       /* check if the target is an attribute. */
-                       if ( val == NULL ) continue;
+                               if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
+                               {
+                                       return 1;
+                               }
 
-                       /* a DN must be present */
-                       if ( BER_BVISEMPTY( &op->o_ndn ) ) {
-                               continue;
+                       } else {
+                               if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
+                               {
+                                       return 1;
+                               }
                        }
+               }
+       }
 
-                       /* target is attribute, check if the attribute value
-                        * is the op dn.
-                        */
-                       rc = value_match( &match, desc,
-                               desc->ad_type->sat_equality, 0,
-                               val, &op->o_ndn, &dummy );
-                       /* on match error or no match, fail the ACL clause */
-                       if ( rc != LDAP_SUCCESS || match != 0 )
-                               continue;
+       if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
+               if ( !op->o_conn->c_peer_domain.bv_val ) {
+                       return 1;
                }
+               Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
+                               b->a_domain_pat.bv_val );
+               if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
+                       if ( b->a_domain_style == ACL_STYLE_REGEX) {
+                               if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
+                                                       &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
+                       } else {
+                               char buf[ACL_BUF_SIZE];
 
-               /* AND <who> clauses */
-               if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
-                       Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
-                               b->a_dn_pat.bv_val );
-                       /*
-                        * if access applies to the entry itself, and the
-                        * user is bound as somebody in the same namespace as
-                        * the entry, OR the given dn matches the dn pattern
-                        */
-                       /*
-                        * 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 maintained in a_dn_pat.
-                        */
+                               struct berval   cmp = op->o_conn->c_peer_domain;
+                               struct berval   pat = b->a_domain_pat;
 
-                       if ( acl_mask_dn( op, e, val, a, matches,
-                               &b->a_dn, &op->o_ndn ) )
-                       {
-                               continue;
-                       }
-               }
+                               if ( b->a_domain_expand ) {
+                                       struct berval bv;
 
-               if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
-                       struct berval   ndn;
+                                       bv.bv_len = sizeof(buf) - 1;
+                                       bv.bv_val = buf;
 
-                       Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
-                               b->a_realdn_pat.bv_val );
-                       /*
-                        * if access applies to the entry itself, and the
-                        * user is bound as somebody in the same namespace as
-                        * the entry, OR the given dn matches the dn pattern
-                        */
-                       /*
-                        * 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 maintained in a_dn_pat.
-                        */
+                                       if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
+                                       {
+                                               return 1;
+                                       }
+                                       pat = bv;
+                               }
 
-                       if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
-                       {
-                               ndn = op->o_conn->c_ndn;
-                       } else {
-                               ndn = op->o_ndn;
-                       }
+                               if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
+                                       int offset = cmp.bv_len - pat.bv_len;
+                                       if ( offset < 0 ) {
+                                               return 1;
+                                       }
 
-                       if ( acl_mask_dn( op, e, val, a, matches,
-                               &b->a_realdn, &ndn ) )
-                       {
-                               continue;
+                                       if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
+                                               return 1;
+                                       }
+
+                                       /* trim the domain */
+                                       cmp.bv_val = &cmp.bv_val[ offset ];
+                                       cmp.bv_len -= offset;
+                               }
+
+                               if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
+                                       return 1;
+                               }
                        }
                }
+       }
 
-               if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
-                       if ( ! op->o_conn->c_listener ) {
-                               continue;
-                       }
-                       Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
-                               b->a_sockurl_pat.bv_val );
+       if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
+               if ( !op->o_conn->c_peer_name.bv_val ) {
+                       return 1;
+               }
+               Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
+                               b->a_peername_pat.bv_val );
+               if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
+                       if ( b->a_peername_style == ACL_STYLE_REGEX ) {
+                               if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
+                                                       &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
 
-                       if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
-                               if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
-                                       if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
-                                                       &e->e_nname, val, matches ) ) 
-                                       {
-                                               continue;
+                       } else {
+                               /* try exact match */
+                               if ( b->a_peername_style == ACL_STYLE_BASE ) {
+                                       if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
+                                               return 1;
                                        }
 
-                               } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
+                               } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
                                        struct berval   bv;
                                        char buf[ACL_BUF_SIZE];
 
                                        bv.bv_len = sizeof( buf ) - 1;
                                        bv.bv_val = buf;
-                                       if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
+                                       if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
                                        {
-                                               continue;
+                                               return 1;
                                        }
 
-                                       if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
-                                       {
-                                               continue;
+                                       if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
+                                               return 1;
                                        }
 
-                               } else {
-                                       if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
-                                       {
-                                               continue;
-                                       }
-                               }
-                       }
-               }
+                                       /* extract IP and try exact match */
+                               } else if ( b->a_peername_style == ACL_STYLE_IP ) {
+                                       char            *port;
+                                       char            buf[STRLENOF("255.255.255.255") + 1];
+                                       struct berval   ip;
+                                       unsigned long   addr;
+                                       int             port_number = -1;
+
+                                       if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
+                                                               acl_bv_ip_eq.bv_val,
+                                                               acl_bv_ip_eq.bv_len ) != 0 )
+                                               return 1;
 
-               if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
-                       if ( !op->o_conn->c_peer_domain.bv_val ) {
-                               continue;
-                       }
-                       Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
-                               b->a_domain_pat.bv_val );
-                       if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
-                               if ( b->a_domain_style == ACL_STYLE_REGEX) {
-                                       if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
-                                                       &e->e_nname, val, matches ) ) 
-                                       {
-                                               continue;
+                                       ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len;
+                                       ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len;
+
+                                       port = strrchr( ip.bv_val, ':' );
+                                       if ( port ) {
+                                               ip.bv_len = port - ip.bv_val;
+                                               ++port;
+                                               if ( lutil_atoi( &port_number, port ) != 0 )
+                                                       return 1;
                                        }
-                               } else {
-                                       char buf[ACL_BUF_SIZE];
 
-                                       struct berval   cmp = op->o_conn->c_peer_domain;
-                                       struct berval   pat = b->a_domain_pat;
+                                       /* the port check can be anticipated here */
+                                       if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
+                                               return 1;
 
-                                       if ( b->a_domain_expand ) {
-                                               struct berval bv;
+                                       /* address longer than expected? */
+                                       if ( ip.bv_len >= sizeof(buf) )
+                                               return 1;
 
-                                               bv.bv_len = sizeof(buf) - 1;
-                                               bv.bv_val = buf;
+                                       AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
+                                       buf[ ip.bv_len ] = '\0';
 
-                                               if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
-                                               {
-                                                       continue;
-                                               }
-                                               pat = bv;
-                                       }
+                                       addr = inet_addr( buf );
 
-                                       if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
-                                               int offset = cmp.bv_len - pat.bv_len;
-                                               if ( offset < 0 ) {
-                                                       continue;
-                                               }
+                                       /* unable to convert? */
+                                       if ( addr == (unsigned long)(-1) )
+                                               return 1;
 
-                                               if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
-                                                       continue;
-                                               }
+                                       if ( (addr & b->a_peername_mask) != b->a_peername_addr )
+                                               return 1;
 
-                                               /* trim the domain */
-                                               cmp.bv_val = &cmp.bv_val[ offset ];
-                                               cmp.bv_len -= offset;
-                                       }
-                                       
-                                       if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
-                                               continue;
-                                       }
-                               }
-                       }
-               }
+#ifdef LDAP_PF_INET6
+                                       /* extract IPv6 and try exact match */
+                               } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
+                                       char            *port;
+                                       char            buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
+                                       struct berval   ip;
+                                       struct in6_addr addr;
+                                       int             port_number = -1;
+
+                                       if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
+                                                               acl_bv_ipv6_eq.bv_val,
+                                                               acl_bv_ipv6_eq.bv_len ) != 0 )
+                                               return 1;
 
-               if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
-                       if ( !op->o_conn->c_peer_name.bv_val ) {
-                               continue;
-                       }
-                       Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
-                               b->a_peername_pat.bv_val );
-                       if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
-                               if ( b->a_peername_style == ACL_STYLE_REGEX ) {
-                                       if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
-                                                       &e->e_nname, val, matches ) ) 
-                                       {
-                                               continue;
-                                       }
+                                       ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len;
+                                       ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len;
 
-                               } else {
-                                       /* try exact match */
-                                       if ( b->a_peername_style == ACL_STYLE_BASE ) {
-                                               if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
-                                                       continue;
-                                               }
+                                       port = strrchr( ip.bv_val, ']' );
+                                       if ( port ) {
+                                               ip.bv_len = port - ip.bv_val;
+                                               ++port;
+                                               if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
+                                                       return 1;
+                                       }
 
-                                       } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
-                                               struct berval   bv;
-                                               char buf[ACL_BUF_SIZE];
+                                       /* the port check can be anticipated here */
+                                       if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
+                                               return 1;
 
-                                               bv.bv_len = sizeof( buf ) - 1;
-                                               bv.bv_val = buf;
-                                               if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
-                                               {
-                                                       continue;
-                                               }
+                                       /* address longer than expected? */
+                                       if ( ip.bv_len >= sizeof(buf) )
+                                               return 1;
 
-                                               if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
-                                                       continue;
-                                               }
+                                       AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
+                                       buf[ ip.bv_len ] = '\0';
 
-                                       /* extract IP and try exact match */
-                                       } else if ( b->a_peername_style == ACL_STYLE_IP ) {
-                                               char            *port;
-                                               char            buf[STRLENOF("255.255.255.255") + 1];
-                                               struct berval   ip;
-                                               unsigned long   addr;
-                                               int             port_number = -1;
-                                               
-                                               if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 
-                                                                       acl_bv_ip_eq.bv_val,
-                                                                       acl_bv_ip_eq.bv_len ) != 0 ) 
-                                                       continue;
+                                       if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
+                                               return 1;
 
-                                               ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len;
-                                               ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len;
+                                       /* check mask */
+                                       if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
+                                               return 1;
+#endif /* LDAP_PF_INET6 */
 
-                                               port = strrchr( ip.bv_val, ':' );
-                                               if ( port ) {
-                                                       ip.bv_len = port - ip.bv_val;
-                                                       ++port;
-                                                       if ( lutil_atoi( &port_number, port ) != 0 )
-                                                               continue;
-                                               }
-                                               
-                                               /* the port check can be anticipated here */
-                                               if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
-                                                       continue;
-                                               
-                                               /* address longer than expected? */
-                                               if ( ip.bv_len >= sizeof(buf) )
-                                                       continue;
+#ifdef LDAP_PF_LOCAL
+                                       /* extract path and try exact match */
+                               } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
+                                       struct berval path;
 
-                                               AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
-                                               buf[ ip.bv_len ] = '\0';
+                                       if ( strncmp( op->o_conn->c_peer_name.bv_val,
+                                                               acl_bv_path_eq.bv_val,
+                                                               acl_bv_path_eq.bv_len ) != 0 )
+                                               return 1;
 
-                                               addr = inet_addr( buf );
+                                       path.bv_val = op->o_conn->c_peer_name.bv_val
+                                               + acl_bv_path_eq.bv_len;
+                                       path.bv_len = op->o_conn->c_peer_name.bv_len
+                                               - acl_bv_path_eq.bv_len;
 
-                                               /* unable to convert? */
-                                               if ( addr == (unsigned long)(-1) )
-                                                       continue;
+                                       if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
+                                               return 1;
 
-                                               if ( (addr & b->a_peername_mask) != b->a_peername_addr )
-                                                       continue;
-
-#ifdef LDAP_PF_INET6
-                                       /* extract IPv6 and try exact match */
-                                       } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
-                                               char            *port;
-                                               char            buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
-                                               struct berval   ip;
-                                               struct in6_addr addr;
-                                               int             port_number = -1;
-                                               
-                                               if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 
-                                                                       acl_bv_ipv6_eq.bv_val,
-                                                                       acl_bv_ipv6_eq.bv_len ) != 0 ) 
-                                                       continue;
-
-                                               ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len;
-                                               ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len;
-
-                                               port = strrchr( ip.bv_val, ']' );
-                                               if ( port ) {
-                                                       ip.bv_len = port - ip.bv_val;
-                                                       ++port;
-                                                       if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
-                                                               continue;
-                                               }
-                                               
-                                               /* the port check can be anticipated here */
-                                               if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
-                                                       continue;
-                                               
-                                               /* address longer than expected? */
-                                               if ( ip.bv_len >= sizeof(buf) )
-                                                       continue;
-
-                                               AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
-                                               buf[ ip.bv_len ] = '\0';
-
-                                               if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
-                                                       continue;
-
-                                               /* check mask */
-                                               if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
-                                                       continue;
-#endif /* LDAP_PF_INET6 */
-
-#ifdef LDAP_PF_LOCAL
-                                       /* extract path and try exact match */
-                                       } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
-                                               struct berval path;
-                                               
-                                               if ( strncmp( op->o_conn->c_peer_name.bv_val,
-                                                                       acl_bv_path_eq.bv_val,
-                                                                       acl_bv_path_eq.bv_len ) != 0 )
-                                                       continue;
-
-                                               path.bv_val = op->o_conn->c_peer_name.bv_val
-                                                       + acl_bv_path_eq.bv_len;
-                                               path.bv_len = op->o_conn->c_peer_name.bv_len
-                                                       - acl_bv_path_eq.bv_len;
-
-                                               if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
-                                                       continue;
-
-#endif /* LDAP_PF_LOCAL */
+#endif /* LDAP_PF_LOCAL */
 
                                        /* exact match (very unlikely...) */
-                                       } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
-                                                       continue;
-                                       }
+                               } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
+                                       return 1;
                                }
                        }
                }
+       }
 
-               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",
+       if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
+               if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
+                       return 1;
+               }
+               Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
                                b->a_sockname_pat.bv_val );
-                       if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
-                               if ( b->a_sockname_style == ACL_STYLE_REGEX) {
-                                       if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
-                                                       &e->e_nname, val, matches ) ) 
-                                       {
-                                               continue;
-                                       }
+               if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
+                       if ( b->a_sockname_style == ACL_STYLE_REGEX) {
+                               if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
+                                                       &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
 
-                               } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
-                                       struct berval   bv;
-                                       char buf[ACL_BUF_SIZE];
+                       } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
+                               struct berval   bv;
+                               char buf[ACL_BUF_SIZE];
 
-                                       bv.bv_len = sizeof( buf ) - 1;
-                                       bv.bv_val = buf;
-                                       if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
-                                       {
-                                               continue;
-                                       }
+                               bv.bv_len = sizeof( buf ) - 1;
+                               bv.bv_val = buf;
+                               if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
+                               {
+                                       return 1;
+                               }
 
-                                       if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
-                                               continue;
-                                       }
+                               if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
+                                       return 1;
+                               }
 
-                               } else {
-                                       if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
-                                               continue;
-                                       }
+                       } else {
+                               if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
+                                       return 1;
                                }
                        }
                }
+       }
 
-               if ( b->a_dn_at != NULL ) {
-                       if ( acl_mask_dnattr( op, e, val, a,
-                                       count, state, mask,
-                                       &b->a_dn, &op->o_ndn ) )
-                       {
-                               continue;
-                       }
+       if ( b->a_dn_at != NULL ) {
+               if ( acl_mask_dnattr( op, e, val, count, &b->a_dn, &op->o_ndn ) )
+               {
+                       return 1;
                }
+       }
 
-               if ( b->a_realdn_at != NULL ) {
-                       struct berval   ndn;
+       if ( b->a_realdn_at != NULL ) {
+               struct berval   ndn;
 
-                       if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
-                       {
-                               ndn = op->o_conn->c_ndn;
-                       } else {
-                               ndn = op->o_ndn;
-                       }
+               if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
+               {
+                       ndn = op->o_conn->c_ndn;
+               } else {
+                       ndn = op->o_ndn;
+               }
 
-                       if ( acl_mask_dnattr( op, e, val, a,
-                                       count, state, mask,
-                                       &b->a_realdn, &ndn ) )
-                       {
-                               continue;
-                       }
+               if ( acl_mask_dnattr( op, e, val, count, &b->a_realdn, &ndn ) )
+               {
+                       return 1;
                }
+       }
 
-               if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
-                       struct berval bv;
-                       struct berval ndn = BER_BVNULL;
-                       int rc;
+       if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
+               struct berval bv;
+               struct berval ndn = BER_BVNULL;
+               int rc;
 
-                       if ( op->o_ndn.bv_len == 0 ) {
-                               continue;
-                       }
+               if ( op->o_ndn.bv_len == 0 ) {
+                       return 1;
+               }
 
-                       Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
+               Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
                                b->a_group_pat.bv_val );
 
-                       /* b->a_group is an unexpanded entry name, expanded it should be an 
-                        * entry with objectclass group* and we test to see if odn is one of
-                        * the values in the attribute group
-                        */
-                       /* see if asker is listed in dnattr */
-                       if ( b->a_group_style == ACL_STYLE_EXPAND ) {
-                               char            buf[ACL_BUF_SIZE];
-                               AclRegexMatches tmp_matches,
-                                               *tmp_matchesp = &tmp_matches;
-                               regmatch_t      *tmp_data;
+               /* b->a_group is an unexpanded entry name, expanded it should be an
+                * entry with objectclass group* and we test to see if odn is one of
+                * the values in the attribute group
+                */
+               /* see if asker is listed in dnattr */
+               if ( b->a_group_style == ACL_STYLE_EXPAND ) {
+                       char            buf[ACL_BUF_SIZE];
+                       AclRegexMatches tmp_matches,
+                                                       *tmp_matchesp = &tmp_matches;
+                       regmatch_t      *tmp_data;
 
-                               MATCHES_MEMSET( &tmp_matches );
-                               tmp_data = &tmp_matches.dn_data[0];
+                       MATCHES_MEMSET( &tmp_matches );
+                       tmp_data = &tmp_matches.dn_data[0];
 
-                               bv.bv_len = sizeof(buf) - 1;
-                               bv.bv_val = buf;
+                       bv.bv_len = sizeof(buf) - 1;
+                       bv.bv_val = buf;
 
-                               rc = 0;
+                       rc = 0;
 
-                               if ( a->acl_attrval_style == ACL_STYLE_REGEX )
-                                       tmp_matchesp = matches;
-                               else switch ( a->acl_dn_style ) {
+                       if ( !a || a->acl_attrval_style == ACL_STYLE_REGEX )
+                               tmp_matchesp = matches;
+                       else switch ( a->acl_dn_style ) {
                                case ACL_STYLE_REGEX:
                                        if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
                                                tmp_matchesp = matches;
                                                break;
                                        }
 
-                               /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+                                       /* FALLTHRU */
+                                       /* applies also to ACL_STYLE_REGEX when pattern is "*" */
                                case ACL_STYLE_BASE:
                                        tmp_data[0].rm_so = 0;
                                        tmp_data[0].rm_eo = e->e_nname.bv_len;
@@ -1614,75 +1528,76 @@ slap_acl_mask(
                                        /* error */
                                        rc = 1;
                                        break;
-                               }
+                       }
 
-                               if ( rc ) {
-                                       continue;
-                               }
-                               
-                               if ( acl_string_expand( &bv, &b->a_group_pat,
+                       if ( rc ) {
+                               return 1;
+                       }
+
+                       if ( acl_string_expand( &bv, &b->a_group_pat,
                                                &e->e_nname, val,
                                                tmp_matchesp ) )
-                               {
-                                       continue;
-                               }
+                       {
+                               return 1;
+                       }
 
-                               if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
+                       if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
                                                op->o_tmpmemctx ) != LDAP_SUCCESS )
-                               {
-                                       /* did not expand to a valid dn */
-                                       continue;
-                               }
+                       {
+                               /* did not expand to a valid dn */
+                               return 1;
+                       }
 
-                               bv = ndn;
+                       bv = ndn;
 
-                       } else {
-                               bv = b->a_group_pat;
-                       }
+               } else {
+                       bv = b->a_group_pat;
+               }
 
-                       rc = backend_group( op, e, &bv, &op->o_ndn,
+               rc = backend_group( op, e, &bv, &op->o_ndn,
                                b->a_group_oc, b->a_group_at );
 
-                       if ( ndn.bv_val ) {
-                               slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-                       }
+               if ( ndn.bv_val ) {
+                       slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+               }
 
-                       if ( rc != 0 ) {
-                               continue;
-                       }
+               if ( rc != 0 ) {
+                       return 1;
                }
+       }
 
-               if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
-                       struct berval   bv;
-                       char            buf[ACL_BUF_SIZE];
+       if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+               struct berval   bv;
+               char            buf[ACL_BUF_SIZE];
 
-                       Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
+               Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
                                b->a_set_pat.bv_val );
 
-                       if ( b->a_set_style == ACL_STYLE_EXPAND ) {
-                               AclRegexMatches tmp_matches,
-                                               *tmp_matchesp = &tmp_matches;
-                               int             rc = 0;
-                               regmatch_t      *tmp_data;
+               if ( b->a_set_style == ACL_STYLE_EXPAND ) {
+                       AclRegexMatches tmp_matches,
+                                                       *tmp_matchesp = &tmp_matches;
+                       int             rc = 0;
+                       regmatch_t      *tmp_data;
 
-                               MATCHES_MEMSET( &tmp_matches );
-                               tmp_data = &tmp_matches.dn_data[0];
+                       MATCHES_MEMSET( &tmp_matches );
+                       tmp_data = &tmp_matches.dn_data[0];
 
-                               bv.bv_len = sizeof( buf ) - 1;
-                               bv.bv_val = buf;
+                       bv.bv_len = sizeof( buf ) - 1;
+                       bv.bv_val = buf;
 
-                               rc = 0;
+                       rc = 0;
 
-                               if ( a->acl_attrval_style == ACL_STYLE_REGEX )
-                                       tmp_matchesp = matches;
-                               else switch ( a->acl_dn_style ) {
+                       if ( !a || a->acl_attrval_style == ACL_STYLE_REGEX )
+                               tmp_matchesp = matches;
+                       else switch ( a->acl_dn_style ) {
                                case ACL_STYLE_REGEX:
                                        if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
                                                tmp_matchesp = matches;
                                                break;
                                        }
 
-                               /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+                                       /* FALLTHRU */
+                                       /* applies also to ACL_STYLE_REGEX when pattern is "*" */
                                case ACL_STYLE_BASE:
                                        tmp_data[0].rm_so = 0;
                                        tmp_data[0].rm_eo = e->e_nname.bv_len;
@@ -1702,61 +1617,159 @@ slap_acl_mask(
                                        /* error */
                                        rc = 1;
                                        break;
-                               }
+                       }
 
-                               if ( rc ) {
-                                       continue;
-                               }
-                               
-                               if ( acl_string_expand( &bv, &b->a_set_pat,
+                       if ( rc ) {
+                               return 1;
+                       }
+
+                       if ( acl_string_expand( &bv, &b->a_set_pat,
                                                &e->e_nname, val,
                                                tmp_matchesp ) )
-                               {
-                                       continue;
-                               }
-
-                       } else {
-                               bv = b->a_set_pat;
-                       }
-                       
-                       if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
-                               continue;
+                       {
+                               return 1;
                        }
+
+               } else {
+                       bv = b->a_set_pat;
                }
 
-               if ( b->a_authz.sai_ssf ) {
-                       Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
+               if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
+                       return 1;
+               }
+       }
+
+       if ( b->a_authz.sai_ssf ) {
+               Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
                                b->a_authz.sai_ssf, op->o_ssf );
-                       if ( b->a_authz.sai_ssf >  op->o_ssf ) {
-                               continue;
-                       }
+               if ( b->a_authz.sai_ssf >  op->o_ssf ) {
+                       return 1;
                }
+       }
 
-               if ( b->a_authz.sai_transport_ssf ) {
-                       Debug( LDAP_DEBUG_ACL,
+       if ( b->a_authz.sai_transport_ssf ) {
+               Debug( LDAP_DEBUG_ACL,
                                "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
                                b->a_authz.sai_transport_ssf, op->o_transport_ssf );
-                       if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
-                               continue;
-                       }
+               if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
+                       return 1;
                }
+       }
 
-               if ( b->a_authz.sai_tls_ssf ) {
-                       Debug( LDAP_DEBUG_ACL,
+       if ( b->a_authz.sai_tls_ssf ) {
+               Debug( LDAP_DEBUG_ACL,
                                "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
                                b->a_authz.sai_tls_ssf, op->o_tls_ssf );
-                       if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
-                               continue;
-                       }
+               if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
+                       return 1;
                }
+       }
 
-               if ( b->a_authz.sai_sasl_ssf ) {
-                       Debug( LDAP_DEBUG_ACL,
+       if ( b->a_authz.sai_sasl_ssf ) {
+               Debug( LDAP_DEBUG_ACL,
                                "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
                                b->a_authz.sai_sasl_ssf, op->o_sasl_ssf );
-                       if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
+               if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * slap_acl_mask - modifies mask based upon the given acl and the
+ * requested access to entry e, attribute attr, value val.  if val
+ * is null, access to the whole attribute is assumed (all values).
+ *
+ * returns     0       access NOT allowed
+ *             1       access allowed
+ */
+
+static slap_control_t
+slap_acl_mask(
+       AccessControl           *a,
+       AccessControl           *prev,
+       slap_mask_t             *mask,
+       Operation               *op,
+       Entry                   *e,
+       AttributeDescription    *desc,
+       struct berval           *val,
+       AclRegexMatches         *matches,
+       int                     count,
+       AccessControlState      *state,
+       slap_access_t   access )
+{
+       int             i;
+       Access          *b;
+#ifdef LDAP_DEBUG
+       char            accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif /* DEBUG */
+       const char      *attr;
+#ifdef SLAP_DYNACL
+       slap_mask_t     a2pmask = ACL_ACCESS2PRIV( access );
+#endif /* SLAP_DYNACL */
+
+       assert( a != NULL );
+       assert( mask != NULL );
+       assert( desc != NULL );
+
+       attr = desc->ad_cname.bv_val;
+
+       assert( attr != NULL );
+
+       Debug( LDAP_DEBUG_ACL,
+               "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
+               e->e_dn, attr );
+
+       Debug( LDAP_DEBUG_ACL,
+               "=> 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, 1 ) );
+
+
+       b = a->acl_access;
+       i = 1;
+
+       for ( ; b != NULL; b = b->a_next, i++ ) {
+               slap_mask_t oldmask, modmask;
+
+               ACL_INVALIDATE( modmask );
+
+               /* check for the "self" modifier in the <access> field */
+               if ( b->a_dn.a_self ) {
+                       const char *dummy;
+                       int rc, match = 0;
+
+                       ACL_RECORD_VALUE_STATE;
+
+                       /* must have DN syntax */
+                       if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName &&
+                               !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue;
+
+                       /* check if the target is an attribute. */
+                       if ( val == NULL ) continue;
+
+                       /* a DN must be present */
+                       if ( BER_BVISEMPTY( &op->o_ndn ) ) {
                                continue;
                        }
+
+                       /* target is attribute, check if the attribute value
+                        * is the op dn.
+                        */
+                       rc = value_match( &match, desc,
+                               desc->ad_type->sat_equality, 0,
+                               val, &op->o_ndn, &dummy );
+                       /* on match error or no match, fail the ACL clause */
+                       if ( rc != LDAP_SUCCESS || match != 0 )
+                               continue;
+               }
+
+               /* check <who> clauses */
+               if ( acl_check_who( op, e, val, a, b, matches, count ) ) {
+                       continue;
                }
 
 #ifdef SLAP_DYNACL
index bf93e7dd3ef9bcdaf5c8e6d18df429f302dac9fb..ff2c3fe3681a33541ddca8468e1b4272ea106fad 100644 (file)
@@ -321,1426 +321,1452 @@ regex_done:;
 }
 
 int
-parse_acl(
+acl_parse_who(
        struct config_args_s *c,
-       int             pos )
+       Access *b,
+       char **argv,
+       int argc,
+       int *pos,
+       char **current )
 {
-       int             i;
-       char            *left, *right, *style;
        struct berval   bv;
-       AccessControl   *a = NULL;
-       Access  *b = NULL;
-       int rc;
+       char *left, *right, *style;
        const char *text;
        Backend *be = c->be;
-       const char *fname = c->fname;
-       int lineno = c->lineno;
-       int argc = c->argc;
-       char **argv = c->argv;
+       int rc, i = *pos;
+
+       for ( ; i < argc; i++ ) {
+               slap_style_t    sty = ACL_STYLE_REGEX;
+               char            *style_modifier = NULL;
+               char            *style_level = NULL;
+               int             level = 0;
+               int             expand = 0;
+               slap_dn_access  *bdn = &b->a_dn;
+               int             is_realdn = 0;
+
+               split( argv[i], '=', &left, &right );
+               split( left, '.', &left, &style );
+               if ( style ) {
+                       split( style, ',', &style, &style_modifier );
+
+                       if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
+                               split( style, '{', &style, &style_level );
+                               if ( style_level != NULL ) {
+                                       char *p = strchr( style_level, '}' );
+                                       if ( p == NULL ) {
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "premature eol: expecting closing '}' in \"level{n}\"");
+                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                               goto fail;
+                                       } else if ( p == style_level ) {
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "empty level in \"level{n}\"");
+                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                               goto fail;
+                                       }
+                                       p[0] = '\0';
+                               }
+                       }
+               }
 
-       for ( i = 1; i < argc; i++ ) {
-               /* to clause - select which entries are protected */
-               if ( strcasecmp( argv[i], "to" ) == 0 ) {
-                       if ( a != NULL ) {
+               if ( style == NULL || *style == '\0' ||
+                               strcasecmp( style, "exact" ) == 0 ||
+                               strcasecmp( style, "baseObject" ) == 0 ||
+                               strcasecmp( style, "base" ) == 0 )
+               {
+                       sty = ACL_STYLE_BASE;
+
+               } else if ( strcasecmp( style, "onelevel" ) == 0 ||
+                               strcasecmp( style, "one" ) == 0 )
+               {
+                       sty = ACL_STYLE_ONE;
+
+               } else if ( strcasecmp( style, "subtree" ) == 0 ||
+                               strcasecmp( style, "sub" ) == 0 )
+               {
+                       sty = ACL_STYLE_SUBTREE;
+
+               } else if ( strcasecmp( style, "children" ) == 0 ) {
+                       sty = ACL_STYLE_CHILDREN;
+
+               } else if ( strcasecmp( style, "level" ) == 0 )
+               {
+                       if ( lutil_atoi( &level, style_level ) != 0 ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "only one to clause allowed in access line" );
+                                               "unable to parse level in \"level{n}\"");
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                goto fail;
                        }
-                       a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
-                       a->acl_attrval_style = ACL_STYLE_NONE;
-                       for ( ++i; i < argc; i++ ) {
-                               if ( strcasecmp( argv[i], "by" ) == 0 ) {
-                                       i--;
-                                       break;
-                               }
 
-                               if ( strcasecmp( argv[i], "*" ) == 0 ) {
-                                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
-                                               a->acl_dn_style != ACL_STYLE_REGEX )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "dn pattern already specified in to clause." );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       sty = ACL_STYLE_LEVEL;
 
-                                       ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
-                                       continue;
-                               }
+               } else if ( strcasecmp( style, "regex" ) == 0 ) {
+                       sty = ACL_STYLE_REGEX;
 
-                               split( argv[i], '=', &left, &right );
-                               split( left, '.', &left, &style );
+               } else if ( strcasecmp( style, "expand" ) == 0 ) {
+                       sty = ACL_STYLE_EXPAND;
 
-                               if ( right == NULL ) {
+               } else if ( strcasecmp( style, "ip" ) == 0 ) {
+                       sty = ACL_STYLE_IP;
+
+               } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
+#ifndef LDAP_PF_INET6
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "IPv6 not supported");
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+#endif /* ! LDAP_PF_INET6 */
+                       sty = ACL_STYLE_IPV6;
+
+               } else if ( strcasecmp( style, "path" ) == 0 ) {
+                       sty = ACL_STYLE_PATH;
+#ifndef LDAP_PF_LOCAL
+                       snprintf( c->cr_msg, sizeof( c->cr_msg),
+                                       "\"path\" style modifier is useless without local");
+                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
+                       goto fail;
+#endif /* LDAP_PF_LOCAL */
+
+               } else {
+                       snprintf( c->cr_msg, sizeof ( c->cr_msg ),
+                                       "unknown style \"%s\" in by clause", style );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                       goto fail;
+               }
+
+               if ( style_modifier &&
+                               strcasecmp( style_modifier, "expand" ) == 0 )
+               {
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                         "missing \"=\" in \"%s\" in to clause", left );
+                                                       "\"regex\" style implies \"expand\" modifier" );
                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                        goto fail;
-                               }
-
-                               if ( strcasecmp( left, "dn" ) == 0 ) {
-                                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
-                                               a->acl_dn_style != ACL_STYLE_REGEX )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg),
-                                               "dn pattern already specified in to clause" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                                       break;
 
-                                       if ( style == NULL || *style == '\0' ||
-                                               strcasecmp( style, "baseObject" ) == 0 ||
-                                               strcasecmp( style, "base" ) == 0 ||
-                                               strcasecmp( style, "exact" ) == 0 )
-                                       {
-                                               a->acl_dn_style = ACL_STYLE_BASE;
-                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+                               case ACL_STYLE_EXPAND:
+                                       break;
 
-                                       } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
-                                               strcasecmp( style, "one" ) == 0 )
-                                       {
-                                               a->acl_dn_style = ACL_STYLE_ONE;
-                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+                               default:
+                                       /* we'll see later if it's pertinent */
+                                       expand = 1;
+                                       break;
+                       }
+               }
 
-                                       } else if ( strcasecmp( style, "subtree" ) == 0 ||
-                                               strcasecmp( style, "sub" ) == 0 )
-                                       {
-                                               if( *right == '\0' ) {
-                                                       ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
+               if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
+                       is_realdn = 1;
+                       bdn = &b->a_realdn;
+                       left += STRLENOF( "real" );
+               }
 
-                                               } else {
-                                                       a->acl_dn_style = ACL_STYLE_SUBTREE;
-                                                       ber_str2bv( right, 0, 1, &a->acl_dn_pat );
-                                               }
+               if ( strcasecmp( left, "*" ) == 0 ) {
+                       if ( is_realdn ) {
+                               goto fail;
+                       }
 
-                                       } else if ( strcasecmp( style, "children" ) == 0 ) {
-                                               a->acl_dn_style = ACL_STYLE_CHILDREN;
-                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+                       ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
+                       sty = ACL_STYLE_REGEX;
 
-                                       } else if ( strcasecmp( style, "regex" ) == 0 ) {
-                                               a->acl_dn_style = ACL_STYLE_REGEX;
+               } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
+                       ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
+                       sty = ACL_STYLE_ANONYMOUS;
 
-                                               if ( *right == '\0' ) {
-                                                       /* empty regex should match empty DN */
-                                                       a->acl_dn_style = ACL_STYLE_BASE;
-                                                       ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+               } else if ( strcasecmp( left, "users" ) == 0 ) {
+                       ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
+                       sty = ACL_STYLE_USERS;
 
-                                               } else if ( strcmp(right, "*") == 0
-                                                       || strcmp(right, ".*") == 0
-                                                       || strcmp(right, ".*$") == 0
-                                                       || strcmp(right, "^.*") == 0
-                                                       || strcmp(right, "^.*$") == 0
-                                                       || strcmp(right, ".*$$") == 0
-                                                       || strcmp(right, "^.*$$") == 0 )
-                                               {
-                                                       ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
+               } else if ( strcasecmp( left, "self" ) == 0 ) {
+                       ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
+                       sty = ACL_STYLE_SELF;
 
-                                               } else {
-                                                       acl_regex_normalized_dn( right, &a->acl_dn_pat );
-                                               }
+               } else if ( strcasecmp( left, "dn" ) == 0 ) {
+                       if ( sty == ACL_STYLE_REGEX ) {
+                               bdn->a_style = ACL_STYLE_REGEX;
+                               if ( right == NULL ) {
+                                       /* no '=' */
+                                       ber_str2bv("users",
+                                                       STRLENOF( "users" ),
+                                                       1, &bv);
+                                       bdn->a_style = ACL_STYLE_USERS;
+
+                               } else if (*right == '\0' ) {
+                                       /* dn="" */
+                                       ber_str2bv("anonymous",
+                                                       STRLENOF( "anonymous" ),
+                                                       1, &bv);
+                                       bdn->a_style = ACL_STYLE_ANONYMOUS;
+
+                               } else if ( strcmp( right, "*" ) == 0 ) {
+                                       /* dn=* */
+                                       /* any or users?  users for now */
+                                       ber_str2bv("users",
+                                                       STRLENOF( "users" ),
+                                                       1, &bv);
+                                       bdn->a_style = ACL_STYLE_USERS;
+
+                               } else if ( strcmp( right, ".+" ) == 0
+                                               || strcmp( right, "^.+" ) == 0
+                                               || strcmp( right, ".+$" ) == 0
+                                               || strcmp( right, "^.+$" ) == 0
+                                               || strcmp( right, ".+$$" ) == 0
+                                               || strcmp( right, "^.+$$" ) == 0 )
+                               {
+                                       ber_str2bv("users",
+                                                       STRLENOF( "users" ),
+                                                       1, &bv);
+                                       bdn->a_style = ACL_STYLE_USERS;
+
+                               } else if ( strcmp( right, ".*" ) == 0
+                                               || strcmp( right, "^.*" ) == 0
+                                               || strcmp( right, ".*$" ) == 0
+                                               || strcmp( right, "^.*$" ) == 0
+                                               || strcmp( right, ".*$$" ) == 0
+                                               || strcmp( right, "^.*$$" ) == 0 )
+                               {
+                                       ber_str2bv("*",
+                                                       STRLENOF( "*" ),
+                                                       1, &bv);
 
-                                       } else {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "unknown dn style \"%s\" in to clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                               } else {
+                                       acl_regex_normalized_dn( right, &bv );
+                                       if ( !ber_bvccmp( &bv, '*' ) ) {
+                                               if ( regtest( c, bv.bv_val ) != 0)
+                                                       goto fail;
                                        }
-
-                                       continue;
                                }
 
-                               if ( strcasecmp( left, "filter" ) == 0 ) {
-                                       if ( (a->acl_filter = str2filter( right )) == NULL ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "bad filter \"%s\" in to clause", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       } else if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
 
-                               } else if ( strcasecmp( left, "attr" ) == 0             /* TOLERATED */
-                                               || strcasecmp( left, "attrs" ) == 0 )   /* DOCUMENTED */
-                               {
-                                       if ( strcasecmp( left, "attr" ) == 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                 "\"attr\" is deprecated (and undocumented); "
-                                                                 "use \"attrs\" instead");
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                       }
+                       } else {
+                               ber_str2bv( right, 0, 1, &bv );
+                       }
 
-                                       a->acl_attrs = str2anlist( a->acl_attrs,
-                                               right, "," );
-                                       if ( a->acl_attrs == NULL ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                 "unknown attr \"%s\" in to clause", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+               } else {
+                       BER_BVZERO( &bv );
+               }
 
-                               } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
-                                       struct berval   bv;
-                                       char            *mr;
+               if ( !BER_BVISNULL( &bv ) ) {
+                       if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "dn pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                 "attr val already specified in to clause" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-                                       if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "attr val requires a single attribute");
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                       if ( sty != ACL_STYLE_REGEX &&
+                                       sty != ACL_STYLE_ANONYMOUS &&
+                                       sty != ACL_STYLE_USERS &&
+                                       sty != ACL_STYLE_SELF &&
+                                       expand == 0 )
+                       {
+                               rc = dnNormalize(0, NULL, NULL,
+                                               &bv, &bdn->a_pat, NULL);
+                               if ( rc != LDAP_SUCCESS ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "bad DN \"%s\" in by DN clause", bv.bv_val );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+                               free( bv.bv_val );
+                               if ( sty == ACL_STYLE_BASE
+                                               && be != NULL
+                                               && !BER_BVISNULL( &be->be_rootndn )
+                                               && dn_match( &bdn->a_pat, &be->be_rootndn ) )
+                               {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "rootdn is always granted unlimited privileges" );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               }
+
+                       } else {
+                               bdn->a_pat = bv;
+                       }
+                       bdn->a_style = sty;
+                       if ( expand ) {
+                               char    *exp;
+                               int     gotit = 0;
+
+                               for ( exp = strchr( bdn->a_pat.bv_val, '$' );
+                                               exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
+                                               < bdn->a_pat.bv_len;
+                                               exp = strchr( exp, '$' ) )
+                               {
+                                       if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
+                                                               exp[ 1 ] == '{' ) ) {
+                                               gotit = 1;
+                                               break;
                                        }
+                               }
 
-                                       ber_str2bv( right, 0, 0, &bv );
-                                       a->acl_attrval_style = ACL_STYLE_BASE;
+                               if ( gotit == 1 ) {
+                                       bdn->a_expand = expand;
 
-                                       mr = strchr( left, '/' );
-                                       if ( mr != NULL ) {
-                                               mr[ 0 ] = '\0';
-                                               mr++;
+                               } else {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "\"expand\" used with no expansions in \"pattern\"");
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+                       }
+                       if ( sty == ACL_STYLE_SELF ) {
+                               bdn->a_self_level = level;
 
-                                               a->acl_attrval_mr = mr_find( mr );
-                                               if ( a->acl_attrval_mr == NULL ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "invalid matching rule \"%s\"", mr);
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
+                       } else {
+                               if ( level < 0 ) {
+                                       snprintf( c->cr_msg, sizeof( c ->cr_msg ),
+                                                       "bad negative level \"%d\" in by DN clause", level );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               } else if ( level == 1 ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "\"onelevel\" should be used instead of \"level{1}\" in by DN clause" );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
+                                       snprintf ( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "\"base\" should be used instead of \"level{0}\" in by DN clause" );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               }
 
-                                               if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
-                                               {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                             "matching rule \"%s\" use " "with attr \"%s\" not appropriate",
-                                                                         mr,
-                                                                         a->acl_attrs[0].an_name.bv_val );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c-> log, c->cr_msg );
-                                                       goto fail;
-                                               }
-                                       }
+                               bdn->a_level = level;
+                       }
+                       continue;
+               }
 
-                                       if ( style != NULL ) {
-                                               if ( strcasecmp( style, "regex" ) == 0 ) {
-                                                       int e = regcomp( &a->acl_attrval_re, bv.bv_val,
-                                                               REG_EXTENDED | REG_ICASE );
-                                                       if ( e ) {
-                                                               char    err[SLAP_TEXT_BUFLEN];
+               if ( strcasecmp( left, "dnattr" ) == 0 ) {
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                               regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                     "regular expression \"%s\" bad because of %s",
-                                                                     right, err );
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
-                                                       a->acl_attrval_style = ACL_STYLE_REGEX;
+                       if( bdn->a_at != NULL ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "dnattr already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                               } else {
-                                                       /* FIXME: if the attribute has DN syntax, we might
-                                                        * allow one, subtree and children styles as well */
-                                                       if ( !strcasecmp( style, "base" ) ||
-                                                               !strcasecmp( style, "exact" ) ) {
-                                                               a->acl_attrval_style = ACL_STYLE_BASE;
+                       rc = slap_str2ad( right, &bdn->a_at, &text );
 
-                                                       } else if ( a->acl_attrs[0].an_desc->ad_type->
-                                                               sat_syntax == slap_schema.si_syn_distinguishedName )
-                                                       {
-                                                               if ( !strcasecmp( style, "baseObject" ) ||
-                                                                       !strcasecmp( style, "base" ) )
-                                                               {
-                                                                       a->acl_attrval_style = ACL_STYLE_BASE;
-                                                               } else if ( !strcasecmp( style, "onelevel" ) ||
-                                                                       !strcasecmp( style, "one" ) )
-                                                               {
-                                                                       a->acl_attrval_style = ACL_STYLE_ONE;
-                                                               } else if ( !strcasecmp( style, "subtree" ) ||
-                                                                       !strcasecmp( style, "sub" ) )
-                                                               {
-                                                                       a->acl_attrval_style = ACL_STYLE_SUBTREE;
-                                                               } else if ( !strcasecmp( style, "children" ) ) {
-                                                                       a->acl_attrval_style = ACL_STYLE_CHILDREN;
-                                                               } else {
-                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                             "unknown val.<style> \"%s\" for attributeType \"%s\" " "with DN syntax",
-                                                                             style,
-                                                                             a->acl_attrs[0].an_desc->ad_cname.bv_val );
-                                                                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
-                                                                       goto fail;
-                                                               }
+                       if( rc != LDAP_SUCCESS ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "dnattr \"%s\": %s", right, text );
+                               Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                               rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
-                                                               if ( rc != LDAP_SUCCESS ) {
-                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                             "unable to normalize DN \"%s\" " "for attributeType \"%s\" (%d)",
-                                                                             bv.bv_val,
-                                                                             a->acl_attrs[0].an_desc->ad_cname.bv_val,
-                                                                             rc );
-                                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                                       goto fail;
-                                                               }
 
-                                                       } else {
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                     "unknown val.<style> \"%s\" for attributeType \"%s\"",
-                                                                     fname,
-                                                                     a->acl_attrs[0].an_desc->ad_cname.bv_val );
-                                                               Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
-                                               }
-                                       }
+                       if( !is_at_syntax( bdn->a_at->ad_type,
+                                               SLAPD_DN_SYNTAX ) &&
+                                       !is_at_syntax( bdn->a_at->ad_type,
+                                               SLAPD_NAMEUID_SYNTAX ))
+                       {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "dnattr \"%s\": " "inappropriate syntax: %s",
+                                               right, bdn->a_at->ad_type->sat_syntax_oid );
+                               Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       /* Check for appropriate matching rule */
-                                       if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
-                                               ber_dupbv( &a->acl_attrval, &bv );
+                       if( bdn->a_at->ad_type->sat_equality == NULL ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "dnattr \"%s\": inappropriate matching (no EQUALITY)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
-                                               int             rc;
-                                               const char      *text;
+                       continue;
+               }
 
-                                               if ( a->acl_attrval_mr == NULL ) {
-                                                       a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
-                                               }
+               if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
+                       char *name = NULL;
+                       char *value = NULL;
+                       char *attr_name = SLAPD_GROUP_ATTR;
 
-                                               if ( a->acl_attrval_mr == NULL ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "attr \"%s\" does not have an EQUALITY matching rule",
-                                                               a->acl_attrs[ 0 ].an_name.bv_val );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
+                                       /* legacy, tolerated */
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "deprecated group style \"regex\"; use \"expand\" instead" );
+                                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
+                                       sty = ACL_STYLE_EXPAND;
+                                       break;
 
-                                               rc = asserted_value_validate_normalize(
-                                                       a->acl_attrs[ 0 ].an_desc,
-                                                       a->acl_attrval_mr,
-                                                       SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
-                                                       &bv,
-                                                       &a->acl_attrval,
-                                                       &text,
-                                                       NULL );
-                                               if ( rc != LDAP_SUCCESS ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                             "attr \"%s\" normalization failed (%d: %s).\n",
-                                                             a->acl_attrs[0].an_name.bv_val,
-                                                             rc, text );
-                                                       Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
-                                       }
+                               case ACL_STYLE_BASE:
+                                       /* legal, traditional */
+                               case ACL_STYLE_EXPAND:
+                                       /* legal, substring expansion; supersedes regex */
+                                       break;
 
-                               } else {
+                               default:
+                                       /* unknown */
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "expecting <what> got \"%s\"",
-                                           left );
+                                                       "inappropriate style \"%s\" in by clause", style );
                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                        goto fail;
-                               }
                        }
 
-                       if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
-                                       ber_bvccmp( &a->acl_dn_pat, '*' ) )
-                       {
-                               free( a->acl_dn_pat.bv_val );
-                               BER_BVZERO( &a->acl_dn_pat );
-                               a->acl_dn_style = ACL_STYLE_REGEX;
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
                        }
 
-                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
-                                       a->acl_dn_style != ACL_STYLE_REGEX )
-                       {
-                               if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
-                                       struct berval bv;
-                                       rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
-                                       if ( rc != LDAP_SUCCESS ) {
-                                               snprintf( c->cr_msg, sizeof(c->cr_msg ),
-                                                       "bad DN \"%s\" in to DN clause",
-                                                       a->acl_dn_pat.bv_val );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-                                       free( a->acl_dn_pat.bv_val );
-                                       a->acl_dn_pat = bv;
+                       if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "group pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               } else {
-                                       int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
-                                               REG_EXTENDED | REG_ICASE );
-                                       if ( e ) {
-                                               char    err[ SLAP_TEXT_BUFLEN ];
+                       /* format of string is
+                          "group/objectClassValue/groupAttrName" */
+                       if ( ( value = strchr(left, '/') ) != NULL ) {
+                               *value++ = '\0';
+                               if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
+                                       *name++ = '\0';
+                               }
+                       }
 
-                                               regerror( e, &a->acl_dn_re, err, sizeof( err ) );
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                     "regular expression \"%s\" bad because of %s",
-                                                     right, err );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                       b->a_group_style = sty;
+                       if ( sty == ACL_STYLE_EXPAND ) {
+                               acl_regex_normalized_dn( right, &bv );
+                               if ( !ber_bvccmp( &bv, '*' ) ) {
+                                       if ( regtest( c, bv.bv_val ) != 0)
                                                goto fail;
-                                       }
+                               }
+                               b->a_group_pat = bv;
+
+                       } else {
+                               ber_str2bv( right, 0, 0, &bv );
+                               rc = dnNormalize( 0, NULL, NULL, &bv,
+                                               &b->a_group_pat, NULL );
+                               if ( rc != LDAP_SUCCESS ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg),
+                                                       "bad DN \"%s\"", right );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
                                }
                        }
 
-               /* by clause - select who has what access to entries */
-               } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
-                       if ( a == NULL ) {
+                       if ( value && *value ) {
+                               b->a_group_oc = oc_find( value );
+                               *--value = '/';
+
+                               if ( b->a_group_oc == NULL ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "group objectclass \"%s\" unknown", value );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+
+                       } else {
+                               b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
+
+                               if( b->a_group_oc == NULL ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "group default objectclass \"%s\" unknown", SLAPD_GROUP_CLASS );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+                       }
+
+                       if ( is_object_subclass( slap_schema.si_oc_referral,
+                                               b->a_group_oc ) )
+                       {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "to clause required before by clause in access line");
+                                               "group objectclass \"%s\" is subclass of referral", value );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                goto fail;
                        }
 
-                       /*
-                        * by clause consists of <who> and <access>
-                        */
-
-                       if ( ++i == argc ) {
+                       if ( is_object_subclass( slap_schema.si_oc_alias,
+                                               b->a_group_oc ) )
+                       {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "premature EOL: expecting <who>");
+                                               "group objectclass \"%s\" is subclass of alias", value );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                goto fail;
                        }
 
-                       b = (Access *) ch_calloc( 1, sizeof(Access) );
-
-                       ACL_INVALIDATE( b->a_access_mask );
-
-                       /* get <who> */
-                       for ( ; i < argc; i++ ) {
-                               slap_style_t    sty = ACL_STYLE_REGEX;
-                               char            *style_modifier = NULL;
-                               char            *style_level = NULL;
-                               int             level = 0;
-                               int             expand = 0;
-                               slap_dn_access  *bdn = &b->a_dn;
-                               int             is_realdn = 0;
-
-                               split( argv[i], '=', &left, &right );
-                               split( left, '.', &left, &style );
-                               if ( style ) {
-                                       split( style, ',', &style, &style_modifier );
-
-                                       if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
-                                               split( style, '{', &style, &style_level );
-                                               if ( style_level != NULL ) {
-                                                       char *p = strchr( style_level, '}' );
-                                                       if ( p == NULL ) {
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                       "premature eol: expecting closing '}' in \"level{n}\"");
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       } else if ( p == style_level ) {
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                       "empty level in \"level{n}\"");
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
-                                                       p[0] = '\0';
-                                               }
-                                       }
-                               }
+                       if ( name && *name ) {
+                               attr_name = name;
+                               *--name = '/';
 
-                               if ( style == NULL || *style == '\0' ||
-                                       strcasecmp( style, "exact" ) == 0 ||
-                                       strcasecmp( style, "baseObject" ) == 0 ||
-                                       strcasecmp( style, "base" ) == 0 )
-                               {
-                                       sty = ACL_STYLE_BASE;
+                       }
 
-                               } else if ( strcasecmp( style, "onelevel" ) == 0 ||
-                                       strcasecmp( style, "one" ) == 0 )
-                               {
-                                       sty = ACL_STYLE_ONE;
+                       rc = slap_str2ad( attr_name, &b->a_group_at, &text );
+                       if ( rc != LDAP_SUCCESS ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "group \"%s\": %s", right, text );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               } else if ( strcasecmp( style, "subtree" ) == 0 ||
-                                       strcasecmp( style, "sub" ) == 0 )
-                               {
-                                       sty = ACL_STYLE_SUBTREE;
+                       if ( !is_at_syntax( b->a_group_at->ad_type,
+                                               SLAPD_DN_SYNTAX ) /* e.g. "member" */
+                                       && !is_at_syntax( b->a_group_at->ad_type,
+                                               SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
+                                       && !is_at_subtype( b->a_group_at->ad_type,
+                                               slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
+                       {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "group \"%s\" attr \"%s\": inappropriate syntax: %s; " "must be " SLAPD_DN_SYNTAX " (DN), " SLAPD_NAMEUID_SYNTAX " (NameUID) " "or a subtype of labeledURI",
+                                               right, attr_name, at_syntax(b->a_group_at->ad_type) );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               } else if ( strcasecmp( style, "children" ) == 0 ) {
-                                       sty = ACL_STYLE_CHILDREN;
 
-                               } else if ( strcasecmp( style, "level" ) == 0 )
-                               {
-                                       if ( lutil_atoi( &level, style_level ) != 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "unable to parse level in \"level{n}\"");
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       {
+                               int rc;
+                               ObjectClass *ocs[2];
 
-                                       sty = ACL_STYLE_LEVEL;
+                               ocs[0] = b->a_group_oc;
+                               ocs[1] = NULL;
 
-                               } else if ( strcasecmp( style, "regex" ) == 0 ) {
-                                       sty = ACL_STYLE_REGEX;
+                               rc = oc_check_allowed( b->a_group_at->ad_type,
+                                               ocs, NULL );
 
-                               } else if ( strcasecmp( style, "expand" ) == 0 ) {
-                                       sty = ACL_STYLE_EXPAND;
+                               if( rc != 0 ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "group: \"%s\" not allowed by \"%s\".\n",
+                                                       b->a_group_at->ad_cname.bv_val,
+                                                       b->a_group_oc->soc_oid );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+                       }
+                       continue;
+               }
 
-                               } else if ( strcasecmp( style, "ip" ) == 0 ) {
-                                       sty = ACL_STYLE_IP;
+               if ( strcasecmp( left, "peername" ) == 0 ) {
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
+                               case ACL_STYLE_BASE:
+                                       /* legal, traditional */
+                               case ACL_STYLE_EXPAND:
+                                       /* cheap replacement to regex for simple expansion */
+                               case ACL_STYLE_IP:
+                               case ACL_STYLE_IPV6:
+                               case ACL_STYLE_PATH:
+                                       /* legal, peername specific */
+                                       break;
 
-                               } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
-#ifndef LDAP_PF_INET6
+                               default:
                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "IPv6 not supported");
+                                                       "inappropriate style \"%s\" in by clause", style );
                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-#endif /* ! LDAP_PF_INET6 */
-                                       sty = ACL_STYLE_IPV6;
+                                       goto fail;
+                       }
 
-                               } else if ( strcasecmp( style, "path" ) == 0 ) {
-                                       sty = ACL_STYLE_PATH;
-#ifndef LDAP_PF_LOCAL
-                                       snprintf( c->cr_msg, sizeof( c->cr_msg),
-                                               "\"path\" style modifier is useless without local");
-                                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
-                                       goto fail;
-#endif /* LDAP_PF_LOCAL */
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left);
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               } else {
-                                       snprintf( c->cr_msg, sizeof ( c->cr_msg ),
-                                               "unknown style \"%s\" in by clause", style );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                       goto fail;
-                               }
+                       if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "peername pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               if ( style_modifier &&
-                                       strcasecmp( style_modifier, "expand" ) == 0 )
-                               {
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "\"regex\" style implies \"expand\" modifier" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                       b->a_peername_style = sty;
+                       if ( sty == ACL_STYLE_REGEX ) {
+                               acl_regex_normalized_dn( right, &bv );
+                               if ( !ber_bvccmp( &bv, '*' ) ) {
+                                       if ( regtest( c, bv.bv_val ) != 0)
                                                goto fail;
-                                               break;
-
-                                       case ACL_STYLE_EXPAND:
-                                               break;
-
-                                       default:
-                                               /* we'll see later if it's pertinent */
-                                               expand = 1;
-                                               break;
-                                       }
-                               }
-
-                               if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
-                                       is_realdn = 1;
-                                       bdn = &b->a_realdn;
-                                       left += STRLENOF( "real" );
                                }
+                               b->a_peername_pat = bv;
 
-                               if ( strcasecmp( left, "*" ) == 0 ) {
-                                       if ( is_realdn ) {
-                                               goto fail;
-                                       }
-
-                                       ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
-                                       sty = ACL_STYLE_REGEX;
-
-                               } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
-                                       ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
-                                       sty = ACL_STYLE_ANONYMOUS;
-
-                               } else if ( strcasecmp( left, "users" ) == 0 ) {
-                                       ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
-                                       sty = ACL_STYLE_USERS;
-
-                               } else if ( strcasecmp( left, "self" ) == 0 ) {
-                                       ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
-                                       sty = ACL_STYLE_SELF;
-
-                               } else if ( strcasecmp( left, "dn" ) == 0 ) {
-                                       if ( sty == ACL_STYLE_REGEX ) {
-                                               bdn->a_style = ACL_STYLE_REGEX;
-                                               if ( right == NULL ) {
-                                                       /* no '=' */
-                                                       ber_str2bv("users",
-                                                               STRLENOF( "users" ),
-                                                               1, &bv);
-                                                       bdn->a_style = ACL_STYLE_USERS;
-
-                                               } else if (*right == '\0' ) {
-                                                       /* dn="" */
-                                                       ber_str2bv("anonymous",
-                                                               STRLENOF( "anonymous" ),
-                                                               1, &bv);
-                                                       bdn->a_style = ACL_STYLE_ANONYMOUS;
-
-                                               } else if ( strcmp( right, "*" ) == 0 ) {
-                                                       /* dn=* */
-                                                       /* any or users?  users for now */
-                                                       ber_str2bv("users",
-                                                               STRLENOF( "users" ),
-                                                               1, &bv);
-                                                       bdn->a_style = ACL_STYLE_USERS;
-
-                                               } else if ( strcmp( right, ".+" ) == 0
-                                                       || strcmp( right, "^.+" ) == 0
-                                                       || strcmp( right, ".+$" ) == 0
-                                                       || strcmp( right, "^.+$" ) == 0
-                                                       || strcmp( right, ".+$$" ) == 0
-                                                       || strcmp( right, "^.+$$" ) == 0 )
-                                               {
-                                                       ber_str2bv("users",
-                                                               STRLENOF( "users" ),
-                                                               1, &bv);
-                                                       bdn->a_style = ACL_STYLE_USERS;
-
-                                               } else if ( strcmp( right, ".*" ) == 0
-                                                       || strcmp( right, "^.*" ) == 0
-                                                       || strcmp( right, ".*$" ) == 0
-                                                       || strcmp( right, "^.*$" ) == 0
-                                                       || strcmp( right, ".*$$" ) == 0
-                                                       || strcmp( right, "^.*$$" ) == 0 )
-                                               {
-                                                       ber_str2bv("*",
-                                                               STRLENOF( "*" ),
-                                                               1, &bv);
-
-                                               } else {
-                                                       acl_regex_normalized_dn( right, &bv );
-                                                       if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                               if ( regtest( c, bv.bv_val ) != 0)
-                                                                       goto fail;
-                                                       }
-                                               }
-
-                                       } else if ( right == NULL || *right == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                       } else {
+                               ber_str2bv( right, 0, 1, &b->a_peername_pat );
 
-                                       } else {
-                                               ber_str2bv( right, 0, 1, &bv );
-                                       }
+                               if ( sty == ACL_STYLE_IP ) {
+                                       char            *addr = NULL,
+                                                               *mask = NULL,
+                                                               *port = NULL;
 
-                               } else {
-                                       BER_BVZERO( &bv );
-                               }
+                                       split( right, '{', &addr, &port );
+                                       split( addr, '%', &addr, &mask );
 
-                               if ( !BER_BVISNULL( &bv ) ) {
-                                       if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
+                                       b->a_peername_addr = inet_addr( addr );
+                                       if ( b->a_peername_addr == (unsigned long)(-1) ) {
+                                               /* illegal address */
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "dn pattern already specified" );
+                                                               "illegal peername address \"%s\"", addr );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       if ( sty != ACL_STYLE_REGEX &&
-                                                       sty != ACL_STYLE_ANONYMOUS &&
-                                                       sty != ACL_STYLE_USERS &&
-                                                       sty != ACL_STYLE_SELF &&
-                                                       expand == 0 )
-                                       {
-                                               rc = dnNormalize(0, NULL, NULL,
-                                                       &bv, &bdn->a_pat, NULL);
-                                               if ( rc != LDAP_SUCCESS ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "bad DN \"%s\" in by DN clause", bv.bv_val );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
-                                               free( bv.bv_val );
-                                               if ( sty == ACL_STYLE_BASE
-                                                       && be != NULL
-                                                       && !BER_BVISNULL( &be->be_rootndn )
-                                                       && dn_match( &bdn->a_pat, &be->be_rootndn ) )
+                                       b->a_peername_mask = (unsigned long)(-1);
+                                       if ( mask != NULL ) {
+                                               b->a_peername_mask = inet_addr( mask );
+                                               if ( b->a_peername_mask ==
+                                                               (unsigned long)(-1) )
                                                {
+                                                       /* illegal mask */
                                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "rootdn is always granted unlimited privileges" );
+                                                                       "illegal peername address mask \"%s\"", mask );
                                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                       goto fail;
                                                }
-
-                                       } else {
-                                               bdn->a_pat = bv;
                                        }
-                                       bdn->a_style = sty;
-                                       if ( expand ) {
-                                               char    *exp;
-                                               int     gotit = 0;
-
-                                               for ( exp = strchr( bdn->a_pat.bv_val, '$' );
-                                                       exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
-                                                               < bdn->a_pat.bv_len;
-                                                       exp = strchr( exp, '$' ) )
-                                               {
-                                                       if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
-                                                                   exp[ 1 ] == '{' ) ) {
-                                                               gotit = 1;
-                                                               break;
-                                                       }
-                                               }
 
-                                               if ( gotit == 1 ) {
-                                                       bdn->a_expand = expand;
+                                       b->a_peername_port = -1;
+                                       if ( port ) {
+                                               char    *end = NULL;
 
-                                               } else {
+                                               b->a_peername_port = strtol( port, &end, 10 );
+                                               if ( end == port || end[0] != '}' ) {
+                                                       /* illegal port */
                                                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "\"expand\" used with no expansions in \"pattern\"");
+                                                                       "illegal peername port specification \"{%s}\"", port );
                                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                        goto fail;
                                                }
                                        }
-                                       if ( sty == ACL_STYLE_SELF ) {
-                                               bdn->a_self_level = level;
 
-                                       } else {
-                                               if ( level < 0 ) {
-                                                       snprintf( c->cr_msg, sizeof( c ->cr_msg ),
-                                                               "bad negative level \"%d\" in by DN clause", level );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               } else if ( level == 1 ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "\"onelevel\" should be used instead of \"level{1}\" in by DN clause" );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
-                                                       snprintf ( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "\"base\" should be used instead of \"level{0}\" in by DN clause" );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               }
+#ifdef LDAP_PF_INET6
+                               } else if ( sty == ACL_STYLE_IPV6 ) {
+                                       char            *addr = NULL,
+                                                               *mask = NULL,
+                                                               *port = NULL;
 
-                                               bdn->a_level = level;
-                                       }
-                                       continue;
-                               }
+                                       split( right, '{', &addr, &port );
+                                       split( addr, '%', &addr, &mask );
 
-                               if ( strcasecmp( left, "dnattr" ) == 0 ) {
-                                       if ( right == NULL || right[0] == '\0' ) {
+                                       if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
+                                               /* illegal address */
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                                                               "illegal peername address \"%s\"", addr );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       if( bdn->a_at != NULL ) {
+                                       if ( mask == NULL ) {
+                                               mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
+                                       }
+
+                                       if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
+                                               /* illegal mask */
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "dnattr already specified" );
+                                                               "illegal peername address mask \"%s\"", mask );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       rc = slap_str2ad( right, &bdn->a_at, &text );
+                                       b->a_peername_port = -1;
+                                       if ( port ) {
+                                               char    *end = NULL;
 
-                                       if( rc != LDAP_SUCCESS ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                     "dnattr \"%s\": %s", right, text );
-                                               Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                                               b->a_peername_port = strtol( port, &end, 10 );
+                                               if ( end == port || end[0] != '}' ) {
+                                                       /* illegal port */
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                       "illegal peername port specification \"{%s}\"", port );
+                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                       goto fail;
+                                               }
                                        }
+#endif /* LDAP_PF_INET6 */
+                               }
+                       }
+                       continue;
+               }
 
+               if ( strcasecmp( left, "sockname" ) == 0 ) {
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
+                               case ACL_STYLE_BASE:
+                                       /* legal, traditional */
+                               case ACL_STYLE_EXPAND:
+                                       /* cheap replacement to regex for simple expansion */
+                                       break;
 
-                                       if( !is_at_syntax( bdn->a_at->ad_type,
-                                               SLAPD_DN_SYNTAX ) &&
-                                               !is_at_syntax( bdn->a_at->ad_type,
-                                               SLAPD_NAMEUID_SYNTAX ))
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                     "dnattr \"%s\": " "inappropriate syntax: %s",
-                                                         right, bdn->a_at->ad_type->sat_syntax_oid );
-                                               Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                               default:
+                                       /* unknown */
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "inappropriate style \"%s\" in by clause", style );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                       }
 
-                                       if( bdn->a_at->ad_type->sat_equality == NULL ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "dnattr \"%s\": inappropriate matching (no EQUALITY)", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       continue;
-                               }
+                       if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "sockname pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
-                                       char *name = NULL;
-                                       char *value = NULL;
-                                       char *attr_name = SLAPD_GROUP_ATTR;
+                       b->a_sockname_style = sty;
+                       if ( sty == ACL_STYLE_REGEX ) {
+                               acl_regex_normalized_dn( right, &bv );
+                               if ( !ber_bvccmp( &bv, '*' ) ) {
+                                       if ( regtest( c, bv.bv_val ) != 0)
+                                               goto fail;
+                               }
+                               b->a_sockname_pat = bv;
 
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                               /* legacy, tolerated */
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "deprecated group style \"regex\"; use \"expand\" instead" );
-                                               Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
-                                               sty = ACL_STYLE_EXPAND;
-                                               break;
+                       } else {
+                               ber_str2bv( right, 0, 1, &b->a_sockname_pat );
+                       }
+                       continue;
+               }
 
-                                       case ACL_STYLE_BASE:
-                                               /* legal, traditional */
-                                       case ACL_STYLE_EXPAND:
-                                               /* legal, substring expansion; supersedes regex */
-                                               break;
+               if ( strcasecmp( left, "domain" ) == 0 ) {
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
+                               case ACL_STYLE_BASE:
+                               case ACL_STYLE_SUBTREE:
+                                       /* legal, traditional */
+                                       break;
 
-                                       default:
-                                               /* unknown */
+                               case ACL_STYLE_EXPAND:
+                                       /* tolerated: means exact,expand */
+                                       if ( expand ) {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
+                                                               "\"expand\" modifier with \"expand\" style" );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
                                        }
+                                       sty = ACL_STYLE_BASE;
+                                       expand = 1;
+                                       break;
 
-                                       if ( right == NULL || right[0] == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                               default:
+                                       /* unknown */
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg),
+                                                       "inappropriate style \"%s\" in by clause", style );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                       }
 
-                                       if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "group pattern already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       /* format of string is
-                                               "group/objectClassValue/groupAttrName" */
-                                       if ( ( value = strchr(left, '/') ) != NULL ) {
-                                               *value++ = '\0';
-                                               if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
-                                                       *name++ = '\0';
-                                               }
-                                       }
+                       if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "domain pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       b->a_group_style = sty;
-                                       if ( sty == ACL_STYLE_EXPAND ) {
-                                               acl_regex_normalized_dn( right, &bv );
-                                               if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                       if ( regtest( c, bv.bv_val ) != 0)
-                                                               goto fail;
-                                               }
-                                               b->a_group_pat = bv;
+                       b->a_domain_style = sty;
+                       b->a_domain_expand = expand;
+                       if ( sty == ACL_STYLE_REGEX ) {
+                               acl_regex_normalized_dn( right, &bv );
+                               if ( !ber_bvccmp( &bv, '*' ) ) {
+                                       if ( regtest( c, bv.bv_val ) != 0)
+                                               goto fail;
+                               }
+                               b->a_domain_pat = bv;
 
-                                       } else {
-                                               ber_str2bv( right, 0, 0, &bv );
-                                               rc = dnNormalize( 0, NULL, NULL, &bv,
-                                                       &b->a_group_pat, NULL );
-                                               if ( rc != LDAP_SUCCESS ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg),
-                                                               "bad DN \"%s\"", right );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
-                                       }
+                       } else {
+                               ber_str2bv( right, 0, 1, &b->a_domain_pat );
+                       }
+                       continue;
+               }
 
-                                       if ( value && *value ) {
-                                               b->a_group_oc = oc_find( value );
-                                               *--value = '/';
+               if ( strcasecmp( left, "sockurl" ) == 0 ) {
+                       switch ( sty ) {
+                               case ACL_STYLE_REGEX:
+                               case ACL_STYLE_BASE:
+                                       /* legal, traditional */
+                               case ACL_STYLE_EXPAND:
+                                       /* cheap replacement to regex for simple expansion */
+                                       break;
 
-                                               if ( b->a_group_oc == NULL ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "group objectclass \"%s\" unknown", value );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
+                               default:
+                                       /* unknown */
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg),
+                                                       "inappropriate style \"%s\" in by clause", style );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                       }
 
-                                       } else {
-                                               b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
+                       if ( right == NULL || right[0] == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "missing \"=\" in (or value after) \"%s\" in by clause", left );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                               if( b->a_group_oc == NULL ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "group default objectclass \"%s\" unknown", SLAPD_GROUP_CLASS );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
-                                       }
+                       if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "sockurl pattern already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       if ( is_object_subclass( slap_schema.si_oc_referral,
-                                               b->a_group_oc ) )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "group objectclass \"%s\" is subclass of referral", value );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                       b->a_sockurl_style = sty;
+                       if ( sty == ACL_STYLE_REGEX ) {
+                               acl_regex_normalized_dn( right, &bv );
+                               if ( !ber_bvccmp( &bv, '*' ) ) {
+                                       if ( regtest( c, bv.bv_val ) != 0)
                                                goto fail;
-                                       }
+                               }
+                               b->a_sockurl_pat = bv;
 
-                                       if ( is_object_subclass( slap_schema.si_oc_alias,
-                                               b->a_group_oc ) )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "group objectclass \"%s\" is subclass of alias", value );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       } else {
+                               ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
+                       }
+                       continue;
+               }
 
-                                       if ( name && *name ) {
-                                               attr_name = name;
-                                               *--name = '/';
+               if ( strcasecmp( left, "set" ) == 0 ) {
+                       switch ( sty ) {
+                               /* deprecated */
+                               case ACL_STYLE_REGEX:
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "deprecated set style "
+                                                       "\"regex\" in <by> clause; "
+                                                       "use \"expand\" instead" );
+                                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
+                                       sty = ACL_STYLE_EXPAND;
+                                       /* FALLTHRU */
 
-                                       }
+                               case ACL_STYLE_BASE:
+                               case ACL_STYLE_EXPAND:
+                                       break;
 
-                                       rc = slap_str2ad( attr_name, &b->a_group_at, &text );
-                                       if ( rc != LDAP_SUCCESS ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                     "group \"%s\": %s", right, text );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                               default:
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "inappropriate style \"%s\" in by clause", style );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                       }
 
-                                       if ( !is_at_syntax( b->a_group_at->ad_type,
-                                                       SLAPD_DN_SYNTAX ) /* e.g. "member" */
-                                               && !is_at_syntax( b->a_group_at->ad_type,
-                                                       SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
-                                               && !is_at_subtype( b->a_group_at->ad_type,
-                                                       slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
-                                       {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                     "group \"%s\" attr \"%s\": inappropriate syntax: %s; " "must be " SLAPD_DN_SYNTAX " (DN), " SLAPD_NAMEUID_SYNTAX " (NameUID) " "or a subtype of labeledURI",
-                                                     right, attr_name, at_syntax(b->a_group_at->ad_type) );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "set attribute already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+
+                       if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "no set is defined" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
+                       b->a_set_style = sty;
+                       ber_str2bv( right, 0, 1, &b->a_set_pat );
 
-                                       {
-                                               int rc;
-                                               ObjectClass *ocs[2];
+                       continue;
+               }
 
-                                               ocs[0] = b->a_group_oc;
-                                               ocs[1] = NULL;
+#ifdef SLAP_DYNACL
+               {
+                       char            *name = NULL,
+                                               *opts = NULL;
 
-                                               rc = oc_check_allowed( b->a_group_at->ad_type,
-                                                       ocs, NULL );
+#if 1 /* tolerate legacy "aci" <who> */
+                       if ( strcasecmp( left, "aci" ) == 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "undocumented deprecated \"aci\" directive "
+                                               "is superseded by \"dynacl/aci\"" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               name = "aci";
 
-                                               if( rc != 0 ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                             "group: \"%s\" not allowed by \"%s\".\n",
-                                                             b->a_group_at->ad_cname.bv_val,
-                                                             b->a_group_oc->soc_oid );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
-                                               }
+                       } else
+#endif /* tolerate legacy "aci" <who> */
+                               if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
+                                       name = &left[ STRLENOF( "dynacl/" ) ];
+                                       opts = strchr( name, '/' );
+                                       if ( opts ) {
+                                               opts[ 0 ] = '\0';
+                                               opts++;
                                        }
-                                       continue;
                                }
 
-                               if ( strcasecmp( left, "peername" ) == 0 ) {
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                       case ACL_STYLE_BASE:
-                                               /* legal, traditional */
-                                       case ACL_STYLE_EXPAND:
-                                               /* cheap replacement to regex for simple expansion */
-                                       case ACL_STYLE_IP:
-                                       case ACL_STYLE_IPV6:
-                                       case ACL_STYLE_PATH:
-                                               /* legal, peername specific */
-                                               break;
-
-                                       default:
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( right == NULL || right[0] == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left);
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "peername pattern already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       b->a_peername_style = sty;
-                                       if ( sty == ACL_STYLE_REGEX ) {
-                                               acl_regex_normalized_dn( right, &bv );
-                                               if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                       if ( regtest( c, bv.bv_val ) != 0)
-                                                               goto fail;
-                                               }
-                                               b->a_peername_pat = bv;
-
-                                       } else {
-                                               ber_str2bv( right, 0, 1, &b->a_peername_pat );
+                       if ( name ) {
+                               if ( slap_dynacl_config( c, b, name, opts, sty, right ) ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                       "unable to configure dynacl \"%s\"", name );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
 
-                                               if ( sty == ACL_STYLE_IP ) {
-                                                       char            *addr = NULL,
-                                                                       *mask = NULL,
-                                                                       *port = NULL;
+                               continue;
+                       }
+               }
+#endif /* SLAP_DYNACL */
 
-                                                       split( right, '{', &addr, &port );
-                                                       split( addr, '%', &addr, &mask );
+               if ( strcasecmp( left, "ssf" ) == 0 ) {
+                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "inappropriate style \"%s\" in by clause", style );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       b->a_peername_addr = inet_addr( addr );
-                                                       if ( b->a_peername_addr == (unsigned long)(-1) ) {
-                                                               /* illegal address */
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                       "illegal peername address \"%s\"", addr );
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
+                       if ( b->a_authz.sai_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "ssf attribute already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       b->a_peername_mask = (unsigned long)(-1);
-                                                       if ( mask != NULL ) {
-                                                               b->a_peername_mask = inet_addr( mask );
-                                                               if ( b->a_peername_mask ==
-                                                                       (unsigned long)(-1) )
-                                                               {
-                                                                       /* illegal mask */
-                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                               "illegal peername address mask \"%s\"", mask );
-                                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                                       goto fail;
-                                                               }
-                                                       }
+                       if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "no ssf is defined" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       b->a_peername_port = -1;
-                                                       if ( port ) {
-                                                               char    *end = NULL;
+                       if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "unable to parse ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                               b->a_peername_port = strtol( port, &end, 10 );
-                                                               if ( end == port || end[0] != '}' ) {
-                                                                       /* illegal port */
-                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                               "illegal peername port specification \"{%s}\"", port );
-                                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                                       goto fail;
-                                                               }
-                                                       }
+                       if ( !b->a_authz.sai_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "invalid ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+                       continue;
+               }
 
-#ifdef LDAP_PF_INET6
-                                               } else if ( sty == ACL_STYLE_IPV6 ) {
-                                                       char            *addr = NULL,
-                                                                       *mask = NULL,
-                                                                       *port = NULL;
+               if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
+                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "inappropriate style \"%s\" in by clause", style );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       split( right, '{', &addr, &port );
-                                                       split( addr, '%', &addr, &mask );
+                       if ( b->a_authz.sai_transport_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "transport_ssf attribute already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
-                                                               /* illegal address */
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                       "illegal peername address \"%s\"", addr );
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
+                       if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "no transport_ssf is defined" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       if ( mask == NULL ) {
-                                                               mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
-                                                       }
+                       if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "unable to parse transport_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                       if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
-                                                               /* illegal mask */
-                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                       "illegal peername address mask \"%s\"", mask );
-                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                               goto fail;
-                                                       }
+                       if ( !b->a_authz.sai_transport_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "invalid transport_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+                       continue;
+               }
 
-                                                       b->a_peername_port = -1;
-                                                       if ( port ) {
-                                                               char    *end = NULL;
+               if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
+                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "inappropriate style \"%s\" in by clause", style );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                                               b->a_peername_port = strtol( port, &end, 10 );
-                                                               if ( end == port || end[0] != '}' ) {
-                                                                       /* illegal port */
-                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                                               "illegal peername port specification \"{%s}\"", port );
-                                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                                       goto fail;
-                                                               }
-                                                       }
-#endif /* LDAP_PF_INET6 */
-                                               }
-                                       }
-                                       continue;
-                               }
+                       if ( b->a_authz.sai_tls_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "tls_ssf attribute already specified" );
+                               goto fail;
+                       }
 
-                               if ( strcasecmp( left, "sockname" ) == 0 ) {
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                       case ACL_STYLE_BASE:
-                                               /* legal, traditional */
-                                       case ACL_STYLE_EXPAND:
-                                               /* cheap replacement to regex for simple expansion */
-                                               break;
+                       if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "no tls_ssf is defined" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       default:
-                                               /* unknown */
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "unable to parse tls_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       if ( right == NULL || right[0] == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                       if ( !b->a_authz.sai_tls_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "invalid tls_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+                       continue;
+               }
 
-                                       if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "sockname pattern already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+               if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
+                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "inappropriate style \"%s\" in by clause", style );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       b->a_sockname_style = sty;
-                                       if ( sty == ACL_STYLE_REGEX ) {
-                                               acl_regex_normalized_dn( right, &bv );
-                                               if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                       if ( regtest( c, bv.bv_val ) != 0)
-                                                               goto fail;
-                                               }
-                                               b->a_sockname_pat = bv;
+                       if ( b->a_authz.sai_sasl_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "sasl_ssf attribute already specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       } else {
-                                               ber_str2bv( right, 0, 1, &b->a_sockname_pat );
-                                       }
-                                       continue;
-                               }
+                       if ( right == NULL || *right == '\0' ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "no sasl_ssf is defined" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                               if ( strcasecmp( left, "domain" ) == 0 ) {
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                       case ACL_STYLE_BASE:
-                                       case ACL_STYLE_SUBTREE:
-                                               /* legal, traditional */
-                                               break;
+                       if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "unable to parse sasl_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
 
-                                       case ACL_STYLE_EXPAND:
-                                               /* tolerated: means exact,expand */
-                                               if ( expand ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "\"expand\" modifier with \"expand\" style" );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               }
-                                               sty = ACL_STYLE_BASE;
-                                               expand = 1;
-                                               break;
+                       if ( !b->a_authz.sai_sasl_ssf ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "invalid sasl_ssf value (%s)", right );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+                       continue;
+               }
 
-                                       default:
-                                               /* unknown */
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+               if ( right != NULL ) {
+                       /* unsplit */
+                       right[-1] = '=';
+               }
+               break;
+       }
 
-                                       if ( right == NULL || right[0] == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+       *pos = i;
+       *current = left;
+       return 0;
 
-                                       if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "domain pattern already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+fail:
+       *pos = i;
+       return 1;
+}
 
-                                       b->a_domain_style = sty;
-                                       b->a_domain_expand = expand;
-                                       if ( sty == ACL_STYLE_REGEX ) {
-                                               acl_regex_normalized_dn( right, &bv );
-                                               if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                       if ( regtest( c, bv.bv_val ) != 0)
-                                                               goto fail;
-                                               }
-                                               b->a_domain_pat = bv;
+int
+parse_acl(
+       struct config_args_s *c,
+       int             pos )
+{
+       int             i;
+       char            *left, *right, *style;
+       AccessControl   *a = NULL;
+       Access  *b = NULL;
+       int rc;
+       Backend *be = c->be;
+       const char *fname = c->fname;
+       int lineno = c->lineno;
+       int argc = c->argc;
+       char **argv = c->argv;
 
-                                       } else {
-                                               ber_str2bv( right, 0, 1, &b->a_domain_pat );
-                                       }
-                                       continue;
+       for ( i = 1; i < argc; i++ ) {
+               /* to clause - select which entries are protected */
+               if ( strcasecmp( argv[i], "to" ) == 0 ) {
+                       if ( a != NULL ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "only one to clause allowed in access line" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+                       a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
+                       a->acl_attrval_style = ACL_STYLE_NONE;
+                       for ( ++i; i < argc; i++ ) {
+                               if ( strcasecmp( argv[i], "by" ) == 0 ) {
+                                       i--;
+                                       break;
                                }
 
-                               if ( strcasecmp( left, "sockurl" ) == 0 ) {
-                                       switch ( sty ) {
-                                       case ACL_STYLE_REGEX:
-                                       case ACL_STYLE_BASE:
-                                               /* legal, traditional */
-                                       case ACL_STYLE_EXPAND:
-                                               /* cheap replacement to regex for simple expansion */
-                                               break;
-
-                                       default:
-                                               /* unknown */
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( right == NULL || right[0] == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "missing \"=\" in (or value after) \"%s\" in by clause", left );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+                               if ( strcasecmp( argv[i], "*" ) == 0 ) {
+                                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+                                               a->acl_dn_style != ACL_STYLE_REGEX )
+                                       {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "sockurl pattern already specified" );
+                                               "dn pattern already specified in to clause." );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       b->a_sockurl_style = sty;
-                                       if ( sty == ACL_STYLE_REGEX ) {
-                                               acl_regex_normalized_dn( right, &bv );
-                                               if ( !ber_bvccmp( &bv, '*' ) ) {
-                                                       if ( regtest( c, bv.bv_val ) != 0)
-                                                               goto fail;
-                                               }
-                                               b->a_sockurl_pat = bv;
-
-                                       } else {
-                                               ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
-                                       }
+                                       ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
                                        continue;
                                }
 
-                               if ( strcasecmp( left, "set" ) == 0 ) {
-                                       switch ( sty ) {
-                                               /* deprecated */
-                                       case ACL_STYLE_REGEX:
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "deprecated set style "
-                                                       "\"regex\" in <by> clause; "
-                                                       "use \"expand\" instead" );
-                                               Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
-                                               sty = ACL_STYLE_EXPAND;
-                                               /* FALLTHRU */
-
-                                       case ACL_STYLE_BASE:
-                                       case ACL_STYLE_EXPAND:
-                                               break;
-
-                                       default:
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                               split( argv[i], '=', &left, &right );
+                               split( left, '.', &left, &style );
 
-                                       if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "set attribute already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                               if ( right == NULL ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                         "missing \"=\" in \"%s\" in to clause", left );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
 
-                                       if ( right == NULL || *right == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "no set is defined" );
+                               if ( strcasecmp( left, "dn" ) == 0 ) {
+                                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+                                               a->acl_dn_style != ACL_STYLE_REGEX )
+                                       {
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg),
+                                               "dn pattern already specified in to clause" );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       b->a_set_style = sty;
-                                       ber_str2bv( right, 0, 1, &b->a_set_pat );
-
-                                       continue;
-                               }
-
-#ifdef SLAP_DYNACL
-                               {
-                                       char            *name = NULL,
-                                                       *opts = NULL;
+                                       if ( style == NULL || *style == '\0' ||
+                                               strcasecmp( style, "baseObject" ) == 0 ||
+                                               strcasecmp( style, "base" ) == 0 ||
+                                               strcasecmp( style, "exact" ) == 0 )
+                                       {
+                                               a->acl_dn_style = ACL_STYLE_BASE;
+                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
 
-#if 1 /* tolerate legacy "aci" <who> */
-                                       if ( strcasecmp( left, "aci" ) == 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "undocumented deprecated \"aci\" directive "
-                                                       "is superseded by \"dynacl/aci\"" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               name = "aci";
+                                       } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
+                                               strcasecmp( style, "one" ) == 0 )
+                                       {
+                                               a->acl_dn_style = ACL_STYLE_ONE;
+                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
 
-                                       } else
-#endif /* tolerate legacy "aci" <who> */
-                                       if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
-                                               name = &left[ STRLENOF( "dynacl/" ) ];
-                                               opts = strchr( name, '/' );
-                                               if ( opts ) {
-                                                       opts[ 0 ] = '\0';
-                                                       opts++;
-                                               }
-                                       }
+                                       } else if ( strcasecmp( style, "subtree" ) == 0 ||
+                                               strcasecmp( style, "sub" ) == 0 )
+                                       {
+                                               if( *right == '\0' ) {
+                                                       ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
 
-                                       if ( name ) {
-                                               if ( slap_dynacl_config( c, b, name, opts, sty, right ) ) {
-                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                               "unable to configure dynacl \"%s\"", name );
-                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                                       goto fail;
+                                               } else {
+                                                       a->acl_dn_style = ACL_STYLE_SUBTREE;
+                                                       ber_str2bv( right, 0, 1, &a->acl_dn_pat );
                                                }
 
-                                               continue;
-                                       }
-                               }
-#endif /* SLAP_DYNACL */
-
-                               if ( strcasecmp( left, "ssf" ) == 0 ) {
-                                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( b->a_authz.sai_ssf ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "ssf attribute already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( right == NULL || *right == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "no ssf is defined" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "unable to parse ssf value (%s)", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-
-                                       if ( !b->a_authz.sai_ssf ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "invalid ssf value (%s)", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
-                                       continue;
-                               }
+                                       } else if ( strcasecmp( style, "children" ) == 0 ) {
+                                               a->acl_dn_style = ACL_STYLE_CHILDREN;
+                                               ber_str2bv( right, 0, 1, &a->acl_dn_pat );
 
-                               if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
-                                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                                       } else if ( strcasecmp( style, "regex" ) == 0 ) {
+                                               a->acl_dn_style = ACL_STYLE_REGEX;
 
-                                       if ( b->a_authz.sai_transport_ssf ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "transport_ssf attribute already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                                               if ( *right == '\0' ) {
+                                                       /* empty regex should match empty DN */
+                                                       a->acl_dn_style = ACL_STYLE_BASE;
+                                                       ber_str2bv( right, 0, 1, &a->acl_dn_pat );
 
-                                       if ( right == NULL || *right == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "no transport_ssf is defined" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                                               } else if ( strcmp(right, "*") == 0
+                                                       || strcmp(right, ".*") == 0
+                                                       || strcmp(right, ".*$") == 0
+                                                       || strcmp(right, "^.*") == 0
+                                                       || strcmp(right, "^.*$") == 0
+                                                       || strcmp(right, ".*$$") == 0
+                                                       || strcmp(right, "^.*$$") == 0 )
+                                               {
+                                                       ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
 
-                                       if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "unable to parse transport_ssf value (%s)", right );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
-                                       }
+                                               } else {
+                                                       acl_regex_normalized_dn( right, &a->acl_dn_pat );
+                                               }
 
-                                       if ( !b->a_authz.sai_transport_ssf ) {
+                                       } else {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "invalid transport_ssf value (%s)", right );
+                                               "unknown dn style \"%s\" in to clause", style );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
+
                                        continue;
                                }
 
-                               if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
-                                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+                               if ( strcasecmp( left, "filter" ) == 0 ) {
+                                       if ( (a->acl_filter = str2filter( right )) == NULL ) {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
+                                               "bad filter \"%s\" in to clause", right );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       if ( b->a_authz.sai_tls_ssf ) {
+                               } else if ( strcasecmp( left, "attr" ) == 0             /* TOLERATED */
+                                               || strcasecmp( left, "attrs" ) == 0 )   /* DOCUMENTED */
+                               {
+                                       if ( strcasecmp( left, "attr" ) == 0 ) {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "tls_ssf attribute already specified" );
-                                               goto fail;
+                                                                 "\"attr\" is deprecated (and undocumented); "
+                                                                 "use \"attrs\" instead");
+                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                        }
 
-                                       if ( right == NULL || *right == '\0' ) {
+                                       a->acl_attrs = str2anlist( a->acl_attrs,
+                                               right, "," );
+                                       if ( a->acl_attrs == NULL ) {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "no tls_ssf is defined" );
+                                                                 "unknown attr \"%s\" in to clause", right );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
 
-                                       if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
+                               } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
+                                       struct berval   bv;
+                                       char            *mr;
+
+                                       if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "unable to parse tls_ssf value (%s)", right );
+                                                                 "attr val already specified in to clause" );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
-
-                                       if ( !b->a_authz.sai_tls_ssf ) {
+                                       if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
+                                       {
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "invalid tls_ssf value (%s)", right );
+                                               "attr val requires a single attribute");
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
-                                       continue;
-                               }
 
-                               if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
-                                       if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "inappropriate style \"%s\" in by clause", style );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                                       ber_str2bv( right, 0, 0, &bv );
+                                       a->acl_attrval_style = ACL_STYLE_BASE;
+
+                                       mr = strchr( left, '/' );
+                                       if ( mr != NULL ) {
+                                               mr[ 0 ] = '\0';
+                                               mr++;
+
+                                               a->acl_attrval_mr = mr_find( mr );
+                                               if ( a->acl_attrval_mr == NULL ) {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "invalid matching rule \"%s\"", mr);
+                                                       Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+                                                       goto fail;
+                                               }
+
+                                               if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
+                                               {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                             "matching rule \"%s\" use " "with attr \"%s\" not appropriate",
+                                                                         mr,
+                                                                         a->acl_attrs[0].an_name.bv_val );
+                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c-> log, c->cr_msg );
+                                                       goto fail;
+                                               }
                                        }
 
-                                       if ( b->a_authz.sai_sasl_ssf ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "sasl_ssf attribute already specified" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                                       if ( style != NULL ) {
+                                               if ( strcasecmp( style, "regex" ) == 0 ) {
+                                                       int e = regcomp( &a->acl_attrval_re, bv.bv_val,
+                                                               REG_EXTENDED | REG_ICASE );
+                                                       if ( e ) {
+                                                               char    err[SLAP_TEXT_BUFLEN];
+
+                                                               regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
+                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                     "regular expression \"%s\" bad because of %s",
+                                                                     right, err );
+                                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                               goto fail;
+                                                       }
+                                                       a->acl_attrval_style = ACL_STYLE_REGEX;
+
+                                               } else {
+                                                       /* FIXME: if the attribute has DN syntax, we might
+                                                        * allow one, subtree and children styles as well */
+                                                       if ( !strcasecmp( style, "base" ) ||
+                                                               !strcasecmp( style, "exact" ) ) {
+                                                               a->acl_attrval_style = ACL_STYLE_BASE;
+
+                                                       } else if ( a->acl_attrs[0].an_desc->ad_type->
+                                                               sat_syntax == slap_schema.si_syn_distinguishedName )
+                                                       {
+                                                               if ( !strcasecmp( style, "baseObject" ) ||
+                                                                       !strcasecmp( style, "base" ) )
+                                                               {
+                                                                       a->acl_attrval_style = ACL_STYLE_BASE;
+                                                               } else if ( !strcasecmp( style, "onelevel" ) ||
+                                                                       !strcasecmp( style, "one" ) )
+                                                               {
+                                                                       a->acl_attrval_style = ACL_STYLE_ONE;
+                                                               } else if ( !strcasecmp( style, "subtree" ) ||
+                                                                       !strcasecmp( style, "sub" ) )
+                                                               {
+                                                                       a->acl_attrval_style = ACL_STYLE_SUBTREE;
+                                                               } else if ( !strcasecmp( style, "children" ) ) {
+                                                                       a->acl_attrval_style = ACL_STYLE_CHILDREN;
+                                                               } else {
+                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                             "unknown val.<style> \"%s\" for attributeType \"%s\" " "with DN syntax",
+                                                                             style,
+                                                                             a->acl_attrs[0].an_desc->ad_cname.bv_val );
+                                                                       Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
+                                                                       goto fail;
+                                                               }
+
+                                                               rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
+                                                               if ( rc != LDAP_SUCCESS ) {
+                                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                             "unable to normalize DN \"%s\" " "for attributeType \"%s\" (%d)",
+                                                                             bv.bv_val,
+                                                                             a->acl_attrs[0].an_desc->ad_cname.bv_val,
+                                                                             rc );
+                                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                                       goto fail;
+                                                               }
+
+                                                       } else {
+                                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                                     "unknown val.<style> \"%s\" for attributeType \"%s\"",
+                                                                     fname,
+                                                                     a->acl_attrs[0].an_desc->ad_cname.bv_val );
+                                                               Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: %s.\n", c->log, c->cr_msg );
+                                                               goto fail;
+                                                       }
+                                               }
                                        }
 
-                                       if ( right == NULL || *right == '\0' ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "no sasl_ssf is defined" );
-                                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
-                                               goto fail;
+                                       /* Check for appropriate matching rule */
+                                       if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
+                                               ber_dupbv( &a->acl_attrval, &bv );
+
+                                       } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
+                                               int             rc;
+                                               const char      *text;
+
+                                               if ( a->acl_attrval_mr == NULL ) {
+                                                       a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
+                                               }
+
+                                               if ( a->acl_attrval_mr == NULL ) {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                               "attr \"%s\" does not have an EQUALITY matching rule",
+                                                               a->acl_attrs[ 0 ].an_name.bv_val );
+                                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                       goto fail;
+                                               }
+
+                                               rc = asserted_value_validate_normalize(
+                                                       a->acl_attrs[ 0 ].an_desc,
+                                                       a->acl_attrval_mr,
+                                                       SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+                                                       &bv,
+                                                       &a->acl_attrval,
+                                                       &text,
+                                                       NULL );
+                                               if ( rc != LDAP_SUCCESS ) {
+                                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                                             "attr \"%s\" normalization failed (%d: %s).\n",
+                                                             a->acl_attrs[0].an_name.bv_val,
+                                                             rc, text );
+                                                       Debug(LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                                       goto fail;
+                                               }
                                        }
 
-                                       if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
-                                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "unable to parse sasl_ssf value (%s)", right );
+                               } else {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "expecting <what> got \"%s\"",
+                                           left );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                                       goto fail;
+                               }
+                       }
+
+                       if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
+                                       ber_bvccmp( &a->acl_dn_pat, '*' ) )
+                       {
+                               free( a->acl_dn_pat.bv_val );
+                               BER_BVZERO( &a->acl_dn_pat );
+                               a->acl_dn_style = ACL_STYLE_REGEX;
+                       }
+
+                       if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+                                       a->acl_dn_style != ACL_STYLE_REGEX )
+                       {
+                               if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
+                                       struct berval bv;
+                                       rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
+                                       if ( rc != LDAP_SUCCESS ) {
+                                               snprintf( c->cr_msg, sizeof(c->cr_msg ),
+                                                       "bad DN \"%s\" in to DN clause",
+                                                       a->acl_dn_pat.bv_val );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
+                                       free( a->acl_dn_pat.bv_val );
+                                       a->acl_dn_pat = bv;
+
+                               } else {
+                                       int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
+                                               REG_EXTENDED | REG_ICASE );
+                                       if ( e ) {
+                                               char    err[ SLAP_TEXT_BUFLEN ];
 
-                                       if ( !b->a_authz.sai_sasl_ssf ) {
+                                               regerror( e, &a->acl_dn_re, err, sizeof( err ) );
                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                                       "invalid sasl_ssf value (%s)", right );
+                                                     "regular expression \"%s\" bad because of %s",
+                                                     right, err );
                                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
                                                goto fail;
                                        }
-                                       continue;
                                }
+                       }
 
-                               if ( right != NULL ) {
-                                       /* unsplit */
-                                       right[-1] = '=';
-                               }
-                               break;
+               /* by clause - select who has what access to entries */
+               } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
+                       if ( a == NULL ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "to clause required before by clause in access line");
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+
+                       /*
+                        * by clause consists of <who> and <access>
+                        */
+
+                       if ( ++i == argc ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "premature EOL: expecting <who>");
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                               goto fail;
+                       }
+
+                       b = (Access *) ch_calloc( 1, sizeof(Access) );
+
+                       ACL_INVALIDATE( b->a_access_mask );
+
+                       /* get <who> */
+                       if ( acl_parse_who( c, b, argv, argc, &i, &left ) ) {
+                               goto fail;
                        }
 
                        if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
@@ -2557,12 +2583,8 @@ dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
 }
 
 static char *
-access2text( Access *b, char *ptr )
+acl_who2text( Access *b, char *ptr )
 {
-       char maskbuf[ACCESSMASK_MAXLEN];
-
-       ptr = acl_safe_strcopy( ptr, "\tby" );
-
        if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
                ptr = dnaccess2text( &b->a_dn, ptr, 0 );
        }
@@ -2683,6 +2705,18 @@ access2text( Access *b, char *ptr )
        }
 
        ptr = acl_safe_strcopy( ptr, " " );
+       return ptr;
+}
+
+static char *
+access2text( Access *b, char *ptr )
+{
+       char maskbuf[ACCESSMASK_MAXLEN];
+
+       ptr = acl_safe_strcopy( ptr, "\tby" );
+
+       ptr = acl_who2text( b, ptr );
+
        if ( b->a_dn_self ) {
                ptr = acl_safe_strcopy( ptr, "self" );
        } else if ( b->a_realdn_self ) {
index b578b9fb21160752d64fb9cd30bc58738d66929b..c5a6ffd03279e374823e6389c536b396c26401cb 100644 (file)
@@ -72,6 +72,14 @@ LDAP_SLAPD_F (int) slap_access_always_allowed LDAP_P((
 
 LDAP_SLAPD_F (int) acl_check_modlist LDAP_P((
        Operation *op, Entry *e, Modifications *ml ));
+LDAP_SLAPD_F (int) acl_check_who LDAP_P((
+       Operation *op,
+       Entry *e,
+       struct berval *val,
+       AccessControl *a,
+       Access *b,
+       AclRegexMatches *matches,
+       int count ));
 
 LDAP_SLAPD_F (void) acl_append( AccessControl **l, AccessControl *a, int pos );
 
@@ -101,6 +109,10 @@ LDAP_SLAPD_F (int) acl_string_expand LDAP_P((
 LDAP_SLAPD_V (LDAP_CONST char *) style_strings[];
 
 LDAP_SLAPD_F (int) parse_acl LDAP_P(( struct config_args_s *ca, int pos ));
+LDAP_SLAPD_F (int) acl_parse_who LDAP_P((
+       struct config_args_s *ca, Access *by,
+       char **argv, int argc, int *pos,
+       char **current ));
 
 LDAP_SLAPD_F (char *) access2str LDAP_P(( slap_access_t access ));
 LDAP_SLAPD_F (slap_access_t) str2access LDAP_P(( const char *str ));