+ if ( !got_match ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+acl_mask_dnattr(
+ Operation *op,
+ Entry *e,
+ struct berval *val,
+ AccessControl *a,
+ Access *b,
+ int i,
+ regmatch_t *matches,
+ int count,
+ AccessControlState *state,
+ slap_dn_access *bdn,
+ struct berval *opndn )
+{
+ Attribute *at;
+ struct berval bv;
+ int rc, match = 0;
+ const char *text;
+ const char *attr = bdn->a_at->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ if ( BER_BVISEMPTY( opndn ) ) {
+ return 1;
+ }
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 );
+ bv = *opndn;
+
+ /* see if asker is listed in dnattr */
+ for ( at = attrs_find( e->e_attrs, bdn->a_at );
+ at != NULL;
+ at = attrs_find( at->a_next, bdn->a_at ) )
+ {
+ if ( value_find_ex( bdn->a_at,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ at->a_nvals,
+ &bv, op->o_tmpmemctx ) == 0 )
+ {
+ /* found it */
+ match = 1;
+ break;
+ }
+ }
+
+ if ( match ) {
+ /* have a dnattr match. if this is a self clause then
+ * the target must also match the op dn.
+ */
+ if ( bdn->a_self ) {
+ /* check if the target is an attribute. */
+ if ( val == NULL ) return 1;
+
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, bdn->a_at,
+ bdn->a_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
+ /* on match error or no match, fail the ACL clause */
+ if ( rc != LDAP_SUCCESS || match != 0 )
+ return 1;
+ }
+
+ } else {
+ /* no dnattr match, check if this is a self clause */
+ if ( ! bdn->a_self )
+ return 1;
+
+ ACL_RECORD_VALUE_STATE;
+
+ /* this is a self clause, check if the target is an
+ * attribute.
+ */
+ if ( val == NULL )
+ return 1;
+
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, bdn->a_at,
+ bdn->a_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
+
+ /* on match error or no match, fail the ACL clause */
+ if ( rc != LDAP_SUCCESS || match != 0 )
+ 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,
+ slap_mask_t *mask,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ int count,
+ AccessControlState *state )
+{
+ int i;
+ Access *b;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif /* DEBUG */
+ const char *attr;
+ slap_mask_t a2pmask = ACL_ACCESS2PRIV( *mask );
+
+ 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, 0 );
+
+ 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 );
+
+ /* 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, 0, 0);
+ /*
+ * 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_mask_dn( op, e, desc, val, a, nmatch, matches,
+ &b->a_dn, &op->o_ndn ) )
+ {
+ continue;