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