]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
cleanup previous commit
[openldap] / servers / slapd / acl.c
1 /* acl.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26
27 #include "portable.h"
28
29 #include <stdio.h>
30
31 #include <ac/regex.h>
32 #include <ac/socket.h>
33 #include <ac/string.h>
34
35 #include "slap.h"
36 #include "sets.h"
37 #include "lber_pvt.h"
38 #include "lutil.h"
39
40 #ifdef LDAP_SLAPI
41 #include "slapi/slapi.h"
42 #endif /* LDAPI_SLAPI */
43
44 #define ACL_BUF_SIZE    1024    /* use most appropriate size */
45
46 /*
47  * speed up compares
48  */
49 static struct berval 
50         aci_bv_entry            = BER_BVC("entry"),
51         aci_bv_children         = BER_BVC("children"),
52         aci_bv_onelevel         = BER_BVC("onelevel"),
53         aci_bv_subtree          = BER_BVC("subtree"),
54         aci_bv_br_entry         = BER_BVC("[entry]"),
55         aci_bv_br_all           = BER_BVC("[all]"),
56         aci_bv_access_id        = BER_BVC("access-id"),
57 #if 0
58         aci_bv_anonymous        = BER_BVC("anonymous"),
59 #endif
60         aci_bv_public           = BER_BVC("public"),
61         aci_bv_users            = BER_BVC("users"),
62         aci_bv_self             = BER_BVC("self"),
63         aci_bv_dnattr           = BER_BVC("dnattr"),
64         aci_bv_group            = BER_BVC("group"),
65         aci_bv_role             = BER_BVC("role"),
66         aci_bv_set              = BER_BVC("set"),
67         aci_bv_set_ref          = BER_BVC("set-ref"),
68         aci_bv_grant            = BER_BVC("grant"),
69         aci_bv_deny             = BER_BVC("deny"),
70
71         aci_bv_ip_eq            = BER_BVC("IP="),
72 #ifdef LDAP_PF_LOCAL
73         aci_bv_path_eq          = BER_BVC("PATH="),
74 #if 0
75         aci_bv_dirsep           = BER_BVC(LDAP_DIRSEP),
76 #endif
77 #endif /* LDAP_PF_LOCAL */
78         
79         aci_bv_group_class      = BER_BVC(SLAPD_GROUP_CLASS),
80         aci_bv_group_attr       = BER_BVC(SLAPD_GROUP_ATTR),
81         aci_bv_role_class       = BER_BVC(SLAPD_ROLE_CLASS),
82         aci_bv_role_attr        = BER_BVC(SLAPD_ROLE_ATTR),
83         aci_bv_set_attr         = BER_BVC(SLAPD_ACI_SET_ATTR);
84
85 typedef enum slap_aci_scope_t {
86         SLAP_ACI_SCOPE_ENTRY            = 0x1,
87         SLAP_ACI_SCOPE_CHILDREN         = 0x2,
88         SLAP_ACI_SCOPE_SUBTREE          = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
89 } slap_aci_scope_t;
90
91 static AccessControl * acl_get(
92         AccessControl *ac, int *count,
93         Operation *op, Entry *e,
94         AttributeDescription *desc,
95         struct berval *val,
96         int nmatch, regmatch_t *matches,
97         AccessControlState *state );
98
99 static slap_control_t acl_mask(
100         AccessControl *ac, slap_mask_t *mask,
101         Operation *op, Entry *e,
102         AttributeDescription *desc,
103         struct berval *val,
104         int nmatch,
105         regmatch_t *matches,
106         int count,
107         AccessControlState *state );
108
109 #ifdef SLAPD_ACI_ENABLED
110 static int aci_mask(
111         Operation *op, Entry *e,
112         AttributeDescription *desc,
113         struct berval *val,
114         struct berval *aci,
115         int nmatch,
116         regmatch_t *matches,
117         slap_access_t *grant,
118         slap_access_t *deny,
119         slap_aci_scope_t scope);
120 #endif /* SLAPD_ACI_ENABLED */
121
122 static int      regex_matches(
123         struct berval *pat, char *str, char *buf,
124         int nmatch, regmatch_t *matches);
125 static int      string_expand(
126         struct berval *newbuf, struct berval *pattern,
127         char *match, int nmatch, regmatch_t *matches);
128
129 typedef struct AciSetCookie {
130         Operation *op;
131         Entry *e;
132 } AciSetCookie;
133
134 SLAP_SET_GATHER aci_set_gather;
135 SLAP_SET_GATHER aci_set_gather2;
136 static int aci_match_set ( struct berval *subj, Operation *op,
137     Entry *e, int setref );
138
139 /*
140  * access_allowed - check whether op->o_ndn is allowed the requested access
141  * to entry e, attribute attr, value val.  if val is null, access to
142  * the whole attribute is assumed (all values).
143  *
144  * This routine loops through all access controls and calls
145  * acl_mask() on each applicable access control.
146  * The loop exits when a definitive answer is reached or
147  * or no more controls remain.
148  *
149  * returns:
150  *              0       access denied
151  *              1       access granted
152  *
153  * Notes:
154  * - can be legally called with op == NULL
155  * - can be legally called with op->o_bd == NULL
156  */
157
158 #ifdef LDAP_DEVEL
159
160 static int
161 slap_access_allowed(
162         Operation               *op,
163         Entry                   *e,
164         AttributeDescription    *desc,
165         struct berval           *val,
166         slap_access_t           access,
167         AccessControlState      *state,
168         slap_mask_t             *maskp )
169 {
170         int                             ret = 1;
171         int                             count;
172         AccessControl                   *a = NULL;
173
174 #ifdef LDAP_DEBUG
175         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
176 #endif
177         slap_mask_t                     mask;
178         slap_control_t                  control;
179         slap_access_t                   access_level;
180         const char                      *attr;
181         regmatch_t                      matches[MAXREMATCHES];
182         int                             st_same_attr = 0;
183
184         assert( op != NULL );
185         assert( e != NULL );
186         assert( desc != NULL );
187         assert( maskp != NULL );
188
189         access_level = ACL_LEVEL( access );
190         attr = desc->ad_cname.bv_val;
191
192         assert( attr != NULL );
193
194 #ifdef LDAP_SLAPI
195         if ( op->o_pb != NULL ) {
196                 ret = slapi_int_access_allowed( op, e, desc, val, access, state );
197                 if ( ret == 0 ) {
198                         /* ACL plugin denied access */
199                         goto done;
200                 }
201         }
202 #endif /* LDAP_SLAPI */
203
204         /* grant database root access */
205         if ( be_isroot( op ) ) {
206                 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
207                 mask = ACL_LVL_MANAGE;
208                 goto done;
209         }
210
211         /*
212          * no-user-modification operational attributes are ignored
213          * by ACL_WRITE checking as any found here are not provided
214          * by the user
215          */
216         if ( access_level >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
217                 && desc != slap_schema.si_ad_entry
218                 && desc != slap_schema.si_ad_children )
219         {
220                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
221                         " %s access granted\n",
222                         attr, 0, 0 );
223                 goto done;
224         }
225
226         /* use backend default access if no backend acls */
227         if ( op->o_bd->be_acl == NULL ) {
228                 int     i;
229
230                 Debug( LDAP_DEBUG_ACL,
231                         "=> slap_access_allowed: backend default %s "
232                         "access %s to \"%s\"\n",
233                         access2str( access ),
234                         op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
235                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
236                 ret = op->o_bd->be_dfltaccess >= access_level;
237
238                 mask = ACL_PRIV_LEVEL;
239                 for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
240                         ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
241                 }
242
243                 goto done;
244         }
245
246         ret = 0;
247         control = ACL_BREAK;
248
249         if ( st_same_attr ) {
250                 assert( state->as_vd_acl != NULL );
251
252                 a = state->as_vd_acl;
253                 count = state->as_vd_acl_count;
254                 if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
255                         mask = state->as_vd_acl_mask;
256                         AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
257                         goto vd_access;
258                 }
259
260         } else {
261                 if ( state ) state->as_vi_acl = NULL;
262                 a = NULL;
263                 ACL_PRIV_ASSIGN( mask, *maskp );
264                 count = 0;
265                 memset( matches, '\0', sizeof( matches ) );
266         }
267
268         while ( ( a = acl_get( a, &count, op, e, desc, val,
269                 MAXREMATCHES, matches, state ) ) != NULL )
270         {
271                 int i;
272
273                 for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
274                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
275                                 (int)matches[i].rm_so, (int)matches[i].rm_eo );
276                         if ( matches[i].rm_so <= matches[0].rm_eo ) {
277                                 int n;
278                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
279                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
280                                 }
281                         }
282                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
283                 }
284
285                 if ( state ) {
286                         if ( state->as_vi_acl == a &&
287                                 ( state->as_recorded & ACL_STATE_RECORDED_NV ) )
288                         {
289                                 Debug( LDAP_DEBUG_ACL,
290                                         "slap_access_allowed: result from state (%s)\n",
291                                         attr, 0, 0 );
292                                 ret = state->as_result;
293                                 goto done;
294                         } else {
295                                 Debug( LDAP_DEBUG_ACL,
296                                         "slap_access_allowed: no res from state (%s)\n",
297                                         attr, 0, 0 );
298                         }
299                 }
300
301 vd_access:
302                 control = acl_mask( a, &mask, op,
303                         e, desc, val, MAXREMATCHES, matches, count, state );
304
305                 if ( control != ACL_BREAK ) {
306                         break;
307                 }
308
309                 memset( matches, '\0', sizeof( matches ) );
310         }
311
312         if ( ACL_IS_INVALID( mask ) ) {
313                 Debug( LDAP_DEBUG_ACL,
314                         "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
315                         e->e_dn, attr, 0 );
316                 ACL_PRIV_ASSIGN( mask, *maskp );
317
318         } else if ( control == ACL_BREAK ) {
319                 Debug( LDAP_DEBUG_ACL,
320                         "=> slap_access_allowed: no more rules\n", 0, 0, 0 );
321
322                 goto done;
323         }
324
325         ret = ACL_GRANT( mask, access );
326
327         Debug( LDAP_DEBUG_ACL,
328                 "=> slap_access_allowed: %s access %s by %s\n",
329                 access2str( access ), ret ? "granted" : "denied",
330                 accessmask2str( mask, accessmaskbuf, 1 ) );
331
332 done:
333         ACL_PRIV_ASSIGN( *maskp, mask );
334         return ret;
335 }
336
337 int
338 access_allowed_mask(
339         Operation               *op,
340         Entry                   *e,
341         AttributeDescription    *desc,
342         struct berval           *val,
343         slap_access_t           access,
344         AccessControlState      *state,
345         slap_mask_t             *maskp )
346 {
347         int                             ret = 1;
348         AccessControl                   *a = NULL;
349         int                             be_null = 0;
350
351 #ifdef LDAP_DEBUG
352         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
353 #endif
354         slap_mask_t                     mask;
355         slap_control_t                  control;
356         slap_access_t                   access_level;
357         const char                      *attr;
358         int                             st_same_attr = 0;
359         static AccessControlState       state_init = ACL_STATE_INIT;
360         BI_access_allowed               *bi_access_allowed = NULL;
361
362         assert( e != NULL );
363         assert( desc != NULL );
364
365         access_level = ACL_LEVEL( access );
366
367         assert( access_level > ACL_NONE );
368
369         ACL_INVALIDATE( mask );
370         if ( maskp ) ACL_INVALIDATE( *maskp );
371
372         attr = desc->ad_cname.bv_val;
373
374         assert( attr != NULL );
375
376         if ( op && op->o_is_auth_check &&
377                 ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
378         {
379                 access = ACL_AUTH;
380         }
381
382         if ( state ) {
383                 if ( state->as_vd_ad == desc ) {
384                         if ( state->as_recorded ) {
385                                 if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
386                                         val == NULL )
387                                 {
388                                         return state->as_result;
389
390                                 } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
391                                         val != NULL && state->as_vd_acl == NULL )
392                                 {
393                                         return state->as_result;
394                                 }
395                         }
396                         st_same_attr = 1;
397                 } else {
398                         *state = state_init;
399                 }
400
401                 state->as_vd_ad = desc;
402         }
403
404         Debug( LDAP_DEBUG_ACL,
405                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
406                 access2str( access ), e->e_dn, attr );
407
408         if ( op == NULL ) {
409                 /* no-op call */
410                 goto done;
411         }
412
413         if ( op->o_bd == NULL ) {
414                 op->o_bd = LDAP_STAILQ_FIRST(&backendDB);
415                 be_null = 1;
416 #ifdef LDAP_DEVEL
417                 /*
418                  * FIXME: experimental; use first backend rules
419                  * iff there is no global_acl (ITS#3100) */
420                 if ( frontendDB->be_acl != NULL ) {
421                         op->o_bd = frontendDB;
422                 }
423 #endif
424         }
425         assert( op->o_bd != NULL );
426
427         /* delegate to backend */
428         if ( op->o_bd->bd_info->bi_access_allowed != NULL ) {
429                 bi_access_allowed = op->o_bd->bd_info->bi_access_allowed;
430         } else {
431                 bi_access_allowed = slap_access_allowed;
432         }
433         ret = bi_access_allowed( op, e, desc, val, access, state, &mask );
434         if ( !ret ) {
435                 if ( ACL_IS_INVALID( mask ) ) {
436                         Debug( LDAP_DEBUG_ACL,
437                                 "=> access_allowed: \"%s\" (%s) invalid!\n",
438                                 e->e_dn, attr, 0 );
439                         ACL_INIT( mask );
440
441                 } else if ( control == ACL_BREAK ) {
442                         Debug( LDAP_DEBUG_ACL,
443                                 "=> access_allowed: no more rules\n", 0, 0, 0 );
444
445                         goto done;
446                 }
447
448                 ret = ACL_GRANT( mask, access );
449         }
450
451         Debug( LDAP_DEBUG_ACL,
452                 "=> access_allowed: %s access %s by %s\n",
453                 access2str( access ), ret ? "granted" : "denied",
454                 accessmask2str( mask, accessmaskbuf, 1 ) );
455
456 done:
457         if ( state != NULL ) {
458                 /* If not value-dependent, save ACL in case of more attrs */
459                 if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
460                         state->as_vi_acl = a;
461                         state->as_result = ret;
462                 }
463                 state->as_recorded |= ACL_STATE_RECORDED;
464         }
465         if ( be_null ) op->o_bd = NULL;
466         if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
467         return ret;
468 }
469
470 #else /* !LDAP_DEVEL */
471 int
472 access_allowed_mask(
473         Operation               *op,
474         Entry                   *e,
475         AttributeDescription    *desc,
476         struct berval           *val,
477         slap_access_t           access,
478         AccessControlState      *state,
479         slap_mask_t             *maskp )
480 {
481         int                             ret = 1;
482         int                             count;
483         AccessControl                   *a = NULL;
484         Backend                         *be;
485         int                             be_null = 0;
486
487 #ifdef LDAP_DEBUG
488         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
489 #endif
490         slap_mask_t                     mask;
491         slap_control_t                  control;
492         slap_access_t                   access_level;
493         const char                      *attr;
494         regmatch_t                      matches[MAXREMATCHES];
495         int                             st_same_attr = 0;
496         static AccessControlState       state_init = ACL_STATE_INIT;
497
498         assert( e != NULL );
499         assert( desc != NULL );
500
501         access_level = ACL_LEVEL( access );
502
503         assert( access_level > ACL_NONE );
504         if ( maskp ) ACL_INVALIDATE( *maskp );
505
506         attr = desc->ad_cname.bv_val;
507
508         assert( attr != NULL );
509
510         if ( op && op->o_is_auth_check &&
511                 ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
512         {
513                 access = ACL_AUTH;
514         }
515
516         if ( state ) {
517                 if ( state->as_vd_ad == desc ) {
518                         if ( state->as_recorded ) {
519                                 if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
520                                         val == NULL )
521                                 {
522                                         return state->as_result;
523
524                                 } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
525                                         val != NULL && state->as_vd_acl == NULL )
526                                 {
527                                         return state->as_result;
528                                 }
529                         }
530                         st_same_attr = 1;
531                 } else {
532                         *state = state_init;
533                 }
534
535                 state->as_vd_ad=desc;
536         }
537
538         Debug( LDAP_DEBUG_ACL,
539                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
540                 access2str( access ), e->e_dn, attr );
541
542         if ( op == NULL ) {
543                 /* no-op call */
544                 goto done;
545         }
546
547         be = op->o_bd;
548         if ( be == NULL ) {
549                 be = LDAP_STAILQ_FIRST(&backendDB);
550                 be_null = 1;
551 #ifdef LDAP_DEVEL
552                 /*
553                  * FIXME: experimental; use first backend rules
554                  * iff there is no global_acl (ITS#3100) */
555                 if ( frontendDB->be_acl == NULL ) 
556 #endif
557                 {
558                         op->o_bd = be;
559                 }
560         }
561         assert( be != NULL );
562
563 #ifdef LDAP_SLAPI
564         if ( op->o_pb != NULL ) {
565                 ret = slapi_int_access_allowed( op, e, desc, val, access, state );
566                 if ( ret == 0 ) {
567                         /* ACL plugin denied access */
568                         goto done;
569                 }
570         }
571 #endif /* LDAP_SLAPI */
572
573         /* grant database root access */
574         if ( be_isroot( op ) ) {
575                 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
576                 if ( maskp ) {
577                         mask = ACL_LVL_MANAGE;
578                 }
579
580                 goto done;
581         }
582
583         /*
584          * no-user-modification operational attributes are ignored
585          * by ACL_WRITE checking as any found here are not provided
586          * by the user
587          */
588         if ( access_level >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
589                 && desc != slap_schema.si_ad_entry
590                 && desc != slap_schema.si_ad_children )
591         {
592                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
593                         " %s access granted\n",
594                         attr, 0, 0 );
595                 goto done;
596         }
597
598         /* use backend default access if no backend acls */
599         if ( be->be_acl == NULL ) {
600                 Debug( LDAP_DEBUG_ACL,
601                         "=> access_allowed: backend default %s "
602                         "access %s to \"%s\"\n",
603                         access2str( access ),
604                         be->be_dfltaccess >= access_level ? "granted" : "denied",
605                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
606                 ret = be->be_dfltaccess >= access_level;
607
608                 if ( maskp ) {
609                         int     i;
610
611                         mask = ACL_PRIV_LEVEL;
612                         for ( i = ACL_NONE; i <= be->be_dfltaccess; i++ ) {
613                                 mask |= ACL_ACCESS2PRIV( i );
614                         }
615                 }
616
617                 goto done;
618
619 #ifdef notdef
620         /* be is always non-NULL */
621         /* use global default access if no global acls */
622         } else if ( be == NULL && frontendDB->be_acl == NULL ) {
623                 Debug( LDAP_DEBUG_ACL,
624                         "=> access_allowed: global default %s access %s to \"%s\"\n",
625                         access2str( access ),
626                         frontendDB->be_dfltaccess >= access_level ?
627                                 "granted" : "denied", op->o_dn.bv_val );
628                 ret = frontendDB->be_dfltaccess >= access_level;
629
630                 if ( maskp ) {
631                         int     i;
632
633                         mask = ACL_PRIV_LEVEL;
634                         for ( i = ACL_NONE; i <= global_default_access; i++ ) {
635                                 mask |= ACL_ACCESS2PRIV( i );
636                         }
637                 }
638
639                 goto done;
640 #endif
641         }
642
643         ret = 0;
644         control = ACL_BREAK;
645
646         if ( st_same_attr ) {
647                 assert( state->as_vd_acl != NULL );
648
649                 a = state->as_vd_acl;
650                 count = state->as_vd_acl_count;
651                 if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
652                         mask = state->as_vd_acl_mask;
653                         AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
654                         goto vd_access;
655                 }
656
657         } else {
658                 if ( state ) state->as_vi_acl = NULL;
659                 a = NULL;
660                 ACL_INIT(mask);
661                 count = 0;
662                 memset( matches, '\0', sizeof(matches) );
663         }
664
665         while ( ( a = acl_get( a, &count, op, e, desc, val,
666                 MAXREMATCHES, matches, state ) ) != NULL )
667         {
668                 int i;
669
670                 for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
671                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
672                                 (int)matches[i].rm_so, (int)matches[i].rm_eo );
673                         if ( matches[i].rm_so <= matches[0].rm_eo ) {
674                                 int n;
675                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
676                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
677                                 }
678                         }
679                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
680                 }
681
682                 if ( state ) {
683                         if ( state->as_vi_acl == a &&
684                                 ( state->as_recorded & ACL_STATE_RECORDED_NV ) )
685                         {
686                                 Debug( LDAP_DEBUG_ACL,
687                                         "access_allowed: result from state (%s)\n",
688                                         attr, 0, 0 );
689                                 ret = state->as_result;
690                                 goto done;
691                         } else {
692                                 Debug( LDAP_DEBUG_ACL,
693                                         "access_allowed: no res from state (%s)\n",
694                                         attr, 0, 0 );
695                         }
696                 }
697
698 vd_access:
699                 control = acl_mask( a, &mask, op,
700                         e, desc, val, MAXREMATCHES, matches, count, state );
701
702                 if ( control != ACL_BREAK ) {
703                         break;
704                 }
705
706                 memset( matches, '\0', sizeof(matches) );
707         }
708
709         if ( ACL_IS_INVALID( mask ) ) {
710                 Debug( LDAP_DEBUG_ACL,
711                         "=> access_allowed: \"%s\" (%s) invalid!\n",
712                         e->e_dn, attr, 0 );
713                 ACL_INIT(mask);
714
715         } else if ( control == ACL_BREAK ) {
716                 Debug( LDAP_DEBUG_ACL,
717                         "=> access_allowed: no more rules\n", 0, 0, 0 );
718
719                 goto done;
720         }
721
722         Debug( LDAP_DEBUG_ACL,
723                 "=> access_allowed: %s access %s by %s\n",
724                 access2str( access ),
725                 ACL_GRANT(mask, access) ? "granted" : "denied",
726                 accessmask2str( mask, accessmaskbuf, 1 ) );
727
728         ret = ACL_GRANT(mask, access);
729
730 done:
731         if ( state != NULL ) {
732                 /* If not value-dependent, save ACL in case of more attrs */
733                 if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
734                         state->as_vi_acl = a;
735                         state->as_result = ret;
736                 }
737                 state->as_recorded |= ACL_STATE_RECORDED;
738         }
739         if ( be_null ) op->o_bd = NULL;
740         if ( maskp ) *maskp = mask;
741         return ret;
742 }
743
744 #endif /* LDAP_DEVEL */
745
746 /*
747  * acl_get - return the acl applicable to entry e, attribute
748  * attr.  the acl returned is suitable for use in subsequent calls to
749  * acl_access_allowed().
750  */
751
752 static AccessControl *
753 acl_get(
754         AccessControl *a,
755         int                     *count,
756         Operation       *op,
757         Entry           *e,
758         AttributeDescription *desc,
759         struct berval   *val,
760         int                     nmatch,
761         regmatch_t      *matches,
762         AccessControlState *state )
763 {
764         const char *attr;
765         int dnlen, patlen;
766         AccessControl *prev;
767
768         assert( e != NULL );
769         assert( count != NULL );
770         assert( desc != NULL );
771
772         attr = desc->ad_cname.bv_val;
773
774         assert( attr != NULL );
775
776         if( a == NULL ) {
777                 if( op->o_bd == NULL ) {
778                         a = frontendDB->be_acl;
779                 } else {
780                         a = op->o_bd->be_acl;
781                 }
782                 prev = NULL;
783
784                 assert( a != NULL );
785
786         } else {
787                 prev = a;
788                 a = a->acl_next;
789         }
790
791         dnlen = e->e_nname.bv_len;
792
793         for ( ; a != NULL; a = a->acl_next ) {
794                 (*count) ++;
795
796                 if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
797                         if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
798                                 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
799                                         *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
800                                 if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
801                                         continue;
802
803                         } else {
804                                 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
805                                         *count, a->acl_dn_pat.bv_val, 0 );
806                                 patlen = a->acl_dn_pat.bv_len;
807                                 if ( dnlen < patlen )
808                                         continue;
809
810                                 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
811                                         /* base dn -- entire object DN must match */
812                                         if ( dnlen != patlen )
813                                                 continue;
814
815                                 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
816                                         int     rdnlen = -1, sep = 0;
817
818                                         if ( dnlen <= patlen )
819                                                 continue;
820
821                                         if ( patlen > 0 ) {
822                                                 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
823                                                         continue;
824                                                 sep = 1;
825                                         }
826
827                                         rdnlen = dn_rdnlen( NULL, &e->e_nname );
828                                         if ( rdnlen != dnlen - patlen - sep )
829                                                 continue;
830
831                                 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
832                                         if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
833                                                 continue;
834
835                                 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
836                                         if ( dnlen <= patlen )
837                                                 continue;
838                                         if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
839                                                 continue;
840                                 }
841
842                                 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
843                                         continue;
844                         }
845
846                         Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
847                                 *count, 0, 0 );
848                 }
849
850                 if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
851                         matches[0].rm_so = matches[0].rm_eo = -1;
852                         continue;
853                 }
854
855                 /* Is this ACL only for a specific value? */
856                 if ( a->acl_attrval.bv_len ) {
857                         if ( val == NULL ) {
858                                 continue;
859                         }
860
861                         if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) {
862                                 state->as_recorded |= ACL_STATE_RECORDED_VD;
863                                 state->as_vd_acl = prev;
864                                 state->as_vd_acl_count = *count;
865                                 state->as_vd_access = a->acl_access;
866                                 state->as_vd_access_count = 1;
867                                 ACL_INVALIDATE( state->as_vd_acl_mask );
868                         }
869
870                         if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
871                                 Debug( LDAP_DEBUG_ACL,
872                                         "acl_get: valpat %s\n",
873                                         a->acl_attrval.bv_val, 0, 0 );
874                                 if ( regexec( &a->acl_attrval_re, val->bv_val, 0, NULL, 0 ) )
875                                 {
876                                         continue;
877                                 }
878
879                         } else {
880                                 int match = 0;
881                                 const char *text;
882                                 Debug( LDAP_DEBUG_ACL,
883                                         "acl_get: val %s\n",
884                                         a->acl_attrval.bv_val, 0, 0 );
885         
886                                 if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
887                                         if (value_match( &match, desc,
888                                                 desc->ad_type->sat_equality, 0,
889                                                 val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
890                                                         match )
891                                                 continue;
892                                         
893                                 } else {
894                                         int             patlen, vdnlen;
895         
896                                         patlen = a->acl_attrval.bv_len;
897                                         vdnlen = val->bv_len;
898         
899                                         if ( vdnlen < patlen )
900                                                 continue;
901         
902                                         if ( a->acl_dn_style == ACL_STYLE_BASE ) {
903                                                 if ( vdnlen > patlen )
904                                                         continue;
905         
906                                         } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
907                                                 int rdnlen = -1;
908         
909                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
910                                                         continue;
911         
912                                                 rdnlen = dn_rdnlen( NULL, val );
913                                                 if ( rdnlen != vdnlen - patlen - 1 )
914                                                         continue;
915         
916                                         } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
917                                                 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
918                                                         continue;
919         
920                                         } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
921                                                 if ( vdnlen <= patlen )
922                                                         continue;
923         
924                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
925                                                         continue;
926                                         }
927         
928                                         if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ))
929                                                 continue;
930                                 }
931                         }
932                 }
933
934                 if ( a->acl_filter != NULL ) {
935                         ber_int_t rc = test_filter( NULL, e, a->acl_filter );
936                         if ( rc != LDAP_COMPARE_TRUE ) {
937                                 continue;
938                         }
939                 }
940
941                 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
942                        *count, attr, 0);
943                 return a;
944         }
945
946         Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
947         return( NULL );
948 }
949
950 static int
951 acl_mask_dn(
952         Operation               *op,
953         Entry                   *e,
954         AccessControl           *a,
955         int                     nmatch,
956         regmatch_t              *matches,
957         slap_dn_access          *b,
958         struct berval           *opndn )
959 {
960         /*
961          * if access applies to the entry itself, and the
962          * user is bound as somebody in the same namespace as
963          * the entry, OR the given dn matches the dn pattern
964          */
965         /*
966          * NOTE: styles "anonymous", "users" and "self" 
967          * have been moved to enum slap_style_t, whose 
968          * value is set in a_dn_style; however, the string
969          * is maintaned in a_dn_pat.
970          */
971         if ( b->a_style == ACL_STYLE_ANONYMOUS ) {
972                 if ( !BER_BVISEMPTY( opndn ) ) {
973                         return 1;
974                 }
975
976         } else if ( b->a_style == ACL_STYLE_USERS ) {
977                 if ( BER_BVISEMPTY( opndn ) ) {
978                         return 1;
979                 }
980
981         } else if ( b->a_style == ACL_STYLE_SELF ) {
982                 struct berval   ndn, selfndn;
983                 int             level;
984
985                 if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
986                         return 1;
987                 }
988
989                 level = b->a_self_level;
990                 if ( level < 0 ) {
991                         selfndn = *opndn;
992                         ndn = e->e_nname;
993                         level = -level;
994
995                 } else {
996                         ndn = *opndn;
997                         selfndn = e->e_nname;
998                 }
999
1000                 for ( ; level > 0; level-- ) {
1001                         if ( BER_BVISEMPTY( &ndn ) ) {
1002                                 break;
1003                         }
1004                         dnParent( &ndn, &ndn );
1005                 }
1006                         
1007                 if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
1008                 {
1009                         return 1;
1010                 }
1011
1012         } else if ( b->a_style == ACL_STYLE_REGEX ) {
1013                 if ( !ber_bvccmp( &b->a_pat, '*' ) ) {
1014                         int             tmp_nmatch;
1015                         regmatch_t      tmp_matches[2],
1016                                         *tmp_matchesp = tmp_matches;
1017
1018                         int             rc = 0;
1019
1020                         switch ( a->acl_dn_style ) {
1021                         case ACL_STYLE_REGEX:
1022                                 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1023                                         tmp_matchesp = matches;
1024                                         tmp_nmatch = nmatch;
1025                                         break;
1026                                 }
1027                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1028
1029                         case ACL_STYLE_BASE:
1030                                 tmp_matches[0].rm_so = 0;
1031                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1032                                 tmp_nmatch = 1;
1033                                 break;
1034
1035                         case ACL_STYLE_ONE:
1036                         case ACL_STYLE_SUBTREE:
1037                         case ACL_STYLE_CHILDREN:
1038                                 tmp_matches[0].rm_so = 0;
1039                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1040                                 tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1041                                 tmp_matches[1].rm_eo = e->e_nname.bv_len;
1042                                 tmp_nmatch = 2;
1043                                 break;
1044
1045                         default:
1046                                 /* error */
1047                                 rc = 1;
1048                                 break;
1049                         }
1050
1051                         if ( rc ) {
1052                                 return 1;
1053                         }
1054
1055                         if ( !regex_matches( &b->a_pat, opndn->bv_val,
1056                                 e->e_ndn, tmp_nmatch, tmp_matchesp ) )
1057                         {
1058                                 return 1;
1059                         }
1060                 }
1061
1062         } else {
1063                 struct berval   pat;
1064                 ber_len_t       patlen, odnlen;
1065                 int             got_match = 0;
1066
1067                 if ( e->e_dn == NULL )
1068                         return 1;
1069
1070                 if ( b->a_expand ) {
1071                         struct berval   bv;
1072                         char            buf[ACL_BUF_SIZE];
1073                         
1074                         int             tmp_nmatch;
1075                         regmatch_t      tmp_matches[2],
1076                                         *tmp_matchesp = tmp_matches;
1077
1078                         int             rc = 0;
1079
1080                         bv.bv_len = sizeof( buf ) - 1;
1081                         bv.bv_val = buf;
1082
1083                         switch ( a->acl_dn_style ) {
1084                         case ACL_STYLE_REGEX:
1085                                 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1086                                         tmp_matchesp = matches;
1087                                         tmp_nmatch = nmatch;
1088                                         break;
1089                                 }
1090                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1091
1092                         case ACL_STYLE_BASE:
1093                                 tmp_matches[0].rm_so = 0;
1094                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1095                                 tmp_nmatch = 1;
1096                                 break;
1097
1098                         case ACL_STYLE_ONE:
1099                         case ACL_STYLE_SUBTREE:
1100                         case ACL_STYLE_CHILDREN:
1101                                 tmp_matches[0].rm_so = 0;
1102                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1103                                 tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1104                                 tmp_matches[1].rm_eo = e->e_nname.bv_len;
1105                                 tmp_nmatch = 2;
1106                                 break;
1107
1108                         default:
1109                                 /* error */
1110                                 rc = 1;
1111                                 break;
1112                         }
1113
1114                         if ( rc ) {
1115                                 return 1;
1116                         }
1117
1118                         if ( string_expand( &bv, &b->a_pat, 
1119                                         e->e_nname.bv_val,
1120                                         tmp_nmatch, tmp_matchesp ) )
1121                         {
1122                                 return 1;
1123                         }
1124                         
1125                         if ( dnNormalize(0, NULL, NULL, &bv,
1126                                         &pat, op->o_tmpmemctx )
1127                                         != LDAP_SUCCESS )
1128                         {
1129                                 /* did not expand to a valid dn */
1130                                 return 1;
1131                         }
1132
1133                 } else {
1134                         pat = b->a_pat;
1135                 }
1136
1137                 patlen = pat.bv_len;
1138                 odnlen = opndn->bv_len;
1139                 if ( odnlen < patlen ) {
1140                         goto dn_match_cleanup;
1141
1142                 }
1143
1144                 if ( b->a_style == ACL_STYLE_BASE ) {
1145                         /* base dn -- entire object DN must match */
1146                         if ( odnlen != patlen ) {
1147                                 goto dn_match_cleanup;
1148                         }
1149
1150                 } else if ( b->a_style == ACL_STYLE_ONE ) {
1151                         int rdnlen = -1;
1152
1153                         if ( odnlen <= patlen ) {
1154                                 goto dn_match_cleanup;
1155                         }
1156
1157                         if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1158                                 goto dn_match_cleanup;
1159                         }
1160
1161                         rdnlen = dn_rdnlen( NULL, opndn );
1162                         if ( rdnlen != odnlen - patlen - 1 ) {
1163                                 goto dn_match_cleanup;
1164                         }
1165
1166                 } else if ( b->a_style == ACL_STYLE_SUBTREE ) {
1167                         if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1168                                 goto dn_match_cleanup;
1169                         }
1170
1171                 } else if ( b->a_style == ACL_STYLE_CHILDREN ) {
1172                         if ( odnlen <= patlen ) {
1173                                 goto dn_match_cleanup;
1174                         }
1175
1176                         if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1177                                 goto dn_match_cleanup;
1178                         }
1179
1180                 } else if ( b->a_style == ACL_STYLE_LEVEL ) {
1181                         int level;
1182                         struct berval ndn;
1183
1184                         if ( odnlen <= patlen ) {
1185                                 goto dn_match_cleanup;
1186                         }
1187
1188                         if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
1189                         {
1190                                 goto dn_match_cleanup;
1191                         }
1192                         
1193                         level = b->a_level;
1194                         ndn = *opndn;
1195                         for ( ; level > 0; level-- ) {
1196                                 if ( BER_BVISEMPTY( &ndn ) ) {
1197                                         goto dn_match_cleanup;
1198                                 }
1199                                 dnParent( &ndn, &ndn );
1200                                 if ( ndn.bv_len < patlen ) {
1201                                         goto dn_match_cleanup;
1202                                 }
1203                         }
1204                         
1205                         if ( ndn.bv_len != patlen ) {
1206                                 goto dn_match_cleanup;
1207                         }
1208                 }
1209
1210                 got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
1211
1212 dn_match_cleanup:;
1213                 if ( pat.bv_val != b->a_pat.bv_val ) {
1214                         slap_sl_free( pat.bv_val, op->o_tmpmemctx );
1215                 }
1216
1217                 if ( !got_match ) {
1218                         return 1;
1219                 }
1220         }
1221
1222         return 0;
1223 }
1224
1225 /*
1226  * Record value-dependent access control state
1227  */
1228 #define ACL_RECORD_VALUE_STATE do { \
1229                 if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
1230                         state->as_recorded |= ACL_STATE_RECORDED_VD; \
1231                         state->as_vd_acl = a; \
1232                         AC_MEMCPY( state->as_vd_acl_matches, matches, \
1233                                 sizeof( state->as_vd_acl_matches )) ; \
1234                         state->as_vd_acl_count = count; \
1235                         state->as_vd_access = b; \
1236                         state->as_vd_access_count = i; \
1237                 } \
1238         } while( 0 )
1239
1240 static int
1241 acl_mask_dnattr(
1242         Operation               *op,
1243         Entry                   *e,
1244         struct berval           *val,
1245         AccessControl           *a,
1246         Access                  *b,
1247         int                     i,
1248         regmatch_t              *matches,
1249         int                     count,
1250         AccessControlState      *state,
1251         slap_dn_access          *bdn,
1252         struct berval           *opndn )
1253 {
1254         Attribute       *at;
1255         struct berval   bv;
1256         int             rc, match = 0;
1257         const char      *text;
1258         const char      *attr = bdn->a_at->ad_cname.bv_val;
1259
1260         assert( attr != NULL );
1261
1262         if ( BER_BVISEMPTY( opndn ) ) {
1263                 return 1;
1264         }
1265
1266         Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 );
1267         bv = *opndn;
1268
1269         /* see if asker is listed in dnattr */
1270         for ( at = attrs_find( e->e_attrs, bdn->a_at );
1271                 at != NULL;
1272                 at = attrs_find( at->a_next, bdn->a_at ) )
1273         {
1274                 if ( value_find_ex( bdn->a_at,
1275                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1276                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1277                         at->a_nvals,
1278                         &bv, op->o_tmpmemctx ) == 0 )
1279                 {
1280                         /* found it */
1281                         match = 1;
1282                         break;
1283                 }
1284         }
1285
1286         if ( match ) {
1287                 /* have a dnattr match. if this is a self clause then
1288                  * the target must also match the op dn.
1289                  */
1290                 if ( bdn->a_self ) {
1291                         /* check if the target is an attribute. */
1292                         if ( val == NULL ) return 1;
1293
1294                         /* target is attribute, check if the attribute value
1295                          * is the op dn.
1296                          */
1297                         rc = value_match( &match, bdn->a_at,
1298                                 bdn->a_at->ad_type->sat_equality, 0,
1299                                 val, &bv, &text );
1300                         /* on match error or no match, fail the ACL clause */
1301                         if ( rc != LDAP_SUCCESS || match != 0 )
1302                                 return 1;
1303                 }
1304
1305         } else {
1306                 /* no dnattr match, check if this is a self clause */
1307                 if ( ! bdn->a_self )
1308                         return 1;
1309
1310                 ACL_RECORD_VALUE_STATE;
1311                 
1312                 /* this is a self clause, check if the target is an
1313                  * attribute.
1314                  */
1315                 if ( val == NULL )
1316                         return 1;
1317
1318                 /* target is attribute, check if the attribute value
1319                  * is the op dn.
1320                  */
1321                 rc = value_match( &match, bdn->a_at,
1322                         bdn->a_at->ad_type->sat_equality, 0,
1323                         val, &bv, &text );
1324
1325                 /* on match error or no match, fail the ACL clause */
1326                 if ( rc != LDAP_SUCCESS || match != 0 )
1327                         return 1;
1328         }
1329
1330         return 0;
1331 }
1332
1333
1334 /*
1335  * acl_mask - modifies mask based upon the given acl and the
1336  * requested access to entry e, attribute attr, value val.  if val
1337  * is null, access to the whole attribute is assumed (all values).
1338  *
1339  * returns      0       access NOT allowed
1340  *              1       access allowed
1341  */
1342
1343 static slap_control_t
1344 acl_mask(
1345         AccessControl           *a,
1346         slap_mask_t             *mask,
1347         Operation               *op,
1348         Entry                   *e,
1349         AttributeDescription    *desc,
1350         struct berval           *val,
1351         int                     nmatch,
1352         regmatch_t              *matches,
1353         int                     count,
1354         AccessControlState      *state )
1355 {
1356         int             i;
1357         Access  *b;
1358 #ifdef LDAP_DEBUG
1359         char accessmaskbuf[ACCESSMASK_MAXLEN];
1360 #if !defined( SLAP_DYNACL ) && defined( SLAPD_ACI_ENABLED )
1361         char accessmaskbuf1[ACCESSMASK_MAXLEN];
1362 #endif /* !SLAP_DYNACL && SLAPD_ACI_ENABLED */
1363 #endif /* DEBUG */
1364         const char *attr;
1365
1366         assert( a != NULL );
1367         assert( mask != NULL );
1368         assert( desc != NULL );
1369
1370         attr = desc->ad_cname.bv_val;
1371
1372         assert( attr != NULL );
1373
1374         Debug( LDAP_DEBUG_ACL,
1375                 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
1376                 e->e_dn, attr, 0 );
1377
1378         Debug( LDAP_DEBUG_ACL,
1379                 "=> acl_mask: to %s by \"%s\", (%s) \n",
1380                 val ? "value" : "all values",
1381                 op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
1382                 accessmask2str( *mask, accessmaskbuf, 1) );
1383
1384
1385         if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
1386                 && state->as_vd_acl == a )
1387         {
1388                 b = state->as_vd_access;
1389                 i = state->as_vd_access_count;
1390
1391         } else {
1392                 b = a->acl_access;
1393                 i = 1;
1394         }
1395
1396         for ( ; b != NULL; b = b->a_next, i++ ) {
1397                 slap_mask_t oldmask, modmask;
1398
1399                 ACL_INVALIDATE( modmask );
1400
1401                 /* AND <who> clauses */
1402                 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
1403                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
1404                                 b->a_dn_pat.bv_val, 0, 0);
1405                         /*
1406                          * if access applies to the entry itself, and the
1407                          * user is bound as somebody in the same namespace as
1408                          * the entry, OR the given dn matches the dn pattern
1409                          */
1410                         /*
1411                          * NOTE: styles "anonymous", "users" and "self" 
1412                          * have been moved to enum slap_style_t, whose 
1413                          * value is set in a_dn_style; however, the string
1414                          * is maintaned in a_dn_pat.
1415                          */
1416
1417                         if ( acl_mask_dn( op, e, a, nmatch, matches,
1418                                 &b->a_dn, &op->o_ndn ) )
1419                         {
1420                                 continue;
1421                         }
1422                 }
1423
1424                 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
1425                         struct berval   ndn;
1426
1427                         Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
1428                                 b->a_realdn_pat.bv_val, 0, 0);
1429                         /*
1430                          * if access applies to the entry itself, and the
1431                          * user is bound as somebody in the same namespace as
1432                          * the entry, OR the given dn matches the dn pattern
1433                          */
1434                         /*
1435                          * NOTE: styles "anonymous", "users" and "self" 
1436                          * have been moved to enum slap_style_t, whose 
1437                          * value is set in a_dn_style; however, the string
1438                          * is maintaned in a_dn_pat.
1439                          */
1440
1441                         if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1442                         {
1443                                 ndn = op->o_conn->c_ndn;
1444                         } else {
1445                                 ndn = op->o_ndn;
1446                         }
1447
1448                         if ( acl_mask_dn( op, e, a, nmatch, matches,
1449                                 &b->a_realdn, &ndn ) )
1450                         {
1451                                 continue;
1452                         }
1453                 }
1454
1455                 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1456                         if ( ! op->o_conn->c_listener ) {
1457                                 continue;
1458                         }
1459                         Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
1460                                 b->a_sockurl_pat.bv_val, 0, 0 );
1461
1462                         if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
1463                                 if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
1464                                         if (!regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
1465                                                         e->e_ndn, nmatch, matches ) ) 
1466                                         {
1467                                                 continue;
1468                                         }
1469
1470                                 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
1471                                         struct berval   bv;
1472                                         char buf[ACL_BUF_SIZE];
1473
1474                                         bv.bv_len = sizeof( buf ) - 1;
1475                                         bv.bv_val = buf;
1476                                         if ( string_expand( &bv, &b->a_sockurl_pat,
1477                                                         e->e_ndn, nmatch, matches ) )
1478                                         {
1479                                                 continue;
1480                                         }
1481
1482                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
1483                                         {
1484                                                 continue;
1485                                         }
1486
1487                                 } else {
1488                                         if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
1489                                         {
1490                                                 continue;
1491                                         }
1492                                 }
1493                         }
1494                 }
1495
1496                 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1497                         if ( !op->o_conn->c_peer_domain.bv_val ) {
1498                                 continue;
1499                         }
1500                         Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
1501                                 b->a_domain_pat.bv_val, 0, 0 );
1502                         if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
1503                                 if ( b->a_domain_style == ACL_STYLE_REGEX) {
1504                                         if (!regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
1505                                                         e->e_ndn, nmatch, matches ) ) 
1506                                         {
1507                                                 continue;
1508                                         }
1509                                 } else {
1510                                         char buf[ACL_BUF_SIZE];
1511
1512                                         struct berval   cmp = op->o_conn->c_peer_domain;
1513                                         struct berval   pat = b->a_domain_pat;
1514
1515                                         if ( b->a_domain_expand ) {
1516                                                 struct berval bv;
1517
1518                                                 bv.bv_len = sizeof(buf) - 1;
1519                                                 bv.bv_val = buf;
1520
1521                                                 if ( string_expand(&bv, &b->a_domain_pat,
1522                                                                 e->e_ndn, nmatch, matches) )
1523                                                 {
1524                                                         continue;
1525                                                 }
1526                                                 pat = bv;
1527                                         }
1528
1529                                         if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
1530                                                 int offset = cmp.bv_len - pat.bv_len;
1531                                                 if ( offset < 0 ) {
1532                                                         continue;
1533                                                 }
1534
1535                                                 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
1536                                                         continue;
1537                                                 }
1538
1539                                                 /* trim the domain */
1540                                                 cmp.bv_val = &cmp.bv_val[ offset ];
1541                                                 cmp.bv_len -= offset;
1542                                         }
1543                                         
1544                                         if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1545                                                 continue;
1546                                         }
1547                                 }
1548                         }
1549                 }
1550
1551                 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1552                         if ( !op->o_conn->c_peer_name.bv_val ) {
1553                                 continue;
1554                         }
1555                         Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1556                                 b->a_peername_pat.bv_val, 0, 0 );
1557                         if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1558                                 if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1559                                         if (!regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1560                                                         e->e_ndn, nmatch, matches ) ) 
1561                                         {
1562                                                 continue;
1563                                         }
1564
1565                                 } else {
1566                                         /* try exact match */
1567                                         if ( b->a_peername_style == ACL_STYLE_BASE ) {
1568                                                 if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
1569                                                         continue;
1570                                                 }
1571
1572                                         } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1573                                                 struct berval   bv;
1574                                                 char buf[ACL_BUF_SIZE];
1575
1576                                                 bv.bv_len = sizeof( buf ) - 1;
1577                                                 bv.bv_val = buf;
1578                                                 if ( string_expand( &bv, &b->a_peername_pat,
1579                                                                 e->e_ndn, nmatch, matches ) )
1580                                                 {
1581                                                         continue;
1582                                                 }
1583
1584                                                 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1585                                                         continue;
1586                                                 }
1587
1588                                         /* extract IP and try exact match */
1589                                         } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1590                                                 char            *port;
1591                                                 char            buf[] = "255.255.255.255";
1592                                                 struct berval   ip;
1593                                                 unsigned long   addr;
1594                                                 int             port_number = -1;
1595                                                 
1596                                                 if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 
1597                                                                         aci_bv_ip_eq.bv_val, aci_bv_ip_eq.bv_len ) != 0 ) 
1598                                                         continue;
1599
1600                                                 ip.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_ip_eq.bv_len;
1601                                                 ip.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_ip_eq.bv_len;
1602
1603                                                 port = strrchr( ip.bv_val, ':' );
1604                                                 if ( port ) {
1605                                                         char    *next;
1606                                                         
1607                                                         ip.bv_len = port - ip.bv_val;
1608                                                         ++port;
1609                                                         port_number = strtol( port, &next, 10 );
1610                                                         if ( next[0] != '\0' )
1611                                                                 continue;
1612                                                 }
1613                                                 
1614                                                 /* the port check can be anticipated here */
1615                                                 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1616                                                         continue;
1617                                                 
1618                                                 /* address longer than expected? */
1619                                                 if ( ip.bv_len >= sizeof(buf) )
1620                                                         continue;
1621
1622                                                 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1623                                                 buf[ ip.bv_len ] = '\0';
1624
1625                                                 addr = inet_addr( buf );
1626
1627                                                 /* unable to convert? */
1628                                                 if ( addr == (unsigned long)(-1) )
1629                                                         continue;
1630
1631                                                 if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1632                                                         continue;
1633
1634 #ifdef LDAP_PF_LOCAL
1635                                         /* extract path and try exact match */
1636                                         } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
1637                                                 struct berval path;
1638                                                 
1639                                                 if ( strncmp( op->o_conn->c_peer_name.bv_val,
1640                                                                         aci_bv_path_eq.bv_val, aci_bv_path_eq.bv_len ) != 0 )
1641                                                         continue;
1642
1643                                                 path.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_path_eq.bv_len;
1644                                                 path.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_path_eq.bv_len;
1645
1646                                                 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1647                                                         continue;
1648
1649 #endif /* LDAP_PF_LOCAL */
1650
1651                                         /* exact match (very unlikely...) */
1652                                         } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1653                                                         continue;
1654                                         }
1655                                 }
1656                         }
1657                 }
1658
1659                 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
1660                         if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
1661                                 continue;
1662                         }
1663                         Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1664                                 b->a_sockname_pat.bv_val, 0, 0 );
1665                         if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1666                                 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1667                                         if (!regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1668                                                         e->e_ndn, nmatch, matches ) ) 
1669                                         {
1670                                                 continue;
1671                                         }
1672
1673                                 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1674                                         struct berval   bv;
1675                                         char buf[ACL_BUF_SIZE];
1676
1677                                         bv.bv_len = sizeof( buf ) - 1;
1678                                         bv.bv_val = buf;
1679                                         if ( string_expand( &bv, &b->a_sockname_pat,
1680                                                         e->e_ndn, nmatch, matches ) )
1681                                         {
1682                                                 continue;
1683                                         }
1684
1685                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1686                                                 continue;
1687                                         }
1688
1689                                 } else {
1690                                         if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
1691                                                 continue;
1692                                         }
1693                                 }
1694                         }
1695                 }
1696
1697                 if ( b->a_dn_at != NULL ) {
1698                         if ( acl_mask_dnattr( op, e, val, a, b, i,
1699                                         matches, count, state,
1700                                         &b->a_dn, &op->o_ndn ) )
1701                         {
1702                                 continue;
1703                         }
1704                 }
1705
1706                 if ( b->a_realdn_at != NULL ) {
1707                         struct berval   ndn;
1708
1709                         if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1710                         {
1711                                 ndn = op->o_conn->c_ndn;
1712                         } else {
1713                                 ndn = op->o_ndn;
1714                         }
1715
1716                         if ( acl_mask_dnattr( op, e, val, a, b, i,
1717                                         matches, count, state,
1718                                         &b->a_realdn, &ndn ) )
1719                         {
1720                                 continue;
1721                         }
1722                 }
1723
1724                 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1725                         struct berval bv;
1726                         struct berval ndn = BER_BVNULL;
1727                         int rc;
1728
1729                         if ( op->o_ndn.bv_len == 0 ) {
1730                                 continue;
1731                         }
1732
1733                         /* b->a_group is an unexpanded entry name, expanded it should be an 
1734                          * entry with objectclass group* and we test to see if odn is one of
1735                          * the values in the attribute group
1736                          */
1737                         /* see if asker is listed in dnattr */
1738                         if ( b->a_group_style == ACL_STYLE_EXPAND ) {
1739                                 char            buf[ACL_BUF_SIZE];
1740                                 int             tmp_nmatch;
1741                                 regmatch_t      tmp_matches[2],
1742                                                 *tmp_matchesp = tmp_matches;
1743
1744                                 bv.bv_len = sizeof(buf) - 1;
1745                                 bv.bv_val = buf;
1746
1747                                 rc = 0;
1748
1749                                 switch ( a->acl_dn_style ) {
1750                                 case ACL_STYLE_REGEX:
1751                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1752                                                 tmp_matchesp = matches;
1753                                                 tmp_nmatch = nmatch;
1754                                                 break;
1755                                         }
1756
1757                                 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1758                                 case ACL_STYLE_BASE:
1759                                         tmp_matches[0].rm_so = 0;
1760                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1761                                         tmp_nmatch = 1;
1762                                         break;
1763
1764                                 case ACL_STYLE_ONE:
1765                                 case ACL_STYLE_SUBTREE:
1766                                 case ACL_STYLE_CHILDREN:
1767                                         tmp_matches[0].rm_so = 0;
1768                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1769                                         tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1770                                         tmp_matches[1].rm_eo = e->e_nname.bv_len;
1771                                         tmp_nmatch = 2;
1772                                         break;
1773
1774                                 default:
1775                                         /* error */
1776                                         rc = 1;
1777                                         break;
1778                                 }
1779
1780                                 if ( rc ) {
1781                                         continue;
1782                                 }
1783                                 
1784                                 if ( string_expand( &bv, &b->a_group_pat,
1785                                                 e->e_nname.bv_val,
1786                                                 tmp_nmatch, tmp_matchesp ) )
1787                                 {
1788                                         continue;
1789                                 }
1790
1791                                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
1792                                                 op->o_tmpmemctx ) != LDAP_SUCCESS )
1793                                 {
1794                                         /* did not expand to a valid dn */
1795                                         continue;
1796                                 }
1797
1798                                 bv = ndn;
1799
1800                         } else {
1801                                 bv = b->a_group_pat;
1802                         }
1803
1804                         rc = backend_group( op, e, &bv, &op->o_ndn,
1805                                 b->a_group_oc, b->a_group_at );
1806
1807                         if ( ndn.bv_val ) {
1808                                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
1809                         }
1810
1811                         if ( rc != 0 ) {
1812                                 continue;
1813                         }
1814                 }
1815
1816                 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1817                         struct berval   bv;
1818                         char            buf[ACL_BUF_SIZE];
1819
1820                         if ( b->a_set_style == ACL_STYLE_EXPAND ) {
1821                                 int             tmp_nmatch;
1822                                 regmatch_t      tmp_matches[2],
1823                                                 *tmp_matchesp = tmp_matches;
1824                                 int             rc = 0;
1825
1826                                 bv.bv_len = sizeof( buf ) - 1;
1827                                 bv.bv_val = buf;
1828
1829                                 rc = 0;
1830
1831                                 switch ( a->acl_dn_style ) {
1832                                 case ACL_STYLE_REGEX:
1833                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1834                                                 tmp_matchesp = matches;
1835                                                 tmp_nmatch = nmatch;
1836                                                 break;
1837                                         }
1838
1839                                 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1840                                 case ACL_STYLE_BASE:
1841                                         tmp_matches[0].rm_so = 0;
1842                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1843                                         tmp_nmatch = 1;
1844                                         break;
1845
1846                                 case ACL_STYLE_ONE:
1847                                 case ACL_STYLE_SUBTREE:
1848                                 case ACL_STYLE_CHILDREN:
1849                                         tmp_matches[0].rm_so = 0;
1850                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1851                                         tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1852                                         tmp_matches[1].rm_eo = e->e_nname.bv_len;
1853                                         tmp_nmatch = 2;
1854                                         break;
1855
1856                                 default:
1857                                         /* error */
1858                                         rc = 1;
1859                                         break;
1860                                 }
1861
1862                                 if ( rc ) {
1863                                         continue;
1864                                 }
1865                                 
1866                                 if ( string_expand( &bv, &b->a_set_pat,
1867                                                 e->e_nname.bv_val,
1868                                                 tmp_nmatch, tmp_matchesp ) )
1869                                 {
1870                                         continue;
1871                                 }
1872
1873                         } else {
1874                                 bv = b->a_set_pat;
1875                         }
1876                         
1877                         if ( aci_match_set( &bv, op, e, 0 ) == 0 ) {
1878                                 continue;
1879                         }
1880                 }
1881
1882                 if ( b->a_authz.sai_ssf ) {
1883                         Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1884                                 b->a_authz.sai_ssf, op->o_ssf, 0 );
1885                         if ( b->a_authz.sai_ssf >  op->o_ssf ) {
1886                                 continue;
1887                         }
1888                 }
1889
1890                 if ( b->a_authz.sai_transport_ssf ) {
1891                         Debug( LDAP_DEBUG_ACL,
1892                                 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1893                                 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
1894                         if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
1895                                 continue;
1896                         }
1897                 }
1898
1899                 if ( b->a_authz.sai_tls_ssf ) {
1900                         Debug( LDAP_DEBUG_ACL,
1901                                 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1902                                 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
1903                         if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
1904                                 continue;
1905                         }
1906                 }
1907
1908                 if ( b->a_authz.sai_sasl_ssf ) {
1909                         Debug( LDAP_DEBUG_ACL,
1910                                 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1911                                 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
1912                         if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
1913                                 continue;
1914                         }
1915                 }
1916
1917 #ifdef SLAP_DYNACL
1918                 if ( b->a_dynacl ) {
1919                         slap_dynacl_t   *da;
1920                         slap_access_t   tgrant, tdeny;
1921
1922                         /* this case works different from the others above.
1923                          * since aci's themselves give permissions, we need
1924                          * to first check b->a_access_mask, the ACL's access level.
1925                          */
1926                         if ( BER_BVISEMPTY( &e->e_nname ) ) {
1927                                 /* no ACIs in the root DSE */
1928                                 continue;
1929                         }
1930
1931                         /* first check if the right being requested
1932                          * is allowed by the ACL clause.
1933                          */
1934                         if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
1935                                 continue;
1936                         }
1937
1938                         /* start out with nothing granted, nothing denied */
1939                         ACL_INIT(tgrant);
1940                         ACL_INIT(tdeny);
1941
1942                         for ( da = b->a_dynacl; da; da = da->da_next ) {
1943                                 slap_access_t   grant, deny;
1944
1945                                 (void)( *da->da_mask )( da->da_private, op, e, desc, val, nmatch, matches, &grant, &deny );
1946
1947                                 tgrant |= grant;
1948                                 tdeny |= deny;
1949                         }
1950
1951                         /* remove anything that the ACL clause does not allow */
1952                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1953                         tdeny &= ACL_PRIV_MASK;
1954
1955                         /* see if we have anything to contribute */
1956                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
1957                                 continue;
1958                         }
1959
1960                         /* this could be improved by changing acl_mask so that it can deal with
1961                          * by clauses that return grant/deny pairs.  Right now, it does either
1962                          * additive or subtractive rights, but not both at the same time.  So,
1963                          * we need to combine the grant/deny pair into a single rights mask in
1964                          * a smart way:  if either grant or deny is "empty", then we use the
1965                          * opposite as is, otherwise we remove any denied rights from the grant
1966                          * rights mask and construct an additive mask.
1967                          */
1968                         if (ACL_IS_INVALID(tdeny)) {
1969                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
1970
1971                         } else if (ACL_IS_INVALID(tgrant)) {
1972                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1973
1974                         } else {
1975                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1976                         }
1977
1978                 } else
1979 #else /* !SLAP_DYNACL */
1980
1981 #ifdef SLAPD_ACI_ENABLED
1982                 if ( b->a_aci_at != NULL ) {
1983                         Attribute       *at;
1984                         slap_access_t   grant, deny, tgrant, tdeny;
1985                         struct berval   parent_ndn;
1986                         BerVarray       bvals = NULL;
1987                         int             ret, stop;
1988
1989                         /* this case works different from the others above.
1990                          * since aci's themselves give permissions, we need
1991                          * to first check b->a_access_mask, the ACL's access level.
1992                          */
1993
1994                         if ( BER_BVISEMPTY( &e->e_nname ) ) {
1995                                 /* no ACIs in the root DSE */
1996                                 continue;
1997                         }
1998
1999                         /* first check if the right being requested
2000                          * is allowed by the ACL clause.
2001                          */
2002                         if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
2003                                 continue;
2004                         }
2005                         /* start out with nothing granted, nothing denied */
2006                         ACL_INIT(tgrant);
2007                         ACL_INIT(tdeny);
2008
2009                         /* get the aci attribute */
2010                         at = attr_find( e->e_attrs, b->a_aci_at );
2011                         if ( at != NULL ) {
2012 #if 0
2013                                 /* FIXME: this breaks acl caching;
2014                                  * see also ACL_RECORD_VALUE_STATE below */
2015                                 ACL_RECORD_VALUE_STATE;
2016 #endif
2017                                 /* the aci is an multi-valued attribute.  The
2018                                 * rights are determined by OR'ing the individual
2019                                 * rights given by the acis.
2020                                 */
2021                                 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
2022                                         if (aci_mask( op,
2023                                                 e, desc, val,
2024                                                 &at->a_nvals[i],
2025                                                 nmatch, matches,
2026                                                 &grant, &deny, SLAP_ACI_SCOPE_ENTRY ) != 0)
2027                                         {
2028                                                 tgrant |= grant;
2029                                                 tdeny |= deny;
2030                                         }
2031                                 }
2032                                 Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
2033                                           accessmask2str(tgrant, accessmaskbuf, 1), 
2034                                           accessmask2str(tdeny, accessmaskbuf1, 1), 0);
2035
2036                         }
2037                         /* If the entry level aci didn't contain anything valid for the 
2038                          * current operation, climb up the tree and evaluate the
2039                          * acis with scope set to subtree
2040                          */
2041                         if ( (tgrant == ACL_PRIV_NONE) && (tdeny == ACL_PRIV_NONE) ) {
2042                                 dnParent( &e->e_nname, &parent_ndn );
2043                                 while ( !BER_BVISEMPTY( &parent_ndn ) ) {
2044                                         Debug(LDAP_DEBUG_ACL, "checking ACI of %s\n", parent_ndn.bv_val, 0, 0);
2045                                         ret = backend_attribute(op, NULL, &parent_ndn, b->a_aci_at, &bvals, ACL_AUTH);
2046                                         switch(ret){
2047                                         case LDAP_SUCCESS :
2048                                                 stop = 0;
2049                                                 if (!bvals){
2050                                                         break;
2051                                                 }
2052
2053                                                 for( i = 0; bvals[i].bv_val != NULL; i++){
2054 #if 0
2055                                                         /* FIXME: this breaks acl caching;
2056                                                          * see also ACL_RECORD_VALUE_STATE above */
2057                                                         ACL_RECORD_VALUE_STATE;
2058 #endif
2059                                                         if (aci_mask(op, e, desc, val, &bvals[i],
2060                                                                         nmatch, matches,
2061                                                                         &grant, &deny, SLAP_ACI_SCOPE_CHILDREN ) != 0 )
2062                                                         {
2063                                                                 tgrant |= grant;
2064                                                                 tdeny |= deny;
2065                                                                 /* evaluation stops as soon as either a "deny" or a 
2066                                                                  * "grant" directive matches.
2067                                                                  */
2068                                                                 if( (tgrant != ACL_PRIV_NONE) || (tdeny != ACL_PRIV_NONE) ){
2069                                                                         stop = 1;
2070                                                                 }
2071                                                         }
2072                                                         Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
2073                                                                 accessmask2str(tgrant, accessmaskbuf, 1),
2074                                                                 accessmask2str(tdeny, accessmaskbuf1, 1), 0);
2075                                                 }
2076                                                 break;
2077
2078                                         case LDAP_NO_SUCH_ATTRIBUTE:
2079                                                 /* just go on if the aci-Attribute is not present in
2080                                                  * the current entry 
2081                                                  */
2082                                                 Debug(LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0);
2083                                                 stop = 0;
2084                                                 break;
2085
2086                                         case LDAP_NO_SUCH_OBJECT:
2087                                                 /* We have reached the base object */
2088                                                 Debug(LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0);
2089                                                 stop = 1;
2090                                                 break;
2091
2092                                         default:
2093                                                 stop = 1;
2094                                                 break;
2095                                         }
2096                                         if (stop){
2097                                                 break;
2098                                         }
2099                                         dnParent( &parent_ndn, &parent_ndn );
2100                                 }
2101                         }
2102
2103
2104                         /* remove anything that the ACL clause does not allow */
2105                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
2106                         tdeny &= ACL_PRIV_MASK;
2107
2108                         /* see if we have anything to contribute */
2109                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
2110                                 continue;
2111                         }
2112
2113                         /* this could be improved by changing acl_mask so that it can deal with
2114                          * by clauses that return grant/deny pairs.  Right now, it does either
2115                          * additive or subtractive rights, but not both at the same time.  So,
2116                          * we need to combine the grant/deny pair into a single rights mask in
2117                          * a smart way:  if either grant or deny is "empty", then we use the
2118                          * opposite as is, otherwise we remove any denied rights from the grant
2119                          * rights mask and construct an additive mask.
2120                          */
2121                         if (ACL_IS_INVALID(tdeny)) {
2122                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
2123
2124                         } else if (ACL_IS_INVALID(tgrant)) {
2125                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
2126
2127                         } else {
2128                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
2129                         }
2130
2131                 } else
2132 #endif /* SLAPD_ACI_ENABLED */
2133 #endif /* !SLAP_DYNACL */
2134                 {
2135                         modmask = b->a_access_mask;
2136                 }
2137
2138                 Debug( LDAP_DEBUG_ACL,
2139                         "<= acl_mask: [%d] applying %s (%s)\n",
2140                         i, accessmask2str( modmask, accessmaskbuf, 1 ), 
2141                         b->a_type == ACL_CONTINUE
2142                                 ? "continue"
2143                                 : b->a_type == ACL_BREAK
2144                                         ? "break"
2145                                         : "stop" );
2146                 /* save old mask */
2147                 oldmask = *mask;
2148
2149                 if( ACL_IS_ADDITIVE(modmask) ) {
2150                         /* add privs */
2151                         ACL_PRIV_SET( *mask, modmask );
2152
2153                         /* cleanup */
2154                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
2155
2156                 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
2157                         /* substract privs */
2158                         ACL_PRIV_CLR( *mask, modmask );
2159
2160                         /* cleanup */
2161                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
2162
2163                 } else {
2164                         /* assign privs */
2165                         *mask = modmask;
2166                 }
2167
2168                 Debug( LDAP_DEBUG_ACL,
2169                         "<= acl_mask: [%d] mask: %s\n",
2170                         i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
2171
2172                 if( b->a_type == ACL_CONTINUE ) {
2173                         continue;
2174
2175                 } else if ( b->a_type == ACL_BREAK ) {
2176                         return ACL_BREAK;
2177
2178                 } else {
2179                         return ACL_STOP;
2180                 }
2181         }
2182
2183         /* implicit "by * none" clause */
2184         ACL_INIT(*mask);
2185
2186         Debug( LDAP_DEBUG_ACL,
2187                 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
2188                 accessmask2str(*mask, accessmaskbuf, 1), 0, 0 );
2189         return ACL_STOP;
2190 }
2191
2192 /*
2193  * acl_check_modlist - check access control on the given entry to see if
2194  * it allows the given modifications by the user associated with op.
2195  * returns      1       if mods allowed ok
2196  *              0       mods not allowed
2197  */
2198
2199 int
2200 acl_check_modlist(
2201         Operation       *op,
2202         Entry   *e,
2203         Modifications   *mlist
2204 )
2205 {
2206         struct berval *bv;
2207         AccessControlState state = ACL_STATE_INIT;
2208         Backend *be;
2209         int be_null = 0;
2210         int ret = 1; /* default is access allowed */
2211
2212         be = op->o_bd;
2213         if ( be == NULL ) {
2214                 be = LDAP_STAILQ_FIRST(&backendDB);
2215                 be_null = 1;
2216                 op->o_bd = be;
2217         }
2218         assert( be != NULL );
2219
2220         /* short circuit root database access */
2221         if ( be_isroot( op ) ) {
2222                 Debug( LDAP_DEBUG_ACL,
2223                         "<= acl_access_allowed: granted to database root\n",
2224                     0, 0, 0 );
2225                 goto done;
2226         }
2227
2228         /* use backend default access if no backend acls */
2229         if( op->o_bd != NULL && op->o_bd->be_acl == NULL ) {
2230                 Debug( LDAP_DEBUG_ACL,
2231                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
2232                         access2str( ACL_WRITE ),
2233                         op->o_bd->be_dfltaccess >= ACL_WRITE
2234                                 ? "granted" : "denied",
2235                         op->o_dn.bv_val );
2236                 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
2237                 goto done;
2238         }
2239
2240         for ( ; mlist != NULL; mlist = mlist->sml_next ) {
2241                 /*
2242                  * no-user-modification operational attributes are ignored
2243                  * by ACL_WRITE checking as any found here are not provided
2244                  * by the user
2245                  */
2246                 if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
2247                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
2248                                 " modify access granted\n",
2249                                 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
2250                         continue;
2251                 }
2252
2253                 switch ( mlist->sml_op ) {
2254                 case LDAP_MOD_REPLACE:
2255                         /*
2256                          * We must check both permission to delete the whole
2257                          * attribute and permission to add the specific attributes.
2258                          * This prevents abuse from selfwriters.
2259                          */
2260                         if ( ! access_allowed( op, e,
2261                                 mlist->sml_desc, NULL, ACL_WDEL, &state ) )
2262                         {
2263                                 ret = 0;
2264                                 goto done;
2265                         }
2266
2267                         if ( mlist->sml_values == NULL ) break;
2268
2269                         /* fall thru to check value to add */
2270
2271                 case LDAP_MOD_ADD:
2272                         assert( mlist->sml_values != NULL );
2273
2274                         for ( bv = mlist->sml_nvalues
2275                                         ? mlist->sml_nvalues : mlist->sml_values;
2276                                 bv->bv_val != NULL; bv++ )
2277                         {
2278                                 if ( ! access_allowed( op, e,
2279                                         mlist->sml_desc, bv, ACL_WADD, &state ) )
2280                                 {
2281                                         ret = 0;
2282                                         goto done;
2283                                 }
2284                         }
2285                         break;
2286
2287                 case LDAP_MOD_DELETE:
2288                         if ( mlist->sml_values == NULL ) {
2289                                 if ( ! access_allowed( op, e,
2290                                         mlist->sml_desc, NULL, ACL_WDEL, NULL ) )
2291                                 {
2292                                         ret = 0;
2293                                         goto done;
2294                                 }
2295                                 break;
2296                         }
2297                         for ( bv = mlist->sml_nvalues
2298                                         ? mlist->sml_nvalues : mlist->sml_values;
2299                                 bv->bv_val != NULL; bv++ )
2300                         {
2301                                 if ( ! access_allowed( op, e,
2302                                         mlist->sml_desc, bv, ACL_WDEL, &state ) )
2303                                 {
2304                                         ret = 0;
2305                                         goto done;
2306                                 }
2307                         }
2308                         break;
2309
2310                 case SLAP_MOD_SOFTADD:
2311                         /* allow adding attribute via modrdn thru */
2312                         break;
2313
2314                 default:
2315                         assert( 0 );
2316                         /* not reached */
2317                         ret = 0;
2318                         break;
2319                 }
2320         }
2321
2322 done:
2323         if (be_null) op->o_bd = NULL;
2324         return( ret );
2325 }
2326
2327 static int
2328 aci_get_part(
2329         struct berval   *list,
2330         int             ix,
2331         char            sep,
2332         struct berval   *bv )
2333 {
2334         int     len;
2335         char    *p;
2336
2337         if ( bv ) {
2338                 BER_BVZERO( bv );
2339         }
2340         len = list->bv_len;
2341         p = list->bv_val;
2342         while ( len >= 0 && --ix >= 0 ) {
2343                 while ( --len >= 0 && *p++ != sep )
2344                         ;
2345         }
2346         while ( len >= 0 && *p == ' ' ) {
2347                 len--;
2348                 p++;
2349         }
2350         if ( len < 0 ) {
2351                 return -1;
2352         }
2353
2354         if ( !bv ) {
2355                 return 0;
2356         }
2357
2358         bv->bv_val = p;
2359         while ( --len >= 0 && *p != sep ) {
2360                 bv->bv_len++;
2361                 p++;
2362         }
2363         while ( bv->bv_len > 0 && *--p == ' ' ) {
2364                 bv->bv_len--;
2365         }
2366         
2367         return bv->bv_len;
2368 }
2369
2370 typedef struct aci_set_gather_t {
2371         SetCookie               *cookie;
2372         BerVarray               bvals;
2373 } aci_set_gather_t;
2374
2375 static int
2376 aci_set_cb_gather( Operation *op, SlapReply *rs )
2377 {
2378         aci_set_gather_t        *p = (aci_set_gather_t *)op->o_callback->sc_private;
2379         
2380         if ( rs->sr_type == REP_SEARCH ) {
2381                 BerValue        bvals[ 2 ];
2382                 BerVarray       bvalsp = NULL;
2383                 int             j;
2384
2385                 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2386                         AttributeDescription    *desc = rs->sr_attrs[ j ].an_desc;
2387                         
2388                         if ( desc == slap_schema.si_ad_entryDN ) {
2389                                 bvalsp = bvals;
2390                                 bvals[ 0 ] = rs->sr_entry->e_nname;
2391                                 BER_BVZERO( &bvals[ 1 ] );
2392
2393                         } else {
2394                                 Attribute       *a;
2395
2396                                 a = attr_find( rs->sr_entry->e_attrs, desc );
2397                                 if ( a != NULL ) {
2398                                         int     i;
2399
2400                                         for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ )
2401                                                 ;
2402
2403                                         bvalsp = a->a_nvals;
2404                                 }
2405                         }
2406                 }
2407
2408                 if ( bvals ) {
2409                         p->bvals = slap_set_join( p->cookie, p->bvals,
2410                                         ( '|' | SLAP_SET_RREF ), bvalsp );
2411                 }
2412
2413         } else {
2414                 assert( rs->sr_type == REP_RESULT );
2415         }
2416
2417         return 0;
2418 }
2419
2420 BerVarray
2421 aci_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2422 {
2423         AciSetCookie            *cp = (AciSetCookie *)cookie;
2424         int                     rc = 0;
2425         LDAPURLDesc             *ludp = NULL;
2426         Operation               op2 = { 0 };
2427         SlapReply               rs = {REP_RESULT};
2428         AttributeName           anlist[ 2 ], *anlistp = NULL;
2429         int                     nattrs = 0;
2430         slap_callback           cb = { NULL, aci_set_cb_gather, NULL, NULL };
2431         aci_set_gather_t        p = { 0 };
2432         const char              *text = NULL;
2433         static struct berval    defaultFilter_bv = BER_BVC( "(objectClass=*)" );
2434
2435         /* this routine needs to return the bervals instead of
2436          * plain strings, since syntax is not known.  It should
2437          * also return the syntax or some "comparison cookie".
2438          */
2439         if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2440                 return aci_set_gather2( cookie, name, desc );
2441         }
2442
2443         rc = ldap_url_parse( name->bv_val, &ludp );
2444         if ( rc != LDAP_URL_SUCCESS ) {
2445                 rc = LDAP_PROTOCOL_ERROR;
2446                 goto url_done;
2447         }
2448         
2449         if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2450         {
2451                 /* host part must be empty */
2452                 /* extensions parts must be empty */
2453                 rc = LDAP_PROTOCOL_ERROR;
2454                 goto url_done;
2455         }
2456
2457         /* Grab the searchbase and see if an appropriate database can be found */
2458         ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2459         rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2460                         &op2.o_req_ndn, cp->op->o_tmpmemctx );
2461         BER_BVZERO( &op2.o_req_dn );
2462         if ( rc != LDAP_SUCCESS ) {
2463                 goto url_done;
2464         }
2465
2466         op2.o_bd = select_backend( &op2.o_req_ndn, 0, 1 );
2467         if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2468                 rc = LDAP_NO_SUCH_OBJECT;
2469                 goto url_done;
2470         }
2471
2472         /* Grab the filter */
2473         if ( ludp->lud_filter ) {
2474                 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2475                                 cp->op->o_tmpmemctx );
2476                 
2477         } else {
2478                 op2.ors_filterstr = defaultFilter_bv;
2479         }
2480
2481         op2.ors_filter = str2filter_x( cp->op, op2.ors_filterstr.bv_val );
2482         if ( op2.ors_filter == NULL ) {
2483                 rc = LDAP_PROTOCOL_ERROR;
2484                 goto url_done;
2485         }
2486
2487         /* Grab the scope */
2488         op2.ors_scope = ludp->lud_scope;
2489
2490         /* Grap the attributes */
2491         if ( ludp->lud_attrs ) {
2492                 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2493                         ;
2494
2495                 anlistp = slap_sl_malloc( sizeof( AttributeName ) * ( nattrs + 2 ),
2496                                 cp->op->o_tmpmemctx );
2497
2498                 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ ) {
2499                         ber_str2bv( ludp->lud_attrs[ nattrs ], 0, 0, &anlistp[ nattrs ].an_name );
2500                         anlistp[ nattrs ].an_desc = NULL;
2501                         rc = slap_bv2ad( &anlistp[ nattrs ].an_name,
2502                                         &anlistp[ nattrs ].an_desc, &text );
2503                         if ( rc != LDAP_SUCCESS ) {
2504                                 goto url_done;
2505                         }
2506                 }
2507
2508         } else {
2509                 anlistp = anlist;
2510         }
2511
2512         anlistp[ nattrs ].an_name = desc->ad_cname;
2513         anlistp[ nattrs ].an_desc = desc;
2514
2515         BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2516         
2517         p.cookie = cookie;
2518         
2519         op2.o_hdr = cp->op->o_hdr;
2520         op2.o_tag = LDAP_REQ_SEARCH;
2521         op2.o_ndn = op2.o_bd->be_rootndn;
2522         op2.o_callback = &cb;
2523         op2.o_time = slap_get_time();
2524         op2.o_do_not_cache = 1;
2525         op2.o_is_auth_check = 0;
2526         ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->op->o_tmpmemctx );
2527         op2.ors_slimit = SLAP_NO_LIMIT;
2528         op2.ors_tlimit = SLAP_NO_LIMIT;
2529         op2.ors_attrs = anlistp;
2530         op2.ors_attrsonly = 0;
2531         op2.o_private = cp->op->o_private;
2532
2533         cb.sc_private = &p;
2534
2535         rc = op2.o_bd->be_search( &op2, &rs );
2536         if ( rc != 0 ) {
2537                 goto url_done;
2538         }
2539
2540 url_done:;
2541         if ( op2.ors_filter ) {
2542                 filter_free_x( cp->op, op2.ors_filter );
2543         }
2544         if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2545                 slap_sl_free( op2.o_req_ndn.bv_val, cp->op->o_tmpmemctx );
2546         }
2547         if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2548                 slap_sl_free( op2.o_req_dn.bv_val, cp->op->o_tmpmemctx );
2549         }
2550         if ( ludp ) {
2551                 ldap_free_urldesc( ludp );
2552         }
2553         if ( anlistp && anlistp != anlist ) {
2554                 slap_sl_free( anlistp, cp->op->o_tmpmemctx );
2555         }
2556
2557         return p.bvals;
2558 }
2559
2560 BerVarray
2561 aci_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2562 {
2563         AciSetCookie    *cp = (AciSetCookie *)cookie;
2564         BerVarray       bvals = NULL;
2565         struct berval   ndn;
2566         int             rc = 0;
2567
2568         /* this routine needs to return the bervals instead of
2569          * plain strings, since syntax is not known.  It should
2570          * also return the syntax or some "comparison cookie".
2571          */
2572         rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->op->o_tmpmemctx );
2573         if ( rc == LDAP_SUCCESS ) {
2574                 if ( desc == slap_schema.si_ad_entryDN ) {
2575                         bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2576                                         cp->op->o_tmpmemctx );
2577                         bvals[ 0 ] = ndn;
2578                         BER_BVZERO( &bvals[ 1 ] );
2579                         BER_BVZERO( &ndn );
2580
2581                 } else {
2582                         backend_attribute( cp->op,
2583                                 cp->e, &ndn, desc, &bvals, ACL_NONE );
2584                 }
2585
2586                 if ( !BER_BVISNULL( &ndn ) ) {
2587                         slap_sl_free( ndn.bv_val, cp->op->o_tmpmemctx );
2588                 }
2589         }
2590
2591         return bvals;
2592 }
2593
2594 static int
2595 aci_match_set (
2596         struct berval *subj,
2597         Operation *op,
2598         Entry *e,
2599         int setref
2600 )
2601 {
2602         struct berval   set = BER_BVNULL;
2603         int             rc = 0;
2604         AciSetCookie    cookie;
2605
2606         if ( setref == 0 ) {
2607                 ber_dupbv_x( &set, subj, op->o_tmpmemctx );
2608
2609         } else {
2610                 struct berval           subjdn, ndn = BER_BVNULL;
2611                 struct berval           setat;
2612                 BerVarray               bvals;
2613                 const char              *text;
2614                 AttributeDescription    *desc = NULL;
2615
2616                 /* format of string is "entry/setAttrName" */
2617                 if ( aci_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2618                         return 0;
2619                 }
2620
2621                 if ( aci_get_part( subj, 1, '/', &setat ) < 0 ) {
2622                         setat = aci_bv_set_attr;
2623                 }
2624
2625                 /*
2626                  * NOTE: dnNormalize honors the ber_len field
2627                  * as the length of the dn to be normalized
2628                  */
2629                 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2630                         if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2631                         {
2632                                 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2633                                 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2634                                         int     i;
2635
2636                                         set = bvals[0];
2637                                         BER_BVZERO( &bvals[0] );
2638                                         for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2639                                                 /* count */ ;
2640                                         bvals[0].bv_val = bvals[i-1].bv_val;
2641                                         BER_BVZERO( &bvals[i-1] );
2642                                 }
2643                                 ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2644                                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2645                         }
2646                 }
2647         }
2648
2649         if ( !BER_BVISNULL( &set ) ) {
2650                 cookie.op = op;
2651                 cookie.e = e;
2652                 rc = ( slap_set_filter( aci_set_gather, (SetCookie *)&cookie, &set,
2653                         &op->o_ndn, &e->e_nname, NULL ) > 0 );
2654                 slap_sl_free( set.bv_val, op->o_tmpmemctx );
2655         }
2656
2657         return(rc);
2658 }
2659
2660 #ifdef SLAPD_ACI_ENABLED
2661 static int
2662 aci_list_map_rights(
2663         struct berval *list )
2664 {
2665         struct berval bv;
2666         slap_access_t mask;
2667         int i;
2668
2669         ACL_INIT(mask);
2670         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
2671                 if (bv.bv_len <= 0)
2672                         continue;
2673                 switch (*bv.bv_val) {
2674                 case 'c':
2675                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
2676                         break;
2677                 case 's':
2678                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
2679                          * the right 's' to mean "set", but in the examples states
2680                          * that the right 's' means "search".  The latter definition
2681                          * is used here.
2682                          */
2683                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
2684                         break;
2685                 case 'r':
2686                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
2687                         break;
2688                 case 'w':
2689                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
2690                         break;
2691                 case 'x':
2692                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
2693                          * define any equivalent to the AUTH right, so I've just used
2694                          * 'x' for now.
2695                          */
2696                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
2697                         break;
2698                 default:
2699                         break;
2700                 }
2701
2702         }
2703         return(mask);
2704 }
2705
2706 static int
2707 aci_list_has_attr(
2708         struct berval *list,
2709         const struct berval *attr,
2710         struct berval *val )
2711 {
2712         struct berval bv, left, right;
2713         int i;
2714
2715         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
2716                 if (aci_get_part(&bv, 0, '=', &left) < 0
2717                         || aci_get_part(&bv, 1, '=', &right) < 0)
2718                 {
2719                         if (ber_bvstrcasecmp(attr, &bv) == 0)
2720                                 return(1);
2721                 } else if (val == NULL) {
2722                         if (ber_bvstrcasecmp(attr, &left) == 0)
2723                                 return(1);
2724                 } else {
2725                         if (ber_bvstrcasecmp(attr, &left) == 0) {
2726                                 /* this is experimental code that implements a
2727                                  * simple (prefix) match of the attribute value.
2728                                  * the ACI draft does not provide for aci's that
2729                                  * apply to specific values, but it would be
2730                                  * nice to have.  If the <attr> part of an aci's
2731                                  * rights list is of the form <attr>=<value>,
2732                                  * that means the aci applies only to attrs with
2733                                  * the given value.  Furthermore, if the attr is
2734                                  * of the form <attr>=<value>*, then <value> is
2735                                  * treated as a prefix, and the aci applies to 
2736                                  * any value with that prefix.
2737                                  *
2738                                  * Ideally, this would allow r.e. matches.
2739                                  */
2740                                 if (aci_get_part(&right, 0, '*', &left) < 0
2741                                         || right.bv_len <= left.bv_len)
2742                                 {
2743                                         if (ber_bvstrcasecmp(val, &right) == 0)
2744                                                 return(1);
2745                                 } else if (val->bv_len >= left.bv_len) {
2746                                         if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
2747                                                 return(1);
2748                                 }
2749                         }
2750                 }
2751         }
2752         return(0);
2753 }
2754
2755 static slap_access_t
2756 aci_list_get_attr_rights(
2757         struct berval *list,
2758         const struct berval *attr,
2759         struct berval *val )
2760 {
2761     struct berval bv;
2762     slap_access_t mask;
2763     int i;
2764
2765         /* loop through each rights/attr pair, skip first part (action) */
2766         ACL_INIT(mask);
2767         for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
2768                 if (aci_list_has_attr(&bv, attr, val) == 0)
2769                         continue;
2770                 if (aci_get_part(list, i, ';', &bv) < 0)
2771                         continue;
2772                 mask |= aci_list_map_rights(&bv);
2773         }
2774         return(mask);
2775 }
2776
2777 static int
2778 aci_list_get_rights(
2779         struct berval *list,
2780         const struct berval *attr,
2781         struct berval *val,
2782         slap_access_t *grant,
2783         slap_access_t *deny )
2784 {
2785     struct berval perm, actn;
2786     slap_access_t *mask;
2787     int i, found;
2788
2789         if (attr == NULL || attr->bv_len == 0 
2790                         || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
2791                 attr = &aci_bv_br_entry;
2792         }
2793
2794         found = 0;
2795         ACL_INIT(*grant);
2796         ACL_INIT(*deny);
2797         /* loop through each permissions clause */
2798         for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
2799                 if (aci_get_part(&perm, 0, ';', &actn) < 0)
2800                         continue;
2801                 if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
2802                         mask = grant;
2803                 } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
2804                         mask = deny;
2805                 } else {
2806                         continue;
2807                 }
2808
2809                 found = 1;
2810                 *mask |= aci_list_get_attr_rights(&perm, attr, val);
2811                 *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
2812         }
2813         return(found);
2814 }
2815
2816 static int
2817 aci_group_member (
2818         struct berval   *subj,
2819         struct berval   *defgrpoc,
2820         struct berval   *defgrpat,
2821         Operation       *op,
2822         Entry           *e,
2823         int             nmatch,
2824         regmatch_t      *matches
2825 )
2826 {
2827         struct berval subjdn;
2828         struct berval grpoc;
2829         struct berval grpat;
2830         ObjectClass *grp_oc = NULL;
2831         AttributeDescription *grp_ad = NULL;
2832         const char *text;
2833         int rc;
2834
2835         /* format of string is "group/objectClassValue/groupAttrName" */
2836         if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
2837                 return(0);
2838         }
2839
2840         if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
2841                 grpoc = *defgrpoc;
2842         }
2843
2844         if (aci_get_part(subj, 2, '/', &grpat) < 0) {
2845                 grpat = *defgrpat;
2846         }
2847
2848         rc = slap_bv2ad( &grpat, &grp_ad, &text );
2849         if( rc != LDAP_SUCCESS ) {
2850                 rc = 0;
2851                 goto done;
2852         }
2853         rc = 0;
2854
2855         grp_oc = oc_bvfind( &grpoc );
2856
2857         if (grp_oc != NULL && grp_ad != NULL ) {
2858                 char buf[ACL_BUF_SIZE];
2859                 struct berval bv, ndn;
2860                 bv.bv_len = sizeof( buf ) - 1;
2861                 bv.bv_val = (char *)&buf;
2862                 if ( string_expand(&bv, &subjdn,
2863                                 e->e_ndn, nmatch, matches) )
2864                 {
2865                         rc = LDAP_OTHER;
2866                         goto done;
2867                 }
2868                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
2869                         rc = ( backend_group( op, e, &ndn, &op->o_ndn,
2870                                 grp_oc, grp_ad ) == 0 );
2871                         slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2872                 }
2873         }
2874
2875 done:
2876         return(rc);
2877 }
2878
2879 static int
2880 aci_mask(
2881         Operation               *op,
2882         Entry                   *e,
2883         AttributeDescription    *desc,
2884         struct berval           *val,
2885         struct berval           *aci,
2886         int                     nmatch,
2887         regmatch_t              *matches,
2888         slap_access_t           *grant,
2889         slap_access_t           *deny,
2890         slap_aci_scope_t        asserted_scope
2891 )
2892 {
2893         struct berval           bv, scope, perms, type, sdn;
2894         int                     rc;
2895                 
2896
2897         assert( !BER_BVISNULL( &desc->ad_cname ) );
2898
2899         /* parse an aci of the form:
2900                 oid # scope # action;rights;attr;rights;attr 
2901                         $ action;rights;attr;rights;attr # type # subject
2902
2903            [NOTE: the following comment is very outdated,
2904            as the draft version it refers to (Ando, 2004-11-20)].
2905
2906            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
2907            a full description of the format for this attribute.
2908            Differences: "this" in the draft is "self" here, and
2909            "self" and "public" is in the position of type.
2910
2911            <scope> = {entry|children|subtree}
2912            <type> = {public|users|access-id|subtree|onelevel|children|
2913                      self|dnattr|group|role|set|set-ref}
2914
2915            This routine now supports scope={ENTRY,CHILDREN}
2916            with the semantics:
2917              - ENTRY applies to "entry" and "subtree";
2918              - CHILDREN aplies to "children" and "subtree"
2919          */
2920
2921         /* check that the aci has all 5 components */
2922         if ( aci_get_part( aci, 4, '#', NULL ) < 0 ) {
2923                 return 0;
2924         }
2925
2926         /* check that the aci family is supported */
2927         if ( aci_get_part( aci, 0, '#', &bv ) < 0 ) {
2928                 return 0;
2929         }
2930
2931         /* check that the scope matches */
2932         if ( aci_get_part( aci, 1, '#', &scope ) < 0 ) {
2933                 return 0;
2934         }
2935
2936         /* note: scope can be either ENTRY or CHILDREN;
2937          * they respectively match "entry" and "children" in bv
2938          * both match "subtree" */
2939         switch ( asserted_scope ) {
2940         case SLAP_ACI_SCOPE_ENTRY:
2941                 if ( ber_bvstrcasecmp( &scope, &aci_bv_entry ) != 0
2942                                 && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
2943                 {
2944                         return 0;
2945                 }
2946                 break;
2947
2948         case SLAP_ACI_SCOPE_CHILDREN:
2949                 if ( ber_bvstrcasecmp( &scope, &aci_bv_children ) != 0
2950                                 && ber_bvstrcasecmp( &scope, &aci_bv_subtree ) != 0 )
2951                 {
2952                         return 0;
2953                 }
2954                 break;
2955
2956         default:
2957                 return 0;
2958         }
2959
2960         /* get the list of permissions clauses, bail if empty */
2961         if ( aci_get_part( aci, 2, '#', &perms ) <= 0 ) {
2962                 return 0;
2963         }
2964
2965         /* check if any permissions allow desired access */
2966         if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
2967                 return 0;
2968         }
2969
2970         /* see if we have a DN match */
2971         if ( aci_get_part( aci, 3, '#', &type ) < 0 ) {
2972                 return 0;
2973         }
2974
2975         /* see if we have a public (i.e. anonymous) access */
2976         if ( ber_bvstrcasecmp( &aci_bv_public, &type ) == 0 ) {
2977                 return 1;
2978         }
2979         
2980         /* otherwise require an identity */
2981         if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
2982                 return 0;
2983         }
2984
2985         /* see if we have a users access */
2986         if ( ber_bvstrcasecmp( &aci_bv_users, &type ) == 0 ) {
2987                 return 1;
2988         }
2989         
2990         /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
2991          * just grab all the berval up to its end (ITS#3303).
2992          * NOTE: the problem could be solved by providing the DN with
2993          * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 
2994          * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
2995 #if 0
2996         if ( aci_get_part( aci, 4, '#', &sdn ) < 0 ) {
2997                 return 0;
2998         }
2999 #endif
3000         sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
3001         sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
3002
3003         if ( ber_bvstrcasecmp( &aci_bv_access_id, &type ) == 0 ) {
3004                 struct berval ndn;
3005                 
3006                 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
3007                 if ( rc != LDAP_SUCCESS ) {
3008                         return 0;
3009                 }
3010
3011                 if ( dn_match( &op->o_ndn, &ndn ) ) {
3012                         rc = 1;
3013                 }
3014                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
3015
3016                 return rc;
3017
3018         } else if ( ber_bvstrcasecmp( &aci_bv_subtree, &type ) == 0 ) {
3019                 struct berval ndn;
3020                 
3021                 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
3022                 if ( rc != LDAP_SUCCESS ) {
3023                         return 0;
3024                 }
3025
3026                 if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
3027                         rc = 1;
3028                 }
3029                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
3030
3031                 return rc;
3032
3033         } else if ( ber_bvstrcasecmp( &aci_bv_onelevel, &type ) == 0 ) {
3034                 struct berval ndn, pndn;
3035                 
3036                 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
3037                 if ( rc != LDAP_SUCCESS ) {
3038                         return 0;
3039                 }
3040
3041                 dnParent( &ndn, &pndn );
3042
3043                 if ( dn_match( &op->o_ndn, &pndn ) ) {
3044                         rc = 1;
3045                 }
3046                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
3047
3048                 return rc;
3049
3050         } else if ( ber_bvstrcasecmp( &aci_bv_children, &type ) == 0 ) {
3051                 struct berval ndn;
3052                 
3053                 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
3054                 if ( rc != LDAP_SUCCESS ) {
3055                         return 0;
3056                 }
3057
3058                 if ( !dn_match( &op->o_ndn, &ndn )
3059                                 && dnIsSuffix( &op->o_ndn, &ndn ) )
3060                 {
3061                         rc = 1;
3062                 }
3063                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
3064
3065                 return rc;
3066
3067         } else if ( ber_bvstrcasecmp( &aci_bv_self, &type ) == 0 ) {
3068                 if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
3069                         return 1;
3070                 }
3071
3072         } else if ( ber_bvstrcasecmp( &aci_bv_dnattr, &type ) == 0 ) {
3073                 Attribute               *at;
3074                 AttributeDescription    *ad = NULL;
3075                 const char              *text;
3076
3077                 rc = slap_bv2ad( &sdn, &ad, &text );
3078
3079                 if( rc != LDAP_SUCCESS ) {
3080                         return 0;
3081                 }
3082
3083                 rc = 0;
3084
3085                 for ( at = attrs_find( e->e_attrs, ad );
3086                                 at != NULL;
3087                                 at = attrs_find( at->a_next, ad ) )
3088                 {
3089                         if ( value_find_ex( ad,
3090                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
3091                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
3092                                 at->a_nvals,
3093                                 &op->o_ndn, op->o_tmpmemctx ) == 0 )
3094                         {
3095                                 rc = 1;
3096                                 break;
3097                         }
3098                 }
3099
3100                 return rc;
3101
3102         } else if ( ber_bvstrcasecmp( &aci_bv_group, &type ) == 0 ) {
3103                 if ( aci_group_member( &sdn, &aci_bv_group_class,
3104                                 &aci_bv_group_attr, op, e, nmatch, matches ) )
3105                 {
3106                         return 1;
3107                 }
3108
3109         } else if ( ber_bvstrcasecmp( &aci_bv_role, &type ) == 0 ) {
3110                 if ( aci_group_member( &sdn, &aci_bv_role_class,
3111                                 &aci_bv_role_attr, op, e, nmatch, matches ) )
3112                 {
3113                         return 1;
3114                 }
3115
3116         } else if ( ber_bvstrcasecmp( &aci_bv_set, &type ) == 0 ) {
3117                 if ( aci_match_set( &sdn, op, e, 0 ) ) {
3118                         return 1;
3119                 }
3120
3121         } else if ( ber_bvstrcasecmp( &aci_bv_set_ref, &type ) == 0 ) {
3122                 if ( aci_match_set( &sdn, op, e, 1 ) ) {
3123                         return 1;
3124                 }
3125         }
3126
3127         return 0;
3128 }
3129
3130 #ifdef SLAP_DYNACL
3131 /*
3132  * FIXME: there is a silly dependence that makes it difficult
3133  * to move ACIs in a run-time loadable module under the "dynacl" 
3134  * umbrella, because sets share some helpers with ACIs.
3135  */
3136 static int
3137 dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
3138 {
3139         AttributeDescription    *ad = NULL;
3140         const char              *text = NULL;
3141
3142         if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
3143                 fprintf( stderr, "%s: line %d: "
3144                         "inappropriate style \"%s\" in \"aci\" by clause\n",
3145                         fname, lineno, sty );
3146                 return -1;
3147         }
3148
3149         if ( right != NULL && *right != '\0' ) {
3150                 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
3151                         fprintf( stderr,
3152                                 "%s: line %d: aci \"%s\": %s\n",
3153                                 fname, lineno, right, text );
3154                         return -1;
3155                 }
3156
3157         } else {
3158                 ad = slap_schema.si_ad_aci;
3159         }
3160
3161         if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
3162                 fprintf( stderr, "%s: line %d: "
3163                         "aci \"%s\": inappropriate syntax: %s\n",
3164                         fname, lineno, right,
3165                         ad->ad_type->sat_syntax_oid );
3166                 return -1;
3167         }
3168
3169         *privp = (void *)ad;
3170
3171         return 0;
3172 }
3173
3174 static int
3175 dynacl_aci_unparse( void *priv, struct berval *bv )
3176 {
3177         AttributeDescription    *ad = ( AttributeDescription * )priv;
3178         char *ptr;
3179
3180         assert( ad );
3181
3182         bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
3183         ptr = lutil_strcopy( bv->bv_val, " aci=" );
3184         ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
3185         bv->bv_len = ptr - bv->bv_val;
3186
3187         return 0;
3188 }
3189
3190
3191 static int
3192 dynacl_aci_mask(
3193                 void                    *priv,
3194                 Operation               *op,
3195                 Entry                   *e,
3196                 AttributeDescription    *desc,
3197                 struct berval           *val,
3198                 int                     nmatch,
3199                 regmatch_t              *matches,
3200                 slap_access_t           *grantp,
3201                 slap_access_t           *denyp )
3202 {
3203         AttributeDescription    *ad = ( AttributeDescription * )priv;
3204         Attribute               *at;
3205         slap_access_t           tgrant, tdeny, grant, deny;
3206 #ifdef LDAP_DEBUG
3207         char                    accessmaskbuf[ACCESSMASK_MAXLEN];
3208         char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
3209 #endif /* LDAP_DEBUG */
3210
3211         /* start out with nothing granted, nothing denied */
3212         ACL_INIT(tgrant);
3213         ACL_INIT(tdeny);
3214
3215         /* get the aci attribute */
3216         at = attr_find( e->e_attrs, ad );
3217         if ( at != NULL ) {
3218                 int             i;
3219
3220                 /* the aci is an multi-valued attribute.  The
3221                  * rights are determined by OR'ing the individual
3222                  * rights given by the acis.
3223                  */
3224                 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
3225                         if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
3226                                         nmatch, matches, &grant, &deny,
3227                                         SLAP_ACI_SCOPE_ENTRY ) != 0 )
3228                         {
3229                                 tgrant |= grant;
3230                                 tdeny |= deny;
3231                         }
3232                 }
3233                 
3234                 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
3235                           accessmask2str( tgrant, accessmaskbuf, 1 ), 
3236                           accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
3237         }
3238
3239         /* If the entry level aci didn't contain anything valid for the 
3240          * current operation, climb up the tree and evaluate the
3241          * acis with scope set to subtree
3242          */
3243         if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
3244                 struct berval   parent_ndn;
3245
3246 #if 1
3247                 /* to solve the chicken'n'egg problem of accessing
3248                  * the OpenLDAPaci attribute, the direct access
3249                  * to the entry's attribute is unchecked; however,
3250                  * further accesses to OpenLDAPaci values in the 
3251                  * ancestors occur through backend_attribute(), i.e.
3252                  * with the identity of the operation, requiring
3253                  * further access checking.  For uniformity, this
3254                  * makes further requests occur as the rootdn, if
3255                  * any, i.e. searching for the OpenLDAPaci attribute
3256                  * is considered an internal search.  If this is not
3257                  * acceptable, then the same check needs be performed
3258                  * when accessing the entry's attribute. */
3259                 Operation       op2 = *op;
3260
3261                 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
3262                         op2.o_dn = op->o_bd->be_rootdn;
3263                         op2.o_ndn = op->o_bd->be_rootndn;
3264                 }
3265 #endif
3266
3267                 dnParent( &e->e_nname, &parent_ndn );
3268                 while ( !BER_BVISEMPTY( &parent_ndn ) ){
3269                         int             i;
3270                         BerVarray       bvals = NULL;
3271                         int             ret, stop;
3272
3273                         Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
3274                         ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
3275
3276                         switch ( ret ) {
3277                         case LDAP_SUCCESS :
3278                                 stop = 0;
3279                                 if ( !bvals ) {
3280                                         break;
3281                                 }
3282
3283                                 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++) {
3284                                         if ( aci_mask( op, e, desc, val,
3285                                                         &bvals[i],
3286                                                         nmatch, matches,
3287                                                         &grant, &deny,
3288                                                         SLAP_ACI_SCOPE_CHILDREN ) != 0 )
3289                                         {
3290                                                 tgrant |= grant;
3291                                                 tdeny |= deny;
3292                                                 /* evaluation stops as soon as either a "deny" or a 
3293                                                  * "grant" directive matches.
3294                                                  */
3295                                                 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
3296                                                         stop = 1;
3297                                                 }
3298                                         }
3299                                         Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
3300                                                 accessmask2str( tgrant, accessmaskbuf, 1 ),
3301                                                 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
3302                                 }
3303                                 break;
3304
3305                         case LDAP_NO_SUCH_ATTRIBUTE:
3306                                 /* just go on if the aci-Attribute is not present in
3307                                  * the current entry 
3308                                  */
3309                                 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
3310                                 stop = 0;
3311                                 break;
3312
3313                         case LDAP_NO_SUCH_OBJECT:
3314                                 /* We have reached the base object */
3315                                 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
3316                                 stop = 1;
3317                                 break;
3318
3319                         default:
3320                                 stop = 1;
3321                                 break;
3322                         }
3323
3324                         if ( stop ) {
3325                                 break;
3326                         }
3327                         dnParent( &parent_ndn, &parent_ndn );
3328                 }
3329         }
3330
3331         *grantp = tgrant;
3332         *denyp = tdeny;
3333
3334         return 0;
3335 }
3336
3337 /* need to register this at some point */
3338 static slap_dynacl_t    dynacl_aci = {
3339         "aci",
3340         dynacl_aci_parse,
3341         dynacl_aci_unparse,
3342         dynacl_aci_mask,
3343         NULL,
3344         NULL,
3345         NULL
3346 };
3347
3348 #endif /* SLAP_DYNACL */
3349
3350 #endif  /* SLAPD_ACI_ENABLED */
3351
3352 #ifdef SLAP_DYNACL
3353
3354 /*
3355  * dynamic ACL infrastructure
3356  */
3357 static slap_dynacl_t    *da_list = NULL;
3358
3359 int
3360 slap_dynacl_register( slap_dynacl_t *da )
3361 {
3362         slap_dynacl_t   *tmp;
3363
3364         for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
3365                 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
3366                         break;
3367                 }
3368         }
3369
3370         if ( tmp != NULL ) {
3371                 return -1;
3372         }
3373         
3374         if ( da->da_mask == NULL ) {
3375                 return -1;
3376         }
3377         
3378         da->da_private = NULL;
3379         da->da_next = da_list;
3380         da_list = da;
3381
3382         return 0;
3383 }
3384
3385 static slap_dynacl_t *
3386 slap_dynacl_next( slap_dynacl_t *da )
3387 {
3388         if ( da ) {
3389                 return da->da_next;
3390         }
3391         return da_list;
3392 }
3393
3394 slap_dynacl_t *
3395 slap_dynacl_get( const char *name )
3396 {
3397         slap_dynacl_t   *da;
3398
3399         for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
3400                 if ( strcasecmp( da->da_name, name ) == 0 ) {
3401                         break;
3402                 }
3403         }
3404
3405         return da;
3406 }
3407 #endif /* SLAP_DYNACL */
3408
3409 int
3410 acl_init( void )
3411 {
3412         int             i, rc;
3413 #ifdef SLAP_DYNACL
3414         slap_dynacl_t   *known_dynacl[] = {
3415 #ifdef SLAPD_ACI_ENABLED
3416                 &dynacl_aci,
3417 #endif  /* SLAPD_ACI_ENABLED */
3418                 NULL
3419         };
3420
3421         for ( i = 0; known_dynacl[ i ]; i++ ) {
3422                 rc = slap_dynacl_register( known_dynacl[ i ] ); 
3423                 if ( rc ) {
3424                         return rc;
3425                 }
3426         }
3427 #endif /* SLAP_DYNACL */
3428
3429         return 0;
3430 }
3431
3432 static int
3433 string_expand(
3434         struct berval   *bv,
3435         struct berval   *pat,
3436         char            *match,
3437         int             nmatch,
3438         regmatch_t      *matches)
3439 {
3440         ber_len_t       size;
3441         char   *sp;
3442         char   *dp;
3443         int     flag;
3444
3445         size = 0;
3446         bv->bv_val[0] = '\0';
3447         bv->bv_len--; /* leave space for lone $ */
3448
3449         flag = 0;
3450         for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
3451                 sp < pat->bv_val + pat->bv_len ; sp++ )
3452         {
3453                 /* did we previously see a $ */
3454                 if ( flag ) {
3455                         if ( flag == 1 && *sp == '$' ) {
3456                                 *dp++ = '$';
3457                                 size++;
3458                                 flag = 0;
3459
3460                         } else if ( flag == 1 && *sp == '{' /*'}'*/) {
3461                                 flag = 2;
3462
3463                         } else if ( *sp >= '0' && *sp <= '9' ) {
3464                                 int     n;
3465                                 int     i;
3466                                 int     l;
3467
3468                                 n = *sp - '0';
3469
3470                                 if ( flag == 2 ) {
3471                                         for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
3472                                                 if ( *sp >= '0' && *sp <= '9' ) {
3473                                                         n = 10*n + ( *sp - '0' );
3474                                                 }
3475                                         }
3476
3477                                         if ( *sp != /*'{'*/ '}' ) {
3478                                                 /* FIXME: error */
3479                                                 return 1;
3480                                         }
3481                                 }
3482
3483                                 if ( n >= nmatch ) {
3484                                         /* FIXME: error */
3485                                         return 1;
3486                                 }
3487                                 
3488                                 *dp = '\0';
3489                                 i = matches[n].rm_so;
3490                                 l = matches[n].rm_eo; 
3491                                 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
3492                                         *dp++ = match[i];
3493                                 }
3494                                 *dp = '\0';
3495
3496                                 flag = 0;
3497                         }
3498                 } else {
3499                         if (*sp == '$') {
3500                                 flag = 1;
3501                         } else {
3502                                 *dp++ = *sp;
3503                                 size++;
3504                         }
3505                 }
3506         }
3507
3508         if ( flag ) {
3509                 /* must have ended with a single $ */
3510                 *dp++ = '$';
3511                 size++;
3512         }
3513
3514         *dp = '\0';
3515         bv->bv_len = size;
3516
3517         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
3518         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
3519
3520         return 0;
3521 }
3522
3523 static int
3524 regex_matches(
3525         struct berval   *pat,           /* pattern to expand and match against */
3526         char            *str,           /* string to match against pattern */
3527         char            *buf,           /* buffer with $N expansion variables */
3528         int             nmatch, /* size of the matches array */
3529         regmatch_t      *matches        /* offsets in buffer for $N expansion variables */
3530 )
3531 {
3532         regex_t re;
3533         char newbuf[ACL_BUF_SIZE];
3534         struct berval bv;
3535         int     rc;
3536
3537         bv.bv_len = sizeof( newbuf ) - 1;
3538         bv.bv_val = newbuf;
3539
3540         if (str == NULL) {
3541                 str = "";
3542         };
3543
3544         string_expand( &bv, pat, buf, nmatch, matches );
3545         rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
3546         if ( rc ) {
3547                 char error[ACL_BUF_SIZE];
3548                 regerror( rc, &re, error, sizeof( error ) );
3549
3550                 Debug( LDAP_DEBUG_TRACE,
3551                     "compile( \"%s\", \"%s\") failed %s\n",
3552                         pat->bv_val, str, error );
3553                 return( 0 );
3554         }
3555
3556         rc = regexec( &re, str, 0, NULL, 0 );
3557         regfree( &re );
3558
3559         Debug( LDAP_DEBUG_TRACE,
3560             "=> regex_matches: string:   %s\n", str, 0, 0 );
3561         Debug( LDAP_DEBUG_TRACE,
3562             "=> regex_matches: rc: %d %s\n",
3563                 rc, !rc ? "matches" : "no matches", 0 );
3564         return( !rc );
3565 }
3566