+static int
+aci_group_member (
+ struct berval *subj,
+ const char *defgrpoc,
+ const char *defgrpat,
+ Backend *be,
+ Entry *e,
+ Operation *op,
+ regmatch_t *matches
+)
+{
+ struct berval bv;
+ char *subjdn, *grpdn = NULL;
+ char *grpoc;
+ char *grpat;
+ ObjectClass *grp_oc = NULL;
+ AttributeDescription *grp_ad = NULL;
+ char *text;
+ int rc;
+
+ /* format of string is "group/objectClassValue/groupAttrName" */
+ if (aci_get_part(subj, 0, '/', &bv) < 0) {
+ return(0);
+ }
+
+ subjdn = aci_bvstrdup(&bv);
+ if (subjdn == NULL) {
+ return(0);
+ }
+
+ if (aci_get_part(subj, 1, '/', &bv) < 0) {
+ grpoc = ch_strdup( defgrpoc );
+ } else {
+ grpoc = aci_bvstrdup(&bv);
+ }
+
+ if (aci_get_part(subj, 2, '/', &bv) < 0) {
+ grpat = ch_strdup( defgrpat );
+ } else {
+ grpat = aci_bvstrdup(&bv);
+ }
+
+ rc = slap_str2ad( grpat, &grp_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ rc = 0;
+ goto done;
+ }
+ rc = 0;
+
+ grp_oc = oc_find( grpoc );
+ grpdn = (char *)ch_malloc(1024);
+
+ if (grp_oc != NULL && grp_ad != NULL && grpdn != NULL) {
+ string_expand(grpdn, 1024, subjdn, e->e_ndn, matches);
+ if ( dn_normalize(grpdn) != NULL ) {
+ rc = (backend_group(be, e, grpdn, op->o_ndn, grp_oc, grp_ad) == 0);
+ }
+ }
+
+done:
+ if( grp_ad != NULL ) ad_free( grp_ad, 1 );
+ ch_free(grpdn);
+ ch_free(grpat);
+ ch_free(grpoc);
+ ch_free(subjdn);
+ return(rc);
+}
+
+static int
+aci_mask(
+ Backend *be,
+ Connection *conn,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny
+)
+{
+ struct berval bv, perms, sdn;
+ char *subjdn;
+ int rc;
+ char *attr = desc->ad_cname->bv_val;
+
+ /* parse an aci of the form:
+ oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
+
+ See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
+ a full description of the format for this attribute.
+
+ For now, this routine only supports scope=entry.
+ */
+
+ /* check that the aci has all 5 components */
+ if (aci_get_part(aci, 4, '#', NULL) < 0)
+ return(0);
+
+ /* check that the aci family is supported */
+ if (aci_get_part(aci, 0, '#', &bv) < 0)
+ return(0);
+
+ /* check that the scope is "entry" */
+ if (aci_get_part(aci, 1, '#', &bv) < 0
+ || aci_strbvcmp( "entry", &bv ) != 0)
+ {
+ return(0);
+ }
+
+ /* get the list of permissions clauses, bail if empty */
+ if (aci_get_part(aci, 2, '#', &perms) <= 0)
+ return(0);
+
+ /* check if any permissions allow desired access */
+ if (aci_list_get_rights(&perms, attr, val, grant, deny) == 0)
+ return(0);
+
+ /* see if we have a DN match */
+ if (aci_get_part(aci, 3, '#', &bv) < 0)
+ return(0);
+
+ if (aci_get_part(aci, 4, '#', &sdn) < 0)
+ return(0);
+
+ if (aci_strbvcmp( "access-id", &bv ) == 0) {
+ subjdn = aci_bvstrdup(&sdn);
+ if (subjdn == NULL)
+ return(0);
+ rc = 1;
+ if ( dn_normalize(subjdn) != NULL )
+ if (strcasecmp(op->o_ndn, subjdn) != 0)
+ rc = 0;
+ ch_free(subjdn);
+ return(rc);
+ }
+
+ if (aci_strbvcmp( "self", &bv ) == 0) {
+ if (strcasecmp(op->o_ndn, e->e_ndn) == 0)
+ return(1);
+
+ } else if (aci_strbvcmp( "dnattr", &bv ) == 0) {
+ char *dnattr = aci_bvstrdup(&sdn);
+ Attribute *at;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_str2ad( dnattr, &ad, &text );
+ ch_free( dnattr );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ rc = 0;
+
+ bv.bv_val = op->o_ndn;
+ bv.bv_len = strlen( bv.bv_val );
+
+ for(at = attrs_find( e->e_attrs, ad );
+ at != NULL;
+ at = attrs_find( at->a_next, ad ) )
+ {
+ if (value_find( ad, at->a_vals, &bv) == 0 ) {
+ rc = 1;
+ break;
+ }
+ }
+
+ ad_free( ad, 1 );
+ return rc;
+
+
+ } else if (aci_strbvcmp( "group", &bv ) == 0) {
+ if (aci_group_member(&sdn, SLAPD_GROUP_CLASS, SLAPD_GROUP_ATTR, be, e, op, matches))
+ return(1);
+
+ } else if (aci_strbvcmp( "role", &bv ) == 0) {
+ if (aci_group_member(&sdn, SLAPD_ROLE_CLASS, SLAPD_ROLE_ATTR, be, e, op, matches))
+ return(1);
+
+ }
+
+ return(0);
+}
+
+#endif /* SLAPD_ACI_ENABLED */
+
+static void
+string_expand(