+ 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(
+ char *newbuf,
+ int bufsiz,
+ char *pat,
+ char *match,
+ regmatch_t *matches)
+{
+ int size;
+ char *sp;
+ char *dp;
+ int flag;
+
+ size = 0;
+ newbuf[0] = '\0';
+ bufsiz--; /* leave space for lone $ */
+
+ flag = 0;
+ for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) {
+ /* did we previously see a $ */
+ if (flag) {
+ if (*sp == '$') {
+ *dp++ = '$';
+ size++;
+ } else if (*sp >= '0' && *sp <= '9' ) {
+ int n;
+ int i;
+ int l;
+
+ n = *sp - '0';
+ *dp = '\0';
+ i = matches[n].rm_so;
+ l = matches[n].rm_eo;
+ for ( ; size < 512 && i < l; size++, i++ ) {
+ *dp++ = match[i];
+ size++;
+ }
+ *dp = '\0';
+ }
+ flag = 0;
+ } else {
+ if (*sp == '$') {
+ flag = 1;
+ } else {
+ *dp++ = *sp;
+ size++;
+ }
+ }
+ }
+
+ if (flag) {
+ /* must have ended with a single $ */
+ *dp++ = '$';
+ size++;
+ }
+
+ *dp = '\0';
+
+ Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern: %s\n", pat, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
+}
+
+static int
+regex_matches(
+ char *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ char *buf, /* buffer with $N expansion variables */
+ regmatch_t *matches /* offsets in buffer for $N expansion variables */
+)
+{
+ regex_t re;
+ char newbuf[512];