]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
cleanup objectclass inheritance; don't proceed if initial lookup resulted in a hit...
[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 #define ACL_BUF_SIZE    1024    /* use most appropriate size */
41
42 /*
43  * speed up compares
44  */
45 const struct berval     aci_bv[] = {
46         BER_BVC("entry"),
47         BER_BVC("children"),
48         BER_BVC("onelevel"),
49         BER_BVC("subtree"),
50         BER_BVC("[entry]"),
51         BER_BVC("[all]"),
52         BER_BVC("access-id"),
53 #if 0
54         BER_BVC("anonymous"),
55 #endif
56         BER_BVC("public"),
57         BER_BVC("users"),
58         BER_BVC("self"),
59         BER_BVC("dnattr"),
60         BER_BVC("group"),
61         BER_BVC("role"),
62         BER_BVC("set"),
63         BER_BVC("set-ref"),
64         BER_BVC("grant"),
65         BER_BVC("deny"),
66
67         BER_BVC("IP="),
68 #ifdef LDAP_PF_LOCAL
69         BER_BVC("PATH="),
70 #if 0
71         BER_BVC(LDAP_DIRSEP),
72 #endif
73 #endif /* LDAP_PF_LOCAL */
74         
75         BER_BVC(SLAPD_GROUP_CLASS),
76         BER_BVC(SLAPD_GROUP_ATTR),
77         BER_BVC(SLAPD_ROLE_CLASS),
78         BER_BVC(SLAPD_ROLE_ATTR),
79
80         BER_BVC(SLAPD_ACI_SET_ATTR)
81 };
82
83 static AccessControl * slap_acl_get(
84         AccessControl *ac, int *count,
85         Operation *op, Entry *e,
86         AttributeDescription *desc,
87         struct berval *val,
88         int nmatch, regmatch_t *matches,
89         AccessControlState *state );
90
91 static slap_control_t slap_acl_mask(
92         AccessControl *ac, slap_mask_t *mask,
93         Operation *op, Entry *e,
94         AttributeDescription *desc,
95         struct berval *val,
96         int nmatch,
97         regmatch_t *matches,
98         int count,
99         AccessControlState *state );
100
101 static int      regex_matches(
102         struct berval *pat, char *str, char *buf,
103         int nmatch, regmatch_t *matches);
104
105 typedef struct AclSetCookie {
106         Operation *op;
107         Entry *e;
108 } AclSetCookie;
109
110 SLAP_SET_GATHER acl_set_gather;
111 SLAP_SET_GATHER acl_set_gather2;
112
113 /*
114  * access_allowed - check whether op->o_ndn is allowed the requested access
115  * to entry e, attribute attr, value val.  if val is null, access to
116  * the whole attribute is assumed (all values).
117  *
118  * This routine loops through all access controls and calls
119  * slap_acl_mask() on each applicable access control.
120  * The loop exits when a definitive answer is reached or
121  * or no more controls remain.
122  *
123  * returns:
124  *              0       access denied
125  *              1       access granted
126  *
127  * Notes:
128  * - can be legally called with op == NULL
129  * - can be legally called with op->o_bd == NULL
130  */
131
132 #ifdef SLAP_OVERLAY_ACCESS
133 int
134 slap_access_always_allowed(
135         Operation               *op,
136         Entry                   *e,
137         AttributeDescription    *desc,
138         struct berval           *val,
139         slap_access_t           access,
140         AccessControlState      *state,
141         slap_mask_t             *maskp )
142 {
143         assert( maskp != NULL );
144
145         ACL_PRIV_SET( *maskp, ACL_ACCESS2PRIV( access ) );
146
147         return 1;
148 }
149
150 int
151 slap_access_allowed(
152         Operation               *op,
153         Entry                   *e,
154         AttributeDescription    *desc,
155         struct berval           *val,
156         slap_access_t           access,
157         AccessControlState      *state,
158         slap_mask_t             *maskp )
159 {
160         int                             ret = 1;
161         int                             count;
162         AccessControl                   *a = NULL;
163
164 #ifdef LDAP_DEBUG
165         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
166 #endif
167         slap_mask_t                     mask;
168         slap_control_t                  control;
169         slap_access_t                   access_level;
170         const char                      *attr;
171         regmatch_t                      matches[MAXREMATCHES];
172         int                             st_same_attr = 0;
173
174         assert( op != NULL );
175         assert( e != NULL );
176         assert( desc != NULL );
177         assert( maskp != NULL );
178
179         access_level = ACL_LEVEL( access );
180         attr = desc->ad_cname.bv_val;
181
182         assert( attr != NULL );
183
184         /* grant database root access */
185         if ( be_isroot( op ) ) {
186                 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
187                 mask = ACL_LVL_MANAGE;
188                 goto done;
189         }
190
191         /*
192          * no-user-modification operational attributes are ignored
193          * by ACL_WRITE checking as any found here are not provided
194          * by the user
195          *
196          * NOTE: but they are not ignored for ACL_MANAGE, because
197          * if we get here it means a non-root user is trying to 
198          * manage data, so we need to check its privileges.
199          */
200         if ( access_level == ACL_WRITE && is_at_no_user_mod( desc->ad_type )
201                 && desc != slap_schema.si_ad_entry
202                 && desc != slap_schema.si_ad_children )
203         {
204                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
205                         " %s access granted\n",
206                         attr, 0, 0 );
207                 goto done;
208         }
209
210         /* use backend default access if no backend acls */
211         if ( op->o_bd->be_acl == NULL ) {
212                 int     i;
213
214                 Debug( LDAP_DEBUG_ACL,
215                         "=> slap_access_allowed: backend default %s "
216                         "access %s to \"%s\"\n",
217                         access2str( access ),
218                         op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
219                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
220                 ret = op->o_bd->be_dfltaccess >= access_level;
221
222                 mask = ACL_PRIV_LEVEL;
223                 for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
224                         ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
225                 }
226
227                 goto done;
228         }
229
230         ret = 0;
231         control = ACL_BREAK;
232
233         if ( st_same_attr ) {
234                 assert( state->as_vd_acl != NULL );
235
236                 a = state->as_vd_acl;
237                 count = state->as_vd_acl_count;
238                 if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
239                         mask = state->as_vd_acl_mask;
240                         AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
241                         goto vd_access;
242                 }
243
244         } else {
245                 if ( state ) state->as_vi_acl = NULL;
246                 a = NULL;
247                 ACL_PRIV_ASSIGN( mask, *maskp );
248                 count = 0;
249                 memset( matches, '\0', sizeof( matches ) );
250         }
251
252         while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
253                 MAXREMATCHES, matches, state ) ) != NULL )
254         {
255                 int i;
256
257                 for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
258                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
259                                 (int)matches[i].rm_so, (int)matches[i].rm_eo );
260                         if ( matches[i].rm_so <= matches[0].rm_eo ) {
261                                 int n;
262                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
263                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
264                                 }
265                         }
266                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
267                 }
268
269                 if ( state ) {
270                         if ( state->as_vi_acl == a &&
271                                 ( state->as_recorded & ACL_STATE_RECORDED_NV ) )
272                         {
273                                 Debug( LDAP_DEBUG_ACL,
274                                         "=> slap_access_allowed: result from state (%s)\n",
275                                         attr, 0, 0 );
276                                 ret = state->as_result;
277                                 goto done;
278                         } else {
279                                 Debug( LDAP_DEBUG_ACL,
280                                         "=> slap_access_allowed: no res from state (%s)\n",
281                                         attr, 0, 0 );
282                         }
283                 }
284
285 vd_access:
286                 control = slap_acl_mask( a, &mask, op,
287                         e, desc, val, MAXREMATCHES, matches, count, state );
288
289                 if ( control != ACL_BREAK ) {
290                         break;
291                 }
292
293                 memset( matches, '\0', sizeof( matches ) );
294         }
295
296         if ( ACL_IS_INVALID( mask ) ) {
297                 Debug( LDAP_DEBUG_ACL,
298                         "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
299                         e->e_dn, attr, 0 );
300                 ACL_PRIV_ASSIGN( mask, *maskp );
301
302         } else if ( control == ACL_BREAK ) {
303                 Debug( LDAP_DEBUG_ACL,
304                         "=> slap_access_allowed: no more rules\n", 0, 0, 0 );
305
306                 goto done;
307         }
308
309         ret = ACL_GRANT( mask, access );
310
311         Debug( LDAP_DEBUG_ACL,
312                 "=> slap_access_allowed: %s access %s by %s\n",
313                 access2str( access ), ret ? "granted" : "denied",
314                 accessmask2str( mask, accessmaskbuf, 1 ) );
315
316 done:
317         ACL_PRIV_ASSIGN( *maskp, mask );
318         return ret;
319 }
320
321 int
322 fe_access_allowed(
323         Operation               *op,
324         Entry                   *e,
325         AttributeDescription    *desc,
326         struct berval           *val,
327         slap_access_t           access,
328         AccessControlState      *state,
329         slap_mask_t             *maskp )
330 {
331         BackendDB               *be_orig;
332         int                     rc;
333
334         /*
335          * NOTE: control gets here if FIXME
336          * if an appropriate backend cannot be selected for the operation,
337          * we assume that the frontend should handle this
338          * FIXME: should select_backend() take care of this,
339          * and return frontendDB instead of NULL?  maybe for some value
340          * of the flags?
341          */
342         be_orig = op->o_bd;
343
344         op->o_bd = select_backend( &op->o_req_ndn, 0, 0 );
345         if ( op->o_bd == NULL ) {
346                 op->o_bd = frontendDB;
347         }
348         rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
349         op->o_bd = be_orig;
350
351         return rc;
352 }
353
354 int
355 access_allowed_mask(
356         Operation               *op,
357         Entry                   *e,
358         AttributeDescription    *desc,
359         struct berval           *val,
360         slap_access_t           access,
361         AccessControlState      *state,
362         slap_mask_t             *maskp )
363 {
364         int                             ret = 1;
365         AccessControl                   *a = NULL;
366         int                             be_null = 0;
367
368 #ifdef LDAP_DEBUG
369         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
370 #endif
371         slap_mask_t                     mask;
372         slap_access_t                   access_level;
373         const char                      *attr;
374         int                             st_same_attr = 0;
375         static AccessControlState       state_init = ACL_STATE_INIT;
376
377         assert( e != NULL );
378         assert( desc != NULL );
379
380         access_level = ACL_LEVEL( access );
381
382         assert( access_level > ACL_NONE );
383
384         ACL_INIT( mask );
385         if ( maskp ) ACL_INVALIDATE( *maskp );
386
387         attr = desc->ad_cname.bv_val;
388
389         assert( attr != NULL );
390
391         if ( op ) {
392                 if ( op->o_is_auth_check &&
393                         ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
394                 {
395                         access = ACL_AUTH;
396
397                 } else if ( get_manageDIT( op ) && access_level == ACL_WRITE &&
398                         desc == slap_schema.si_ad_entry )
399                 {
400                         access = ACL_MANAGE;
401                 }
402         }
403
404         if ( state ) {
405                 if ( state->as_vd_ad == desc ) {
406                         if ( state->as_recorded ) {
407                                 if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
408                                         val == NULL )
409                                 {
410                                         return state->as_result;
411
412                                 } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
413                                         val != NULL && state->as_vd_acl == NULL )
414                                 {
415                                         return state->as_result;
416                                 }
417                         }
418                         st_same_attr = 1;
419                 } else {
420                         *state = state_init;
421                 }
422
423                 state->as_vd_ad = desc;
424         }
425
426         Debug( LDAP_DEBUG_ACL,
427                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
428                 access2str( access ), e->e_dn, attr );
429
430         if ( op == NULL ) {
431                 /* no-op call */
432                 goto done;
433         }
434
435         if ( op->o_bd == NULL ) {
436                 op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
437                 be_null = 1;
438
439 #ifdef LDAP_DEVEL
440                 /*
441                  * FIXME: experimental; use first backend rules
442                  * iff there is no global_acl (ITS#3100) */
443                 if ( frontendDB->be_acl != NULL ) {
444                         op->o_bd = frontendDB;
445                 }
446 #endif /* LDAP_DEVEL */
447         }
448         assert( op->o_bd != NULL );
449
450         /* this is enforced in backend_add() */
451         if ( op->o_bd->bd_info->bi_access_allowed ) {
452                 /* delegate to backend */
453                 ret = op->o_bd->bd_info->bi_access_allowed( op, e,
454                                 desc, val, access, state, &mask );
455
456         } else {
457                 BackendDB       *be_orig = op->o_bd;
458
459                 /* use default (but pass through frontend
460                  * for global ACL overlays) */
461                 op->o_bd = frontendDB;
462                 ret = frontendDB->bd_info->bi_access_allowed( op, e,
463                                 desc, val, access, state, &mask );
464                 op->o_bd = be_orig;
465         }
466
467         if ( !ret ) {
468                 if ( ACL_IS_INVALID( mask ) ) {
469                         Debug( LDAP_DEBUG_ACL,
470                                 "=> access_allowed: \"%s\" (%s) invalid!\n",
471                                 e->e_dn, attr, 0 );
472                         ACL_INIT( mask );
473
474                 } else {
475                         Debug( LDAP_DEBUG_ACL,
476                                 "=> access_allowed: no more rules\n", 0, 0, 0 );
477
478                         goto done;
479                 }
480         }
481
482         Debug( LDAP_DEBUG_ACL,
483                 "=> access_allowed: %s access %s by %s\n",
484                 access2str( access ), ret ? "granted" : "denied",
485                 accessmask2str( mask, accessmaskbuf, 1 ) );
486
487 done:
488         if ( state != NULL ) {
489                 /* If not value-dependent, save ACL in case of more attrs */
490                 if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
491                         state->as_vi_acl = a;
492                         state->as_result = ret;
493                 }
494                 state->as_recorded |= ACL_STATE_RECORDED;
495         }
496         if ( be_null ) op->o_bd = NULL;
497         if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
498         return ret;
499 }
500
501 #else /* !SLAP_OVERLAY_ACCESS */
502
503 int
504 access_allowed_mask(
505         Operation               *op,
506         Entry                   *e,
507         AttributeDescription    *desc,
508         struct berval           *val,
509         slap_access_t           access,
510         AccessControlState      *state,
511         slap_mask_t             *maskp )
512 {
513         int                             ret = 1;
514         int                             count;
515         AccessControl                   *a = NULL;
516         Backend                         *be;
517         int                             be_null = 0;
518
519 #ifdef LDAP_DEBUG
520         char                            accessmaskbuf[ACCESSMASK_MAXLEN];
521 #endif
522         slap_mask_t                     mask;
523         slap_control_t                  control;
524         slap_access_t                   access_level;
525         const char                      *attr;
526         regmatch_t                      matches[MAXREMATCHES];
527         int                             st_same_attr = 0;
528         static AccessControlState       state_init = ACL_STATE_INIT;
529
530         assert( e != NULL );
531         assert( desc != NULL );
532
533         access_level = ACL_LEVEL( access );
534
535         assert( access_level > ACL_NONE );
536         if ( maskp ) ACL_INVALIDATE( *maskp );
537
538         attr = desc->ad_cname.bv_val;
539
540         assert( attr != NULL );
541
542         if ( op ) {
543                 if ( op->o_is_auth_check &&
544                         ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
545                 {
546                         access = ACL_AUTH;
547
548                 } else if ( get_manageDIT( op ) && access_level == ACL_WRITE &&
549                         desc == slap_schema.si_ad_entry )
550                 {
551                         access = ACL_MANAGE;
552                 }
553         }
554
555         if ( state ) {
556                 if ( state->as_vd_ad == desc ) {
557                         if ( state->as_recorded ) {
558                                 if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
559                                         val == NULL )
560                                 {
561                                         return state->as_result;
562
563                                 } else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
564                                         val != NULL && state->as_vd_acl == NULL )
565                                 {
566                                         return state->as_result;
567                                 }
568                         }
569                         st_same_attr = 1;
570                 } else {
571                         *state = state_init;
572                 }
573
574                 state->as_vd_ad=desc;
575         }
576
577         Debug( LDAP_DEBUG_ACL,
578                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
579                 access2str( access ), e->e_dn, attr );
580
581         if ( op == NULL ) {
582                 /* no-op call */
583                 goto done;
584         }
585
586         be = op->o_bd;
587         if ( be == NULL ) {
588                 be = LDAP_STAILQ_FIRST(&backendDB);
589                 be_null = 1;
590 #ifdef LDAP_DEVEL
591                 /*
592                  * FIXME: experimental; use first backend rules
593                  * iff there is no global_acl (ITS#3100) */
594                 if ( frontendDB->be_acl == NULL ) 
595 #endif
596                 {
597                         op->o_bd = be;
598                 }
599         }
600         assert( be != NULL );
601
602         /* grant database root access */
603         if ( be_isroot( op ) ) {
604                 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
605                 if ( maskp ) {
606                         mask = ACL_LVL_MANAGE;
607                 }
608
609                 goto done;
610         }
611
612         /*
613          * no-user-modification operational attributes are ignored
614          * by ACL_WRITE checking as any found here are not provided
615          * by the user
616          *
617          * NOTE: but they are not ignored for ACL_MANAGE, because
618          * if we get here it means a non-root user is trying to 
619          * manage data, so we need to check its privileges.
620          */
621         if ( access_level == ACL_WRITE && is_at_no_user_mod( desc->ad_type )
622                 && desc != slap_schema.si_ad_entry
623                 && desc != slap_schema.si_ad_children )
624         {
625                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
626                         " %s access granted\n",
627                         attr, 0, 0 );
628                 goto done;
629         }
630
631         /* use backend default access if no backend acls */
632         if ( be->be_acl == NULL ) {
633                 Debug( LDAP_DEBUG_ACL,
634                         "=> access_allowed: backend default %s "
635                         "access %s to \"%s\"\n",
636                         access2str( access ),
637                         be->be_dfltaccess >= access_level ? "granted" : "denied",
638                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
639                 ret = be->be_dfltaccess >= access_level;
640
641                 if ( maskp ) {
642                         int     i;
643
644                         mask = ACL_PRIV_LEVEL;
645                         for ( i = ACL_NONE; i <= be->be_dfltaccess; i++ ) {
646                                 mask |= ACL_ACCESS2PRIV( i );
647                         }
648                 }
649
650                 goto done;
651
652 #ifdef notdef
653         /* be is always non-NULL */
654         /* use global default access if no global acls */
655         } else if ( be == NULL && frontendDB->be_acl == NULL ) {
656                 Debug( LDAP_DEBUG_ACL,
657                         "=> access_allowed: global default %s access %s to \"%s\"\n",
658                         access2str( access ),
659                         frontendDB->be_dfltaccess >= access_level ?
660                                 "granted" : "denied", op->o_dn.bv_val );
661                 ret = frontendDB->be_dfltaccess >= access_level;
662
663                 if ( maskp ) {
664                         int     i;
665
666                         mask = ACL_PRIV_LEVEL;
667                         for ( i = ACL_NONE; i <= global_default_access; i++ ) {
668                                 mask |= ACL_ACCESS2PRIV( i );
669                         }
670                 }
671
672                 goto done;
673 #endif
674         }
675
676         ret = 0;
677         control = ACL_BREAK;
678
679         if ( st_same_attr ) {
680                 assert( state->as_vd_acl != NULL );
681
682                 a = state->as_vd_acl;
683                 count = state->as_vd_acl_count;
684                 if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
685                         mask = state->as_vd_acl_mask;
686                         AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
687                         goto vd_access;
688                 }
689
690         } else {
691                 if ( state ) state->as_vi_acl = NULL;
692                 a = NULL;
693                 ACL_INIT(mask);
694                 count = 0;
695                 memset( matches, '\0', sizeof(matches) );
696         }
697
698         while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
699                 MAXREMATCHES, matches, state ) ) != NULL )
700         {
701                 int i;
702
703                 for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
704                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
705                                 (int)matches[i].rm_so, (int)matches[i].rm_eo );
706                         if ( matches[i].rm_so <= matches[0].rm_eo ) {
707                                 int n;
708                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
709                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
710                                 }
711                         }
712                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
713                 }
714
715                 if ( state ) {
716                         if ( state->as_vi_acl == a &&
717                                 ( state->as_recorded & ACL_STATE_RECORDED_NV ) )
718                         {
719                                 Debug( LDAP_DEBUG_ACL,
720                                         "access_allowed: result from state (%s)\n",
721                                         attr, 0, 0 );
722                                 ret = state->as_result;
723                                 goto done;
724                         } else {
725                                 Debug( LDAP_DEBUG_ACL,
726                                         "access_allowed: no res from state (%s)\n",
727                                         attr, 0, 0 );
728                         }
729                 }
730
731 vd_access:
732                 control = slap_acl_mask( a, &mask, op,
733                         e, desc, val, MAXREMATCHES, matches, count, state );
734
735                 if ( control != ACL_BREAK ) {
736                         break;
737                 }
738
739                 memset( matches, '\0', sizeof(matches) );
740         }
741
742         if ( ACL_IS_INVALID( mask ) ) {
743                 Debug( LDAP_DEBUG_ACL,
744                         "=> access_allowed: \"%s\" (%s) invalid!\n",
745                         e->e_dn, attr, 0 );
746                 ACL_INIT(mask);
747
748         } else if ( control == ACL_BREAK ) {
749                 Debug( LDAP_DEBUG_ACL,
750                         "=> access_allowed: no more rules\n", 0, 0, 0 );
751
752                 goto done;
753         }
754
755         Debug( LDAP_DEBUG_ACL,
756                 "=> access_allowed: %s access %s by %s\n",
757                 access2str( access ),
758                 ACL_GRANT(mask, access) ? "granted" : "denied",
759                 accessmask2str( mask, accessmaskbuf, 1 ) );
760
761         ret = ACL_GRANT(mask, access);
762
763 done:
764         if ( state != NULL ) {
765                 /* If not value-dependent, save ACL in case of more attrs */
766                 if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
767                         state->as_vi_acl = a;
768                         state->as_result = ret;
769                 }
770                 state->as_recorded |= ACL_STATE_RECORDED;
771         }
772         if ( be_null ) op->o_bd = NULL;
773         if ( maskp ) *maskp = mask;
774         return ret;
775 }
776
777 #endif /* SLAP_OVERLAY_ACCESS */
778
779 /*
780  * slap_acl_get - return the acl applicable to entry e, attribute
781  * attr.  the acl returned is suitable for use in subsequent calls to
782  * acl_access_allowed().
783  */
784
785 static AccessControl *
786 slap_acl_get(
787         AccessControl *a,
788         int                     *count,
789         Operation       *op,
790         Entry           *e,
791         AttributeDescription *desc,
792         struct berval   *val,
793         int                     nmatch,
794         regmatch_t      *matches,
795         AccessControlState *state )
796 {
797         const char *attr;
798         int dnlen, patlen;
799         AccessControl *prev;
800
801         assert( e != NULL );
802         assert( count != NULL );
803         assert( desc != NULL );
804
805         attr = desc->ad_cname.bv_val;
806
807         assert( attr != NULL );
808
809         if( a == NULL ) {
810                 if( op->o_bd == NULL ) {
811                         a = frontendDB->be_acl;
812                 } else {
813                         a = op->o_bd->be_acl;
814                 }
815                 prev = NULL;
816
817                 assert( a != NULL );
818
819         } else {
820                 prev = a;
821                 a = a->acl_next;
822         }
823
824         dnlen = e->e_nname.bv_len;
825
826         for ( ; a != NULL; a = a->acl_next ) {
827                 (*count) ++;
828
829                 if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
830                         if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
831                                 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
832                                         *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
833                                 if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
834                                         continue;
835
836                         } else {
837                                 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
838                                         *count, a->acl_dn_pat.bv_val, 0 );
839                                 patlen = a->acl_dn_pat.bv_len;
840                                 if ( dnlen < patlen )
841                                         continue;
842
843                                 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
844                                         /* base dn -- entire object DN must match */
845                                         if ( dnlen != patlen )
846                                                 continue;
847
848                                 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
849                                         int     rdnlen = -1, sep = 0;
850
851                                         if ( dnlen <= patlen )
852                                                 continue;
853
854                                         if ( patlen > 0 ) {
855                                                 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
856                                                         continue;
857                                                 sep = 1;
858                                         }
859
860                                         rdnlen = dn_rdnlen( NULL, &e->e_nname );
861                                         if ( rdnlen != dnlen - patlen - sep )
862                                                 continue;
863
864                                 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
865                                         if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
866                                                 continue;
867
868                                 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
869                                         if ( dnlen <= patlen )
870                                                 continue;
871                                         if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
872                                                 continue;
873                                 }
874
875                                 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
876                                         continue;
877                         }
878
879                         Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
880                                 *count, 0, 0 );
881                 }
882
883                 if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
884                         matches[0].rm_so = matches[0].rm_eo = -1;
885                         continue;
886                 }
887
888                 /* Is this ACL only for a specific value? */
889                 if ( a->acl_attrval.bv_len ) {
890                         if ( val == NULL ) {
891                                 continue;
892                         }
893
894                         if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) {
895                                 state->as_recorded |= ACL_STATE_RECORDED_VD;
896                                 state->as_vd_acl = a;
897                                 state->as_vd_acl_count = *count;
898                                 state->as_vd_access = a->acl_access;
899                                 state->as_vd_access_count = 1;
900                                 ACL_INVALIDATE( state->as_vd_acl_mask );
901                         }
902
903                         if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
904                                 Debug( LDAP_DEBUG_ACL,
905                                         "acl_get: valpat %s\n",
906                                         a->acl_attrval.bv_val, 0, 0 );
907                                 if ( regexec( &a->acl_attrval_re, val->bv_val, 0, NULL, 0 ) )
908                                 {
909                                         continue;
910                                 }
911
912                         } else {
913                                 int match = 0;
914                                 const char *text;
915                                 Debug( LDAP_DEBUG_ACL,
916                                         "acl_get: val %s\n",
917                                         a->acl_attrval.bv_val, 0, 0 );
918         
919                                 if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
920                                         if (value_match( &match, desc,
921                                                 /* desc->ad_type->sat_equality */ a->acl_attrval_mr, 0,
922                                                 val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
923                                                         match )
924                                                 continue;
925                                         
926                                 } else {
927                                         int             patlen, vdnlen;
928         
929                                         patlen = a->acl_attrval.bv_len;
930                                         vdnlen = val->bv_len;
931         
932                                         if ( vdnlen < patlen )
933                                                 continue;
934         
935                                         if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
936                                                 if ( vdnlen > patlen )
937                                                         continue;
938         
939                                         } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
940                                                 int rdnlen = -1;
941         
942                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
943                                                         continue;
944         
945                                                 rdnlen = dn_rdnlen( NULL, val );
946                                                 if ( rdnlen != vdnlen - patlen - 1 )
947                                                         continue;
948         
949                                         } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
950                                                 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
951                                                         continue;
952         
953                                         } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
954                                                 if ( vdnlen <= patlen )
955                                                         continue;
956         
957                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
958                                                         continue;
959                                         }
960         
961                                         if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ))
962                                                 continue;
963                                 }
964                         }
965                 }
966
967                 if ( a->acl_filter != NULL ) {
968                         ber_int_t rc = test_filter( NULL, e, a->acl_filter );
969                         if ( rc != LDAP_COMPARE_TRUE ) {
970                                 continue;
971                         }
972                 }
973
974                 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
975                        *count, attr, 0);
976                 return a;
977         }
978
979         Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
980         return( NULL );
981 }
982
983 static int
984 acl_mask_dn(
985         Operation               *op,
986         Entry                   *e,
987         AccessControl           *a,
988         int                     nmatch,
989         regmatch_t              *matches,
990         slap_dn_access          *b,
991         struct berval           *opndn )
992 {
993         /*
994          * if access applies to the entry itself, and the
995          * user is bound as somebody in the same namespace as
996          * the entry, OR the given dn matches the dn pattern
997          */
998         /*
999          * NOTE: styles "anonymous", "users" and "self" 
1000          * have been moved to enum slap_style_t, whose 
1001          * value is set in a_dn_style; however, the string
1002          * is maintaned in a_dn_pat.
1003          */
1004         if ( b->a_style == ACL_STYLE_ANONYMOUS ) {
1005                 if ( !BER_BVISEMPTY( opndn ) ) {
1006                         return 1;
1007                 }
1008
1009         } else if ( b->a_style == ACL_STYLE_USERS ) {
1010                 if ( BER_BVISEMPTY( opndn ) ) {
1011                         return 1;
1012                 }
1013
1014         } else if ( b->a_style == ACL_STYLE_SELF ) {
1015                 struct berval   ndn, selfndn;
1016                 int             level;
1017
1018                 if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
1019                         return 1;
1020                 }
1021
1022                 level = b->a_self_level;
1023                 if ( level < 0 ) {
1024                         selfndn = *opndn;
1025                         ndn = e->e_nname;
1026                         level = -level;
1027
1028                 } else {
1029                         ndn = *opndn;
1030                         selfndn = e->e_nname;
1031                 }
1032
1033                 for ( ; level > 0; level-- ) {
1034                         if ( BER_BVISEMPTY( &ndn ) ) {
1035                                 break;
1036                         }
1037                         dnParent( &ndn, &ndn );
1038                 }
1039                         
1040                 if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
1041                 {
1042                         return 1;
1043                 }
1044
1045         } else if ( b->a_style == ACL_STYLE_REGEX ) {
1046                 if ( !ber_bvccmp( &b->a_pat, '*' ) ) {
1047                         int             tmp_nmatch;
1048                         regmatch_t      tmp_matches[2],
1049                                         *tmp_matchesp = tmp_matches;
1050
1051                         int             rc = 0;
1052
1053                         switch ( a->acl_dn_style ) {
1054                         case ACL_STYLE_REGEX:
1055                                 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1056                                         tmp_matchesp = matches;
1057                                         tmp_nmatch = nmatch;
1058                                         break;
1059                                 }
1060                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1061
1062                         case ACL_STYLE_BASE:
1063                                 tmp_matches[0].rm_so = 0;
1064                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1065                                 tmp_nmatch = 1;
1066                                 break;
1067
1068                         case ACL_STYLE_ONE:
1069                         case ACL_STYLE_SUBTREE:
1070                         case ACL_STYLE_CHILDREN:
1071                                 tmp_matches[0].rm_so = 0;
1072                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1073                                 tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1074                                 tmp_matches[1].rm_eo = e->e_nname.bv_len;
1075                                 tmp_nmatch = 2;
1076                                 break;
1077
1078                         default:
1079                                 /* error */
1080                                 rc = 1;
1081                                 break;
1082                         }
1083
1084                         if ( rc ) {
1085                                 return 1;
1086                         }
1087
1088                         if ( !regex_matches( &b->a_pat, opndn->bv_val,
1089                                 e->e_ndn, tmp_nmatch, tmp_matchesp ) )
1090                         {
1091                                 return 1;
1092                         }
1093                 }
1094
1095         } else {
1096                 struct berval   pat;
1097                 ber_len_t       patlen, odnlen;
1098                 int             got_match = 0;
1099
1100                 if ( e->e_dn == NULL )
1101                         return 1;
1102
1103                 if ( b->a_expand ) {
1104                         struct berval   bv;
1105                         char            buf[ACL_BUF_SIZE];
1106                         
1107                         int             tmp_nmatch;
1108                         regmatch_t      tmp_matches[2],
1109                                         *tmp_matchesp = tmp_matches;
1110
1111                         int             rc = 0;
1112
1113                         bv.bv_len = sizeof( buf ) - 1;
1114                         bv.bv_val = buf;
1115
1116                         switch ( a->acl_dn_style ) {
1117                         case ACL_STYLE_REGEX:
1118                                 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1119                                         tmp_matchesp = matches;
1120                                         tmp_nmatch = nmatch;
1121                                         break;
1122                                 }
1123                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1124
1125                         case ACL_STYLE_BASE:
1126                                 tmp_matches[0].rm_so = 0;
1127                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1128                                 tmp_nmatch = 1;
1129                                 break;
1130
1131                         case ACL_STYLE_ONE:
1132                         case ACL_STYLE_SUBTREE:
1133                         case ACL_STYLE_CHILDREN:
1134                                 tmp_matches[0].rm_so = 0;
1135                                 tmp_matches[0].rm_eo = e->e_nname.bv_len;
1136                                 tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1137                                 tmp_matches[1].rm_eo = e->e_nname.bv_len;
1138                                 tmp_nmatch = 2;
1139                                 break;
1140
1141                         default:
1142                                 /* error */
1143                                 rc = 1;
1144                                 break;
1145                         }
1146
1147                         if ( rc ) {
1148                                 return 1;
1149                         }
1150
1151                         if ( acl_string_expand( &bv, &b->a_pat, 
1152                                         e->e_nname.bv_val,
1153                                         tmp_nmatch, tmp_matchesp ) )
1154                         {
1155                                 return 1;
1156                         }
1157                         
1158                         if ( dnNormalize(0, NULL, NULL, &bv,
1159                                         &pat, op->o_tmpmemctx )
1160                                         != LDAP_SUCCESS )
1161                         {
1162                                 /* did not expand to a valid dn */
1163                                 return 1;
1164                         }
1165
1166                 } else {
1167                         pat = b->a_pat;
1168                 }
1169
1170                 patlen = pat.bv_len;
1171                 odnlen = opndn->bv_len;
1172                 if ( odnlen < patlen ) {
1173                         goto dn_match_cleanup;
1174
1175                 }
1176
1177                 if ( b->a_style == ACL_STYLE_BASE ) {
1178                         /* base dn -- entire object DN must match */
1179                         if ( odnlen != patlen ) {
1180                                 goto dn_match_cleanup;
1181                         }
1182
1183                 } else if ( b->a_style == ACL_STYLE_ONE ) {
1184                         int rdnlen = -1;
1185
1186                         if ( odnlen <= patlen ) {
1187                                 goto dn_match_cleanup;
1188                         }
1189
1190                         if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1191                                 goto dn_match_cleanup;
1192                         }
1193
1194                         rdnlen = dn_rdnlen( NULL, opndn );
1195                         if ( rdnlen != odnlen - patlen - 1 ) {
1196                                 goto dn_match_cleanup;
1197                         }
1198
1199                 } else if ( b->a_style == ACL_STYLE_SUBTREE ) {
1200                         if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1201                                 goto dn_match_cleanup;
1202                         }
1203
1204                 } else if ( b->a_style == ACL_STYLE_CHILDREN ) {
1205                         if ( odnlen <= patlen ) {
1206                                 goto dn_match_cleanup;
1207                         }
1208
1209                         if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
1210                                 goto dn_match_cleanup;
1211                         }
1212
1213                 } else if ( b->a_style == ACL_STYLE_LEVEL ) {
1214                         int level;
1215                         struct berval ndn;
1216
1217                         if ( odnlen <= patlen ) {
1218                                 goto dn_match_cleanup;
1219                         }
1220
1221                         if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
1222                         {
1223                                 goto dn_match_cleanup;
1224                         }
1225                         
1226                         level = b->a_level;
1227                         ndn = *opndn;
1228                         for ( ; level > 0; level-- ) {
1229                                 if ( BER_BVISEMPTY( &ndn ) ) {
1230                                         goto dn_match_cleanup;
1231                                 }
1232                                 dnParent( &ndn, &ndn );
1233                                 if ( ndn.bv_len < patlen ) {
1234                                         goto dn_match_cleanup;
1235                                 }
1236                         }
1237                         
1238                         if ( ndn.bv_len != patlen ) {
1239                                 goto dn_match_cleanup;
1240                         }
1241                 }
1242
1243                 got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
1244
1245 dn_match_cleanup:;
1246                 if ( pat.bv_val != b->a_pat.bv_val ) {
1247                         slap_sl_free( pat.bv_val, op->o_tmpmemctx );
1248                 }
1249
1250                 if ( !got_match ) {
1251                         return 1;
1252                 }
1253         }
1254
1255         return 0;
1256 }
1257
1258 /*
1259  * Record value-dependent access control state
1260  */
1261 #define ACL_RECORD_VALUE_STATE do { \
1262                 if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
1263                         state->as_recorded |= ACL_STATE_RECORDED_VD; \
1264                         state->as_vd_acl = a; \
1265                         AC_MEMCPY( state->as_vd_acl_matches, matches, \
1266                                 sizeof( state->as_vd_acl_matches )) ; \
1267                         state->as_vd_acl_count = count; \
1268                         state->as_vd_access = b; \
1269                         state->as_vd_access_count = i; \
1270                 } \
1271         } while( 0 )
1272
1273 static int
1274 acl_mask_dnattr(
1275         Operation               *op,
1276         Entry                   *e,
1277         struct berval           *val,
1278         AccessControl           *a,
1279         Access                  *b,
1280         int                     i,
1281         regmatch_t              *matches,
1282         int                     count,
1283         AccessControlState      *state,
1284         slap_dn_access          *bdn,
1285         struct berval           *opndn )
1286 {
1287         Attribute       *at;
1288         struct berval   bv;
1289         int             rc, match = 0;
1290         const char      *text;
1291         const char      *attr = bdn->a_at->ad_cname.bv_val;
1292
1293         assert( attr != NULL );
1294
1295         if ( BER_BVISEMPTY( opndn ) ) {
1296                 return 1;
1297         }
1298
1299         Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 );
1300         bv = *opndn;
1301
1302         /* see if asker is listed in dnattr */
1303         for ( at = attrs_find( e->e_attrs, bdn->a_at );
1304                 at != NULL;
1305                 at = attrs_find( at->a_next, bdn->a_at ) )
1306         {
1307                 if ( value_find_ex( bdn->a_at,
1308                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1309                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1310                         at->a_nvals,
1311                         &bv, op->o_tmpmemctx ) == 0 )
1312                 {
1313                         /* found it */
1314                         match = 1;
1315                         break;
1316                 }
1317         }
1318
1319         if ( match ) {
1320                 /* have a dnattr match. if this is a self clause then
1321                  * the target must also match the op dn.
1322                  */
1323                 if ( bdn->a_self ) {
1324                         /* check if the target is an attribute. */
1325                         if ( val == NULL ) return 1;
1326
1327                         /* target is attribute, check if the attribute value
1328                          * is the op dn.
1329                          */
1330                         rc = value_match( &match, bdn->a_at,
1331                                 bdn->a_at->ad_type->sat_equality, 0,
1332                                 val, &bv, &text );
1333                         /* on match error or no match, fail the ACL clause */
1334                         if ( rc != LDAP_SUCCESS || match != 0 )
1335                                 return 1;
1336                 }
1337
1338         } else {
1339                 /* no dnattr match, check if this is a self clause */
1340                 if ( ! bdn->a_self )
1341                         return 1;
1342
1343                 ACL_RECORD_VALUE_STATE;
1344                 
1345                 /* this is a self clause, check if the target is an
1346                  * attribute.
1347                  */
1348                 if ( val == NULL )
1349                         return 1;
1350
1351                 /* target is attribute, check if the attribute value
1352                  * is the op dn.
1353                  */
1354                 rc = value_match( &match, bdn->a_at,
1355                         bdn->a_at->ad_type->sat_equality, 0,
1356                         val, &bv, &text );
1357
1358                 /* on match error or no match, fail the ACL clause */
1359                 if ( rc != LDAP_SUCCESS || match != 0 )
1360                         return 1;
1361         }
1362
1363         return 0;
1364 }
1365
1366
1367 /*
1368  * slap_acl_mask - modifies mask based upon the given acl and the
1369  * requested access to entry e, attribute attr, value val.  if val
1370  * is null, access to the whole attribute is assumed (all values).
1371  *
1372  * returns      0       access NOT allowed
1373  *              1       access allowed
1374  */
1375
1376 static slap_control_t
1377 slap_acl_mask(
1378         AccessControl           *a,
1379         slap_mask_t             *mask,
1380         Operation               *op,
1381         Entry                   *e,
1382         AttributeDescription    *desc,
1383         struct berval           *val,
1384         int                     nmatch,
1385         regmatch_t              *matches,
1386         int                     count,
1387         AccessControlState      *state )
1388 {
1389         int             i;
1390         Access          *b;
1391 #ifdef LDAP_DEBUG
1392         char            accessmaskbuf[ACCESSMASK_MAXLEN];
1393 #if !defined( SLAP_DYNACL ) && defined( SLAPD_ACI_ENABLED )
1394         char            accessmaskbuf1[ACCESSMASK_MAXLEN];
1395 #endif /* !SLAP_DYNACL && SLAPD_ACI_ENABLED */
1396 #endif /* DEBUG */
1397         const char      *attr;
1398         slap_mask_t     a2pmask = ACL_ACCESS2PRIV( *mask );
1399
1400         assert( a != NULL );
1401         assert( mask != NULL );
1402         assert( desc != NULL );
1403
1404         attr = desc->ad_cname.bv_val;
1405
1406         assert( attr != NULL );
1407
1408         Debug( LDAP_DEBUG_ACL,
1409                 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
1410                 e->e_dn, attr, 0 );
1411
1412         Debug( LDAP_DEBUG_ACL,
1413                 "=> acl_mask: to %s by \"%s\", (%s) \n",
1414                 val ? "value" : "all values",
1415                 op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
1416                 accessmask2str( *mask, accessmaskbuf, 1 ) );
1417
1418
1419         if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
1420                 && state->as_vd_acl == a )
1421         {
1422                 b = state->as_vd_access;
1423                 i = state->as_vd_access_count;
1424
1425         } else {
1426                 b = a->acl_access;
1427                 i = 1;
1428         }
1429
1430         for ( ; b != NULL; b = b->a_next, i++ ) {
1431                 slap_mask_t oldmask, modmask;
1432
1433                 ACL_INVALIDATE( modmask );
1434
1435                 /* AND <who> clauses */
1436                 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
1437                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
1438                                 b->a_dn_pat.bv_val, 0, 0);
1439                         /*
1440                          * if access applies to the entry itself, and the
1441                          * user is bound as somebody in the same namespace as
1442                          * the entry, OR the given dn matches the dn pattern
1443                          */
1444                         /*
1445                          * NOTE: styles "anonymous", "users" and "self" 
1446                          * have been moved to enum slap_style_t, whose 
1447                          * value is set in a_dn_style; however, the string
1448                          * is maintaned in a_dn_pat.
1449                          */
1450
1451                         if ( acl_mask_dn( op, e, a, nmatch, matches,
1452                                 &b->a_dn, &op->o_ndn ) )
1453                         {
1454                                 continue;
1455                         }
1456                 }
1457
1458                 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
1459                         struct berval   ndn;
1460
1461                         Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
1462                                 b->a_realdn_pat.bv_val, 0, 0);
1463                         /*
1464                          * if access applies to the entry itself, and the
1465                          * user is bound as somebody in the same namespace as
1466                          * the entry, OR the given dn matches the dn pattern
1467                          */
1468                         /*
1469                          * NOTE: styles "anonymous", "users" and "self" 
1470                          * have been moved to enum slap_style_t, whose 
1471                          * value is set in a_dn_style; however, the string
1472                          * is maintaned in a_dn_pat.
1473                          */
1474
1475                         if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1476                         {
1477                                 ndn = op->o_conn->c_ndn;
1478                         } else {
1479                                 ndn = op->o_ndn;
1480                         }
1481
1482                         if ( acl_mask_dn( op, e, a, nmatch, matches,
1483                                 &b->a_realdn, &ndn ) )
1484                         {
1485                                 continue;
1486                         }
1487                 }
1488
1489                 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1490                         if ( ! op->o_conn->c_listener ) {
1491                                 continue;
1492                         }
1493                         Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
1494                                 b->a_sockurl_pat.bv_val, 0, 0 );
1495
1496                         if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
1497                                 if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
1498                                         if (!regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
1499                                                         e->e_ndn, nmatch, matches ) ) 
1500                                         {
1501                                                 continue;
1502                                         }
1503
1504                                 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
1505                                         struct berval   bv;
1506                                         char buf[ACL_BUF_SIZE];
1507
1508                                         bv.bv_len = sizeof( buf ) - 1;
1509                                         bv.bv_val = buf;
1510                                         if ( acl_string_expand( &bv, &b->a_sockurl_pat,
1511                                                         e->e_ndn, nmatch, matches ) )
1512                                         {
1513                                                 continue;
1514                                         }
1515
1516                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
1517                                         {
1518                                                 continue;
1519                                         }
1520
1521                                 } else {
1522                                         if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
1523                                         {
1524                                                 continue;
1525                                         }
1526                                 }
1527                         }
1528                 }
1529
1530                 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1531                         if ( !op->o_conn->c_peer_domain.bv_val ) {
1532                                 continue;
1533                         }
1534                         Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
1535                                 b->a_domain_pat.bv_val, 0, 0 );
1536                         if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
1537                                 if ( b->a_domain_style == ACL_STYLE_REGEX) {
1538                                         if (!regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
1539                                                         e->e_ndn, nmatch, matches ) ) 
1540                                         {
1541                                                 continue;
1542                                         }
1543                                 } else {
1544                                         char buf[ACL_BUF_SIZE];
1545
1546                                         struct berval   cmp = op->o_conn->c_peer_domain;
1547                                         struct berval   pat = b->a_domain_pat;
1548
1549                                         if ( b->a_domain_expand ) {
1550                                                 struct berval bv;
1551
1552                                                 bv.bv_len = sizeof(buf) - 1;
1553                                                 bv.bv_val = buf;
1554
1555                                                 if ( acl_string_expand(&bv, &b->a_domain_pat,
1556                                                                 e->e_ndn, nmatch, matches) )
1557                                                 {
1558                                                         continue;
1559                                                 }
1560                                                 pat = bv;
1561                                         }
1562
1563                                         if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
1564                                                 int offset = cmp.bv_len - pat.bv_len;
1565                                                 if ( offset < 0 ) {
1566                                                         continue;
1567                                                 }
1568
1569                                                 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
1570                                                         continue;
1571                                                 }
1572
1573                                                 /* trim the domain */
1574                                                 cmp.bv_val = &cmp.bv_val[ offset ];
1575                                                 cmp.bv_len -= offset;
1576                                         }
1577                                         
1578                                         if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1579                                                 continue;
1580                                         }
1581                                 }
1582                         }
1583                 }
1584
1585                 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1586                         if ( !op->o_conn->c_peer_name.bv_val ) {
1587                                 continue;
1588                         }
1589                         Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1590                                 b->a_peername_pat.bv_val, 0, 0 );
1591                         if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1592                                 if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1593                                         if (!regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1594                                                         e->e_ndn, nmatch, matches ) ) 
1595                                         {
1596                                                 continue;
1597                                         }
1598
1599                                 } else {
1600                                         /* try exact match */
1601                                         if ( b->a_peername_style == ACL_STYLE_BASE ) {
1602                                                 if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
1603                                                         continue;
1604                                                 }
1605
1606                                         } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1607                                                 struct berval   bv;
1608                                                 char buf[ACL_BUF_SIZE];
1609
1610                                                 bv.bv_len = sizeof( buf ) - 1;
1611                                                 bv.bv_val = buf;
1612                                                 if ( acl_string_expand( &bv, &b->a_peername_pat,
1613                                                                 e->e_ndn, nmatch, matches ) )
1614                                                 {
1615                                                         continue;
1616                                                 }
1617
1618                                                 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1619                                                         continue;
1620                                                 }
1621
1622                                         /* extract IP and try exact match */
1623                                         } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1624                                                 char            *port;
1625                                                 char            buf[] = "255.255.255.255";
1626                                                 struct berval   ip;
1627                                                 unsigned long   addr;
1628                                                 int             port_number = -1;
1629                                                 
1630                                                 if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 
1631                                                                         aci_bv[ ACI_BV_IP_EQ ].bv_val,
1632                                                                         aci_bv[ ACI_BV_IP_EQ ].bv_len ) != 0 ) 
1633                                                         continue;
1634
1635                                                 ip.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv[ ACI_BV_IP_EQ ].bv_len;
1636                                                 ip.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv[ ACI_BV_IP_EQ ].bv_len;
1637
1638                                                 port = strrchr( ip.bv_val, ':' );
1639                                                 if ( port ) {
1640                                                         char    *next;
1641                                                         
1642                                                         ip.bv_len = port - ip.bv_val;
1643                                                         ++port;
1644                                                         port_number = strtol( port, &next, 10 );
1645                                                         if ( next[0] != '\0' )
1646                                                                 continue;
1647                                                 }
1648                                                 
1649                                                 /* the port check can be anticipated here */
1650                                                 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1651                                                         continue;
1652                                                 
1653                                                 /* address longer than expected? */
1654                                                 if ( ip.bv_len >= sizeof(buf) )
1655                                                         continue;
1656
1657                                                 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1658                                                 buf[ ip.bv_len ] = '\0';
1659
1660                                                 addr = inet_addr( buf );
1661
1662                                                 /* unable to convert? */
1663                                                 if ( addr == (unsigned long)(-1) )
1664                                                         continue;
1665
1666                                                 if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1667                                                         continue;
1668
1669 #ifdef LDAP_PF_LOCAL
1670                                         /* extract path and try exact match */
1671                                         } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
1672                                                 struct berval path;
1673                                                 
1674                                                 if ( strncmp( op->o_conn->c_peer_name.bv_val,
1675                                                                         aci_bv[ ACI_BV_PATH_EQ ].bv_val,
1676                                                                         aci_bv[ ACI_BV_PATH_EQ ].bv_len ) != 0 )
1677                                                         continue;
1678
1679                                                 path.bv_val = op->o_conn->c_peer_name.bv_val
1680                                                         + aci_bv[ ACI_BV_PATH_EQ ].bv_len;
1681                                                 path.bv_len = op->o_conn->c_peer_name.bv_len
1682                                                         - aci_bv[ ACI_BV_PATH_EQ ].bv_len;
1683
1684                                                 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1685                                                         continue;
1686
1687 #endif /* LDAP_PF_LOCAL */
1688
1689                                         /* exact match (very unlikely...) */
1690                                         } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1691                                                         continue;
1692                                         }
1693                                 }
1694                         }
1695                 }
1696
1697                 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
1698                         if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
1699                                 continue;
1700                         }
1701                         Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1702                                 b->a_sockname_pat.bv_val, 0, 0 );
1703                         if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1704                                 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1705                                         if (!regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1706                                                         e->e_ndn, nmatch, matches ) ) 
1707                                         {
1708                                                 continue;
1709                                         }
1710
1711                                 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1712                                         struct berval   bv;
1713                                         char buf[ACL_BUF_SIZE];
1714
1715                                         bv.bv_len = sizeof( buf ) - 1;
1716                                         bv.bv_val = buf;
1717                                         if ( acl_string_expand( &bv, &b->a_sockname_pat,
1718                                                         e->e_ndn, nmatch, matches ) )
1719                                         {
1720                                                 continue;
1721                                         }
1722
1723                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1724                                                 continue;
1725                                         }
1726
1727                                 } else {
1728                                         if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
1729                                                 continue;
1730                                         }
1731                                 }
1732                         }
1733                 }
1734
1735                 if ( b->a_dn_at != NULL ) {
1736                         if ( acl_mask_dnattr( op, e, val, a, b, i,
1737                                         matches, count, state,
1738                                         &b->a_dn, &op->o_ndn ) )
1739                         {
1740                                 continue;
1741                         }
1742                 }
1743
1744                 if ( b->a_realdn_at != NULL ) {
1745                         struct berval   ndn;
1746
1747                         if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1748                         {
1749                                 ndn = op->o_conn->c_ndn;
1750                         } else {
1751                                 ndn = op->o_ndn;
1752                         }
1753
1754                         if ( acl_mask_dnattr( op, e, val, a, b, i,
1755                                         matches, count, state,
1756                                         &b->a_realdn, &ndn ) )
1757                         {
1758                                 continue;
1759                         }
1760                 }
1761
1762                 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1763                         struct berval bv;
1764                         struct berval ndn = BER_BVNULL;
1765                         int rc;
1766
1767                         if ( op->o_ndn.bv_len == 0 ) {
1768                                 continue;
1769                         }
1770
1771                         Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
1772                                 b->a_group_pat.bv_val, 0, 0 );
1773
1774                         /* b->a_group is an unexpanded entry name, expanded it should be an 
1775                          * entry with objectclass group* and we test to see if odn is one of
1776                          * the values in the attribute group
1777                          */
1778                         /* see if asker is listed in dnattr */
1779                         if ( b->a_group_style == ACL_STYLE_EXPAND ) {
1780                                 char            buf[ACL_BUF_SIZE];
1781                                 int             tmp_nmatch;
1782                                 regmatch_t      tmp_matches[2],
1783                                                 *tmp_matchesp = tmp_matches;
1784
1785                                 bv.bv_len = sizeof(buf) - 1;
1786                                 bv.bv_val = buf;
1787
1788                                 rc = 0;
1789
1790                                 switch ( a->acl_dn_style ) {
1791                                 case ACL_STYLE_REGEX:
1792                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1793                                                 tmp_matchesp = matches;
1794                                                 tmp_nmatch = nmatch;
1795                                                 break;
1796                                         }
1797
1798                                 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1799                                 case ACL_STYLE_BASE:
1800                                         tmp_matches[0].rm_so = 0;
1801                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1802                                         tmp_nmatch = 1;
1803                                         break;
1804
1805                                 case ACL_STYLE_ONE:
1806                                 case ACL_STYLE_SUBTREE:
1807                                 case ACL_STYLE_CHILDREN:
1808                                         tmp_matches[0].rm_so = 0;
1809                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1810                                         tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1811                                         tmp_matches[1].rm_eo = e->e_nname.bv_len;
1812                                         tmp_nmatch = 2;
1813                                         break;
1814
1815                                 default:
1816                                         /* error */
1817                                         rc = 1;
1818                                         break;
1819                                 }
1820
1821                                 if ( rc ) {
1822                                         continue;
1823                                 }
1824                                 
1825                                 if ( acl_string_expand( &bv, &b->a_group_pat,
1826                                                 e->e_nname.bv_val,
1827                                                 tmp_nmatch, tmp_matchesp ) )
1828                                 {
1829                                         continue;
1830                                 }
1831
1832                                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
1833                                                 op->o_tmpmemctx ) != LDAP_SUCCESS )
1834                                 {
1835                                         /* did not expand to a valid dn */
1836                                         continue;
1837                                 }
1838
1839                                 bv = ndn;
1840
1841                         } else {
1842                                 bv = b->a_group_pat;
1843                         }
1844
1845                         rc = backend_group( op, e, &bv, &op->o_ndn,
1846                                 b->a_group_oc, b->a_group_at );
1847
1848                         if ( ndn.bv_val ) {
1849                                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
1850                         }
1851
1852                         if ( rc != 0 ) {
1853                                 continue;
1854                         }
1855                 }
1856
1857                 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1858                         struct berval   bv;
1859                         char            buf[ACL_BUF_SIZE];
1860
1861                         Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
1862                                 b->a_set_pat.bv_val, 0, 0 );
1863
1864                         if ( b->a_set_style == ACL_STYLE_EXPAND ) {
1865                                 int             tmp_nmatch;
1866                                 regmatch_t      tmp_matches[2],
1867                                                 *tmp_matchesp = tmp_matches;
1868                                 int             rc = 0;
1869
1870                                 bv.bv_len = sizeof( buf ) - 1;
1871                                 bv.bv_val = buf;
1872
1873                                 rc = 0;
1874
1875                                 switch ( a->acl_dn_style ) {
1876                                 case ACL_STYLE_REGEX:
1877                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1878                                                 tmp_matchesp = matches;
1879                                                 tmp_nmatch = nmatch;
1880                                                 break;
1881                                         }
1882
1883                                 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1884                                 case ACL_STYLE_BASE:
1885                                         tmp_matches[0].rm_so = 0;
1886                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1887                                         tmp_nmatch = 1;
1888                                         break;
1889
1890                                 case ACL_STYLE_ONE:
1891                                 case ACL_STYLE_SUBTREE:
1892                                 case ACL_STYLE_CHILDREN:
1893                                         tmp_matches[0].rm_so = 0;
1894                                         tmp_matches[0].rm_eo = e->e_nname.bv_len;
1895                                         tmp_matches[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1896                                         tmp_matches[1].rm_eo = e->e_nname.bv_len;
1897                                         tmp_nmatch = 2;
1898                                         break;
1899
1900                                 default:
1901                                         /* error */
1902                                         rc = 1;
1903                                         break;
1904                                 }
1905
1906                                 if ( rc ) {
1907                                         continue;
1908                                 }
1909                                 
1910                                 if ( acl_string_expand( &bv, &b->a_set_pat,
1911                                                 e->e_nname.bv_val,
1912                                                 tmp_nmatch, tmp_matchesp ) )
1913                                 {
1914                                         continue;
1915                                 }
1916
1917                         } else {
1918                                 bv = b->a_set_pat;
1919                         }
1920                         
1921                         if ( acl_match_set( &bv, op, e, 0 ) == 0 ) {
1922                                 continue;
1923                         }
1924                 }
1925
1926                 if ( b->a_authz.sai_ssf ) {
1927                         Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1928                                 b->a_authz.sai_ssf, op->o_ssf, 0 );
1929                         if ( b->a_authz.sai_ssf >  op->o_ssf ) {
1930                                 continue;
1931                         }
1932                 }
1933
1934                 if ( b->a_authz.sai_transport_ssf ) {
1935                         Debug( LDAP_DEBUG_ACL,
1936                                 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1937                                 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
1938                         if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
1939                                 continue;
1940                         }
1941                 }
1942
1943                 if ( b->a_authz.sai_tls_ssf ) {
1944                         Debug( LDAP_DEBUG_ACL,
1945                                 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1946                                 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
1947                         if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
1948                                 continue;
1949                         }
1950                 }
1951
1952                 if ( b->a_authz.sai_sasl_ssf ) {
1953                         Debug( LDAP_DEBUG_ACL,
1954                                 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1955                                 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
1956                         if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
1957                                 continue;
1958                         }
1959                 }
1960
1961 #ifdef SLAP_DYNACL
1962                 if ( b->a_dynacl ) {
1963                         slap_dynacl_t   *da;
1964                         slap_access_t   tgrant, tdeny;
1965
1966                         Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n",
1967                                 0, 0, 0 );
1968
1969                         /* this case works different from the others above.
1970                          * since aci's themselves give permissions, we need
1971                          * to first check b->a_access_mask, the ACL's access level.
1972                          */
1973                         if ( BER_BVISEMPTY( &e->e_nname ) ) {
1974                                 /* no ACIs in the root DSE */
1975                                 continue;
1976                         }
1977
1978                         /* first check if the right being requested
1979                          * is allowed by the ACL clause.
1980                          */
1981                         if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
1982                                 continue;
1983                         }
1984
1985                         /* start out with nothing granted, nothing denied */
1986                         ACL_INIT(tgrant);
1987                         ACL_INIT(tdeny);
1988
1989                         for ( da = b->a_dynacl; da; da = da->da_next ) {
1990                                 slap_access_t   grant,
1991                                                 deny;
1992
1993                                 ACL_INIT(grant);
1994                                 ACL_INIT(deny);
1995
1996                                 Debug( LDAP_DEBUG_ACL, "    <= check a_dynacl: %s\n",
1997                                         da->da_name, 0, 0 );
1998
1999                                 (void)( *da->da_mask )( da->da_private, op, e, desc, val, nmatch, matches, &grant, &deny );
2000
2001                                 tgrant |= grant;
2002                                 tdeny |= deny;
2003                         }
2004
2005                         /* remove anything that the ACL clause does not allow */
2006                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
2007                         tdeny &= ACL_PRIV_MASK;
2008
2009                         /* see if we have anything to contribute */
2010                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
2011                                 continue;
2012                         }
2013
2014                         /* this could be improved by changing slap_acl_mask so that it can deal with
2015                          * by clauses that return grant/deny pairs.  Right now, it does either
2016                          * additive or subtractive rights, but not both at the same time.  So,
2017                          * we need to combine the grant/deny pair into a single rights mask in
2018                          * a smart way:  if either grant or deny is "empty", then we use the
2019                          * opposite as is, otherwise we remove any denied rights from the grant
2020                          * rights mask and construct an additive mask.
2021                          */
2022                         if (ACL_IS_INVALID(tdeny)) {
2023                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
2024
2025                         } else if (ACL_IS_INVALID(tgrant)) {
2026                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
2027
2028                         } else {
2029                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
2030                         }
2031
2032                 } else
2033 #else /* !SLAP_DYNACL */
2034
2035 #ifdef SLAPD_ACI_ENABLED
2036                 if ( b->a_aci_at != NULL ) {
2037                         Attribute       *at;
2038                         slap_access_t   grant, deny, tgrant, tdeny;
2039                         struct berval   parent_ndn;
2040                         BerVarray       bvals = NULL;
2041                         int             ret, stop;
2042
2043                         Debug( LDAP_DEBUG_ACL, "    <= check a_aci_at: %s\n",
2044                                 b->a_aci_at->ad_cname.bv_val, 0, 0 );
2045
2046                         /* this case works different from the others above.
2047                          * since aci's themselves give permissions, we need
2048                          * to first check b->a_access_mask, the ACL's access level.
2049                          */
2050
2051                         if ( BER_BVISEMPTY( &e->e_nname ) ) {
2052                                 /* no ACIs in the root DSE */
2053                                 continue;
2054                         }
2055
2056                         /* first check if the right being requested
2057                          * is allowed by the ACL clause.
2058                          */
2059                         if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
2060                                 continue;
2061                         }
2062                         /* start out with nothing granted, nothing denied */
2063                         ACL_INIT(tgrant);
2064                         ACL_INIT(tdeny);
2065
2066                         /* get the aci attribute */
2067                         at = attr_find( e->e_attrs, b->a_aci_at );
2068                         if ( at != NULL ) {
2069 #if 0
2070                                 /* FIXME: this breaks acl caching;
2071                                  * see also ACL_RECORD_VALUE_STATE below */
2072                                 ACL_RECORD_VALUE_STATE;
2073 #endif
2074                                 /* the aci is an multi-valued attribute.  The
2075                                 * rights are determined by OR'ing the individual
2076                                 * rights given by the acis.
2077                                 */
2078                                 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
2079                                         if ( aci_mask( op,
2080                                                 e, desc, val,
2081                                                 &at->a_nvals[i],
2082                                                 nmatch, matches,
2083                                                 &grant, &deny, SLAP_ACI_SCOPE_ENTRY ) != 0 )
2084                                         {
2085                                                 tgrant |= grant;
2086                                                 tdeny |= deny;
2087                                         }
2088                                 }
2089                                 Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
2090                                           accessmask2str(tgrant, accessmaskbuf, 1), 
2091                                           accessmask2str(tdeny, accessmaskbuf1, 1), 0);
2092
2093                         }
2094                         /* If the entry level aci didn't contain anything valid for the 
2095                          * current operation, climb up the tree and evaluate the
2096                          * acis with scope set to subtree
2097                          */
2098                         if ( (tgrant == ACL_PRIV_NONE) && (tdeny == ACL_PRIV_NONE) ) {
2099                                 dnParent( &e->e_nname, &parent_ndn );
2100                                 while ( !BER_BVISEMPTY( &parent_ndn ) ) {
2101                                         Debug(LDAP_DEBUG_ACL, "checking ACI of %s\n", parent_ndn.bv_val, 0, 0);
2102                                         ret = backend_attribute(op, NULL, &parent_ndn, b->a_aci_at, &bvals, ACL_AUTH);
2103                                         switch(ret){
2104                                         case LDAP_SUCCESS :
2105                                                 stop = 0;
2106                                                 if (!bvals){
2107                                                         break;
2108                                                 }
2109
2110                                                 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
2111 #if 0
2112                                                         /* FIXME: this breaks acl caching;
2113                                                          * see also ACL_RECORD_VALUE_STATE above */
2114                                                         ACL_RECORD_VALUE_STATE;
2115 #endif
2116                                                         if ( aci_mask( op, e, desc, val, &bvals[i],
2117                                                                         nmatch, matches,
2118                                                                         &grant, &deny, SLAP_ACI_SCOPE_CHILDREN ) != 0 )
2119                                                         {
2120                                                                 tgrant |= grant;
2121                                                                 tdeny |= deny;
2122                                                                 /* evaluation stops as soon as either a "deny" or a 
2123                                                                  * "grant" directive matches.
2124                                                                  */
2125                                                                 if( (tgrant != ACL_PRIV_NONE) || (tdeny != ACL_PRIV_NONE) ){
2126                                                                         stop = 1;
2127                                                                 }
2128                                                         }
2129                                                         Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
2130                                                                 accessmask2str(tgrant, accessmaskbuf, 1),
2131                                                                 accessmask2str(tdeny, accessmaskbuf1, 1), 0);
2132                                                 }
2133                                                 break;
2134
2135                                         case LDAP_NO_SUCH_ATTRIBUTE:
2136                                                 /* just go on if the aci-Attribute is not present in
2137                                                  * the current entry 
2138                                                  */
2139                                                 Debug(LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0);
2140                                                 stop = 0;
2141                                                 break;
2142
2143                                         case LDAP_NO_SUCH_OBJECT:
2144                                                 /* We have reached the base object */
2145                                                 Debug(LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0);
2146                                                 stop = 1;
2147                                                 break;
2148
2149                                         default:
2150                                                 stop = 1;
2151                                                 break;
2152                                         }
2153                                         if (stop){
2154                                                 break;
2155                                         }
2156                                         dnParent( &parent_ndn, &parent_ndn );
2157                                 }
2158                         }
2159
2160
2161                         /* remove anything that the ACL clause does not allow */
2162                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
2163                         tdeny &= ACL_PRIV_MASK;
2164
2165                         /* see if we have anything to contribute */
2166                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
2167                                 continue;
2168                         }
2169
2170                         /* this could be improved by changing slap_acl_mask so that it can deal with
2171                          * by clauses that return grant/deny pairs.  Right now, it does either
2172                          * additive or subtractive rights, but not both at the same time.  So,
2173                          * we need to combine the grant/deny pair into a single rights mask in
2174                          * a smart way:  if either grant or deny is "empty", then we use the
2175                          * opposite as is, otherwise we remove any denied rights from the grant
2176                          * rights mask and construct an additive mask.
2177                          */
2178                         if (ACL_IS_INVALID(tdeny)) {
2179                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
2180
2181                         } else if (ACL_IS_INVALID(tgrant)) {
2182                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
2183
2184                         } else {
2185                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
2186                         }
2187
2188                 } else
2189 #endif /* SLAPD_ACI_ENABLED */
2190 #endif /* !SLAP_DYNACL */
2191                 {
2192                         modmask = b->a_access_mask;
2193                 }
2194
2195                 Debug( LDAP_DEBUG_ACL,
2196                         "<= acl_mask: [%d] applying %s (%s)\n",
2197                         i, accessmask2str( modmask, accessmaskbuf, 1 ), 
2198                         b->a_type == ACL_CONTINUE
2199                                 ? "continue"
2200                                 : b->a_type == ACL_BREAK
2201                                         ? "break"
2202                                         : "stop" );
2203                 /* save old mask */
2204                 oldmask = *mask;
2205
2206                 if( ACL_IS_ADDITIVE(modmask) ) {
2207                         /* add privs */
2208                         ACL_PRIV_SET( *mask, modmask );
2209
2210                         /* cleanup */
2211                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
2212
2213                 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
2214                         /* substract privs */
2215                         ACL_PRIV_CLR( *mask, modmask );
2216
2217                         /* cleanup */
2218                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
2219
2220                 } else {
2221                         /* assign privs */
2222                         *mask = modmask;
2223                 }
2224
2225                 a2pmask = *mask;
2226
2227                 Debug( LDAP_DEBUG_ACL,
2228                         "<= acl_mask: [%d] mask: %s\n",
2229                         i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
2230
2231                 if( b->a_type == ACL_CONTINUE ) {
2232                         continue;
2233
2234                 } else if ( b->a_type == ACL_BREAK ) {
2235                         return ACL_BREAK;
2236
2237                 } else {
2238                         return ACL_STOP;
2239                 }
2240         }
2241
2242         /* implicit "by * none" clause */
2243         ACL_INIT(*mask);
2244
2245         Debug( LDAP_DEBUG_ACL,
2246                 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
2247                 accessmask2str(*mask, accessmaskbuf, 1), 0, 0 );
2248         return ACL_STOP;
2249 }
2250
2251 /*
2252  * acl_check_modlist - check access control on the given entry to see if
2253  * it allows the given modifications by the user associated with op.
2254  * returns      1       if mods allowed ok
2255  *              0       mods not allowed
2256  */
2257
2258 int
2259 acl_check_modlist(
2260         Operation       *op,
2261         Entry   *e,
2262         Modifications   *mlist )
2263 {
2264         struct berval *bv;
2265         AccessControlState state = ACL_STATE_INIT;
2266         Backend *be;
2267         int be_null = 0;
2268         int ret = 1; /* default is access allowed */
2269
2270         be = op->o_bd;
2271         if ( be == NULL ) {
2272                 be = LDAP_STAILQ_FIRST(&backendDB);
2273                 be_null = 1;
2274                 op->o_bd = be;
2275         }
2276         assert( be != NULL );
2277
2278         /* short circuit root database access */
2279         if ( be_isroot( op ) ) {
2280                 Debug( LDAP_DEBUG_ACL,
2281                         "<= acl_access_allowed: granted to database root\n",
2282                     0, 0, 0 );
2283                 goto done;
2284         }
2285
2286         /* use backend default access if no backend acls */
2287         if( op->o_bd != NULL && op->o_bd->be_acl == NULL ) {
2288                 Debug( LDAP_DEBUG_ACL,
2289                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
2290                         access2str( ACL_WRITE ),
2291                         op->o_bd->be_dfltaccess >= ACL_WRITE
2292                                 ? "granted" : "denied",
2293                         op->o_dn.bv_val );
2294                 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
2295                 goto done;
2296         }
2297
2298         for ( ; mlist != NULL; mlist = mlist->sml_next ) {
2299                 /*
2300                  * Internal mods are ignored by ACL_WRITE checking
2301                  */
2302                 if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
2303                         Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
2304                                 " modify access granted\n",
2305                                 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
2306                         continue;
2307                 }
2308
2309                 /*
2310                  * no-user-modification operational attributes are ignored
2311                  * by ACL_WRITE checking as any found here are not provided
2312                  * by the user
2313                  */
2314                 if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
2315                                 && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
2316                 {
2317                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
2318                                 " modify access granted\n",
2319                                 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
2320                         continue;
2321                 }
2322
2323                 switch ( mlist->sml_op ) {
2324                 case LDAP_MOD_REPLACE:
2325                         /*
2326                          * We must check both permission to delete the whole
2327                          * attribute and permission to add the specific attributes.
2328                          * This prevents abuse from selfwriters.
2329                          */
2330                         if ( ! access_allowed( op, e,
2331                                 mlist->sml_desc, NULL,
2332                                 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2333                                 &state ) )
2334                         {
2335                                 ret = 0;
2336                                 goto done;
2337                         }
2338
2339                         if ( mlist->sml_values == NULL ) break;
2340
2341                         /* fall thru to check value to add */
2342
2343                 case LDAP_MOD_ADD:
2344                         assert( mlist->sml_values != NULL );
2345
2346                         for ( bv = mlist->sml_nvalues
2347                                         ? mlist->sml_nvalues : mlist->sml_values;
2348                                 bv->bv_val != NULL; bv++ )
2349                         {
2350                                 if ( ! access_allowed( op, e,
2351                                         mlist->sml_desc, bv,
2352                                         ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
2353                                         &state ) )
2354                                 {
2355                                         ret = 0;
2356                                         goto done;
2357                                 }
2358                         }
2359                         break;
2360
2361                 case LDAP_MOD_DELETE:
2362                         if ( mlist->sml_values == NULL ) {
2363                                 if ( ! access_allowed( op, e,
2364                                         mlist->sml_desc, NULL,
2365                                         ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2366                                         NULL ) )
2367                                 {
2368                                         ret = 0;
2369                                         goto done;
2370                                 }
2371                                 break;
2372                         }
2373                         for ( bv = mlist->sml_nvalues
2374                                         ? mlist->sml_nvalues : mlist->sml_values;
2375                                 bv->bv_val != NULL; bv++ )
2376                         {
2377                                 if ( ! access_allowed( op, e,
2378                                         mlist->sml_desc, bv,
2379                                         ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2380                                         &state ) )
2381                                 {
2382                                         ret = 0;
2383                                         goto done;
2384                                 }
2385                         }
2386                         break;
2387
2388                 case SLAP_MOD_SOFTADD:
2389                         /* allow adding attribute via modrdn thru */
2390                         break;
2391
2392                 default:
2393                         assert( 0 );
2394                         /* not reached */
2395                         ret = 0;
2396                         break;
2397                 }
2398         }
2399
2400 done:
2401         if (be_null) op->o_bd = NULL;
2402         return( ret );
2403 }
2404
2405 int
2406 acl_get_part(
2407         struct berval   *list,
2408         int             ix,
2409         char            sep,
2410         struct berval   *bv )
2411 {
2412         int     len;
2413         char    *p;
2414
2415         if ( bv ) {
2416                 BER_BVZERO( bv );
2417         }
2418         len = list->bv_len;
2419         p = list->bv_val;
2420         while ( len >= 0 && --ix >= 0 ) {
2421                 while ( --len >= 0 && *p++ != sep )
2422                         ;
2423         }
2424         while ( len >= 0 && *p == ' ' ) {
2425                 len--;
2426                 p++;
2427         }
2428         if ( len < 0 ) {
2429                 return -1;
2430         }
2431
2432         if ( !bv ) {
2433                 return 0;
2434         }
2435
2436         bv->bv_val = p;
2437         while ( --len >= 0 && *p != sep ) {
2438                 bv->bv_len++;
2439                 p++;
2440         }
2441         while ( bv->bv_len > 0 && *--p == ' ' ) {
2442                 bv->bv_len--;
2443         }
2444         
2445         return bv->bv_len;
2446 }
2447
2448 typedef struct acl_set_gather_t {
2449         SetCookie               *cookie;
2450         BerVarray               bvals;
2451 } acl_set_gather_t;
2452
2453 static int
2454 acl_set_cb_gather( Operation *op, SlapReply *rs )
2455 {
2456         acl_set_gather_t        *p = (acl_set_gather_t *)op->o_callback->sc_private;
2457         
2458         if ( rs->sr_type == REP_SEARCH ) {
2459                 BerValue        bvals[ 2 ];
2460                 BerVarray       bvalsp = NULL;
2461                 int             j;
2462
2463                 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2464                         AttributeDescription    *desc = rs->sr_attrs[ j ].an_desc;
2465                         
2466                         if ( desc == slap_schema.si_ad_entryDN ) {
2467                                 bvalsp = bvals;
2468                                 bvals[ 0 ] = rs->sr_entry->e_nname;
2469                                 BER_BVZERO( &bvals[ 1 ] );
2470
2471                         } else {
2472                                 Attribute       *a;
2473
2474                                 a = attr_find( rs->sr_entry->e_attrs, desc );
2475                                 if ( a != NULL ) {
2476                                         int     i;
2477
2478                                         for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ )
2479                                                 ;
2480
2481                                         bvalsp = a->a_nvals;
2482                                 }
2483                         }
2484                 }
2485
2486                 if ( bvals ) {
2487                         p->bvals = slap_set_join( p->cookie, p->bvals,
2488                                         ( '|' | SLAP_SET_RREF ), bvalsp );
2489                 }
2490
2491         } else {
2492                 assert( rs->sr_type == REP_RESULT );
2493         }
2494
2495         return 0;
2496 }
2497
2498 BerVarray
2499 acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2500 {
2501         AclSetCookie            *cp = (AclSetCookie *)cookie;
2502         int                     rc = 0;
2503         LDAPURLDesc             *ludp = NULL;
2504         Operation               op2 = { 0 };
2505         SlapReply               rs = {REP_RESULT};
2506         AttributeName           anlist[ 2 ], *anlistp = NULL;
2507         int                     nattrs = 0;
2508         slap_callback           cb = { NULL, acl_set_cb_gather, NULL, NULL };
2509         acl_set_gather_t        p = { 0 };
2510         const char              *text = NULL;
2511         static struct berval    defaultFilter_bv = BER_BVC( "(objectClass=*)" );
2512
2513         /* this routine needs to return the bervals instead of
2514          * plain strings, since syntax is not known.  It should
2515          * also return the syntax or some "comparison cookie".
2516          */
2517         if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2518                 return acl_set_gather2( cookie, name, desc );
2519         }
2520
2521         rc = ldap_url_parse( name->bv_val, &ludp );
2522         if ( rc != LDAP_URL_SUCCESS ) {
2523                 rc = LDAP_PROTOCOL_ERROR;
2524                 goto url_done;
2525         }
2526         
2527         if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2528         {
2529                 /* host part must be empty */
2530                 /* extensions parts must be empty */
2531                 rc = LDAP_PROTOCOL_ERROR;
2532                 goto url_done;
2533         }
2534
2535         /* Grab the searchbase and see if an appropriate database can be found */
2536         ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2537         rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2538                         &op2.o_req_ndn, cp->op->o_tmpmemctx );
2539         BER_BVZERO( &op2.o_req_dn );
2540         if ( rc != LDAP_SUCCESS ) {
2541                 goto url_done;
2542         }
2543
2544         op2.o_bd = select_backend( &op2.o_req_ndn, 0, 1 );
2545         if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2546                 rc = LDAP_NO_SUCH_OBJECT;
2547                 goto url_done;
2548         }
2549
2550         /* Grab the filter */
2551         if ( ludp->lud_filter ) {
2552                 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2553                                 cp->op->o_tmpmemctx );
2554                 
2555         } else {
2556                 op2.ors_filterstr = defaultFilter_bv;
2557         }
2558
2559         op2.ors_filter = str2filter_x( cp->op, op2.ors_filterstr.bv_val );
2560         if ( op2.ors_filter == NULL ) {
2561                 rc = LDAP_PROTOCOL_ERROR;
2562                 goto url_done;
2563         }
2564
2565         /* Grab the scope */
2566         op2.ors_scope = ludp->lud_scope;
2567
2568         /* Grap the attributes */
2569         if ( ludp->lud_attrs ) {
2570                 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2571                         ;
2572
2573                 anlistp = slap_sl_malloc( sizeof( AttributeName ) * ( nattrs + 2 ),
2574                                 cp->op->o_tmpmemctx );
2575
2576                 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ ) {
2577                         ber_str2bv( ludp->lud_attrs[ nattrs ], 0, 0, &anlistp[ nattrs ].an_name );
2578                         anlistp[ nattrs ].an_desc = NULL;
2579                         rc = slap_bv2ad( &anlistp[ nattrs ].an_name,
2580                                         &anlistp[ nattrs ].an_desc, &text );
2581                         if ( rc != LDAP_SUCCESS ) {
2582                                 goto url_done;
2583                         }
2584                 }
2585
2586         } else {
2587                 anlistp = anlist;
2588         }
2589
2590         anlistp[ nattrs ].an_name = desc->ad_cname;
2591         anlistp[ nattrs ].an_desc = desc;
2592
2593         BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2594         
2595         p.cookie = cookie;
2596         
2597         op2.o_hdr = cp->op->o_hdr;
2598         op2.o_tag = LDAP_REQ_SEARCH;
2599         op2.o_ndn = op2.o_bd->be_rootndn;
2600         op2.o_callback = &cb;
2601         op2.o_time = slap_get_time();
2602         op2.o_do_not_cache = 1;
2603         op2.o_is_auth_check = 0;
2604         ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->op->o_tmpmemctx );
2605         op2.ors_slimit = SLAP_NO_LIMIT;
2606         op2.ors_tlimit = SLAP_NO_LIMIT;
2607         op2.ors_attrs = anlistp;
2608         op2.ors_attrsonly = 0;
2609         op2.o_private = cp->op->o_private;
2610
2611         cb.sc_private = &p;
2612
2613         rc = op2.o_bd->be_search( &op2, &rs );
2614         if ( rc != 0 ) {
2615                 goto url_done;
2616         }
2617
2618 url_done:;
2619         if ( op2.ors_filter ) {
2620                 filter_free_x( cp->op, op2.ors_filter );
2621         }
2622         if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2623                 slap_sl_free( op2.o_req_ndn.bv_val, cp->op->o_tmpmemctx );
2624         }
2625         if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2626                 slap_sl_free( op2.o_req_dn.bv_val, cp->op->o_tmpmemctx );
2627         }
2628         if ( ludp ) {
2629                 ldap_free_urldesc( ludp );
2630         }
2631         if ( anlistp && anlistp != anlist ) {
2632                 slap_sl_free( anlistp, cp->op->o_tmpmemctx );
2633         }
2634
2635         return p.bvals;
2636 }
2637
2638 BerVarray
2639 acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2640 {
2641         AclSetCookie    *cp = (AclSetCookie *)cookie;
2642         BerVarray       bvals = NULL;
2643         struct berval   ndn;
2644         int             rc = 0;
2645
2646         /* this routine needs to return the bervals instead of
2647          * plain strings, since syntax is not known.  It should
2648          * also return the syntax or some "comparison cookie".
2649          */
2650         rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->op->o_tmpmemctx );
2651         if ( rc == LDAP_SUCCESS ) {
2652                 if ( desc == slap_schema.si_ad_entryDN ) {
2653                         bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2654                                         cp->op->o_tmpmemctx );
2655                         bvals[ 0 ] = ndn;
2656                         BER_BVZERO( &bvals[ 1 ] );
2657                         BER_BVZERO( &ndn );
2658
2659                 } else {
2660                         backend_attribute( cp->op,
2661                                 cp->e, &ndn, desc, &bvals, ACL_NONE );
2662                 }
2663
2664                 if ( !BER_BVISNULL( &ndn ) ) {
2665                         slap_sl_free( ndn.bv_val, cp->op->o_tmpmemctx );
2666                 }
2667         }
2668
2669         return bvals;
2670 }
2671
2672 int
2673 acl_match_set (
2674         struct berval *subj,
2675         Operation *op,
2676         Entry *e,
2677         int setref )
2678 {
2679         struct berval   set = BER_BVNULL;
2680         int             rc = 0;
2681         AclSetCookie    cookie;
2682
2683         if ( setref == 0 ) {
2684                 ber_dupbv_x( &set, subj, op->o_tmpmemctx );
2685
2686         } else {
2687                 struct berval           subjdn, ndn = BER_BVNULL;
2688                 struct berval           setat;
2689                 BerVarray               bvals;
2690                 const char              *text;
2691                 AttributeDescription    *desc = NULL;
2692
2693                 /* format of string is "entry/setAttrName" */
2694                 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2695                         return 0;
2696                 }
2697
2698                 if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
2699                         setat = aci_bv[ ACI_BV_SET_ATTR ];
2700                 }
2701
2702                 /*
2703                  * NOTE: dnNormalize honors the ber_len field
2704                  * as the length of the dn to be normalized
2705                  */
2706                 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2707                         if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2708                         {
2709                                 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2710                                 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2711                                         int     i;
2712
2713                                         set = bvals[0];
2714                                         BER_BVZERO( &bvals[0] );
2715                                         for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2716                                                 /* count */ ;
2717                                         bvals[0].bv_val = bvals[i-1].bv_val;
2718                                         BER_BVZERO( &bvals[i-1] );
2719                                 }
2720                                 ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2721                                 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2722                         }
2723                 }
2724         }
2725
2726         if ( !BER_BVISNULL( &set ) ) {
2727                 cookie.op = op;
2728                 cookie.e = e;
2729                 rc = ( slap_set_filter( acl_set_gather, (SetCookie *)&cookie, &set,
2730                         &op->o_ndn, &e->e_nname, NULL ) > 0 );
2731                 slap_sl_free( set.bv_val, op->o_tmpmemctx );
2732         }
2733
2734         return(rc);
2735 }
2736
2737 #ifdef SLAP_DYNACL
2738
2739 /*
2740  * dynamic ACL infrastructure
2741  */
2742 static slap_dynacl_t    *da_list = NULL;
2743
2744 int
2745 slap_dynacl_register( slap_dynacl_t *da )
2746 {
2747         slap_dynacl_t   *tmp;
2748
2749         for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
2750                 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
2751                         break;
2752                 }
2753         }
2754
2755         if ( tmp != NULL ) {
2756                 return -1;
2757         }
2758         
2759         if ( da->da_mask == NULL ) {
2760                 return -1;
2761         }
2762         
2763         da->da_private = NULL;
2764         da->da_next = da_list;
2765         da_list = da;
2766
2767         return 0;
2768 }
2769
2770 static slap_dynacl_t *
2771 slap_dynacl_next( slap_dynacl_t *da )
2772 {
2773         if ( da ) {
2774                 return da->da_next;
2775         }
2776         return da_list;
2777 }
2778
2779 slap_dynacl_t *
2780 slap_dynacl_get( const char *name )
2781 {
2782         slap_dynacl_t   *da;
2783
2784         for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
2785                 if ( strcasecmp( da->da_name, name ) == 0 ) {
2786                         break;
2787                 }
2788         }
2789
2790         return da;
2791 }
2792 #endif /* SLAP_DYNACL */
2793
2794 int
2795 acl_init( void )
2796 {
2797 #ifdef SLAP_DYNACL
2798         int     rc;
2799
2800 #ifdef SLAPD_ACI_ENABLED
2801         rc = dynacl_aci_init();
2802         if ( rc != 0 ) {
2803                 return rc;
2804         }
2805 #endif /* SLAPD_ACI_ENABLED */
2806
2807 #endif /* SLAP_DYNACL */
2808
2809         return 0;
2810 }
2811
2812 int
2813 acl_string_expand(
2814         struct berval   *bv,
2815         struct berval   *pat,
2816         char            *match,
2817         int             nmatch,
2818         regmatch_t      *matches)
2819 {
2820         ber_len_t       size;
2821         char   *sp;
2822         char   *dp;
2823         int     flag;
2824
2825         size = 0;
2826         bv->bv_val[0] = '\0';
2827         bv->bv_len--; /* leave space for lone $ */
2828
2829         flag = 0;
2830         for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2831                 sp < pat->bv_val + pat->bv_len ; sp++ )
2832         {
2833                 /* did we previously see a $ */
2834                 if ( flag ) {
2835                         if ( flag == 1 && *sp == '$' ) {
2836                                 *dp++ = '$';
2837                                 size++;
2838                                 flag = 0;
2839
2840                         } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2841                                 flag = 2;
2842
2843                         } else if ( *sp >= '0' && *sp <= '9' ) {
2844                                 int     n;
2845                                 int     i;
2846                                 int     l;
2847
2848                                 n = *sp - '0';
2849
2850                                 if ( flag == 2 ) {
2851                                         for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2852                                                 if ( *sp >= '0' && *sp <= '9' ) {
2853                                                         n = 10*n + ( *sp - '0' );
2854                                                 }
2855                                         }
2856
2857                                         if ( *sp != /*'{'*/ '}' ) {
2858                                                 /* FIXME: error */
2859                                                 return 1;
2860                                         }
2861                                 }
2862
2863                                 if ( n >= nmatch ) {
2864                                         /* FIXME: error */
2865                                         return 1;
2866                                 }
2867                                 
2868                                 *dp = '\0';
2869                                 i = matches[n].rm_so;
2870                                 l = matches[n].rm_eo; 
2871                                 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2872                                         *dp++ = match[i];
2873                                 }
2874                                 *dp = '\0';
2875
2876                                 flag = 0;
2877                         }
2878                 } else {
2879                         if (*sp == '$') {
2880                                 flag = 1;
2881                         } else {
2882                                 *dp++ = *sp;
2883                                 size++;
2884                         }
2885                 }
2886         }
2887
2888         if ( flag ) {
2889                 /* must have ended with a single $ */
2890                 *dp++ = '$';
2891                 size++;
2892         }
2893
2894         *dp = '\0';
2895         bv->bv_len = size;
2896
2897         Debug( LDAP_DEBUG_TRACE, "=> acl_string_expand: pattern:  %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
2898         Debug( LDAP_DEBUG_TRACE, "=> acl_string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
2899
2900         return 0;
2901 }
2902
2903 static int
2904 regex_matches(
2905         struct berval   *pat,           /* pattern to expand and match against */
2906         char            *str,           /* string to match against pattern */
2907         char            *buf,           /* buffer with $N expansion variables */
2908         int             nmatch, /* size of the matches array */
2909         regmatch_t      *matches        /* offsets in buffer for $N expansion variables */
2910 )
2911 {
2912         regex_t re;
2913         char newbuf[ACL_BUF_SIZE];
2914         struct berval bv;
2915         int     rc;
2916
2917         bv.bv_len = sizeof( newbuf ) - 1;
2918         bv.bv_val = newbuf;
2919
2920         if (str == NULL) {
2921                 str = "";
2922         };
2923
2924         acl_string_expand( &bv, pat, buf, nmatch, matches );
2925         rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
2926         if ( rc ) {
2927                 char error[ACL_BUF_SIZE];
2928                 regerror( rc, &re, error, sizeof( error ) );
2929
2930                 Debug( LDAP_DEBUG_TRACE,
2931                     "compile( \"%s\", \"%s\") failed %s\n",
2932                         pat->bv_val, str, error );
2933                 return( 0 );
2934         }
2935
2936         rc = regexec( &re, str, 0, NULL, 0 );
2937         regfree( &re );
2938
2939         Debug( LDAP_DEBUG_TRACE,
2940             "=> regex_matches: string:   %s\n", str, 0, 0 );
2941         Debug( LDAP_DEBUG_TRACE,
2942             "=> regex_matches: rc: %d %s\n",
2943                 rc, !rc ? "matches" : "no matches", 0 );
2944         return( !rc );
2945 }
2946