]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
ACIs from Mark Valence <kurash@sassafras.com> (ITS#261)
[openldap] / servers / slapd / acl.c
1 /* acl.c - routines to parse and check acl's */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <stdio.h>
10
11 #include <ac/regex.h>
12 #include <ac/socket.h>
13 #include <ac/string.h>
14
15 #include "slap.h"
16
17 #ifdef SLAPD_ACI_ENABLED
18 int aci_access_allowed (struct berval *aci, char *attr, Backend *be, Entry *e,
19                                         Operation *op, int access, char *edn, regmatch_t *matches);
20 #endif
21
22 static int      regex_matches(char *pat, char *str, char *buf, regmatch_t *matches);
23 static void     string_expand(char *newbuf, int bufsiz, char *pattern,
24                               char *match, regmatch_t *matches);
25
26
27 /*
28  * access_allowed - check whether op->o_ndn is allowed the requested access
29  * to entry e, attribute attr, value val.  if val is null, access to
30  * the whole attribute is assumed (all values).  this routine finds
31  * the applicable acl and calls acl_access_allowed() to make the
32  * decision.
33  *
34  * returns      0       access NOT allowed
35  *              1       access allowed
36  */
37
38 int
39 access_allowed(
40     Backend             *be,
41     Connection          *conn,
42     Operation           *op,
43     Entry               *e,
44     char                *attr,
45     struct berval       *val,
46     int                 access
47 )
48 {
49         int                             rc;
50         AccessControl   *a;
51         char            *edn;
52
53         regmatch_t       matches[MAXREMATCHES];
54         int              i;
55         int              n;
56
57         if ( be == NULL ) {
58                 return( 0 );
59         }
60
61         edn = e->e_ndn;
62
63         Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n",
64                 e->e_dn, attr, 0 );
65
66         /* the lastmod attributes are ignored by ACL checking */
67         if ( oc_check_no_usermod_attr( attr ) ) {
68                 Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n",
69                         attr, 0, 0 );
70                 return(1);
71         }
72
73         memset(matches, 0, sizeof(matches));
74
75         a = acl_get_applicable( be, op, e, attr, MAXREMATCHES, matches );
76
77         if (a) {
78                 for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
79                         Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ", i,
80                                (int)matches[i].rm_so, (int)matches[i].rm_eo );
81
82                         if( matches[i].rm_so <= matches[0].rm_eo ) {
83                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
84                                         Debug( LDAP_DEBUG_ARGS, "%c", edn[n], 0, 0 );
85                                 }
86                         }
87                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
88                 }
89         }
90
91         rc = acl_access_allowed( a, attr, be, conn, e, val, op, access, edn, matches );
92
93         Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n",
94                 e->e_dn, attr, 0);
95
96         return( rc );
97 }
98
99 /*
100  * acl_get_applicable - return the acl applicable to entry e, attribute
101  * attr.  the acl returned is suitable for use in subsequent calls to
102  * acl_access_allowed().
103  */
104
105 AccessControl *
106 acl_get_applicable(
107     Backend             *be,
108     Operation           *op,
109     Entry               *e,
110     char                *attr,
111     int                 nmatch,
112     regmatch_t  *matches
113 )
114 {
115         int             i;
116         AccessControl   *a;
117     char                *edn;
118
119         Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n",
120                 e->e_dn, attr, 0 );
121
122         if ( be_isroot( be, op->o_ndn ) ) {
123                 Debug( LDAP_DEBUG_ACL,
124                     "<= acl_get: no acl applicable to database root\n", 0, 0,
125                     0 );
126                 return( NULL );
127         }
128
129     edn = e->e_ndn;
130
131         Debug( LDAP_DEBUG_ARGS, "=> acl_get: edn %s\n", edn, 0, 0 );
132
133         /* check for a backend-specific acl that matches the entry */
134         for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
135                 if (a->acl_dn_pat != NULL) {
136                         Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
137                                 i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub);
138
139                         if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) {
140                                 continue;
141
142                         } else {
143                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d]  backend ACL match\n",
144                                         i, 0, 0);
145                         }
146                 }
147
148                 if ( a->acl_filter != NULL ) {
149                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
150                                 continue;
151                         }
152                 }
153
154         Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0);
155
156                 if ( attr == NULL || a->acl_attrs == NULL ||
157                         charray_inlist( a->acl_attrs, attr ) )
158                 {
159                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n",
160                                 i, e->e_dn, attr );
161                         return( a );
162                 }
163                 matches[0].rm_so = matches[0].rm_eo = -1;
164         }
165
166         /* check for a global acl that matches the entry */
167         for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
168                 if (a->acl_dn_pat != NULL) {
169                         Debug( LDAP_DEBUG_TRACE, "=> dn pat: [%d] %s nsub: %d\n", 
170                                 i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub);
171
172                         if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) {
173                                 continue;
174
175                         } else {
176                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n",
177                                         i, 0, 0);
178                         }
179                 }
180
181                 if ( a->acl_filter != NULL ) {
182                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
183                                 continue;
184                         }
185                 }
186
187                 Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0);
188
189                 if ( attr == NULL || a->acl_attrs == NULL ||
190                         charray_inlist( a->acl_attrs, attr ) )
191                 {
192                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n",
193                                 i, e->e_dn, attr );
194                         return( a );
195                 }
196
197                 matches[0].rm_so = matches[0].rm_eo = -1;
198         }
199
200         Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
201         return( NULL );
202 }
203
204 /*
205  * acl_access_allowed - check whether the given acl allows dn the
206  * requested access to entry e, attribute attr, value val.  if val
207  * is null, access to the whole attribute is assumed (all values).
208  *
209  * returns      0       access NOT allowed
210  *              1       access allowed
211  */
212
213 int
214 acl_access_allowed(
215     AccessControl       *a,
216     char                *attr,
217     Backend             *be,
218     Connection          *conn,
219     Entry               *e,
220     struct berval       *val,
221     Operation           *op,
222     int                 access,
223         char            *edn,
224         regmatch_t      *matches
225 )
226 {
227         int             i;
228         Access  *b;
229         int             default_access;
230
231         Debug( LDAP_DEBUG_ACL,
232                 "\n=> acl_access_allowed: %s access to entry \"%s\"\n",
233                 access2str( access ), e->e_dn, 0 );
234
235         Debug( LDAP_DEBUG_ACL,
236                 "\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n",
237             access2str( access ),
238                 val ? val->bv_val : "any",
239                 op->o_ndn ?  op->o_ndn : "" );
240
241         if ( be_isroot( be, op->o_ndn ) ) {
242                 Debug( LDAP_DEBUG_ACL,
243                         "<= acl_access_allowed: granted to database root\n",
244                     0, 0, 0 );
245                 return( 1 );
246         }
247
248         default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access;
249
250         if ( a == NULL ) {
251                 Debug( LDAP_DEBUG_ACL,
252                     "<= acl_access_allowed: %s by default (no matching to)\n",
253                     default_access >= access ? "granted" : "denied", 0, 0 );
254                 return( default_access >= access );
255         }
256
257         for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
258                 /* AND <who> clauses */
259                 if ( b->a_dn_pat != NULL ) {
260                         Debug( LDAP_DEBUG_TRACE, "<= check a_dn_pat: %s\n",
261                                 b->a_dn_pat, 0, 0);
262                         /*
263                          * if access applies to the entry itself, and the
264                          * user is bound as somebody in the same namespace as
265                          * the entry, OR the given dn matches the dn pattern
266                          */
267                         if ( strcasecmp( b->a_dn_pat, "anonymous" ) == 0 ) {
268                                 if (op->o_ndn != NULL && op->o_ndn[0] != '\0' ) {
269                                         continue;
270                                 }
271
272                         } else if ( strcasecmp( b->a_dn_pat, "self" ) == 0 ) {
273                                 if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
274                                         continue;
275                                 }
276                                 
277                                 if ( e->e_dn == NULL || strcmp( edn, op->o_ndn ) != 0 ) {
278                                         continue;
279                                 }
280
281                         } else if ( strcmp( b->a_dn_pat, ".*" ) != 0 &&
282                                 !regex_matches( b->a_dn_pat, op->o_ndn, edn, matches ) )
283                         {
284                                 continue;
285                         }
286                 }
287
288                 if ( b->a_sockurl_pat != NULL ) {
289                         Debug( LDAP_DEBUG_ARGS, "<= check a_sockurl_pat: %s\n",
290                                 b->a_sockurl_pat, 0, 0 );
291
292                         if ( strcmp( b->a_sockurl_pat, ".*" ) != 0 &&
293                                 !regex_matches( b->a_sockurl_pat, conn->c_listener_url,
294                                 edn, matches ) ) 
295                         {
296                                 continue;
297                         }
298                 }
299
300                 if ( b->a_domain_pat != NULL ) {
301                         Debug( LDAP_DEBUG_ARGS, "<= check a_domain_pat: %s\n",
302                                 b->a_domain_pat, 0, 0 );
303
304                         if ( strcmp( b->a_domain_pat, ".*" ) != 0 &&
305                                 !regex_matches( b->a_domain_pat, conn->c_peer_domain,
306                                 edn, matches ) ) 
307                         {
308                                 continue;
309                         }
310                 }
311
312                 if ( b->a_peername_pat != NULL ) {
313                         Debug( LDAP_DEBUG_ARGS, "<= check a_peername_path: %s\n",
314                                 b->a_peername_pat, 0, 0 );
315
316                         if ( strcmp( b->a_peername_pat, ".*" ) != 0 &&
317                                 !regex_matches( b->a_peername_pat, conn->c_peer_name,
318                                 edn, matches ) )
319                         {
320                                 continue;
321                         }
322                 }
323
324                 if ( b->a_sockname_pat != NULL ) {
325                         Debug( LDAP_DEBUG_ARGS, "<= check a_sockname_path: %s\n",
326                                 b->a_sockname_pat, 0, 0 );
327
328                         if ( strcmp( b->a_sockname_pat, ".*" ) != 0 &&
329                                 !regex_matches( b->a_sockname_pat, conn->c_sock_name,
330                                 edn, matches ) )
331                         {
332                                 continue;
333                         }
334                 }
335
336                 if ( b->a_dn_at != NULL && op->o_ndn != NULL ) {
337                         Attribute       *at;
338                         struct berval   bv;
339
340                         Debug( LDAP_DEBUG_ARGS, "<= check a_dn_at: %s\n",
341                                 b->a_dn_at, 0, 0);
342
343                         bv.bv_val = op->o_ndn;
344                         bv.bv_len = strlen( bv.bv_val );
345
346                         /* see if asker is listed in dnattr */ 
347                         if ( (at = attr_find( e->e_attrs, b->a_dn_at )) != NULL &&
348                                 value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
349                         {
350                                 if ( ACL_IS_SELF(b->a_access) && 
351                                         (val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) )
352                                 {
353                                         continue;
354                                 }
355
356                         /* asker not listed in dnattr - check for self access */
357                         } else if ( ! ACL_IS_SELF(b->a_access) || val == NULL ||
358                                 value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
359                         {
360                                 continue;
361                         }
362                 }
363
364                 if ( b->a_group_pat != NULL && op->o_ndn != NULL ) {
365                         char buf[1024];
366
367                         /* b->a_group is an unexpanded entry name, expanded it should be an 
368                          * entry with objectclass group* and we test to see if odn is one of
369                          * the values in the attribute group
370                          */
371                         /* see if asker is listed in dnattr */
372                         string_expand(buf, sizeof(buf), b->a_group_pat, edn, matches);
373                         if ( dn_normalize_case(buf) == NULL ) {
374                                 /* did not expand to a valid dn */
375                                 continue;
376                         }
377
378                         if (backend_group(be, e, buf, op->o_ndn,
379                                 b->a_group_oc, b->a_group_at) != 0)
380                         {
381                                 continue;
382                         }
383                 }
384
385 #ifdef SLAPD_ACI_ENABLED
386                 if ( b->a_aci_at != NULL ) {                            
387                         Attribute       *at;
388
389                         /* this case works different from the others above.
390                          * since aci's themselves give permissions, we need
391                          * to first check b->a_access, the ACL's access level.
392                          */
393
394                         if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
395                                 continue;
396                         }
397
398                         if ( e->e_dn == NULL ) {
399                                 continue;
400                         }
401
402                         /* first check if the right being requested is
403                          * higher than allowed by the ACL clause.
404                          */
405                         if ( ! ACL_GRANT( b->a_access, access ) ) {
406                                 continue;
407                         }
408
409                         /* get the aci attribute */
410                         at = attr_find( e->e_attrs, b->a_aci_at );
411                         if ( at == NULL ) {
412                                 continue;
413                         }
414
415                         /* the aci is an multi-valued attribute.  The
416                          * rights are determined by OR'ing the individual
417                          * rights given by the acis.
418                          */
419                         for ( i = 0; at->a_vals[i] != NULL; i++ ) {
420                                 if ( aci_access_allowed( at->a_vals[i], attr, be, e, op, access, edn, matches ) ) {
421                                         Debug( LDAP_DEBUG_ACL,
422                                                 "<= acl_access_allowed: matched by clause #%d access granted\n",
423                                                 i, 0, 0 );
424                                         return(1);
425                                 }
426                         }
427                         continue;
428                 }
429 #endif
430
431                 Debug( LDAP_DEBUG_ACL,
432                         "<= acl_access_allowed: matched by clause #%d access %s\n",
433                         i,
434                         ACL_GRANT(b->a_access, access) ? "granted" : "denied",
435                         0 );
436
437                 return ACL_GRANT(b->a_access, access );
438         }
439
440         Debug( LDAP_DEBUG_ACL,
441                 "<= acl_access_allowed: %s by default (no matching by)\n",
442             default_access >= access ? "granted" : "denied", 0, 0 );
443
444         return( default_access >= access );
445 }
446
447 /*
448  * acl_check_modlist - check access control on the given entry to see if
449  * it allows the given modifications by the user associated with op.
450  * returns      LDAP_SUCCESS    mods allowed ok
451  *              anything else   mods not allowed - return is an error
452  *                              code indicating the problem
453  */
454
455 int
456 acl_check_modlist(
457     Backend     *be,
458     Connection  *conn,
459     Operation   *op,
460     Entry       *e,
461     LDAPModList *mlist
462 )
463 {
464         int             i;
465         AccessControl   *a;
466         char    *edn = e->e_ndn;
467
468         for ( ; mlist != NULL; mlist = mlist->ml_next ) {
469                 regmatch_t       matches[MAXREMATCHES];
470
471                 /* the lastmod attributes are ignored by ACL checking */
472                 if ( oc_check_no_usermod_attr( mlist->ml_type ) ) {
473                         Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n",
474                                 mlist->ml_type, 0, 0 );
475                         continue;
476                 }
477
478                 a = acl_get_applicable( be, op, e, mlist->ml_type,
479                         MAXREMATCHES, matches );
480
481                 switch ( mlist->ml_op & ~LDAP_MOD_BVALUES ) {
482                 case LDAP_MOD_REPLACE:
483                 case LDAP_MOD_ADD:
484                         if ( mlist->ml_bvalues == NULL ) {
485                                 break;
486                         }
487                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
488                                 if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e, mlist->ml_bvalues[i], 
489                                         op, ACL_WRITE, edn, matches) ) 
490                                 {
491                                         return( LDAP_INSUFFICIENT_ACCESS );
492                                 }
493                         }
494                         break;
495
496                 case LDAP_MOD_DELETE:
497                         if ( mlist->ml_bvalues == NULL ) {
498                                 if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e,
499                                         NULL, op, ACL_WRITE, edn, matches) ) 
500                                 {
501                                         return( LDAP_INSUFFICIENT_ACCESS );
502                                 }
503                                 break;
504                         }
505                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
506                                 if ( ! acl_access_allowed( a, mlist->ml_type, be, conn, e, mlist->ml_bvalues[i], 
507                                         op, ACL_WRITE, edn, matches) ) 
508                                 {
509                                         return( LDAP_INSUFFICIENT_ACCESS );
510                                 }
511                         }
512                         break;
513                 }
514         }
515
516         return( LDAP_SUCCESS );
517 }
518
519 #ifdef SLAPD_ACI_ENABLED
520 char *
521 aci_bvstrdup (struct berval *bv)
522 {
523         char *s;
524
525         s = (char *)ch_malloc(bv->bv_len + 1);
526         if (s != NULL) {
527                 memcpy(s, bv->bv_val, bv->bv_len);
528                 s[bv->bv_len] = 0;
529         }
530         return(s);
531 }
532
533 int
534 aci_strbvcmp (char *s, struct berval *bv)
535 {
536         int res, len;
537
538         res = strncasecmp( s, bv->bv_val, bv->bv_len );
539         if (res)
540                 return(res);
541         len = strlen(s);
542         if (len > bv->bv_len)
543                 return(1);
544         if (len < bv->bv_len)
545                 return(-1);
546         return(0);
547 }
548
549 int
550 aci_get_part (struct berval *list, int ix, char sep, struct berval *bv)
551 {
552         int len;
553         char *p;
554
555         if (bv) {
556                 bv->bv_len = 0;
557                 bv->bv_val = NULL;
558         }
559         len = list->bv_len;
560         p = list->bv_val;
561         while (len >= 0 && --ix >= 0) {
562                 while (--len >= 0 && *p++ != sep) ;
563         }
564         while (len >= 0 && *p == ' ') {
565                 len--;
566                 p++;
567         }
568         if (len < 0)
569                 return(-1);
570
571         if (!bv)
572                 return(0);
573
574         bv->bv_val = p;
575         while (--len >= 0 && *p != sep) {
576                 bv->bv_len++;
577                 p++;
578         }
579         while (bv->bv_len > 0 && *--p == ' ')
580                 bv->bv_len--;
581         return(bv->bv_len);
582 }
583
584 int
585 aci_list_has_right (struct berval *list, int access, int action)
586 {
587         struct berval bv;
588         int i, right;
589
590         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
591                 if (bv.bv_len <= 0)
592                         continue;
593                 switch (*bv.bv_val) {
594                 case 'c':
595                         right = ACL_COMPARE;
596                         break;
597                 case 's':
598                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
599                          * the right 's' to mean "set", but in the examples states
600                          * that the right 's' means "search".  The latter definition
601                          * is used here.
602                          */
603                         right = ACL_SEARCH;
604                         break;
605                 case 'r':
606                         right = ACL_READ;
607                         break;
608                 case 'w':
609                         right = ACL_WRITE;
610                         break;
611                 case 'x':
612                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
613                          * define any equivalent to the AUTH right, so I've just used
614                          * 'x' for now.
615                          */
616                         right = ACL_AUTH;
617                         break;
618                 default:
619                         right = 0;
620                         break;
621                 }
622 #ifdef SLAPD_ACI_DISCRETE_RIGHTS
623                 if (right & access) {
624                         return(action);
625                 }
626         }
627         return(!action);
628 #else
629                 if (action != 0) {
630                         // check granted
631                         if (ACL_GRANT(right, access))
632                                 return(1);
633                 } else {
634                         // check denied
635                         if (right <= access)
636                                 return(1);
637                 }
638         }
639         return(0);
640 #endif
641 }
642
643 int
644 aci_list_has_attr (struct berval *list, char *attr)
645 {
646         struct berval bv;
647         int i;
648
649         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
650                 if (aci_strbvcmp(attr, &bv) == 0) {
651                         return(1);
652                 }
653         }
654         return(0);
655 }
656
657 int
658 aci_list_has_attr_right (struct berval *list, char *attr, int access, int action)
659 {
660     struct berval bv, entry;
661     int i, found;
662
663         /* loop through each rights/attr pair, skip first part (action) */
664         found = -1;
665         for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
666                 if (aci_list_has_attr(&bv, attr) == 0)
667                         continue;
668                 found = 0;
669                 if (aci_get_part(list, i, ';', &bv) < 0)
670                         continue;
671                 if (aci_list_has_right(&bv, access, action) != 0)
672                         return(1);
673         }
674         return(found);
675 }
676
677 int
678 aci_list_has_permission (struct berval *list, char *attr, int access)
679 {
680     struct berval perm, actn;
681     int i, action, specific, general;
682
683         if (attr == NULL || *attr == 0 || strcasecmp(attr, "entry") == 0) {
684                 attr = "[entry]";
685         }
686
687         /* loop through each permissions clause */
688         for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
689                 if (aci_get_part(&perm, 0, ';', &actn) < 0)
690                         continue;
691                 if (aci_strbvcmp( "grant", &actn ) == 0) {
692                         action = 1;
693                 } else if (aci_strbvcmp( "deny", &actn ) == 0) {
694                         action = 0;
695                 } else {
696                         continue;
697                 }
698
699                 specific = aci_list_has_attr_right(&perm, attr, access, action);
700                 if (specific >= 0)
701                         return(specific);
702
703                 general = aci_list_has_attr_right(&perm, "[all]", access, action);
704                 if (general >= 0)
705                         return(general);
706         }
707         return(0);
708 }
709
710 int
711 aci_group_member (
712         struct berval *subj,
713         char *grpoc,
714         char *grpat,
715     Backend             *be,
716     Entry               *e,
717     Operation           *op,
718         char            *edn,
719         regmatch_t      *matches
720 )
721 {
722         struct berval bv;
723         char *subjdn, *grpdn;
724         int rc = 0;
725
726         /* format of string is "group/objectClassValue/groupAttrName" */
727         if (aci_get_part(subj, 0, '/', &bv) < 0)
728                 return(0);
729         subjdn = aci_bvstrdup(&bv);
730         if (subjdn == NULL)
731                 return(0);
732
733         if (aci_get_part(subj, 1, '/', &bv) < 0)
734                 grpoc = ch_strdup(grpoc);
735         else
736                 grpoc = aci_bvstrdup(&bv);
737
738         if (aci_get_part(subj, 2, '/', &bv) < 0)
739                 grpat = ch_strdup(grpat);
740         else
741                 grpat = aci_bvstrdup(&bv);
742
743         grpdn = (char *)ch_malloc(1024);
744         if (grpoc != NULL && grpat != NULL && grpdn != NULL) {
745                 string_expand(grpdn, 1024, subjdn, edn, matches);
746                 if ( dn_normalize_case(grpdn) != NULL ) {
747                         rc = (backend_group(be, e, grpdn, op->o_ndn, grpoc, grpat) == 0);
748                 }
749                 ch_free(grpdn);
750         }
751         if (grpat != NULL)
752                 ch_free(grpat);
753         if (grpoc != NULL)
754                 ch_free(grpoc);
755         ch_free(subjdn);
756         return(rc);
757 }
758
759 int
760 aci_access_allowed (
761     struct berval       *aci,
762     char                        *attr,
763     Backend                     *be,
764     Entry                       *e,
765     Operation           *op,
766     int                         access,
767         char                    *edn,
768         regmatch_t              *matches
769 )
770 {
771     struct berval bv, perms, sdn;
772     char *subjdn;
773         int rc;
774
775         Debug( LDAP_DEBUG_ACL,
776                 "\n=> aci_access_allowed: %s access to entry \"%s\"\n",
777                 access2str( access ), e->e_dn, 0 );
778
779         Debug( LDAP_DEBUG_ACL,
780                 "\n=> aci_access_allowed: %s access to attribute \"%s\" by \"%s\"\n",
781             access2str( access ),
782                 attr,
783                 op->o_ndn ? op->o_ndn : "" );
784
785         /* parse an aci of the form:
786                 oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
787
788            See draft-ietf-ldapext-aci-model-0.3.txt section 9.1 for
789            a full description of the format for this attribute.
790
791            For now, this routine only supports scope=entry.
792          */
793
794         /* check that the aci has all 5 components */
795         if (aci_get_part(aci, 4, '#', NULL) < 0)
796                 return(0);
797
798         /* check that the scope is "entry" */
799         if (aci_get_part(aci, 1, '#', &bv) < 0
800                 || aci_strbvcmp( "entry", &bv ) != 0)
801         {
802                 return(0);
803         }
804
805         /* get the list of permissions clauses, bail if empty */
806         if (aci_get_part(aci, 2, '#', &perms) <= 0)
807                 return(0);
808
809         /* check if any permissions allow desired access */
810         if (aci_list_has_permission(&perms, attr, access) == 0)
811                 return(0);
812
813         /* see if we have a DN match */
814         if (aci_get_part(aci, 3, '#', &bv) < 0)
815                 return(0);
816
817         if (aci_get_part(aci, 4, '#', &sdn) < 0)
818                 return(0);
819         if (aci_strbvcmp( "access-id", &bv ) == 0) {
820                 subjdn = aci_bvstrdup(&sdn);
821                 if (subjdn == NULL)
822                         return(0);
823                 rc = 0;
824                 if (dn_normalize_case(subjdn) != NULL)
825                         rc = (strcasecmp(op->o_ndn, subjdn) == 0);
826                 ch_free(subjdn);
827                 return(rc);
828         }
829
830         if (aci_strbvcmp( "self", &bv ) == 0) {
831                 return(strcasecmp(op->o_ndn, edn) == 0);
832         }
833
834         if (aci_strbvcmp( "group", &bv ) == 0) {
835                 return(aci_group_member(&sdn, "groupOfNames", "member", be, e, op, edn, matches));
836         }
837
838         if (aci_strbvcmp( "role", &bv ) == 0) {
839                 return(aci_group_member(&sdn, "organizationalRole", "roleOccupant", be, e, op, edn, matches));
840         }
841
842         return(0);
843 }
844 #endif  /* SLAPD_ACI_ENABLED */
845
846 static void
847 string_expand(
848         char *newbuf,
849         int bufsiz,
850         char *pat,
851         char *match,
852         regmatch_t *matches)
853 {
854         int     size;
855         char   *sp;
856         char   *dp;
857         int     flag;
858
859         size = 0;
860         newbuf[0] = '\0';
861
862         flag = 0;
863         for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) {
864                 /* did we previously see a $ */
865                 if (flag) {
866                         if (*sp == '$') {
867                                 *dp++ = '$';
868                                 size++;
869                         } else if (*sp >= '0' && *sp <= '9' ) {
870                                 int     n;
871                                 int     i;
872                                 int     l;
873
874                                 n = *sp - '0';
875                                 *dp = '\0';
876                                 i = matches[n].rm_so;
877                                 l = matches[n].rm_eo; 
878                                 for ( ; size < 512 && i < l; size++, i++ ) {
879                                         *dp++ = match[i];
880                                         size++;
881                                 }
882                                 *dp = '\0';
883                         }
884                         flag = 0;
885                 } else {
886                         if (*sp == '$') {
887                                 flag = 1;
888                         } else {
889                                 *dp++ = *sp;
890                                 size++;
891                         }
892                 }
893         }
894         *dp = '\0';
895
896         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
897         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
898 }
899
900 static int
901 regex_matches(
902         char *pat,                              /* pattern to expand and match against */
903         char *str,                              /* string to match against pattern */
904         char *buf,                              /* buffer with $N expansion variables */
905         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
906 )
907 {
908         regex_t re;
909         char newbuf[512];
910         int     rc;
911
912         if(str == NULL) str = "";
913
914         string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
915         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
916                 char error[512];
917                 regerror(rc, &re, error, sizeof(error));
918
919                 Debug( LDAP_DEBUG_TRACE,
920                     "compile( \"%s\", \"%s\") failed %s\n",
921                         pat, str, error );
922                 return( 0 );
923         }
924
925         rc = regexec(&re, str, 0, NULL, 0);
926         regfree( &re );
927
928         Debug( LDAP_DEBUG_TRACE,
929             "=> regex_matches: string:   %s\n", str, 0, 0 );
930         Debug( LDAP_DEBUG_TRACE,
931             "=> regex_matches: rc: %d %s\n",
932                 rc, !rc ? "matches" : "no matches", 0 );
933         return( !rc );
934 }
935