]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
added ber_[mem|case]cmp() macros for fast berval comparison; extensively used in...
[openldap] / servers / slapd / acl.c
1 /* acl.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 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 #include "sets.h"
18
19
20 /*
21  * speed up compares
22  */
23 static struct berval 
24         aci_bv_entry            = { sizeof("entry") - 1,        "entry" },
25         aci_bv_br_entry         = { sizeof("[entry]") - 1,      "[entry]" },
26         aci_bv_br_all           = { sizeof("[all]") - 1,        "[all]" },
27         aci_bv_access_id        = { sizeof("access-id") - 1,    "access-id" },
28         aci_bv_anonymous        = { sizeof("anonymous") - 1,    "anonymous" },
29         aci_bv_users            = { sizeof("users") - 1,        "users" },
30         aci_bv_self             = { sizeof("self") - 1,         "self" },
31         aci_bv_dnattr           = { sizeof("dnattr") - 1,       "dnattr" },
32         aci_bv_group            = { sizeof("group") - 1,        "group" },
33         aci_bv_role             = { sizeof("role") - 1,         "role" },
34         aci_bv_set              = { sizeof("set") - 1,          "set" },
35         aci_bv_set_ref          = { sizeof("set-ref") - 1,      "set-ref"},
36         aci_bv_grant            = { sizeof("grant") - 1,        "grant" },
37         aci_bv_deny             = { sizeof("deny") - 1,         "deny" };
38
39 static AccessControl * acl_get(
40         AccessControl *ac, int *count,
41         Backend *be, Operation *op,
42         Entry *e,
43         AttributeDescription *desc,
44         int nmatches, regmatch_t *matches );
45
46 static slap_control_t acl_mask(
47         AccessControl *ac, slap_mask_t *mask,
48         Backend *be, Connection *conn, Operation *op,
49         Entry *e,
50         AttributeDescription *desc,
51         struct berval *val,
52         regmatch_t *matches );
53
54 #ifdef SLAPD_ACI_ENABLED
55 static int aci_mask(
56         Backend *be,
57     Connection *conn,
58         Operation *op,
59         Entry *e,
60         AttributeDescription *desc,
61         struct berval *val,
62         struct berval *aci,
63         regmatch_t *matches,
64         slap_access_t *grant,
65         slap_access_t *deny );
66 #endif
67
68 static int      regex_matches(
69         char *pat, char *str, char *buf, regmatch_t *matches);
70 static void     string_expand(
71         struct berval *newbuf, char *pattern,
72         char *match, regmatch_t *matches);
73
74 typedef struct AciSetCookie {
75         Backend *be;
76         Entry *e;
77         Connection *conn;
78         Operation *op;
79 } AciSetCookie;
80
81 BerVarray aci_set_gather (void *cookie, char *name, struct berval *attr);
82 static int aci_match_set ( struct berval *subj, Backend *be,
83     Entry *e, Connection *conn, Operation *op, int setref );
84
85 /*
86  * access_allowed - check whether op->o_ndn is allowed the requested access
87  * to entry e, attribute attr, value val.  if val is null, access to
88  * the whole attribute is assumed (all values).
89  *
90  * This routine loops through all access controls and calls
91  * acl_mask() on each applicable access control.
92  * The loop exits when a definitive answer is reached or
93  * or no more controls remain.
94  *
95  * returns:
96  *              0       access denied
97  *              1       access granted
98  */
99
100 int
101 access_allowed(
102     Backend             *be,
103     Connection          *conn,
104     Operation           *op,
105     Entry               *e,
106         AttributeDescription    *desc,
107     struct berval       *val,
108     slap_access_t       access )
109 {
110         int                             count;
111         AccessControl   *a;
112 #ifdef LDAP_DEBUG
113         char accessmaskbuf[ACCESSMASK_MAXLEN];
114 #endif
115         slap_mask_t mask;
116         slap_control_t control;
117         const char *attr;
118         regmatch_t matches[MAXREMATCHES];
119
120         assert( e != NULL );
121         assert( desc != NULL );
122         assert( access > ACL_NONE );
123
124         attr = desc->ad_cname.bv_val;
125
126         assert( attr != NULL );
127
128 #ifdef NEW_LOGGING
129         LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
130                 "access_allowed: conn %d %s access to \"%s\" \"%s\" requested\n",
131                 conn ? conn->c_connid : -1, access2str( access ), e->e_dn, attr ));
132 #else
133         Debug( LDAP_DEBUG_ACL,
134                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
135             access2str( access ), e->e_dn, attr );
136 #endif
137
138         if ( op == NULL ) {
139                 /* no-op call */
140                 return 1;
141         }
142
143         if ( be == NULL ) be = &backends[0];
144         assert( be != NULL );
145
146         /* grant database root access */
147         if ( be != NULL && be_isroot( be, &op->o_ndn ) ) {
148 #ifdef NEW_LOGGING
149                 LDAP_LOG(( "acl", LDAP_LEVEL_INFO,
150                        "access_allowed: conn %d root access granted\n",
151                        conn->c_connid));
152 #else
153                 Debug( LDAP_DEBUG_ACL,
154                     "<= root access granted\n",
155                         0, 0, 0 );
156 #endif
157                 return 1;
158         }
159
160         /*
161          * no-user-modification operational attributes are ignored
162          * by ACL_WRITE checking as any found here are not provided
163          * by the user
164          */
165         if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
166                 && desc != slap_schema.si_ad_entry
167                 && desc != slap_schema.si_ad_children )
168         {
169 #ifdef NEW_LOGGING
170                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
171                        "access_allowed: conn %d NoUserMod Operational attribute: %s access granted\n",
172                        conn->c_connid, attr ));
173 #else
174                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
175                         " %s access granted\n",
176                         attr, 0, 0 );
177 #endif
178                 return 1;
179         }
180
181         /* use backend default access if no backend acls */
182         if( be != NULL && be->be_acl == NULL ) {
183 #ifdef NEW_LOGGING
184                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
185                        "access_allowed: conn %d backend default %s access %s to \"%s\"\n",
186                        conn->c_connid, access2str( access ),
187                        be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val ));
188 #else
189                 Debug( LDAP_DEBUG_ACL,
190                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
191                         access2str( access ),
192                         be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val );
193 #endif
194                 return be->be_dfltaccess >= access;
195
196 #ifdef notdef
197         /* be is always non-NULL */
198         /* use global default access if no global acls */
199         } else if ( be == NULL && global_acl == NULL ) {
200 #ifdef NEW_LOGGING
201                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
202                        "access_allowed: conn %d global default %s access %s to \"%s\"\n",
203                        conn->c_connid, access2str( access ),
204                        global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val ));
205 #else
206                 Debug( LDAP_DEBUG_ACL,
207                         "=> access_allowed: global default %s access %s to \"%s\"\n",
208                         access2str( access ),
209                         global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val );
210 #endif
211                 return global_default_access >= access;
212 #endif
213         }
214
215         ACL_INIT(mask);
216         memset(matches, '\0', sizeof(matches));
217         
218         control = ACL_BREAK;
219         a = NULL;
220         count = 0;
221
222         while((a = acl_get( a, &count, be, op, e, desc, MAXREMATCHES, matches )) != NULL)
223         {
224                 int i;
225
226                 for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
227 #ifdef NEW_LOGGING
228                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
229                                "access_allowed: conn %d match[%d]:  %d %d ",
230                                conn->c_connid, i, (int)matches[i].rm_so, (int)matches[i].rm_eo ));
231 #else
232                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
233                                (int)matches[i].rm_so, (int)matches[i].rm_eo );
234 #endif
235                         if( matches[i].rm_so <= matches[0].rm_eo ) {
236                                 int n;
237                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
238                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
239                                 }
240                         }
241 #ifdef NEW_LOGGING
242                         LDAP_LOG(( "acl", LDAP_LEVEL_ARGS, "\n" ));
243 #else
244                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
245 #endif
246                 }
247
248                 control = acl_mask( a, &mask, be, conn, op,
249                         e, desc, val, matches );
250
251                 if ( control != ACL_BREAK ) {
252                         break;
253                 }
254
255                 memset(matches, '\0', sizeof(matches));
256         }
257
258         if ( ACL_IS_INVALID( mask ) ) {
259 #ifdef NEW_LOGGING
260                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
261                        "access_allowed: conn %d  \"%s\" (%s) invalid!\n",
262                        conn->c_connid, e->e_dn, attr ));
263 #else
264                 Debug( LDAP_DEBUG_ACL,
265                         "=> access_allowed: \"%s\" (%s) invalid!\n",
266                         e->e_dn, attr, 0 );
267 #endif
268                 ACL_INIT( mask );
269
270         } else if ( control == ACL_BREAK ) {
271 #ifdef NEW_LOGGING
272                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
273                        "access_allowed: conn %d  no more rules\n", conn->c_connid ));
274 #else
275                 Debug( LDAP_DEBUG_ACL,
276                         "=> access_allowed: no more rules\n", 0, 0, 0);
277 #endif
278                 ACL_INIT( mask );
279         }
280
281 #ifdef NEW_LOGGING
282         LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
283                    "access_allowed: conn %d  %s access %s by %s\n",
284                    conn->c_connid,
285                    access2str( access ),
286                    ACL_GRANT( mask, access ) ? "granted" : "denied",
287                    accessmask2str( mask, accessmaskbuf ) ));
288 #else
289         Debug( LDAP_DEBUG_ACL,
290                 "=> access_allowed: %s access %s by %s\n",
291                 access2str( access ),
292                 ACL_GRANT(mask, access) ? "granted" : "denied",
293                 accessmask2str( mask, accessmaskbuf ) );
294 #endif
295         return ACL_GRANT(mask, access);
296 }
297
298 /*
299  * acl_get - return the acl applicable to entry e, attribute
300  * attr.  the acl returned is suitable for use in subsequent calls to
301  * acl_access_allowed().
302  */
303
304 static AccessControl *
305 acl_get(
306         AccessControl *a,
307         int                     *count,
308     Backend             *be,
309     Operation   *op,
310     Entry               *e,
311         AttributeDescription *desc,
312     int                 nmatch,
313     regmatch_t  *matches )
314 {
315         const char *attr;
316         int dnlen, patlen;
317
318         assert( e != NULL );
319         assert( count != NULL );
320         assert( desc != NULL );
321
322         attr = desc->ad_cname.bv_val;
323
324         assert( attr != NULL );
325
326         if( a == NULL ) {
327                 if( be == NULL ) {
328                         a = global_acl;
329                 } else {
330                         a = be->be_acl;
331                 }
332
333                 assert( a != NULL );
334
335         } else {
336                 a = a->acl_next;
337         }
338
339         dnlen = e->e_nname.bv_len;
340
341         for ( ; a != NULL; a = a->acl_next ) {
342                 (*count) ++;
343
344                 if (a->acl_dn_pat.bv_len != 0) {
345                         if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
346 #ifdef NEW_LOGGING
347                                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
348                                            "acl_get: dnpat [%d] %s nsub: %d\n",
349                                            *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub ));
350 #else
351                                 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
352                                         *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
353 #endif
354                                 if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
355                                         continue;
356
357                         } else {
358 #ifdef NEW_LOGGING
359                                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
360                                            "acl_get: dn [%d] %s\n",
361                                            *count, a->acl_dn_pat.bv_val ));
362 #else
363                                 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
364                                         *count, a->acl_dn_pat.bv_val, 0 );
365 #endif
366                                 patlen = a->acl_dn_pat.bv_len;
367                                 if ( dnlen < patlen )
368                                         continue;
369
370                                 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
371                                         /* base dn -- entire object DN must match */
372                                         if ( dnlen != patlen )
373                                                 continue;
374
375                                 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
376                                         int rdnlen = -1;
377
378                                         if ( dnlen <= patlen )
379                                                 continue;
380
381                                         if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) )
382                                                 continue;
383
384                                         rdnlen = dn_rdnlen( NULL, &e->e_nname );
385                                         if ( rdnlen != dnlen - patlen - 1 )
386                                                 continue;
387
388                                 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
389                                         if ( dnlen > patlen && ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) ) )
390                                                 continue;
391
392                                 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
393                                         if ( dnlen <= patlen )
394                                                 continue;
395                                         if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) )
396                                                 continue;
397                                 }
398
399                                 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
400                                         continue;
401                         }
402
403 #ifdef NEW_LOGGING
404                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
405                                    "acl_get: [%d] matched\n",
406                                    *count ));
407 #else
408                         Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
409                                 *count, 0, 0 );
410 #endif
411                 }
412
413                 if ( a->acl_filter != NULL ) {
414                         ber_int_t rc = test_filter( NULL, NULL, NULL, e, a->acl_filter );
415                         if ( rc != LDAP_COMPARE_TRUE ) {
416                                 continue;
417                         }
418                 }
419
420 #ifdef NEW_LOGGING
421                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
422                            "acl_get: [%d] check attr %s\n",
423                            *count, attr ));
424 #else
425                 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
426                        *count, attr, 0);
427 #endif
428                 if ( attr == NULL || a->acl_attrs == NULL ||
429                         ad_inlist( desc, a->acl_attrs ) )
430                 {
431 #ifdef NEW_LOGGING
432                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
433                                    "acl_get:  [%d] acl %s attr: %s\n",
434                                    *count, e->e_dn, attr ));
435 #else
436                         Debug( LDAP_DEBUG_ACL,
437                                 "<= acl_get: [%d] acl %s attr: %s\n",
438                                 *count, e->e_dn, attr );
439 #endif
440                         return a;
441                 }
442                 matches[0].rm_so = matches[0].rm_eo = -1;
443         }
444
445 #ifdef NEW_LOGGING
446         LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
447                    "acl_get: done.\n" ));
448 #else
449         Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
450 #endif
451         return( NULL );
452 }
453
454
455 /*
456  * acl_mask - modifies mask based upon the given acl and the
457  * requested access to entry e, attribute attr, value val.  if val
458  * is null, access to the whole attribute is assumed (all values).
459  *
460  * returns      0       access NOT allowed
461  *              1       access allowed
462  */
463
464 static slap_control_t
465 acl_mask(
466     AccessControl       *a,
467         slap_mask_t *mask,
468     Backend             *be,
469     Connection  *conn,
470     Operation   *op,
471     Entry               *e,
472         AttributeDescription *desc,
473     struct berval       *val,
474         regmatch_t      *matches
475 )
476 {
477         int             i, odnlen, patlen;
478         Access  *b;
479 #ifdef LDAP_DEBUG
480         char accessmaskbuf[ACCESSMASK_MAXLEN];
481 #endif
482         const char *attr;
483
484         assert( a != NULL );
485         assert( mask != NULL );
486         assert( desc != NULL );
487
488         attr = desc->ad_cname.bv_val;
489
490         assert( attr != NULL );
491
492 #ifdef NEW_LOGGING
493         LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
494                    "acl_mask: conn %d  access to entry \"%s\", attr \"%s\" requested\n",
495                    conn->c_connid, e->e_dn, attr ));
496
497         LDAP_LOG(( "acl", LDAP_LEVEL_ARGS,
498                    " to %s by \"%s\", (%s) \n",
499                    val ? "value" : "all values",
500                    op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
501                    accessmask2str( *mask, accessmaskbuf ) ));
502 #else
503         Debug( LDAP_DEBUG_ACL,
504                 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
505                 e->e_dn, attr, 0 );
506
507         Debug( LDAP_DEBUG_ACL,
508                 "=> acl_mask: to %s by \"%s\", (%s) \n",
509                 val ? "value" : "all values",
510                 op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
511                 accessmask2str( *mask, accessmaskbuf ) );
512 #endif
513
514         for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
515                 slap_mask_t oldmask, modmask;
516
517                 ACL_INVALIDATE( modmask );
518
519                 /* AND <who> clauses */
520                 if ( b->a_dn_pat.bv_len != 0 ) {
521 #ifdef NEW_LOGGING
522                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
523                                    "acl_mask: conn %d  check a_dn_pat: %s\n",
524                                    conn->c_connid, b->a_dn_pat.bv_val ));
525 #else
526                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
527                                 b->a_dn_pat.bv_val, 0, 0);
528 #endif
529                         /*
530                          * if access applies to the entry itself, and the
531                          * user is bound as somebody in the same namespace as
532                          * the entry, OR the given dn matches the dn pattern
533                          */
534                         if ( ber_cmp( &b->a_dn_pat, &aci_bv_anonymous ) == 0 ) {
535                                 if ( op->o_ndn.bv_len != 0 ) {
536                                         continue;
537                                 }
538
539                         } else if ( ber_cmp( &b->a_dn_pat, &aci_bv_users ) == 0 ) {
540                                 if ( op->o_ndn.bv_len == 0 ) {
541                                         continue;
542                                 }
543
544                         } else if ( ber_cmp( &b->a_dn_pat, &aci_bv_self ) == 0 ) {
545                                 if ( op->o_ndn.bv_len == 0 ) {
546                                         continue;
547                                 }
548                                 
549                                 if ( e->e_dn == NULL || !dn_match( &e->e_nname, &op->o_ndn ) ) {
550                                         continue;
551                                 }
552
553                         } else if ( b->a_dn_style == ACL_STYLE_REGEX ) {
554                                 if ( b->a_dn_pat.bv_len != 1 || 
555                                     ber_charcmp( &b->a_dn_pat, '*' ) != 0 ) {
556                                         int ret = regex_matches( b->a_dn_pat.bv_val,
557                                                 op->o_ndn.bv_val, e->e_ndn, matches );
558
559                                         if( ret == 0 ) {
560                                                 continue;
561                                         }
562                                 }
563
564                         } else {
565                                 if ( e->e_dn == NULL )
566                                         continue;
567
568                                 patlen = b->a_dn_pat.bv_len;
569                                 odnlen = op->o_ndn.bv_len;
570                                 if ( odnlen < patlen )
571                                         continue;
572
573                                 if ( b->a_dn_style == ACL_STYLE_BASE ) {
574                                         /* base dn -- entire object DN must match */
575                                         if ( odnlen != patlen )
576                                                 continue;
577
578                                 } else if ( b->a_dn_style == ACL_STYLE_ONE ) {
579                                         int rdnlen = -1;
580
581                                         if ( odnlen <= patlen )
582                                                 continue;
583
584                                         if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn.bv_val[odnlen - patlen - 2] ) )
585                                                 continue;
586
587                                         rdnlen = dn_rdnlen( NULL, &op->o_ndn );
588                                         if ( rdnlen != odnlen - patlen - 1 )
589                                                 continue;
590
591                                 } else if ( b->a_dn_style == ACL_STYLE_SUBTREE ) {
592                                         if ( odnlen > patlen && ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn.bv_val[odnlen - patlen - 2] ) ) )
593                                                 continue;
594
595                                 } else if ( b->a_dn_style == ACL_STYLE_CHILDREN ) {
596                                         if ( odnlen <= patlen )
597                                                 continue;
598                                         if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn.bv_val[odnlen - patlen - 2] ) )
599                                                 continue;
600                                 }
601
602                                 if ( strcmp( b->a_dn_pat.bv_val, op->o_ndn.bv_val + odnlen - patlen ) != 0 )
603                                         continue;
604
605                         }
606                 }
607
608                 if ( b->a_sockurl_pat != NULL ) {
609 #ifdef NEW_LOGGING
610                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
611                                    "acl_mask: conn %d  check a_sockurl_pat: %s\n",
612                                    conn->c_connid, b->a_sockurl_pat ));
613 #else
614                         Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
615                                 b->a_sockurl_pat, 0, 0 );
616 #endif
617
618                         if ( strcmp( b->a_sockurl_pat, "*" ) != 0) {
619                                 if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
620                                         if (!regex_matches( b->a_sockurl_pat, conn->c_listener_url,
621                                                         e->e_ndn, matches ) ) 
622                                         {
623                                                 continue;
624                                         }
625                                 } else {
626                                         if ( strcasecmp( b->a_sockurl_pat, conn->c_listener_url ) != 0 )
627                                                 continue;
628                                 }
629                         }
630                 }
631
632                 if ( b->a_domain_pat != NULL ) {
633 #ifdef NEW_LOGGING
634                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
635                                    "acl_mask: conn %d  check a_domain_pat: %s\n",
636                                    conn->c_connid, b->a_domain_pat ));
637 #else
638                         Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
639                                 b->a_domain_pat, 0, 0 );
640 #endif
641                         if ( strcmp( b->a_domain_pat, "*" ) != 0) {
642                                 if ( b->a_domain_style == ACL_STYLE_REGEX) {
643                                         if (!regex_matches( b->a_domain_pat, conn->c_peer_domain,
644                                                         e->e_ndn, matches ) ) 
645                                         {
646                                                 continue;
647                                         }
648                                 } else {
649                                         if ( strcasecmp( b->a_domain_pat, conn->c_peer_domain ) != 0 )
650                                                 continue;
651                                 }
652                         }
653                 }
654
655                 if ( b->a_peername_pat != NULL ) {
656 #ifdef NEW_LOGGING
657                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
658                                    "acl_mask: conn %d  check a_perrname_path: %s\n",
659                                    conn->c_connid, b->a_peername_pat ));
660 #else
661                         Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
662                                 b->a_peername_pat, 0, 0 );
663 #endif
664                         if ( strcmp( b->a_peername_pat, "*" ) != 0) {
665                                 if ( b->a_peername_style == ACL_STYLE_REGEX) {
666                                         if (!regex_matches( b->a_peername_pat, conn->c_peer_name,
667                                                         e->e_ndn, matches ) ) 
668                                         {
669                                                 continue;
670                                         }
671                                 } else {
672                                         if ( strcasecmp( b->a_peername_pat, conn->c_peer_name ) != 0 )
673                                                 continue;
674                                 }
675                         }
676                 }
677
678                 if ( b->a_sockname_pat != NULL ) {
679 #ifdef NEW_LOGGING
680                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
681                                    "acl_mask: conn %d  check a_sockname_path: %s\n",
682                                    conn->c_connid, b->a_sockname_pat ));
683 #else
684                         Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
685                                 b->a_sockname_pat, 0, 0 );
686 #endif
687                         if ( strcmp( b->a_sockname_pat, "*" ) != 0) {
688                                 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
689                                         if (!regex_matches( b->a_sockname_pat, conn->c_sock_name,
690                                                         e->e_ndn, matches ) ) 
691                                         {
692                                                 continue;
693                                         }
694                                 } else {
695                                         if ( strcasecmp( b->a_sockname_pat, conn->c_sock_name ) != 0 )
696                                                 continue;
697                                 }
698                         }
699                 }
700
701                 if ( b->a_dn_at != NULL && op->o_ndn.bv_len != 0 ) {
702                         Attribute       *at;
703                         struct berval   bv;
704                         int rc, match = 0;
705                         const char *text;
706                         const char *attr = b->a_dn_at->ad_cname.bv_val;
707
708                         assert( attr != NULL );
709
710 #ifdef NEW_LOGGING
711                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
712                                    "acl_mask: conn %d  check a_dn_pat: %s\n",
713                                    conn->c_connid, attr ));
714 #else
715                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
716                                 attr, 0, 0);
717 #endif
718                         bv = op->o_ndn;
719
720                         /* see if asker is listed in dnattr */
721                         for( at = attrs_find( e->e_attrs, b->a_dn_at );
722                                 at != NULL;
723                                 at = attrs_find( at->a_next, b->a_dn_at ) )
724                         {
725                                 if( value_find( b->a_dn_at, at->a_vals, &bv ) == 0 ) {
726                                         /* found it */
727                                         match = 1;
728                                         break;
729                                 }
730                         }
731
732                         if( match ) {
733                                 /* have a dnattr match. if this is a self clause then
734                                  * the target must also match the op dn.
735                                  */
736                                 if ( b->a_dn_self ) {
737                                         /* check if the target is an attribute. */
738                                         if ( val == NULL )
739                                                 continue;
740                                         /* target is attribute, check if the attribute value
741                                          * is the op dn.
742                                          */
743                                         rc = value_match( &match, b->a_dn_at,
744                                                 b->a_dn_at->ad_type->sat_equality, 0,
745                                                 val, &bv, &text );
746                                         /* on match error or no match, fail the ACL clause */
747                                         if (rc != LDAP_SUCCESS || match != 0 )
748                                                 continue;
749                                 }
750                         } else {
751                                 /* no dnattr match, check if this is a self clause */
752                                 if ( ! b->a_dn_self )
753                                         continue;
754                                 /* this is a self clause, check if the target is an
755                                  * attribute.
756                                  */
757                                 if ( val == NULL )
758                                         continue;
759                                 /* target is attribute, check if the attribute value
760                                  * is the op dn.
761                                  */
762                                 rc = value_match( &match, b->a_dn_at,
763                                         b->a_dn_at->ad_type->sat_equality, 0,
764                                         val, &bv, &text );
765
766                                 /* on match error or no match, fail the ACL clause */
767                                 if (rc != LDAP_SUCCESS || match != 0 )
768                                         continue;
769                         }
770                 }
771
772                 if ( b->a_group_pat.bv_len && op->o_ndn.bv_len ) {
773                         char buf[1024];
774                         struct berval bv = { sizeof(buf) - 1, buf };
775                         struct berval ndn = { 0, NULL };
776                         int rc;
777
778                         /* b->a_group is an unexpanded entry name, expanded it should be an 
779                          * entry with objectclass group* and we test to see if odn is one of
780                          * the values in the attribute group
781                          */
782                         /* see if asker is listed in dnattr */
783                         if ( b->a_group_style == ACL_STYLE_REGEX ) {
784                                 string_expand(&bv, b->a_group_pat.bv_val, e->e_ndn, matches);
785                                 if ( dnNormalize2(NULL, &bv, &ndn) != LDAP_SUCCESS ) {
786                                         /* did not expand to a valid dn */
787                                         continue;
788                                 }
789                                 bv = ndn;
790                         } else {
791                                 bv = b->a_group_pat;
792                         }
793
794                         rc = backend_group(be, conn, op, e, &bv, &op->o_ndn,
795                                 b->a_group_oc, b->a_group_at);
796                         if ( ndn.bv_val )
797                                 free( ndn.bv_val );
798                         if ( rc != 0 )
799                         {
800                                 continue;
801                         }
802                 }
803
804                 if ( b->a_set_pat.bv_len != 0 ) {
805                         if (aci_match_set( &b->a_set_pat, be, e, conn, op, 0 ) == 0) {
806                                 continue;
807                         }
808                 }
809
810                 if ( b->a_authz.sai_ssf ) {
811 #ifdef NEW_LOGGING
812                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
813                                    "acl_mask: conn %d  check a_authz.sai_ssf: ACL %u > OP %u\n",
814                                    conn->c_connid, b->a_authz.sai_ssf, op->o_ssf ));
815 #else
816                         Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
817                                 b->a_authz.sai_ssf, op->o_ssf, 0 );
818 #endif
819                         if ( b->a_authz.sai_ssf >  op->o_ssf ) {
820                                 continue;
821                         }
822                 }
823
824                 if ( b->a_authz.sai_transport_ssf ) {
825 #ifdef NEW_LOGGING
826                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
827                                    "acl_mask: conn %d  check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
828                                    conn->c_connid, b->a_authz.sai_transport_ssf, op->o_transport_ssf ));
829 #else
830                         Debug( LDAP_DEBUG_ACL,
831                                 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
832                                 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
833 #endif
834                         if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
835                                 continue;
836                         }
837                 }
838
839                 if ( b->a_authz.sai_tls_ssf ) {
840 #ifdef NEW_LOGGING
841                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
842                                    "acl_mask: conn %d  check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
843                                    conn->c_connid, b->a_authz.sai_tls_ssf, op->o_tls_ssf ));
844 #else
845                         Debug( LDAP_DEBUG_ACL,
846                                 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
847                                 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
848 #endif
849                         if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
850                                 continue;
851                         }
852                 }
853
854                 if ( b->a_authz.sai_sasl_ssf ) {
855 #ifdef NEW_LOGGING
856                         LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
857                                    "acl_mask: conn %d check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
858                                    conn->c_connid, b->a_authz.sai_sasl_ssf, op->o_sasl_ssf ));
859 #else
860                         Debug( LDAP_DEBUG_ACL,
861                                 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
862                                 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
863 #endif
864                         if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
865                                 continue;
866                         }
867                 }
868
869 #ifdef SLAPD_ACI_ENABLED
870                 if ( b->a_aci_at != NULL ) {
871                         Attribute       *at;
872                         slap_access_t grant, deny, tgrant, tdeny;
873
874                         /* this case works different from the others above.
875                          * since aci's themselves give permissions, we need
876                          * to first check b->a_access_mask, the ACL's access level.
877                          */
878
879                         if ( e->e_nname.bv_len == 0 ) {
880                                 /* no ACIs in the root DSE */
881                                 continue;
882                         }
883
884                         /* first check if the right being requested
885                          * is allowed by the ACL clause.
886                          */
887                         if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
888                                 continue;
889                         }
890
891                         /* get the aci attribute */
892                         at = attr_find( e->e_attrs, b->a_aci_at );
893                         if ( at == NULL ) {
894                                 continue;
895                         }
896
897                         /* start out with nothing granted, nothing denied */
898                         ACL_INIT(tgrant);
899                         ACL_INIT(tdeny);
900
901                         /* the aci is an multi-valued attribute.  The
902                          * rights are determined by OR'ing the individual
903                          * rights given by the acis.
904                          */
905                         for ( i = 0; at->a_vals[i].bv_val != NULL; i++ ) {
906                                 if (aci_mask( be, conn, op,
907                                         e, desc, val, &at->a_vals[i],
908                                         matches, &grant, &deny ) != 0)
909                                 {
910                                         tgrant |= grant;
911                                         tdeny |= deny;
912                                 }
913                         }
914
915                         /* remove anything that the ACL clause does not allow */
916                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
917                         tdeny &= ACL_PRIV_MASK;
918
919                         /* see if we have anything to contribute */
920                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
921                                 continue;
922                         }
923
924                         /* this could be improved by changing acl_mask so that it can deal with
925                          * by clauses that return grant/deny pairs.  Right now, it does either
926                          * additive or subtractive rights, but not both at the same time.  So,
927                          * we need to combine the grant/deny pair into a single rights mask in
928                          * a smart way:  if either grant or deny is "empty", then we use the
929                          * opposite as is, otherwise we remove any denied rights from the grant
930                          * rights mask and construct an additive mask.
931                          */
932                         if (ACL_IS_INVALID(tdeny)) {
933                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
934
935                         } else if (ACL_IS_INVALID(tgrant)) {
936                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
937
938                         } else {
939                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
940                         }
941
942                 } else
943 #endif
944                 {
945                         modmask = b->a_access_mask;
946                 }
947
948 #ifdef NEW_LOGGING
949                 LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
950                            "acl_mask: conn %d  [%d] applying %s (%s)\n",
951                            conn->c_connid, i, accessmask2str( modmask, accessmaskbuf),
952                            b->a_type == ACL_CONTINUE ? "continue" : b->a_type == ACL_BREAK
953                            ? "break" : "stop" ));
954 #else
955                 Debug( LDAP_DEBUG_ACL,
956                         "<= acl_mask: [%d] applying %s (%s)\n",
957                         i, accessmask2str( modmask, accessmaskbuf ), 
958                         b->a_type == ACL_CONTINUE
959                                 ? "continue"
960                                 : b->a_type == ACL_BREAK
961                                         ? "break"
962                                         : "stop" );
963 #endif
964                 /* save old mask */
965                 oldmask = *mask;
966
967                 if( ACL_IS_ADDITIVE(modmask) ) {
968                         /* add privs */
969                         ACL_PRIV_SET( *mask, modmask );
970
971                         /* cleanup */
972                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
973
974                 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
975                         /* substract privs */
976                         ACL_PRIV_CLR( *mask, modmask );
977
978                         /* cleanup */
979                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
980
981                 } else {
982                         /* assign privs */
983                         *mask = modmask;
984                 }
985
986 #ifdef NEW_LOGGING
987                 LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
988                            "acl_mask: conn %d  [%d] mask: %s\n",
989                            conn->c_connid, i, accessmask2str( *mask, accessmaskbuf) ));
990 #else
991                 Debug( LDAP_DEBUG_ACL,
992                         "<= acl_mask: [%d] mask: %s\n",
993                         i, accessmask2str(*mask, accessmaskbuf), 0 );
994 #endif
995
996                 if( b->a_type == ACL_CONTINUE ) {
997                         continue;
998
999                 } else if ( b->a_type == ACL_BREAK ) {
1000                         return ACL_BREAK;
1001
1002                 } else {
1003                         return ACL_STOP;
1004                 }
1005         }
1006
1007         /* implicit "by * none" clause */
1008         ACL_INIT(*mask);
1009
1010 #ifdef NEW_LOGGING
1011         LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
1012                    "acl_mask: conn %d  no more <who> clauses, returning %d (stop)\n",
1013                    conn->c_connid, accessmask2str( *mask, accessmaskbuf) ));
1014 #else
1015         Debug( LDAP_DEBUG_ACL,
1016                 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1017                 accessmask2str(*mask, accessmaskbuf), 0, 0 );
1018 #endif
1019         return ACL_STOP;
1020 }
1021
1022 /*
1023  * acl_check_modlist - check access control on the given entry to see if
1024  * it allows the given modifications by the user associated with op.
1025  * returns      1       if mods allowed ok
1026  *                      0       mods not allowed
1027  */
1028
1029 int
1030 acl_check_modlist(
1031     Backend     *be,
1032     Connection  *conn,
1033     Operation   *op,
1034     Entry       *e,
1035     Modifications       *mlist
1036 )
1037 {
1038         struct berval *bv;
1039
1040         assert( be != NULL );
1041
1042         /* short circuit root database access */
1043         if ( be_isroot( be, &op->o_ndn ) ) {
1044 #ifdef NEW_LOGGING
1045                 LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
1046                            "acl_check_modlist: conn %d  access granted to root user\n",
1047                            conn->c_connid ));
1048 #else
1049                 Debug( LDAP_DEBUG_ACL,
1050                         "<= acl_access_allowed: granted to database root\n",
1051                     0, 0, 0 );
1052 #endif
1053                 return 1;
1054         }
1055
1056         /* use backend default access if no backend acls */
1057         if( be != NULL && be->be_acl == NULL ) {
1058 #ifdef NEW_LOGGING
1059                 LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
1060                            "acl_check_modlist: conn %d  backend default %s access %s to \"%s\"\n",
1061                            conn->c_connid, access2str( ACL_WRITE ),
1062                            be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val ));
1063 #else
1064                 Debug( LDAP_DEBUG_ACL,
1065                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
1066                         access2str( ACL_WRITE ),
1067                         be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val );
1068 #endif
1069                 return be->be_dfltaccess >= ACL_WRITE;
1070
1071 #ifdef notdef
1072         /* be is always non-NULL */
1073         /* use global default access if no global acls */
1074         } else if ( be == NULL && global_acl == NULL ) {
1075 #ifdef NEW_LOGGING
1076                 LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
1077                            "acl_check_modlist: conn %d  global default %s access %s to \"%s\"\n",
1078                            conn->c_connid, access2str( ACL_WRITE ),
1079                            global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn ));
1080 #else
1081                 Debug( LDAP_DEBUG_ACL,
1082                         "=> access_allowed: global default %s access %s to \"%s\"\n",
1083                         access2str( ACL_WRITE ),
1084                         global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn );
1085 #endif
1086                 return global_default_access >= ACL_WRITE;
1087 #endif
1088         }
1089
1090         for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1091                 /*
1092                  * no-user-modification operational attributes are ignored
1093                  * by ACL_WRITE checking as any found here are not provided
1094                  * by the user
1095                  */
1096                 if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
1097 #ifdef NEW_LOGGING
1098                         LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
1099                                    "acl_check_modlist: conn %d  no-user-mod %s: modify access granted\n",
1100                                    conn->c_connid, mlist->sml_desc->ad_cname.bv_val ));
1101 #else
1102                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1103                                 " modify access granted\n",
1104                                 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1105 #endif
1106                         continue;
1107                 }
1108
1109                 switch ( mlist->sml_op ) {
1110                 case LDAP_MOD_REPLACE:
1111                         /*
1112                          * We must check both permission to delete the whole
1113                          * attribute and permission to add the specific attributes.
1114                          * This prevents abuse from selfwriters.
1115                          */
1116                         if ( ! access_allowed( be, conn, op, e,
1117                                 mlist->sml_desc, NULL, ACL_WRITE ) )
1118                         {
1119                                 return( 0 );
1120                         }
1121
1122                         if ( mlist->sml_bvalues == NULL ) break;
1123
1124                         /* fall thru to check value to add */
1125
1126                 case LDAP_MOD_ADD:
1127                         assert( mlist->sml_bvalues != NULL );
1128
1129                         for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
1130                                 if ( ! access_allowed( be, conn, op, e,
1131                                         mlist->sml_desc, bv, ACL_WRITE ) )
1132                                 {
1133                                         return( 0 );
1134                                 }
1135                         }
1136                         break;
1137
1138                 case LDAP_MOD_DELETE:
1139                         if ( mlist->sml_bvalues == NULL ) {
1140                                 if ( ! access_allowed( be, conn, op, e,
1141                                         mlist->sml_desc, NULL, ACL_WRITE ) )
1142                                 {
1143                                         return( 0 );
1144                                 }
1145                                 break;
1146                         }
1147                         for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
1148                                 if ( ! access_allowed( be, conn, op, e,
1149                                         mlist->sml_desc, bv, ACL_WRITE ) )
1150                                 {
1151                                         return( 0 );
1152                                 }
1153                         }
1154                         break;
1155
1156                 default:
1157                         assert( 0 );
1158                         return( 0 );
1159                 }
1160         }
1161
1162         return( 1 );
1163 }
1164
1165 static char *
1166 aci_bvstrdup( struct berval *bv )
1167 {
1168         char *s;
1169
1170         s = (char *)ch_malloc(bv->bv_len + 1);
1171         if (s != NULL) {
1172                 AC_MEMCPY(s, bv->bv_val, bv->bv_len);
1173                 s[bv->bv_len] = 0;
1174         }
1175         return(s);
1176 }
1177
1178 #ifdef SLAPD_ACI_ENABLED
1179 static int
1180 aci_strbvcmp(
1181         const char *s,
1182         struct berval *bv )
1183 {
1184         int res, len;
1185
1186         res = strncasecmp( s, bv->bv_val, bv->bv_len );
1187         if (res)
1188                 return(res);
1189         len = strlen(s);
1190         if (len > (int)bv->bv_len)
1191                 return(1);
1192         if (len < (int)bv->bv_len)
1193                 return(-1);
1194         return(0);
1195 }
1196 #endif
1197
1198 static int
1199 aci_get_part(
1200         struct berval *list,
1201         int ix,
1202         char sep,
1203         struct berval *bv )
1204 {
1205         int len;
1206         char *p;
1207
1208         if (bv) {
1209                 bv->bv_len = 0;
1210                 bv->bv_val = NULL;
1211         }
1212         len = list->bv_len;
1213         p = list->bv_val;
1214         while (len >= 0 && --ix >= 0) {
1215                 while (--len >= 0 && *p++ != sep) ;
1216         }
1217         while (len >= 0 && *p == ' ') {
1218                 len--;
1219                 p++;
1220         }
1221         if (len < 0)
1222                 return(-1);
1223
1224         if (!bv)
1225                 return(0);
1226
1227         bv->bv_val = p;
1228         while (--len >= 0 && *p != sep) {
1229                 bv->bv_len++;
1230                 p++;
1231         }
1232         while (bv->bv_len > 0 && *--p == ' ')
1233                 bv->bv_len--;
1234         return(bv->bv_len);
1235 }
1236
1237 BerVarray
1238 aci_set_gather (void *cookie, char *name, struct berval *attr)
1239 {
1240         AciSetCookie *cp = cookie;
1241         BerVarray bvals = NULL;
1242         struct berval bv, ndn;
1243
1244         /* this routine needs to return the bervals instead of
1245          * plain strings, since syntax is not known.  It should
1246          * also return the syntax or some "comparison cookie".
1247          */
1248
1249         bv.bv_val = name;
1250         bv.bv_len = strlen( name );
1251         if (dnNormalize2(NULL, &bv, &ndn) == LDAP_SUCCESS) {
1252                 const char *text;
1253                 AttributeDescription *desc = NULL;
1254                 if (slap_bv2ad(attr, &desc, &text) == LDAP_SUCCESS) {
1255                         backend_attribute(cp->be, NULL, NULL,
1256                                 cp->e, &ndn, desc, &bvals);
1257                 }
1258                 free(ndn.bv_val);
1259         }
1260         return(bvals);
1261 }
1262
1263 static int
1264 aci_match_set (
1265         struct berval *subj,
1266     Backend *be,
1267     Entry *e,
1268     Connection *conn,
1269     Operation *op,
1270     int setref
1271 )
1272 {
1273         struct berval set = { 0, NULL };
1274         int rc = 0;
1275         AciSetCookie cookie;
1276
1277         if (setref == 0) {
1278                 ber_dupbv( &set, subj );
1279         } else {
1280                 struct berval subjdn, ndn = { 0, NULL };
1281                 struct berval setat;
1282                 BerVarray bvals;
1283                 const char *text;
1284                 AttributeDescription *desc = NULL;
1285
1286                 /* format of string is "entry/setAttrName" */
1287                 if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
1288                         return(0);
1289                 } else {
1290                         /* FIXME: If dnNormalize was based on ldap_bv2dn
1291                          * instead of ldap_str2dn and would honor the bv_len
1292                          * we could skip this step and not worry about the
1293                          * unterminated string.
1294                          */
1295                         char *s = ch_malloc(subjdn.bv_len + 1);
1296                         AC_MEMCPY(s, subjdn.bv_val, subjdn.bv_len);
1297                         subjdn.bv_val = s;
1298                 }
1299
1300                 if ( aci_get_part(subj, 1, '/', &setat) < 0 ) {
1301                         setat.bv_val = SLAPD_ACI_SET_ATTR;
1302                         setat.bv_len = sizeof(SLAPD_ACI_SET_ATTR)-1;
1303                 }
1304                 if ( setat.bv_val != NULL ) {
1305                         if ( dnNormalize2(NULL, &subjdn, &ndn) == LDAP_SUCCESS
1306                                 && slap_bv2ad(&setat, &desc, &text) == LDAP_SUCCESS )
1307                         {
1308                                 backend_attribute(be, NULL, NULL, e,
1309                                         &ndn, desc, &bvals);
1310                                 if ( bvals != NULL ) {
1311                                         if ( bvals[0].bv_val != NULL ) {
1312                                                 int i;
1313                                                 set = bvals[0];
1314                                                 bvals[0].bv_val = NULL;
1315                                                 for (i=1;bvals[i].bv_val;i++);
1316                                                 bvals[0].bv_val = bvals[i-1].bv_val;
1317                                                 bvals[i-1].bv_val = NULL;
1318                                         }
1319                                         ber_bvarray_free(bvals);
1320                                 }
1321                         }
1322                         if (ndn.bv_val)
1323                                 free(ndn.bv_val);
1324                 }
1325                 ch_free(subjdn.bv_val);
1326         }
1327
1328         if (set.bv_val != NULL) {
1329                 cookie.be = be;
1330                 cookie.e = e;
1331                 cookie.conn = conn;
1332                 cookie.op = op;
1333                 rc = (slap_set_filter(aci_set_gather, &cookie, &set,
1334                         op->o_ndn.bv_val, e->e_ndn, NULL) > 0);
1335                 ch_free(set.bv_val);
1336         }
1337         return(rc);
1338 }
1339
1340 #ifdef SLAPD_ACI_ENABLED
1341 static int
1342 aci_list_map_rights(
1343         struct berval *list )
1344 {
1345         struct berval bv;
1346         slap_access_t mask;
1347         int i;
1348
1349         ACL_INIT(mask);
1350         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
1351                 if (bv.bv_len <= 0)
1352                         continue;
1353                 switch (*bv.bv_val) {
1354                 case 'c':
1355                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1356                         break;
1357                 case 's':
1358                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
1359                          * the right 's' to mean "set", but in the examples states
1360                          * that the right 's' means "search".  The latter definition
1361                          * is used here.
1362                          */
1363                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1364                         break;
1365                 case 'r':
1366                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
1367                         break;
1368                 case 'w':
1369                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1370                         break;
1371                 case 'x':
1372                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
1373                          * define any equivalent to the AUTH right, so I've just used
1374                          * 'x' for now.
1375                          */
1376                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1377                         break;
1378                 default:
1379                         break;
1380                 }
1381
1382         }
1383         return(mask);
1384 }
1385
1386 static int
1387 aci_list_has_attr(
1388         struct berval *list,
1389         const struct berval *attr,
1390         struct berval *val )
1391 {
1392         struct berval bv, left, right;
1393         int i;
1394
1395         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
1396                 if (aci_get_part(&bv, 0, '=', &left) < 0
1397                         || aci_get_part(&bv, 1, '=', &right) < 0)
1398                 {
1399                         if (ber_casecmp(attr, &bv) == 0)
1400                                 return(1);
1401                 } else if (val == NULL) {
1402                         if (ber_casecmp(attr, &left) == 0)
1403                                 return(1);
1404                 } else {
1405                         if (ber_casecmp(attr, &left) == 0) {
1406                                 /* this is experimental code that implements a
1407                                  * simple (prefix) match of the attribute value.
1408                                  * the ACI draft does not provide for aci's that
1409                                  * apply to specific values, but it would be
1410                                  * nice to have.  If the <attr> part of an aci's
1411                                  * rights list is of the form <attr>=<value>,
1412                                  * that means the aci applies only to attrs with
1413                                  * the given value.  Furthermore, if the attr is
1414                                  * of the form <attr>=<value>*, then <value> is
1415                                  * treated as a prefix, and the aci applies to 
1416                                  * any value with that prefix.
1417                                  *
1418                                  * Ideally, this would allow r.e. matches.
1419                                  */
1420                                 if (aci_get_part(&right, 0, '*', &left) < 0
1421                                         || right.bv_len <= left.bv_len)
1422                                 {
1423                                         if (ber_casecmp(val, &right) == 0)
1424                                                 return(1);
1425                                 } else if (val->bv_len >= left.bv_len) {
1426                                         if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
1427                                                 return(1);
1428                                 }
1429                         }
1430                 }
1431         }
1432         return(0);
1433 }
1434
1435 static slap_access_t
1436 aci_list_get_attr_rights(
1437         struct berval *list,
1438         const struct berval *attr,
1439         struct berval *val )
1440 {
1441     struct berval bv;
1442     slap_access_t mask;
1443     int i;
1444
1445         /* loop through each rights/attr pair, skip first part (action) */
1446         ACL_INIT(mask);
1447         for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
1448                 if (aci_list_has_attr(&bv, attr, val) == 0)
1449                         continue;
1450                 if (aci_get_part(list, i, ';', &bv) < 0)
1451                         continue;
1452                 mask |= aci_list_map_rights(&bv);
1453         }
1454         return(mask);
1455 }
1456
1457 static int
1458 aci_list_get_rights(
1459         struct berval *list,
1460         const struct berval *attr,
1461         struct berval *val,
1462         slap_access_t *grant,
1463         slap_access_t *deny )
1464 {
1465     struct berval perm, actn;
1466     slap_access_t *mask;
1467     int i, found;
1468
1469         if (attr == NULL || attr->bv_len == 0 
1470                         || ber_casecmp( attr, &aci_bv_entry ) == 0) {
1471                 attr = &aci_bv_br_entry;
1472         }
1473
1474         found = 0;
1475         ACL_INIT(*grant);
1476         ACL_INIT(*deny);
1477         /* loop through each permissions clause */
1478         for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
1479                 if (aci_get_part(&perm, 0, ';', &actn) < 0)
1480                         continue;
1481                 if (ber_casecmp( &aci_bv_grant, &actn ) == 0) {
1482                         mask = grant;
1483                 } else if (ber_casecmp( &aci_bv_deny, &actn ) == 0) {
1484                         mask = deny;
1485                 } else {
1486                         continue;
1487                 }
1488
1489                 found = 1;
1490                 *mask |= aci_list_get_attr_rights(&perm, attr, val);
1491                 *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
1492         }
1493         return(found);
1494 }
1495
1496 static int
1497 aci_group_member (
1498         struct berval *subj,
1499         struct berval *defgrpoc,
1500         struct berval *defgrpat,
1501     Backend             *be,
1502     Entry               *e,
1503     Connection          *conn,
1504     Operation           *op,
1505         regmatch_t      *matches
1506 )
1507 {
1508         struct berval bv;
1509         char *subjdn;
1510         struct berval grpoc;
1511         struct berval grpat;
1512         ObjectClass *grp_oc = NULL;
1513         AttributeDescription *grp_ad = NULL;
1514         const char *text;
1515         int rc;
1516
1517         /* format of string is "group/objectClassValue/groupAttrName" */
1518         if (aci_get_part(subj, 0, '/', &bv) < 0) {
1519                 return(0);
1520         }
1521
1522         subjdn = aci_bvstrdup(&bv);
1523         if (subjdn == NULL) {
1524                 return(0);
1525         }
1526
1527         if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
1528                 grpoc = *defgrpoc;
1529         }
1530
1531         if (aci_get_part(subj, 2, '/', &grpat) < 0) {
1532                 grpat = *defgrpat;
1533         }
1534
1535         rc = slap_bv2ad( &grpat, &grp_ad, &text );
1536         if( rc != LDAP_SUCCESS ) {
1537                 rc = 0;
1538                 goto done;
1539         }
1540         rc = 0;
1541
1542         grp_oc = oc_bvfind( &grpoc );
1543
1544         if (grp_oc != NULL && grp_ad != NULL ) {
1545                 struct berval ndn;
1546                 bv.bv_val = (char *)ch_malloc(1024);
1547                 bv.bv_len = 1024;
1548                 string_expand(&bv, subjdn, e->e_ndn, matches);
1549                 if ( dnNormalize2(NULL, &bv, &ndn) == LDAP_SUCCESS ) {
1550                         rc = (backend_group(be, conn, op, e, &ndn, &op->o_ndn, grp_oc, grp_ad) == 0);
1551                         free( ndn.bv_val );
1552                 }
1553                 ch_free(bv.bv_val);
1554         }
1555
1556 done:
1557         ch_free(subjdn);
1558         return(rc);
1559 }
1560
1561 static struct berval GroupClass = {
1562         sizeof(SLAPD_GROUP_CLASS)-1, SLAPD_GROUP_CLASS };
1563 static struct berval GroupAttr = {
1564         sizeof(SLAPD_GROUP_ATTR)-1, SLAPD_GROUP_ATTR };
1565 static struct berval RoleClass = {
1566         sizeof(SLAPD_ROLE_CLASS)-1, SLAPD_ROLE_CLASS };
1567 static struct berval RoleAttr = {
1568         sizeof(SLAPD_ROLE_ATTR)-1, SLAPD_ROLE_ATTR };
1569
1570 static int
1571 aci_mask(
1572     Backend                     *be,
1573     Connection          *conn,
1574     Operation           *op,
1575     Entry                       *e,
1576         AttributeDescription *desc,
1577     struct berval       *val,
1578     struct berval       *aci,
1579         regmatch_t              *matches,
1580         slap_access_t   *grant,
1581         slap_access_t   *deny
1582 )
1583 {
1584     struct berval bv, perms, sdn;
1585         int rc;
1586                 
1587
1588         assert( desc->ad_cname.bv_val != NULL );
1589
1590         /* parse an aci of the form:
1591                 oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
1592
1593            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
1594            a full description of the format for this attribute.
1595
1596            For now, this routine only supports scope=entry.
1597          */
1598
1599         /* check that the aci has all 5 components */
1600         if (aci_get_part(aci, 4, '#', NULL) < 0)
1601                 return(0);
1602
1603         /* check that the aci family is supported */
1604         if (aci_get_part(aci, 0, '#', &bv) < 0)
1605                 return(0);
1606
1607         /* check that the scope is "entry" */
1608         if (aci_get_part(aci, 1, '#', &bv) < 0
1609                 || ber_casecmp( &aci_bv_entry, &bv ) != 0)
1610         {
1611                 return(0);
1612         }
1613
1614         /* get the list of permissions clauses, bail if empty */
1615         if (aci_get_part(aci, 2, '#', &perms) <= 0)
1616                 return(0);
1617
1618         /* check if any permissions allow desired access */
1619         if (aci_list_get_rights(&perms, &desc->ad_cname, val, grant, deny) == 0)
1620                 return(0);
1621
1622         /* see if we have a DN match */
1623         if (aci_get_part(aci, 3, '#', &bv) < 0)
1624                 return(0);
1625
1626         if (aci_get_part(aci, 4, '#', &sdn) < 0)
1627                 return(0);
1628
1629         if (ber_casecmp( &aci_bv_access_id, &bv ) == 0) {
1630                 struct berval ndn;
1631                 rc = 1;
1632                 if ( dnNormalize2(NULL, &sdn, &ndn) == LDAP_SUCCESS ) {
1633                         if (!dn_match( &op->o_ndn, &ndn))
1634                                 rc = 0;
1635                         free(ndn.bv_val);
1636                 }
1637                 return(rc);
1638         }
1639
1640         if (ber_casecmp( &aci_bv_self, &bv ) == 0) {
1641                 if (dn_match(&op->o_ndn, &e->e_nname))
1642                         return(1);
1643
1644         } else if (ber_casecmp( &aci_bv_dnattr, &bv ) == 0) {
1645                 Attribute *at;
1646                 AttributeDescription *ad = NULL;
1647                 const char *text;
1648
1649                 rc = slap_bv2ad( &sdn, &ad, &text );
1650
1651                 if( rc != LDAP_SUCCESS ) {
1652                         return 0;
1653                 }
1654
1655                 rc = 0;
1656
1657                 bv = op->o_ndn;
1658
1659                 for(at = attrs_find( e->e_attrs, ad );
1660                         at != NULL;
1661                         at = attrs_find( at->a_next, ad ) )
1662                 {
1663                         if (value_find( ad, at->a_vals, &bv) == 0 ) {
1664                                 rc = 1;
1665                                 break;
1666                         }
1667                 }
1668
1669                 return rc;
1670
1671
1672         } else if (ber_casecmp( &aci_bv_group, &bv ) == 0) {
1673                 if (aci_group_member(&sdn, &GroupClass, &GroupAttr, be, e, conn, op, matches))
1674                         return(1);
1675
1676         } else if (ber_casecmp( &aci_bv_role, &bv ) == 0) {
1677                 if (aci_group_member(&sdn, &RoleClass, &RoleAttr, be, e, conn, op, matches))
1678                         return(1);
1679
1680         } else if (ber_casecmp( &aci_bv_set, &bv ) == 0) {
1681                 if (aci_match_set(&sdn, be, e, conn, op, 0))
1682                         return(1);
1683
1684         } else if (ber_casecmp( &aci_bv_set_ref, &bv ) == 0) {
1685                 if (aci_match_set(&sdn, be, e, conn, op, 1))
1686                         return(1);
1687
1688         }
1689
1690         return(0);
1691 }
1692
1693 #endif  /* SLAPD_ACI_ENABLED */
1694
1695 static void
1696 string_expand(
1697         struct berval *bv,
1698         char *pat,
1699         char *match,
1700         regmatch_t *matches)
1701 {
1702         ber_len_t       size;
1703         char   *sp;
1704         char   *dp;
1705         int     flag;
1706
1707         size = 0;
1708         bv->bv_val[0] = '\0';
1709         bv->bv_len--; /* leave space for lone $ */
1710
1711         flag = 0;
1712         for ( dp = bv->bv_val, sp = pat; size < bv->bv_len && *sp ; sp++) {
1713                 /* did we previously see a $ */
1714                 if (flag) {
1715                         if (*sp == '$') {
1716                                 *dp++ = '$';
1717                                 size++;
1718                         } else if (*sp >= '0' && *sp <= '9' ) {
1719                                 int     n;
1720                                 int     i;
1721                                 int     l;
1722
1723                                 n = *sp - '0';
1724                                 *dp = '\0';
1725                                 i = matches[n].rm_so;
1726                                 l = matches[n].rm_eo; 
1727                                 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
1728                                         *dp++ = match[i];
1729                                         size++;
1730                                 }
1731                                 *dp = '\0';
1732                         }
1733                         flag = 0;
1734                 } else {
1735                         if (*sp == '$') {
1736                                 flag = 1;
1737                         } else {
1738                                 *dp++ = *sp;
1739                                 size++;
1740                         }
1741                 }
1742         }
1743
1744         if (flag) {
1745                 /* must have ended with a single $ */
1746                 *dp++ = '$';
1747                 size++;
1748         }
1749
1750         *dp = '\0';
1751         bv->bv_len = size;
1752
1753 #ifdef NEW_LOGGING
1754         LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
1755                    "string_expand:  pattern = %s\n", pat ));
1756         LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
1757                    "string_expand:  expanded = %s\n", bv->bv_val ));
1758 #else
1759         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
1760         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
1761 #endif
1762 }
1763
1764 static int
1765 regex_matches(
1766         char *pat,                              /* pattern to expand and match against */
1767         char *str,                              /* string to match against pattern */
1768         char *buf,                              /* buffer with $N expansion variables */
1769         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
1770 )
1771 {
1772         regex_t re;
1773         char newbuf[512];
1774         struct berval bv = {sizeof(newbuf), newbuf};
1775         int     rc;
1776
1777         if(str == NULL) str = "";
1778
1779         string_expand(&bv, pat, buf, matches);
1780         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
1781                 char error[512];
1782                 regerror(rc, &re, error, sizeof(error));
1783
1784 #ifdef NEW_LOGGING
1785                 LDAP_LOG(( "aci", LDAP_LEVEL_ERR,
1786                            "regex_matches: compile( \"%s\", \"%s\") failed %s\n",
1787                            pat, str, error ));
1788 #else
1789                 Debug( LDAP_DEBUG_TRACE,
1790                     "compile( \"%s\", \"%s\") failed %s\n",
1791                         pat, str, error );
1792 #endif
1793                 return( 0 );
1794         }
1795
1796         rc = regexec(&re, str, 0, NULL, 0);
1797         regfree( &re );
1798
1799 #ifdef NEW_LOGGING
1800         LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
1801                    "regex_matches: string:   %s\n", str ));
1802         LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
1803                    "regex_matches: rc:  %d  %s\n",
1804                    rc, rc ? "matches" : "no matches" ));
1805 #else
1806         Debug( LDAP_DEBUG_TRACE,
1807             "=> regex_matches: string:   %s\n", str, 0, 0 );
1808         Debug( LDAP_DEBUG_TRACE,
1809             "=> regex_matches: rc: %d %s\n",
1810                 rc, !rc ? "matches" : "no matches", 0 );
1811 #endif
1812         return( !rc );
1813 }
1814