]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
58d4e7db8682e1feaaa062ef77c75f466d0c1b7f
[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-2004 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
39 #ifdef LDAP_SLAPI
40 #include "slapi/slapi.h"
41 #endif /* LDAPI_SLAPI */
42
43 #define ACL_BUF_SIZE    1024    /* use most appropriate size */
44
45 /*
46  * speed up compares
47  */
48 static struct berval 
49         aci_bv_entry            = BER_BVC("entry"),
50         aci_bv_children         = BER_BVC("children"),
51         aci_bv_br_entry         = BER_BVC("[entry]"),
52         aci_bv_br_all           = BER_BVC("[all]"),
53         aci_bv_access_id        = BER_BVC("access-id"),
54         aci_bv_anonymous        = BER_BVC("anonymous"),
55         aci_bv_public           = BER_BVC("public"),
56         aci_bv_users            = BER_BVC("users"),
57         aci_bv_self             = BER_BVC("self"),
58         aci_bv_dnattr           = BER_BVC("dnattr"),
59         aci_bv_group            = BER_BVC("group"),
60         aci_bv_role             = BER_BVC("role"),
61         aci_bv_set              = BER_BVC("set"),
62         aci_bv_set_ref          = BER_BVC("set-ref"),
63         aci_bv_grant            = BER_BVC("grant"),
64         aci_bv_deny             = BER_BVC("deny"),
65
66         aci_bv_ip_eq            = BER_BVC("IP="),
67 #ifdef LDAP_PF_LOCAL
68         aci_bv_path_eq          = BER_BVC("PATH="),
69         aci_bv_dirsep           = BER_BVC(LDAP_DIRSEP),
70 #endif /* LDAP_PF_LOCAL */
71         
72         aci_bv_group_class      = BER_BVC(SLAPD_GROUP_CLASS),
73         aci_bv_group_attr       = BER_BVC(SLAPD_GROUP_ATTR),
74         aci_bv_role_class       = BER_BVC(SLAPD_ROLE_CLASS),
75         aci_bv_role_attr        = BER_BVC(SLAPD_ROLE_ATTR);
76
77
78 static AccessControl * acl_get(
79         AccessControl *ac, int *count,
80         Operation *op, Entry *e,
81         AttributeDescription *desc,
82         struct berval *val,
83         int nmatches, regmatch_t *matches,
84         AccessControlState *state );
85
86 static slap_control_t acl_mask(
87         AccessControl *ac, slap_mask_t *mask,
88         Operation *op, Entry *e,
89         AttributeDescription *desc,
90         struct berval *val,
91         regmatch_t *matches,
92         int count,
93         AccessControlState *state );
94
95 #ifdef SLAPD_ACI_ENABLED
96 static int aci_mask(
97         Operation *op, Entry *e,
98         AttributeDescription *desc,
99         struct berval *val,
100         struct berval *aci,
101         regmatch_t *matches,
102         slap_access_t *grant,
103         slap_access_t *deny,
104         struct berval *scope);
105 #endif
106
107 static int      regex_matches(
108         struct berval *pat, char *str, char *buf, regmatch_t *matches);
109 static void     string_expand(
110         struct berval *newbuf, struct berval *pattern,
111         char *match, regmatch_t *matches);
112
113 typedef struct AciSetCookie {
114         Operation *op;
115         Entry *e;
116 } AciSetCookie;
117
118 SLAP_SET_GATHER aci_set_gather;
119 static int aci_match_set ( struct berval *subj, Operation *op,
120     Entry *e, int setref );
121
122 /*
123  * access_allowed - check whether op->o_ndn is allowed the requested access
124  * to entry e, attribute attr, value val.  if val is null, access to
125  * the whole attribute is assumed (all values).
126  *
127  * This routine loops through all access controls and calls
128  * acl_mask() on each applicable access control.
129  * The loop exits when a definitive answer is reached or
130  * or no more controls remain.
131  *
132  * returns:
133  *              0       access denied
134  *              1       access granted
135  *
136  * Notes:
137  * - can be legally called with op == NULL
138  * - can be legally called with op->o_bd == NULL
139  */
140
141 int
142 access_allowed_mask(
143         Operation               *op,
144         Entry           *e,
145         AttributeDescription    *desc,
146         struct berval   *val,
147         slap_access_t   access,
148         AccessControlState *state,
149         slap_mask_t *maskp )
150 {
151         int                             ret = 1;
152         int                             count;
153         AccessControl                   *a = NULL;
154         Backend *be;
155         int     be_null = 0;
156
157 #ifdef LDAP_DEBUG
158         char accessmaskbuf[ACCESSMASK_MAXLEN];
159 #endif
160         slap_mask_t mask;
161         slap_control_t control;
162         const char *attr;
163         regmatch_t matches[MAXREMATCHES];
164         int        st_same_attr = 0;
165         static AccessControlState state_init = ACL_STATE_INIT;
166
167         assert( e != NULL );
168         assert( desc != NULL );
169         assert( access > ACL_NONE );
170         if ( maskp ) ACL_INVALIDATE( *maskp );
171
172         attr = desc->ad_cname.bv_val;
173
174         assert( attr != NULL );
175
176         if( op && op->o_is_auth_check &&
177                 ( access == ACL_SEARCH || access == ACL_READ ))
178         {
179                 access = ACL_AUTH;
180         }
181
182         if( state ) {
183                 if ( state->as_vd_ad==desc) {
184                         if ( state->as_recorded ) {
185                                 if( state->as_recorded & ACL_STATE_RECORDED_NV &&
186                                         val == NULL )
187                                 {
188                                         return state->as_result;
189                                 } else if ( state->as_recorded & ACL_STATE_RECORDED_VD &&
190                                         val != NULL && state->as_vd_acl == NULL )
191                                 {
192                                         return state->as_result;
193                                 }
194                         }
195                         st_same_attr = 1;
196                 } else {
197                         *state = state_init;
198                 }
199
200                 state->as_vd_ad=desc;
201         }
202
203 #ifdef NEW_LOGGING
204         LDAP_LOG( ACL, ENTRY, 
205                 "access_allowed: %s access to \"%s\" \"%s\" requested\n",
206                 access2str( access ), e->e_dn, attr );
207 #else
208         Debug( LDAP_DEBUG_ACL,
209                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
210             access2str( access ), e->e_dn, attr );
211 #endif
212
213         if ( op == NULL ) {
214                 /* no-op call */
215                 goto done;
216         }
217
218         be = op->o_bd;
219         if ( be == NULL ) {
220                 be = &backends[0];
221                 be_null = 1;
222                 op->o_bd = be;
223         }
224         assert( be != NULL );
225
226 #ifdef LDAP_SLAPI
227         if ( op->o_pb != NULL ) {
228                 ret = slapi_int_access_allowed( op, e, desc, val, access, state );
229                 if ( ret == 0 ) {
230                         /* ACL plugin denied access */
231                         goto done;
232                 }
233         }
234 #endif /* LDAP_SLAPI */
235
236         /* grant database root access */
237         if ( be != NULL && be_isroot( op ) ) {
238 #ifdef NEW_LOGGING
239                 LDAP_LOG( ACL, INFO, 
240                         "access_allowed: conn %lu root access granted\n", 
241                         op->o_connid, 0, 0 );
242 #else
243                 Debug( LDAP_DEBUG_ACL,
244                     "<= root access granted\n",
245                         0, 0, 0 );
246 #endif
247                 if ( maskp ) {
248                         mask = ACL_LVL_WRITE;
249                 }
250
251                 goto done;
252         }
253
254         /*
255          * no-user-modification operational attributes are ignored
256          * by ACL_WRITE checking as any found here are not provided
257          * by the user
258          */
259         if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
260                 && desc != slap_schema.si_ad_entry
261                 && desc != slap_schema.si_ad_children )
262         {
263 #ifdef NEW_LOGGING
264                 LDAP_LOG( ACL, DETAIL1, 
265                         "access_allowed: conn %lu NoUserMod Operational attribute: %s "
266                         "access granted\n", op->o_connid, attr , 0 );
267 #else
268                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
269                         " %s access granted\n",
270                         attr, 0, 0 );
271 #endif
272                 goto done;
273         }
274
275         /* use backend default access if no backend acls */
276         if( be != NULL && be->be_acl == NULL ) {
277 #ifdef NEW_LOGGING
278                 LDAP_LOG( ACL, DETAIL1, 
279                         "access_allowed: backend default %s access %s to \"%s\"\n",
280                     access2str( access ),
281                     be->be_dfltaccess >= access ? "granted" : "denied", 
282                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
283 #else
284                 Debug( LDAP_DEBUG_ACL,
285                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
286                         access2str( access ),
287                         be->be_dfltaccess >= access ? "granted" : "denied",
288                         op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
289 #endif
290                 ret = be->be_dfltaccess >= access;
291
292                 if ( maskp ) {
293                         int     i;
294
295                         mask = ACL_PRIV_LEVEL;
296                         for ( i = ACL_NONE; i <= be->be_dfltaccess; i++ ) {
297                                 mask |= ACL_ACCESS2PRIV( i );
298                         }
299                 }
300
301                 goto done;
302
303 #ifdef notdef
304         /* be is always non-NULL */
305         /* use global default access if no global acls */
306         } else if ( be == NULL && global_acl == NULL ) {
307 #ifdef NEW_LOGGING
308                 LDAP_LOG( ACL, DETAIL1, 
309                         "access_allowed: global default %s access %s to \"%s\"\n",
310                     access2str( access ),
311                     global_default_access >= access ? "granted" : "denied", 
312                         op->o_dn.bv_val );
313 #else
314                 Debug( LDAP_DEBUG_ACL,
315                         "=> access_allowed: global default %s access %s to \"%s\"\n",
316                         access2str( access ),
317                         global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val );
318 #endif
319                 ret = global_default_access >= access;
320
321                 if ( maskp ) {
322                         int     i;
323
324                         mask = ACL_PRIV_LEVEL;
325                         for ( i = ACL_NONE; i <= global_default_access; i++ ) {
326                                 mask |= ACL_ACCESS2PRIV( i );
327                         }
328                 }
329
330                 goto done;
331 #endif
332         }
333
334         ret = 0;
335         control = ACL_BREAK;
336
337         if( st_same_attr ) {
338                 assert( state->as_vd_acl != NULL );
339
340                 a = state->as_vd_acl;
341                 count = state->as_vd_acl_count;
342                 if ( !ACL_IS_INVALID( state->as_vd_acl_mask )) {
343                         mask = state->as_vd_acl_mask;
344                         AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
345                         goto vd_access;
346                 }
347
348         } else {
349                 if ( state ) state->as_vi_acl = NULL;
350                 a = NULL;
351                 ACL_INIT(mask);
352                 count = 0;
353                 memset(matches, '\0', sizeof(matches));
354         }
355
356         while((a = acl_get( a, &count, op, e, desc, val,
357                 MAXREMATCHES, matches, state )) != NULL)
358         {
359                 int i;
360
361                 for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
362 #ifdef NEW_LOGGING
363                         LDAP_LOG( ACL, DETAIL1, 
364                                 "access_allowed: match[%d]:  %d %d ",
365                             i, (int)matches[i].rm_so, (int)matches[i].rm_eo );
366 #else
367                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
368                             (int)matches[i].rm_so, (int)matches[i].rm_eo );
369 #endif
370                         if( matches[i].rm_so <= matches[0].rm_eo ) {
371                                 int n;
372                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
373                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
374                                 }
375                         }
376 #ifdef NEW_LOGGING
377                         LDAP_LOG( ACL, ARGS, "\n" , 0, 0, 0 );
378 #else
379                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
380 #endif
381                 }
382
383                 if (state) {
384                         if (state->as_vi_acl == a && (state->as_recorded & ACL_STATE_RECORDED_NV)) {
385                                 Debug( LDAP_DEBUG_ACL, "access_allowed: result from state (%s)\n", attr, 0, 0 );
386                                 ret = state->as_result;
387                                 goto done;
388                         } else {
389                                 Debug( LDAP_DEBUG_ACL, "access_allowed: no res from state (%s)\n", attr, 0, 0);
390                         }
391                 }
392
393 vd_access:
394                 control = acl_mask( a, &mask, op,
395                         e, desc, val, matches, count, state );
396
397                 if ( control != ACL_BREAK ) {
398                         break;
399                 }
400
401                 memset(matches, '\0', sizeof(matches));
402         }
403
404         if ( ACL_IS_INVALID( mask ) ) {
405 #ifdef NEW_LOGGING
406                 LDAP_LOG( ACL, DETAIL1, 
407                         "access_allowed: conn %lu \"%s\" (%s) invalid!\n",
408                     op->o_connid, e->e_dn, attr );
409 #else
410                 Debug( LDAP_DEBUG_ACL,
411                         "=> access_allowed: \"%s\" (%s) invalid!\n",
412                         e->e_dn, attr, 0 );
413 #endif
414                 ACL_INIT(mask);
415
416         } else if ( control == ACL_BREAK ) {
417 #ifdef NEW_LOGGING
418                 LDAP_LOG( ACL, DETAIL1, 
419                         "access_allowed: conn %lu        no more rules\n", op->o_connid, 0,0 );
420 #else
421                 Debug( LDAP_DEBUG_ACL,
422                         "=> access_allowed: no more rules\n", 0, 0, 0);
423 #endif
424
425                 goto done;
426         }
427
428 #ifdef NEW_LOGGING
429         LDAP_LOG( ACL, ENTRY, 
430                 "access_allowed: %s access %s by %s\n", 
431                 access2str( access ), ACL_GRANT( mask, access ) ? "granted" : "denied",
432                 accessmask2str( mask, accessmaskbuf ) );
433 #else
434         Debug( LDAP_DEBUG_ACL,
435                 "=> access_allowed: %s access %s by %s\n",
436                 access2str( access ),
437                 ACL_GRANT(mask, access) ? "granted" : "denied",
438                 accessmask2str( mask, accessmaskbuf ) );
439 #endif
440
441         ret = ACL_GRANT(mask, access);
442
443 done:
444         if( state != NULL ) {
445                 /* If not value-dependent, save ACL in case of more attrs */
446                 if ( !(state->as_recorded & ACL_STATE_RECORDED_VD) ) {
447                         state->as_vi_acl = a;
448                         state->as_result = ret;
449                 }
450                 state->as_recorded |= ACL_STATE_RECORDED;
451         }
452         if (be_null) op->o_bd = NULL;
453         if ( maskp ) *maskp = mask;
454         return ret;
455 }
456
457
458 /*
459  * acl_get - return the acl applicable to entry e, attribute
460  * attr.  the acl returned is suitable for use in subsequent calls to
461  * acl_access_allowed().
462  */
463
464 static AccessControl *
465 acl_get(
466         AccessControl *a,
467         int                     *count,
468         Operation       *op,
469         Entry           *e,
470         AttributeDescription *desc,
471         struct berval   *val,
472         int                     nmatch,
473         regmatch_t      *matches,
474         AccessControlState *state )
475 {
476         const char *attr;
477         int dnlen, patlen;
478         AccessControl *prev;
479
480         assert( e != NULL );
481         assert( count != NULL );
482         assert( desc != NULL );
483
484         attr = desc->ad_cname.bv_val;
485
486         assert( attr != NULL );
487
488         if( a == NULL ) {
489                 if( op->o_bd == NULL ) {
490                         a = global_acl;
491                 } else {
492                         a = op->o_bd->be_acl;
493                 }
494                 prev = NULL;
495
496                 assert( a != NULL );
497
498         } else {
499                 prev = a;
500                 a = a->acl_next;
501         }
502
503         dnlen = e->e_nname.bv_len;
504
505         for ( ; a != NULL; a = a->acl_next ) {
506                 (*count) ++;
507
508                 if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
509                         if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
510 #ifdef NEW_LOGGING
511                                 LDAP_LOG( ACL, DETAIL1, 
512                                         "acl_get: dnpat [%d] %s nsub: %d\n",
513                                         *count, a->acl_dn_pat.bv_val, 
514                                         (int) a->acl_dn_re.re_nsub );
515 #else
516                                 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
517                                         *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
518 #endif
519                                 if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
520                                         continue;
521
522                         } else {
523 #ifdef NEW_LOGGING
524                                 LDAP_LOG( ACL, DETAIL1, "acl_get: dn [%d] %s\n",
525                                            *count, a->acl_dn_pat.bv_val, 0 );
526 #else
527                                 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
528                                         *count, a->acl_dn_pat.bv_val, 0 );
529 #endif
530                                 patlen = a->acl_dn_pat.bv_len;
531                                 if ( dnlen < patlen )
532                                         continue;
533
534                                 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
535                                         /* base dn -- entire object DN must match */
536                                         if ( dnlen != patlen )
537                                                 continue;
538
539                                 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
540                                         int     rdnlen = -1, sep = 0;
541
542                                         if ( dnlen <= patlen )
543                                                 continue;
544
545                                         if ( patlen > 0 ) {
546                                                 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
547                                                         continue;
548                                                 sep = 1;
549                                         }
550
551                                         rdnlen = dn_rdnlen( NULL, &e->e_nname );
552                                         if ( rdnlen != dnlen - patlen - sep )
553                                                 continue;
554
555                                 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
556                                         if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
557                                                 continue;
558
559                                 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
560                                         if ( dnlen <= patlen )
561                                                 continue;
562                                         if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
563                                                 continue;
564                                 }
565
566                                 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
567                                         continue;
568                         }
569
570 #ifdef NEW_LOGGING
571                         LDAP_LOG( ACL, DETAIL1, 
572                                 "acl_get: [%d] matched\n", *count, 0, 0 );
573 #else
574                         Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
575                                 *count, 0, 0 );
576 #endif
577                 }
578
579                 if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
580                         matches[0].rm_so = matches[0].rm_eo = -1;
581                         continue;
582                 }
583
584                 /* Is this ACL only for a specific value? */
585                 if ( a->acl_attrval.bv_len ) {
586                         if ( val == NULL ) {
587                                 continue;
588                         }
589
590                         if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) {
591                                 state->as_recorded |= ACL_STATE_RECORDED_VD;
592                                 state->as_vd_acl = prev;
593                                 state->as_vd_acl_count = *count;
594                                 state->as_vd_access = a->acl_access;
595                                 state->as_vd_access_count = 1;
596                                 ACL_INVALIDATE( state->as_vd_acl_mask );
597                         }
598
599                         if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
600 #ifdef NEW_LOGGING
601                                 LDAP_LOG( ACL, DETAIL1, 
602                                         "acl_get: valpat %s\n",
603                                         a->acl_attrval.bv_val, 0, 0 );
604 #else
605                                 Debug( LDAP_DEBUG_ACL,
606                                         "acl_get: valpat %s\n",
607                                         a->acl_attrval.bv_val, 0, 0 );
608 #endif
609                                 if (regexec(&a->acl_attrval_re, val->bv_val, 0, NULL, 0))
610                                         continue;
611                         } else {
612                                 int match = 0;
613                                 const char *text;
614 #ifdef NEW_LOGGING
615                                 LDAP_LOG( ACL, DETAIL1, 
616                                         "acl_get: val %s\n",
617                                         a->acl_attrval.bv_val, 0, 0 );
618 #else
619                                 Debug( LDAP_DEBUG_ACL,
620                                         "acl_get: val %s\n",
621                                         a->acl_attrval.bv_val, 0, 0 );
622 #endif
623         
624                                 if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
625                                         if (value_match( &match, desc,
626                                                 desc->ad_type->sat_equality, 0,
627                                                 val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
628                                                         match )
629                                                 continue;
630                                         
631                                 } else {
632                                         int             patlen, vdnlen;
633         
634                                         patlen = a->acl_attrval.bv_len;
635                                         vdnlen = val->bv_len;
636         
637                                         if ( vdnlen < patlen )
638                                                 continue;
639         
640                                         if ( a->acl_dn_style == ACL_STYLE_BASE ) {
641                                                 if ( vdnlen > patlen )
642                                                         continue;
643         
644                                         } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
645                                                 int rdnlen = -1;
646         
647                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
648                                                         continue;
649         
650                                                 rdnlen = dn_rdnlen( NULL, val );
651                                                 if ( rdnlen != vdnlen - patlen - 1 )
652                                                         continue;
653         
654                                         } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
655                                                 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
656                                                         continue;
657         
658                                         } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
659                                                 if ( vdnlen <= patlen )
660                                                         continue;
661         
662                                                 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
663                                                         continue;
664                                         }
665         
666                                         if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ))
667                                                 continue;
668                                 }
669                         }
670                 }
671
672                 if ( a->acl_filter != NULL ) {
673                         ber_int_t rc = test_filter( NULL, e, a->acl_filter );
674                         if ( rc != LDAP_COMPARE_TRUE ) {
675                                 continue;
676                         }
677                 }
678
679 #ifdef NEW_LOGGING
680                 LDAP_LOG( ACL, DETAIL1, 
681                         "acl_get: [%d] attr %s\n", *count, attr ,0 );
682 #else
683                 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
684                        *count, attr, 0);
685 #endif
686                 return a;
687         }
688
689 #ifdef NEW_LOGGING
690         LDAP_LOG( ACL, RESULTS, "acl_get: done.\n", 0, 0, 0 );
691 #else
692         Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
693 #endif
694         return( NULL );
695 }
696
697 /*
698  * Record value-dependent access control state
699  */
700 #define ACL_RECORD_VALUE_STATE do { \
701                 if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
702                         state->as_recorded |= ACL_STATE_RECORDED_VD; \
703                         state->as_vd_acl = a; \
704                         AC_MEMCPY( state->as_vd_acl_matches, matches, \
705                                 sizeof( state->as_vd_acl_matches )) ; \
706                         state->as_vd_acl_count = count; \
707                         state->as_vd_access = b; \
708                         state->as_vd_access_count = i; \
709                 } \
710         } while( 0 )
711
712 /*
713  * acl_mask - modifies mask based upon the given acl and the
714  * requested access to entry e, attribute attr, value val.  if val
715  * is null, access to the whole attribute is assumed (all values).
716  *
717  * returns      0       access NOT allowed
718  *              1       access allowed
719  */
720
721 static slap_control_t
722 acl_mask(
723         AccessControl   *a,
724         slap_mask_t *mask,
725         Operation       *op,
726         Entry           *e,
727         AttributeDescription *desc,
728         struct berval   *val,
729         regmatch_t      *matches,
730         int     count,
731         AccessControlState *state )
732 {
733         int             i, odnlen, patlen;
734         Access  *b;
735 #ifdef LDAP_DEBUG
736         char accessmaskbuf[ACCESSMASK_MAXLEN];
737         char accessmaskbuf1[ACCESSMASK_MAXLEN];
738 #endif
739         const char *attr;
740
741         assert( a != NULL );
742         assert( mask != NULL );
743         assert( desc != NULL );
744
745         attr = desc->ad_cname.bv_val;
746
747         assert( attr != NULL );
748
749 #ifdef NEW_LOGGING
750         LDAP_LOG( ACL, ENTRY, 
751                 "acl_mask: conn %lu  access to entry \"%s\", attr \"%s\" requested\n",
752                 op->o_connid, e->e_dn, attr );
753
754         LDAP_LOG( ACL, ARGS, 
755                 " to %s by \"%s\", (%s) \n", val ? "value" : "all values",
756                 op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
757                 accessmask2str( *mask, accessmaskbuf ) );
758 #else
759         Debug( LDAP_DEBUG_ACL,
760                 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
761                 e->e_dn, attr, 0 );
762
763         Debug( LDAP_DEBUG_ACL,
764                 "=> acl_mask: to %s by \"%s\", (%s) \n",
765                 val ? "value" : "all values",
766                 op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
767                 accessmask2str( *mask, accessmaskbuf ) );
768 #endif
769
770
771         if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
772                 && state->as_vd_acl == a )
773         {
774                 b = state->as_vd_access;
775                 i = state->as_vd_access_count;
776
777         } else {
778                 b = a->acl_access;
779                 i = 1;
780         }
781
782         for ( ; b != NULL; b = b->a_next, i++ ) {
783                 slap_mask_t oldmask, modmask;
784
785                 ACL_INVALIDATE( modmask );
786
787                 /* AND <who> clauses */
788                 if ( b->a_dn_pat.bv_len != 0 ) {
789 #ifdef NEW_LOGGING
790                         LDAP_LOG( ACL, DETAIL1, 
791                                 "acl_mask: conn %lu  check a_dn_pat: %s\n",
792                                 op->o_connid, b->a_dn_pat.bv_val ,0 );
793 #else
794                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
795                                 b->a_dn_pat.bv_val, 0, 0);
796 #endif
797                         /*
798                          * if access applies to the entry itself, and the
799                          * user is bound as somebody in the same namespace as
800                          * the entry, OR the given dn matches the dn pattern
801                          */
802                         if ( bvmatch( &b->a_dn_pat, &aci_bv_anonymous ) ) {
803                                 if ( op->o_ndn.bv_len != 0 ) {
804                                         continue;
805                                 }
806
807                         } else if ( bvmatch( &b->a_dn_pat, &aci_bv_users ) ) {
808                                 if ( op->o_ndn.bv_len == 0 ) {
809                                         continue;
810                                 }
811
812                         } else if ( bvmatch( &b->a_dn_pat, &aci_bv_self ) ) {
813                                 if ( op->o_ndn.bv_len == 0 ) {
814                                         continue;
815                                 }
816                                 
817                                 if ( e->e_dn == NULL || !dn_match( &e->e_nname, &op->o_ndn ) ) {
818                                         continue;
819                                 }
820
821                         } else if ( b->a_dn_style == ACL_STYLE_REGEX ) {
822                                 if ( !ber_bvccmp( &b->a_dn_pat, '*' ) ) {
823                                         int ret = regex_matches( &b->a_dn_pat,
824                                                 op->o_ndn.bv_val, e->e_ndn, matches );
825
826                                         if( ret == 0 ) {
827                                                 continue;
828                                         }
829                                 }
830
831                         } else {
832                                 struct berval pat;
833                                 int got_match = 0;
834
835                                 if ( e->e_dn == NULL )
836                                         continue;
837
838                                 if ( b->a_dn_expand ) {
839                                         struct berval bv;
840                                         char buf[ACL_BUF_SIZE];
841
842                                         bv.bv_len = sizeof( buf ) - 1;
843                                         bv.bv_val = buf;
844
845                                         string_expand(&bv, &b->a_dn_pat, 
846                                                         e->e_ndn, matches);
847                                         if ( dnNormalize(0, NULL, NULL, &bv, &pat, op->o_tmpmemctx ) != LDAP_SUCCESS ) {
848                                                 /* did not expand to a valid dn */
849                                                 continue;
850                                         }
851                                 } else {
852                                         pat = b->a_dn_pat;
853                                 }
854
855                                 patlen = pat.bv_len;
856                                 odnlen = op->o_ndn.bv_len;
857                                 if ( odnlen < patlen ) {
858                                         goto dn_match_cleanup;
859
860                                 }
861
862                                 if ( b->a_dn_style == ACL_STYLE_BASE ) {
863                                         /* base dn -- entire object DN must match */
864                                         if ( odnlen != patlen ) {
865                                                 goto dn_match_cleanup;
866                                         }
867
868                                 } else if ( b->a_dn_style == ACL_STYLE_ONE ) {
869                                         int rdnlen = -1;
870
871                                         if ( odnlen <= patlen ) {
872                                                 goto dn_match_cleanup;
873                                         }
874
875                                         if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) ) {
876                                                 goto dn_match_cleanup;
877                                         }
878
879                                         rdnlen = dn_rdnlen( NULL, &op->o_ndn );
880                                         if ( rdnlen != odnlen - patlen - 1 ) {
881                                                 goto dn_match_cleanup;
882                                         }
883
884                                 } else if ( b->a_dn_style == ACL_STYLE_SUBTREE ) {
885                                         if ( odnlen > patlen && !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) ) {
886                                                 goto dn_match_cleanup;
887                                         }
888
889                                 } else if ( b->a_dn_style == ACL_STYLE_CHILDREN ) {
890                                         if ( odnlen <= patlen ) {
891                                                 goto dn_match_cleanup;
892                                         }
893
894                                         if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) ) {
895                                                 goto dn_match_cleanup;
896                                         }
897                                 }
898
899                                 got_match = !strcmp( pat.bv_val, op->o_ndn.bv_val + odnlen - patlen );
900
901 dn_match_cleanup:;
902                                 if ( pat.bv_val != b->a_dn_pat.bv_val ) {
903                                         free( pat.bv_val );
904                                 }
905
906                                 if ( !got_match ) {
907                                         continue;
908                                 }
909                         }
910                 }
911
912                 if ( b->a_sockurl_pat.bv_len ) {
913                         if ( ! op->o_conn->c_listener ) {
914                                 continue;
915                         }
916 #ifdef NEW_LOGGING
917                         LDAP_LOG( ACL, DETAIL1, 
918                                    "acl_mask: conn %lu  check a_sockurl_pat: %s\n",
919                                    op->o_connid, b->a_sockurl_pat.bv_val , 0 );
920 #else
921                         Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
922                                 b->a_sockurl_pat.bv_val, 0, 0 );
923 #endif
924
925                         if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
926                                 if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
927                                         if (!regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
928                                                         e->e_ndn, matches ) ) 
929                                         {
930                                                 continue;
931                                         }
932
933                                 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
934                                         struct berval   bv;
935                                         char buf[ACL_BUF_SIZE];
936
937                                         bv.bv_len = sizeof( buf ) - 1;
938                                         bv.bv_val = buf;
939                                         string_expand( &bv, &b->a_sockurl_pat, e->e_ndn, matches );
940
941                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 ) {
942                                                 continue;
943                                         }
944
945                                 } else {
946                                         if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
947                                                 continue;
948                                 }
949                         }
950                 }
951
952                 if ( b->a_domain_pat.bv_len ) {
953                         if ( !op->o_conn->c_peer_domain.bv_val ) {
954                                 continue;
955                         }
956 #ifdef NEW_LOGGING
957                         LDAP_LOG( ACL, DETAIL1, 
958                                    "acl_mask: conn %lu  check a_domain_pat: %s\n",
959                                    op->o_connid, b->a_domain_pat.bv_val , 0 );
960 #else
961                         Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
962                                 b->a_domain_pat.bv_val, 0, 0 );
963 #endif
964                         if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
965                                 if ( b->a_domain_style == ACL_STYLE_REGEX) {
966                                         if (!regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
967                                                         e->e_ndn, matches ) ) 
968                                         {
969                                                 continue;
970                                         }
971                                 } else {
972                                         char buf[ACL_BUF_SIZE];
973
974                                         struct berval   cmp = op->o_conn->c_peer_domain;
975                                         struct berval   pat = b->a_domain_pat;
976
977                                         if ( b->a_domain_expand ) {
978                                                 struct berval bv;
979
980                                                 bv.bv_len = sizeof(buf) - 1;
981                                                 bv.bv_val = buf;
982
983                                                 string_expand(&bv, &b->a_domain_pat, e->e_ndn, matches);
984                                                 pat = bv;
985                                         }
986
987                                         if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
988                                                 int offset = cmp.bv_len - pat.bv_len;
989                                                 if ( offset < 0 ) {
990                                                         continue;
991                                                 }
992
993                                                 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
994                                                         continue;
995                                                 }
996
997                                                 /* trim the domain */
998                                                 cmp.bv_val = &cmp.bv_val[ offset ];
999                                                 cmp.bv_len -= offset;
1000                                         }
1001                                         
1002                                         if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1003                                                 continue;
1004                                         }
1005                                 }
1006                         }
1007                 }
1008
1009                 if ( b->a_peername_pat.bv_len ) {
1010                         if ( !op->o_conn->c_peer_name.bv_val ) {
1011                                 continue;
1012                         }
1013 #ifdef NEW_LOGGING
1014                         LDAP_LOG( ACL, DETAIL1, 
1015                                    "acl_mask: conn %lu  check a_peername_path: %s\n",
1016                                    op->o_connid, b->a_peername_pat.bv_val , 0 );
1017 #else
1018                         Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1019                                 b->a_peername_pat.bv_val, 0, 0 );
1020 #endif
1021                         if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1022                                 if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1023                                         if (!regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1024                                                         e->e_ndn, matches ) ) 
1025                                         {
1026                                                 continue;
1027                                         }
1028
1029                                 } else {
1030                                         /* try exact match */
1031                                         if ( b->a_peername_style == ACL_STYLE_BASE ) {
1032                                                 if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
1033                                                         continue;
1034                                                 }
1035
1036                                         } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1037                                                 struct berval   bv;
1038                                                 char buf[ACL_BUF_SIZE];
1039
1040                                                 bv.bv_len = sizeof( buf ) - 1;
1041                                                 bv.bv_val = buf;
1042                                                 string_expand( &bv, &b->a_peername_pat, e->e_ndn, matches );
1043
1044                                                 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1045                                                         continue;
1046                                                 }
1047
1048                                         /* extract IP and try exact match */
1049                                         } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1050                                                 char            *port;
1051                                                 char            buf[] = "255.255.255.255";
1052                                                 struct berval   ip;
1053                                                 unsigned long   addr;
1054                                                 int             port_number = -1;
1055                                                 
1056                                                 if ( strncasecmp( op->o_conn->c_peer_name.bv_val, 
1057                                                                         aci_bv_ip_eq.bv_val, aci_bv_ip_eq.bv_len ) != 0 ) 
1058                                                         continue;
1059
1060                                                 ip.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_ip_eq.bv_len;
1061                                                 ip.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_ip_eq.bv_len;
1062
1063                                                 port = strrchr( ip.bv_val, ':' );
1064                                                 if ( port ) {
1065                                                         char    *next;
1066                                                         
1067                                                         ip.bv_len = port - ip.bv_val;
1068                                                         ++port;
1069                                                         port_number = strtol( port, &next, 10 );
1070                                                         if ( next[0] != '\0' )
1071                                                                 continue;
1072                                                 }
1073                                                 
1074                                                 /* the port check can be anticipated here */
1075                                                 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1076                                                         continue;
1077                                                 
1078                                                 /* address longer than expected? */
1079                                                 if ( ip.bv_len >= sizeof(buf) )
1080                                                         continue;
1081
1082                                                 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1083                                                 buf[ ip.bv_len ] = '\0';
1084
1085                                                 addr = inet_addr( buf );
1086
1087                                                 /* unable to convert? */
1088                                                 if ( addr == (unsigned long)(-1) )
1089                                                         continue;
1090
1091                                                 if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1092                                                         continue;
1093
1094 #ifdef LDAP_PF_LOCAL
1095                                         /* extract path and try exact match */
1096                                         } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
1097                                                 struct berval path;
1098                                                 
1099                                                 if ( strncmp( op->o_conn->c_peer_name.bv_val,
1100                                                                         aci_bv_path_eq.bv_val, aci_bv_path_eq.bv_len ) != 0 )
1101                                                         continue;
1102
1103                                                 path.bv_val = op->o_conn->c_peer_name.bv_val + aci_bv_path_eq.bv_len;
1104                                                 path.bv_len = op->o_conn->c_peer_name.bv_len - aci_bv_path_eq.bv_len;
1105
1106                                                 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1107                                                         continue;
1108
1109 #endif /* LDAP_PF_LOCAL */
1110
1111                                         /* exact match (very unlikely...) */
1112                                         } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1113                                                         continue;
1114                                         }
1115                                 }
1116                         }
1117                 }
1118
1119                 if ( b->a_sockname_pat.bv_len ) {
1120                         if ( !op->o_conn->c_sock_name.bv_val ) {
1121                                 continue;
1122                         }
1123 #ifdef NEW_LOGGING
1124                         LDAP_LOG( ACL, DETAIL1, 
1125                                    "acl_mask: conn %lu  check a_sockname_path: %s\n",
1126                                    op->o_connid, b->a_sockname_pat.bv_val , 0 );
1127 #else
1128                         Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1129                                 b->a_sockname_pat.bv_val, 0, 0 );
1130 #endif
1131                         if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1132                                 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1133                                         if (!regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1134                                                         e->e_ndn, matches ) ) 
1135                                         {
1136                                                 continue;
1137                                         }
1138
1139                                 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1140                                         struct berval   bv;
1141                                         char buf[ACL_BUF_SIZE];
1142
1143                                         bv.bv_len = sizeof( buf ) - 1;
1144                                         bv.bv_val = buf;
1145                                         string_expand( &bv, &b->a_sockname_pat, e->e_ndn, matches );
1146
1147                                         if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1148                                                 continue;
1149                                         }
1150
1151                                 } else {
1152                                         if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 )
1153                                                 continue;
1154                                 }
1155                         }
1156                 }
1157
1158                 if ( b->a_dn_at != NULL ) {
1159                         Attribute       *at;
1160                         struct berval   bv;
1161                         int rc, match = 0;
1162                         const char *text;
1163                         const char *attr = b->a_dn_at->ad_cname.bv_val;
1164
1165                         assert( attr != NULL );
1166
1167                         if ( op->o_ndn.bv_len == 0 ) {
1168                                 continue;
1169                         }
1170
1171 #ifdef NEW_LOGGING
1172                         LDAP_LOG( ACL, DETAIL1, 
1173                                    "acl_mask: conn %lu  check a_dn_pat: %s\n",
1174                                    op->o_connid, attr , 0 );
1175 #else
1176                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
1177                                 attr, 0, 0);
1178 #endif
1179                         bv = op->o_ndn;
1180
1181                         /* see if asker is listed in dnattr */
1182                         for( at = attrs_find( e->e_attrs, b->a_dn_at );
1183                                 at != NULL;
1184                                 at = attrs_find( at->a_next, b->a_dn_at ) )
1185                         {
1186                                 if( value_find_ex( b->a_dn_at,
1187                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1188                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1189                                         at->a_nvals,
1190                                         &bv, op->o_tmpmemctx ) == 0 )
1191                                 {
1192                                         /* found it */
1193                                         match = 1;
1194                                         break;
1195                                 }
1196                         }
1197
1198                         if( match ) {
1199                                 /* have a dnattr match. if this is a self clause then
1200                                  * the target must also match the op dn.
1201                                  */
1202                                 if ( b->a_dn_self ) {
1203                                         /* check if the target is an attribute. */
1204                                         if ( val == NULL ) continue;
1205
1206                                         /* target is attribute, check if the attribute value
1207                                          * is the op dn.
1208                                          */
1209                                         rc = value_match( &match, b->a_dn_at,
1210                                                 b->a_dn_at->ad_type->sat_equality, 0,
1211                                                 val, &bv, &text );
1212                                         /* on match error or no match, fail the ACL clause */
1213                                         if (rc != LDAP_SUCCESS || match != 0 )
1214                                                 continue;
1215                                 }
1216                         } else {
1217                                 /* no dnattr match, check if this is a self clause */
1218                                 if ( ! b->a_dn_self )
1219                                         continue;
1220
1221                                 ACL_RECORD_VALUE_STATE;
1222                                 
1223                                 /* this is a self clause, check if the target is an
1224                                  * attribute.
1225                                  */
1226                                 if ( val == NULL )
1227                                         continue;
1228
1229                                 /* target is attribute, check if the attribute value
1230                                  * is the op dn.
1231                                  */
1232                                 rc = value_match( &match, b->a_dn_at,
1233                                         b->a_dn_at->ad_type->sat_equality, 0,
1234                                         val, &bv, &text );
1235
1236                                 /* on match error or no match, fail the ACL clause */
1237                                 if (rc != LDAP_SUCCESS || match != 0 )
1238                                         continue;
1239                         }
1240                 }
1241
1242                 if ( b->a_group_pat.bv_len ) {
1243                         struct berval bv;
1244                         struct berval ndn = BER_BVNULL;
1245                         int rc;
1246
1247                         if ( op->o_ndn.bv_len == 0 ) {
1248                                 continue;
1249                         }
1250
1251                         /* b->a_group is an unexpanded entry name, expanded it should be an 
1252                          * entry with objectclass group* and we test to see if odn is one of
1253                          * the values in the attribute group
1254                          */
1255                         /* see if asker is listed in dnattr */
1256                         if ( b->a_group_style == ACL_STYLE_EXPAND ) {
1257                                 char buf[ACL_BUF_SIZE];
1258                                 bv.bv_len = sizeof(buf) - 1;
1259                                 bv.bv_val = buf; 
1260
1261                                 string_expand( &bv, &b->a_group_pat, e->e_ndn, matches );
1262                                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) != LDAP_SUCCESS ) {
1263                                         /* did not expand to a valid dn */
1264                                         continue;
1265                                 }
1266
1267                                 bv = ndn;
1268
1269                         } else {
1270                                 bv = b->a_group_pat;
1271                         }
1272
1273                         rc = backend_group( op, e, &bv, &op->o_ndn,
1274                                 b->a_group_oc, b->a_group_at );
1275
1276                         if ( ndn.bv_val ) free( ndn.bv_val );
1277
1278                         if ( rc != 0 ) {
1279                                 continue;
1280                         }
1281                 }
1282
1283                 if ( b->a_set_pat.bv_len != 0 ) {
1284                         struct berval bv;
1285                         char buf[ACL_BUF_SIZE];
1286                         if( b->a_set_style == ACL_STYLE_REGEX ){
1287                                 bv.bv_len = sizeof(buf) - 1;
1288                                 bv.bv_val = buf;
1289                                 string_expand( &bv, &b->a_set_pat, e->e_ndn, matches );
1290                         }else{
1291                                 bv = b->a_set_pat;
1292                         }
1293                         if (aci_match_set( &bv, op, e, 0 ) == 0) {
1294                                 continue;
1295                         }
1296                 }
1297
1298                 if ( b->a_authz.sai_ssf ) {
1299 #ifdef NEW_LOGGING
1300                         LDAP_LOG( ACL, DETAIL1, 
1301                                 "acl_mask: conn %lu  check a_authz.sai_ssf: ACL %u > OP %u\n",
1302                                 op->o_connid, b->a_authz.sai_ssf, op->o_ssf  );
1303 #else
1304                         Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1305                                 b->a_authz.sai_ssf, op->o_ssf, 0 );
1306 #endif
1307                         if ( b->a_authz.sai_ssf >  op->o_ssf ) {
1308                                 continue;
1309                         }
1310                 }
1311
1312                 if ( b->a_authz.sai_transport_ssf ) {
1313 #ifdef NEW_LOGGING
1314                         LDAP_LOG( ACL, DETAIL1, 
1315                                 "acl_mask: conn %lu  check a_authz.sai_transport_ssf: "
1316                                 "ACL %u > OP %u\n",
1317                                 op->o_connid, b->a_authz.sai_transport_ssf, 
1318                                 op->o_transport_ssf  );
1319 #else
1320                         Debug( LDAP_DEBUG_ACL,
1321                                 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1322                                 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
1323 #endif
1324                         if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
1325                                 continue;
1326                         }
1327                 }
1328
1329                 if ( b->a_authz.sai_tls_ssf ) {
1330 #ifdef NEW_LOGGING
1331                         LDAP_LOG( ACL, DETAIL1, 
1332                                 "acl_mask: conn %lu  check a_authz.sai_tls_ssf: ACL %u > "
1333                                 "OP %u\n",
1334                                 op->o_connid, b->a_authz.sai_tls_ssf, op->o_tls_ssf  );
1335 #else
1336                         Debug( LDAP_DEBUG_ACL,
1337                                 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1338                                 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
1339 #endif
1340                         if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
1341                                 continue;
1342                         }
1343                 }
1344
1345                 if ( b->a_authz.sai_sasl_ssf ) {
1346 #ifdef NEW_LOGGING
1347                         LDAP_LOG( ACL, DETAIL1, 
1348                            "acl_mask: conn %lu check a_authz.sai_sasl_ssf: " 
1349                            "ACL %u > OP %u\n",
1350                                 op->o_connid, b->a_authz.sai_sasl_ssf, op->o_sasl_ssf );
1351 #else
1352                         Debug( LDAP_DEBUG_ACL,
1353                                 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1354                                 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
1355 #endif
1356                         if ( b->a_authz.sai_sasl_ssf >  op->o_sasl_ssf ) {
1357                                 continue;
1358                         }
1359                 }
1360
1361 #ifdef SLAPD_ACI_ENABLED
1362                 if ( b->a_aci_at != NULL ) {
1363                         Attribute       *at;
1364                         slap_access_t grant, deny, tgrant, tdeny;
1365                         struct berval parent_ndn, old_parent_ndn;
1366                         BerVarray bvals = NULL;
1367                         int ret,stop;
1368
1369                         /* this case works different from the others above.
1370                          * since aci's themselves give permissions, we need
1371                          * to first check b->a_access_mask, the ACL's access level.
1372                          */
1373
1374                         if ( e->e_nname.bv_len == 0 ) {
1375                                 /* no ACIs in the root DSE */
1376                                 continue;
1377                         }
1378
1379                         /* first check if the right being requested
1380                          * is allowed by the ACL clause.
1381                          */
1382                         if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
1383                                 continue;
1384                         }
1385                         /* start out with nothing granted, nothing denied */
1386                         ACL_INIT(tgrant);
1387                         ACL_INIT(tdeny);
1388
1389                         /* get the aci attribute */
1390                         at = attr_find( e->e_attrs, b->a_aci_at );
1391                         if ( at != NULL ) {
1392                                 ACL_RECORD_VALUE_STATE;
1393                                 /* the aci is an multi-valued attribute.  The
1394                                 * rights are determined by OR'ing the individual
1395                                 * rights given by the acis.
1396                                 */
1397                                 for ( i = 0; at->a_vals[i].bv_val != NULL; i++ ) {
1398                                         if (aci_mask( op,
1399                                                 e, desc, val,
1400                                                 &at->a_nvals[i],
1401                                                 matches, &grant, &deny,  &aci_bv_entry ) != 0)
1402                                         {
1403                                                 tgrant |= grant;
1404                                                 tdeny |= deny;
1405                                         }
1406                                 }
1407                                 Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
1408                                           accessmask2str(tgrant,accessmaskbuf), 
1409                                           accessmask2str(tdeny, accessmaskbuf1), 0);
1410
1411                         }
1412                         /* If the entry level aci didn't contain anything valid for the 
1413                          * current operation, climb up the tree and evaluate the
1414                          * acis with scope set to subtree
1415                          */
1416                         if( (tgrant == ACL_PRIV_NONE) && (tdeny == ACL_PRIV_NONE) ){
1417                                 dnParent(&(e->e_nname), &parent_ndn);
1418                                 while ( parent_ndn.bv_val != old_parent_ndn.bv_val ){
1419                                         old_parent_ndn = parent_ndn;
1420                                         Debug(LDAP_DEBUG_ACL, "checking ACI of %s\n", parent_ndn.bv_val, 0, 0);
1421                                         ret=backend_attribute(op, NULL, &parent_ndn, b->a_aci_at, &bvals);
1422                                         switch(ret){
1423                                                 case LDAP_SUCCESS :
1424                                                         if(bvals){
1425                                                                 for( i = 0; bvals[i].bv_val != NULL; i++){
1426                                                                         ACL_RECORD_VALUE_STATE;
1427                                                                         if (aci_mask(op, e, desc, val, &bvals[i], matches,
1428                                                                                         &grant, &deny, &aci_bv_children) != 0) {
1429                                                                                 tgrant |= grant;
1430                                                                                 tdeny |= deny;
1431                                                                                 /* evaluation stops as soon as either a "deny" or a 
1432                                                                                  * "grant" directive matches.
1433                                                                                  */
1434                                                                                 if( (tgrant != ACL_PRIV_NONE) || (tdeny != ACL_PRIV_NONE) ){
1435                                                                                         stop=1;
1436                                                                                 }
1437                                                                         }
1438                                                                         Debug(LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
1439                                                                                 accessmask2str(tgrant,accessmaskbuf),
1440                                                                                 accessmask2str(tdeny, accessmaskbuf1), 0);
1441                                                                 }
1442                                                         }
1443                                                         stop=0;
1444                                                         break;
1445                                                 case LDAP_NO_SUCH_ATTRIBUTE:
1446                                                         /* just go on if the aci-Attribute is not present in
1447                                                          * the current entry 
1448                                                          */
1449                                                         Debug(LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0);
1450                                                         stop=0;
1451                                                         break;
1452                                                 case LDAP_NO_SUCH_OBJECT:
1453                                                         /* We have reached the base object */
1454                                                         Debug(LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0);
1455                                                         stop=1;
1456                                                         break;
1457                                                 default:
1458                                                         stop=1;
1459                                                         break;
1460                                         }
1461                                         if(stop){
1462                                                 break;
1463                                         }
1464                                         dnParent(&old_parent_ndn, &parent_ndn);
1465                                 }
1466                         }
1467
1468
1469                         /* remove anything that the ACL clause does not allow */
1470                         tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1471                         tdeny &= ACL_PRIV_MASK;
1472
1473                         /* see if we have anything to contribute */
1474                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
1475                                 continue;
1476                         }
1477
1478                         /* this could be improved by changing acl_mask so that it can deal with
1479                          * by clauses that return grant/deny pairs.  Right now, it does either
1480                          * additive or subtractive rights, but not both at the same time.  So,
1481                          * we need to combine the grant/deny pair into a single rights mask in
1482                          * a smart way:  if either grant or deny is "empty", then we use the
1483                          * opposite as is, otherwise we remove any denied rights from the grant
1484                          * rights mask and construct an additive mask.
1485                          */
1486                         if (ACL_IS_INVALID(tdeny)) {
1487                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
1488
1489                         } else if (ACL_IS_INVALID(tgrant)) {
1490                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1491
1492                         } else {
1493                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1494                         }
1495
1496                 } else
1497 #endif
1498                 {
1499                         modmask = b->a_access_mask;
1500                 }
1501
1502 #ifdef NEW_LOGGING
1503                 LDAP_LOG( ACL, RESULTS, 
1504                            "acl_mask: [%d] applying %s (%s)\n",
1505                            i, accessmask2str( modmask, accessmaskbuf),
1506                            b->a_type == ACL_CONTINUE ? "continue" : b->a_type == ACL_BREAK
1507                            ? "break" : "stop"  );
1508 #else
1509                 Debug( LDAP_DEBUG_ACL,
1510                         "<= acl_mask: [%d] applying %s (%s)\n",
1511                         i, accessmask2str( modmask, accessmaskbuf ), 
1512                         b->a_type == ACL_CONTINUE
1513                                 ? "continue"
1514                                 : b->a_type == ACL_BREAK
1515                                         ? "break"
1516                                         : "stop" );
1517 #endif
1518                 /* save old mask */
1519                 oldmask = *mask;
1520
1521                 if( ACL_IS_ADDITIVE(modmask) ) {
1522                         /* add privs */
1523                         ACL_PRIV_SET( *mask, modmask );
1524
1525                         /* cleanup */
1526                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1527
1528                 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
1529                         /* substract privs */
1530                         ACL_PRIV_CLR( *mask, modmask );
1531
1532                         /* cleanup */
1533                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1534
1535                 } else {
1536                         /* assign privs */
1537                         *mask = modmask;
1538                 }
1539
1540 #ifdef NEW_LOGGING
1541                 LDAP_LOG( ACL, DETAIL1, 
1542                            "acl_mask: conn %lu  [%d] mask: %s\n",
1543                            op->o_connid, i, accessmask2str( *mask, accessmaskbuf)  );
1544 #else
1545                 Debug( LDAP_DEBUG_ACL,
1546                         "<= acl_mask: [%d] mask: %s\n",
1547                         i, accessmask2str(*mask, accessmaskbuf), 0 );
1548 #endif
1549
1550                 if( b->a_type == ACL_CONTINUE ) {
1551                         continue;
1552
1553                 } else if ( b->a_type == ACL_BREAK ) {
1554                         return ACL_BREAK;
1555
1556                 } else {
1557                         return ACL_STOP;
1558                 }
1559         }
1560
1561         /* implicit "by * none" clause */
1562         ACL_INIT(*mask);
1563
1564 #ifdef NEW_LOGGING
1565         LDAP_LOG( ACL, RESULTS, 
1566                    "acl_mask: conn %lu  no more <who> clauses, returning %d (stop)\n",
1567                    op->o_connid, accessmask2str( *mask, accessmaskbuf) , 0 );
1568 #else
1569         Debug( LDAP_DEBUG_ACL,
1570                 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1571                 accessmask2str(*mask, accessmaskbuf), 0, 0 );
1572 #endif
1573         return ACL_STOP;
1574 }
1575
1576 /*
1577  * acl_check_modlist - check access control on the given entry to see if
1578  * it allows the given modifications by the user associated with op.
1579  * returns      1       if mods allowed ok
1580  *                      0       mods not allowed
1581  */
1582
1583 int
1584 acl_check_modlist(
1585         Operation       *op,
1586         Entry   *e,
1587         Modifications   *mlist
1588 )
1589 {
1590         struct berval *bv;
1591         AccessControlState state = ACL_STATE_INIT;
1592         Backend *be;
1593         int be_null = 0;
1594         int ret = 1; /* default is access allowed */
1595
1596         be = op->o_bd;
1597         if ( be == NULL ) {
1598                 be = &backends[0];
1599                 be_null = 1;
1600                 op->o_bd = be;
1601         }
1602         assert( be != NULL );
1603
1604         /* short circuit root database access */
1605         if ( be_isroot( op ) ) {
1606 #ifdef NEW_LOGGING
1607                 LDAP_LOG( ACL, DETAIL1, 
1608                            "acl_check_modlist: conn %lu  access granted to root user\n",
1609                            op->o_connid, 0, 0 );
1610 #else
1611                 Debug( LDAP_DEBUG_ACL,
1612                         "<= acl_access_allowed: granted to database root\n",
1613                     0, 0, 0 );
1614 #endif
1615                 goto done;
1616         }
1617
1618         /* use backend default access if no backend acls */
1619         if( op->o_bd != NULL && op->o_bd->be_acl == NULL ) {
1620 #ifdef NEW_LOGGING
1621                 LDAP_LOG( ACL, DETAIL1, 
1622                         "acl_check_modlist: backend default %s access %s to \"%s\"\n",
1623                         access2str( ACL_WRITE ),
1624                         op->o_bd->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", 
1625                         op->o_dn.bv_val  );
1626 #else
1627                 Debug( LDAP_DEBUG_ACL,
1628                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
1629                         access2str( ACL_WRITE ),
1630                         op->o_bd->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val );
1631 #endif
1632                 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
1633                 goto done;
1634         }
1635
1636         for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1637                 /*
1638                  * no-user-modification operational attributes are ignored
1639                  * by ACL_WRITE checking as any found here are not provided
1640                  * by the user
1641                  */
1642                 if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
1643 #ifdef NEW_LOGGING
1644                         LDAP_LOG( ACL, DETAIL1, 
1645                                    "acl_check_modlist: conn %lu  no-user-mod %s: modify access granted\n",
1646                                    op->o_connid, mlist->sml_desc->ad_cname.bv_val , 0 );
1647 #else
1648                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1649                                 " modify access granted\n",
1650                                 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1651 #endif
1652                         continue;
1653                 }
1654
1655                 switch ( mlist->sml_op ) {
1656                 case LDAP_MOD_REPLACE:
1657                         /*
1658                          * We must check both permission to delete the whole
1659                          * attribute and permission to add the specific attributes.
1660                          * This prevents abuse from selfwriters.
1661                          */
1662                         if ( ! access_allowed( op, e,
1663                                 mlist->sml_desc, NULL, ACL_WRITE, &state ) )
1664                         {
1665                                 ret = 0;
1666                                 goto done;
1667                         }
1668
1669                         if ( mlist->sml_values == NULL ) break;
1670
1671                         /* fall thru to check value to add */
1672
1673                 case LDAP_MOD_ADD:
1674                         assert( mlist->sml_values != NULL );
1675
1676                         for ( bv = mlist->sml_nvalues
1677                                         ? mlist->sml_nvalues : mlist->sml_values;
1678                                 bv->bv_val != NULL; bv++ )
1679                         {
1680                                 if ( ! access_allowed( op, e,
1681                                         mlist->sml_desc, bv, ACL_WRITE, &state ) )
1682                                 {
1683                                         ret = 0;
1684                                         goto done;
1685                                 }
1686                         }
1687                         break;
1688
1689                 case LDAP_MOD_DELETE:
1690                         if ( mlist->sml_values == NULL ) {
1691                                 if ( ! access_allowed( op, e,
1692                                         mlist->sml_desc, NULL, ACL_WRITE, NULL ) )
1693                                 {
1694                                         ret = 0;
1695                                         goto done;
1696                                 }
1697                                 break;
1698                         }
1699                         for ( bv = mlist->sml_nvalues
1700                                         ? mlist->sml_nvalues : mlist->sml_values;
1701                                 bv->bv_val != NULL; bv++ )
1702                         {
1703                                 if ( ! access_allowed( op, e,
1704                                         mlist->sml_desc, bv, ACL_WRITE, &state ) )
1705                                 {
1706                                         ret = 0;
1707                                         goto done;
1708                                 }
1709                         }
1710                         break;
1711
1712                 case SLAP_MOD_SOFTADD:
1713                         /* allow adding attribute via modrdn thru */
1714                         break;
1715
1716                 default:
1717                         assert( 0 );
1718                         /* not reached */
1719                         ret = 0;
1720                         break;
1721                 }
1722         }
1723
1724 done:
1725         if (be_null) op->o_bd = NULL;
1726         return( ret );
1727 }
1728
1729 static int
1730 aci_get_part(
1731         struct berval *list,
1732         int ix,
1733         char sep,
1734         struct berval *bv )
1735 {
1736         int len;
1737         char *p;
1738
1739         if (bv) {
1740                 bv->bv_len = 0;
1741                 bv->bv_val = NULL;
1742         }
1743         len = list->bv_len;
1744         p = list->bv_val;
1745         while (len >= 0 && --ix >= 0) {
1746                 while (--len >= 0 && *p++ != sep) ;
1747         }
1748         while (len >= 0 && *p == ' ') {
1749                 len--;
1750                 p++;
1751         }
1752         if (len < 0)
1753                 return(-1);
1754
1755         if (!bv)
1756                 return(0);
1757
1758         bv->bv_val = p;
1759         while (--len >= 0 && *p != sep) {
1760                 bv->bv_len++;
1761                 p++;
1762         }
1763         while (bv->bv_len > 0 && *--p == ' ')
1764                 bv->bv_len--;
1765         return(bv->bv_len);
1766 }
1767
1768 BerVarray
1769 aci_set_gather (SetCookie *cookie, struct berval *name, struct berval *attr)
1770 {
1771         AciSetCookie *cp = (AciSetCookie *)cookie;
1772         BerVarray bvals = NULL;
1773         struct berval ndn;
1774
1775         /* this routine needs to return the bervals instead of
1776          * plain strings, since syntax is not known.  It should
1777          * also return the syntax or some "comparison cookie".
1778          */
1779
1780         if (dnNormalize(0, NULL, NULL, name, &ndn, cp->op->o_tmpmemctx) == LDAP_SUCCESS) {
1781                 const char *text;
1782                 AttributeDescription *desc = NULL;
1783                 if (slap_bv2ad(attr, &desc, &text) == LDAP_SUCCESS) {
1784                         backend_attribute(cp->op,
1785                                 cp->e, &ndn, desc, &bvals);
1786                 }
1787                 slap_sl_free(ndn.bv_val, cp->op->o_tmpmemctx);
1788         }
1789         return(bvals);
1790 }
1791
1792 static int
1793 aci_match_set (
1794         struct berval *subj,
1795         Operation *op,
1796         Entry *e,
1797         int setref
1798 )
1799 {
1800         struct berval set = BER_BVNULL;
1801         int rc = 0;
1802         AciSetCookie cookie;
1803
1804         if (setref == 0) {
1805                 ber_dupbv_x( &set, subj, op->o_tmpmemctx );
1806         } else {
1807                 struct berval subjdn, ndn = BER_BVNULL;
1808                 struct berval setat;
1809                 BerVarray bvals;
1810                 const char *text;
1811                 AttributeDescription *desc = NULL;
1812
1813                 /* format of string is "entry/setAttrName" */
1814                 if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
1815                         return(0);
1816                 }
1817
1818                 if ( aci_get_part(subj, 1, '/', &setat) < 0 ) {
1819                         setat.bv_val = SLAPD_ACI_SET_ATTR;
1820                         setat.bv_len = sizeof(SLAPD_ACI_SET_ATTR)-1;
1821                 }
1822
1823                 if ( setat.bv_val != NULL ) {
1824                         /*
1825                          * NOTE: dnNormalize honors the ber_len field
1826                          * as the length of the dn to be normalized
1827                          */
1828                         if ( dnNormalize(0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx) == LDAP_SUCCESS
1829                                 && slap_bv2ad(&setat, &desc, &text) == LDAP_SUCCESS )
1830                         {
1831                                 backend_attribute(op, e,
1832                                         &ndn, desc, &bvals);
1833                                 if ( bvals != NULL ) {
1834                                         if ( bvals[0].bv_val != NULL ) {
1835                                                 int i;
1836                                                 set = bvals[0];
1837                                                 bvals[0].bv_val = NULL;
1838                                                 for (i=1;bvals[i].bv_val;i++);
1839                                                 bvals[0].bv_val = bvals[i-1].bv_val;
1840                                                 bvals[i-1].bv_val = NULL;
1841                                         }
1842                                         ber_bvarray_free_x(bvals, op->o_tmpmemctx);
1843                                 }
1844                         }
1845                         if (ndn.bv_val)
1846                                 free(ndn.bv_val);
1847                 }
1848         }
1849
1850         if (set.bv_val != NULL) {
1851                 cookie.op = op;
1852                 cookie.e = e;
1853                 rc = (slap_set_filter(aci_set_gather, (SetCookie *)&cookie, &set,
1854                         &op->o_ndn, &e->e_nname, NULL) > 0);
1855                 slap_sl_free(set.bv_val, op->o_tmpmemctx);
1856         }
1857         return(rc);
1858 }
1859
1860 #ifdef SLAPD_ACI_ENABLED
1861 static int
1862 aci_list_map_rights(
1863         struct berval *list )
1864 {
1865         struct berval bv;
1866         slap_access_t mask;
1867         int i;
1868
1869         ACL_INIT(mask);
1870         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
1871                 if (bv.bv_len <= 0)
1872                         continue;
1873                 switch (*bv.bv_val) {
1874                 case 'c':
1875                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1876                         break;
1877                 case 's':
1878                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
1879                          * the right 's' to mean "set", but in the examples states
1880                          * that the right 's' means "search".  The latter definition
1881                          * is used here.
1882                          */
1883                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1884                         break;
1885                 case 'r':
1886                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
1887                         break;
1888                 case 'w':
1889                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1890                         break;
1891                 case 'x':
1892                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
1893                          * define any equivalent to the AUTH right, so I've just used
1894                          * 'x' for now.
1895                          */
1896                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1897                         break;
1898                 default:
1899                         break;
1900                 }
1901
1902         }
1903         return(mask);
1904 }
1905
1906 static int
1907 aci_list_has_attr(
1908         struct berval *list,
1909         const struct berval *attr,
1910         struct berval *val )
1911 {
1912         struct berval bv, left, right;
1913         int i;
1914
1915         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
1916                 if (aci_get_part(&bv, 0, '=', &left) < 0
1917                         || aci_get_part(&bv, 1, '=', &right) < 0)
1918                 {
1919                         if (ber_bvstrcasecmp(attr, &bv) == 0)
1920                                 return(1);
1921                 } else if (val == NULL) {
1922                         if (ber_bvstrcasecmp(attr, &left) == 0)
1923                                 return(1);
1924                 } else {
1925                         if (ber_bvstrcasecmp(attr, &left) == 0) {
1926                                 /* this is experimental code that implements a
1927                                  * simple (prefix) match of the attribute value.
1928                                  * the ACI draft does not provide for aci's that
1929                                  * apply to specific values, but it would be
1930                                  * nice to have.  If the <attr> part of an aci's
1931                                  * rights list is of the form <attr>=<value>,
1932                                  * that means the aci applies only to attrs with
1933                                  * the given value.  Furthermore, if the attr is
1934                                  * of the form <attr>=<value>*, then <value> is
1935                                  * treated as a prefix, and the aci applies to 
1936                                  * any value with that prefix.
1937                                  *
1938                                  * Ideally, this would allow r.e. matches.
1939                                  */
1940                                 if (aci_get_part(&right, 0, '*', &left) < 0
1941                                         || right.bv_len <= left.bv_len)
1942                                 {
1943                                         if (ber_bvstrcasecmp(val, &right) == 0)
1944                                                 return(1);
1945                                 } else if (val->bv_len >= left.bv_len) {
1946                                         if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
1947                                                 return(1);
1948                                 }
1949                         }
1950                 }
1951         }
1952         return(0);
1953 }
1954
1955 static slap_access_t
1956 aci_list_get_attr_rights(
1957         struct berval *list,
1958         const struct berval *attr,
1959         struct berval *val )
1960 {
1961     struct berval bv;
1962     slap_access_t mask;
1963     int i;
1964
1965         /* loop through each rights/attr pair, skip first part (action) */
1966         ACL_INIT(mask);
1967         for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
1968                 if (aci_list_has_attr(&bv, attr, val) == 0)
1969                         continue;
1970                 if (aci_get_part(list, i, ';', &bv) < 0)
1971                         continue;
1972                 mask |= aci_list_map_rights(&bv);
1973         }
1974         return(mask);
1975 }
1976
1977 static int
1978 aci_list_get_rights(
1979         struct berval *list,
1980         const struct berval *attr,
1981         struct berval *val,
1982         slap_access_t *grant,
1983         slap_access_t *deny )
1984 {
1985     struct berval perm, actn;
1986     slap_access_t *mask;
1987     int i, found;
1988
1989         if (attr == NULL || attr->bv_len == 0 
1990                         || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
1991                 attr = &aci_bv_br_entry;
1992         }
1993
1994         found = 0;
1995         ACL_INIT(*grant);
1996         ACL_INIT(*deny);
1997         /* loop through each permissions clause */
1998         for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
1999                 if (aci_get_part(&perm, 0, ';', &actn) < 0)
2000                         continue;
2001                 if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
2002                         mask = grant;
2003                 } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
2004                         mask = deny;
2005                 } else {
2006                         continue;
2007                 }
2008
2009                 found = 1;
2010                 *mask |= aci_list_get_attr_rights(&perm, attr, val);
2011                 *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
2012         }
2013         return(found);
2014 }
2015
2016 static int
2017 aci_group_member (
2018         struct berval *subj,
2019         struct berval *defgrpoc,
2020         struct berval *defgrpat,
2021         Operation               *op,
2022         Entry           *e,
2023         regmatch_t      *matches
2024 )
2025 {
2026         struct berval subjdn;
2027         struct berval grpoc;
2028         struct berval grpat;
2029         ObjectClass *grp_oc = NULL;
2030         AttributeDescription *grp_ad = NULL;
2031         const char *text;
2032         int rc;
2033
2034         /* format of string is "group/objectClassValue/groupAttrName" */
2035         if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
2036                 return(0);
2037         }
2038
2039         if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
2040                 grpoc = *defgrpoc;
2041         }
2042
2043         if (aci_get_part(subj, 2, '/', &grpat) < 0) {
2044                 grpat = *defgrpat;
2045         }
2046
2047         rc = slap_bv2ad( &grpat, &grp_ad, &text );
2048         if( rc != LDAP_SUCCESS ) {
2049                 rc = 0;
2050                 goto done;
2051         }
2052         rc = 0;
2053
2054         grp_oc = oc_bvfind( &grpoc );
2055
2056         if (grp_oc != NULL && grp_ad != NULL ) {
2057                 char buf[ACL_BUF_SIZE];
2058                 struct berval bv, ndn;
2059                 bv.bv_len = sizeof( buf ) - 1;
2060                 bv.bv_val = (char *)&buf;
2061                 string_expand(&bv, &subjdn, e->e_ndn, matches);
2062                 if ( dnNormalize(0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx) == LDAP_SUCCESS ) {
2063                         rc = (backend_group(op, e, &ndn, &op->o_ndn,
2064                                 grp_oc, grp_ad) == 0);
2065                         free( ndn.bv_val );
2066                 }
2067         }
2068
2069 done:
2070         return(rc);
2071 }
2072
2073 static int
2074 aci_mask(
2075     Operation           *op,
2076     Entry                       *e,
2077         AttributeDescription *desc,
2078     struct berval       *val,
2079     struct berval       *aci,
2080         regmatch_t              *matches,
2081         slap_access_t   *grant,
2082         slap_access_t   *deny,
2083         struct berval   *scope
2084 )
2085 {
2086     struct berval bv, perms, sdn;
2087         int rc;
2088                 
2089
2090         assert( desc->ad_cname.bv_val != NULL );
2091
2092         /* parse an aci of the form:
2093                 oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
2094
2095            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
2096            a full description of the format for this attribute.
2097            Differences: "this" in the draft is "self" here, and
2098            "self" and "public" is in the position of dnType.
2099
2100            For now, this routine only supports scope=entry.
2101          */
2102         /* check that the aci has all 5 components */
2103         if (aci_get_part(aci, 4, '#', NULL) < 0)
2104                 return(0);
2105
2106         /* check that the aci family is supported */
2107         if (aci_get_part(aci, 0, '#', &bv) < 0)
2108                 return(0);
2109
2110         /* check that the scope matches */
2111         if (aci_get_part(aci, 1, '#', &bv) < 0
2112                 || ber_bvstrcasecmp( scope, &bv ) != 0)
2113         {
2114                 return(0);
2115         }
2116
2117         /* get the list of permissions clauses, bail if empty */
2118         if (aci_get_part(aci, 2, '#', &perms) <= 0)
2119                 return(0);
2120
2121         /* check if any permissions allow desired access */
2122         if (aci_list_get_rights(&perms, &desc->ad_cname, val, grant, deny) == 0)
2123                 return(0);
2124
2125         /* see if we have a DN match */
2126         if (aci_get_part(aci, 3, '#', &bv) < 0)
2127                 return(0);
2128
2129         if (aci_get_part(aci, 4, '#', &sdn) < 0)
2130                 return(0);
2131
2132         if (ber_bvstrcasecmp( &aci_bv_access_id, &bv ) == 0) {
2133                 struct berval ndn;
2134                 rc = 0;
2135                 if ( dnNormalize(0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx) == LDAP_SUCCESS ) {
2136                         if (dn_match( &op->o_ndn, &ndn))
2137                                 rc = 1;
2138                         free(ndn.bv_val);
2139                 }
2140                 return (rc);
2141
2142         } else if (ber_bvstrcasecmp( &aci_bv_public, &bv ) == 0) {
2143                 return(1);
2144
2145         } else if (ber_bvstrcasecmp( &aci_bv_self, &bv ) == 0) {
2146                 if (dn_match(&op->o_ndn, &e->e_nname))
2147                         return(1);
2148
2149         } else if (ber_bvstrcasecmp( &aci_bv_dnattr, &bv ) == 0) {
2150                 Attribute *at;
2151                 AttributeDescription *ad = NULL;
2152                 const char *text;
2153
2154                 rc = slap_bv2ad( &sdn, &ad, &text );
2155
2156                 if( rc != LDAP_SUCCESS ) {
2157                         return 0;
2158                 }
2159
2160                 rc = 0;
2161
2162                 bv = op->o_ndn;
2163
2164                 for(at = attrs_find( e->e_attrs, ad );
2165                         at != NULL;
2166                         at = attrs_find( at->a_next, ad ) )
2167                 {
2168                         if (value_find_ex( ad,
2169                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
2170                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
2171                                 at->a_nvals,
2172                                 &bv, op->o_tmpmemctx) == 0 )
2173                         {
2174                                 rc = 1;
2175                                 break;
2176                         }
2177                 }
2178
2179                 return rc;
2180
2181
2182         } else if (ber_bvstrcasecmp( &aci_bv_group, &bv ) == 0) {
2183                 if (aci_group_member(&sdn, &aci_bv_group_class, &aci_bv_group_attr, op, e, matches))
2184                         return(1);
2185
2186         } else if (ber_bvstrcasecmp( &aci_bv_role, &bv ) == 0) {
2187                 if (aci_group_member(&sdn, &aci_bv_role_class, &aci_bv_role_attr, op, e, matches))
2188                         return(1);
2189
2190         } else if (ber_bvstrcasecmp( &aci_bv_set, &bv ) == 0) {
2191                 if (aci_match_set(&sdn, op, e, 0))
2192                         return(1);
2193
2194         } else if (ber_bvstrcasecmp( &aci_bv_set_ref, &bv ) == 0) {
2195                 if (aci_match_set(&sdn, op, e, 1))
2196                         return(1);
2197
2198         }
2199
2200         return(0);
2201 }
2202
2203 #endif  /* SLAPD_ACI_ENABLED */
2204
2205 static void
2206 string_expand(
2207         struct berval *bv,
2208         struct berval *pat,
2209         char *match,
2210         regmatch_t *matches)
2211 {
2212         ber_len_t       size;
2213         char   *sp;
2214         char   *dp;
2215         int     flag;
2216
2217         size = 0;
2218         bv->bv_val[0] = '\0';
2219         bv->bv_len--; /* leave space for lone $ */
2220
2221         flag = 0;
2222         for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2223                 sp < pat->bv_val + pat->bv_len ; sp++ )
2224         {
2225                 /* did we previously see a $ */
2226                 if ( flag ) {
2227                         if ( flag == 1 && *sp == '$' ) {
2228                                 *dp++ = '$';
2229                                 size++;
2230                                 flag = 0;
2231
2232                         } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2233                                 flag = 2;
2234
2235                         } else if ( *sp >= '0' && *sp <= '9' ) {
2236                                 int     n;
2237                                 int     i;
2238                                 int     l;
2239
2240                                 n = *sp - '0';
2241
2242                                 if ( flag == 2 ) {
2243                                         for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2244                                                 if ( *sp >= '0' && *sp <= '9' ) {
2245                                                         n = 10*n + ( *sp - '0' );
2246                                                 }
2247                                         }
2248
2249                                         if ( *sp != /*'{'*/ '}' ) {
2250                                                 /* error */
2251                                         }
2252                                 }
2253
2254                                 if ( n >= MAXREMATCHES ) {
2255                                 
2256                                 }
2257                                 
2258                                 *dp = '\0';
2259                                 i = matches[n].rm_so;
2260                                 l = matches[n].rm_eo; 
2261                                 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2262                                         *dp++ = match[i];
2263                                 }
2264                                 *dp = '\0';
2265
2266                                 flag = 0;
2267                         }
2268                 } else {
2269                         if (*sp == '$') {
2270                                 flag = 1;
2271                         } else {
2272                                 *dp++ = *sp;
2273                                 size++;
2274                         }
2275                 }
2276         }
2277
2278         if ( flag ) {
2279                 /* must have ended with a single $ */
2280                 *dp++ = '$';
2281                 size++;
2282         }
2283
2284         *dp = '\0';
2285         bv->bv_len = size;
2286
2287 #ifdef NEW_LOGGING
2288         LDAP_LOG( ACL, DETAIL1, 
2289            "string_expand:  pattern = %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
2290         LDAP_LOG( ACL, DETAIL1, "string_expand:  expanded = %s\n", bv->bv_val, 0, 0 );
2291 #else
2292         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
2293         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
2294 #endif
2295 }
2296
2297 static int
2298 regex_matches(
2299         struct berval *pat,                     /* pattern to expand and match against */
2300         char *str,                              /* string to match against pattern */
2301         char *buf,                              /* buffer with $N expansion variables */
2302         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
2303 )
2304 {
2305         regex_t re;
2306         char newbuf[ACL_BUF_SIZE];
2307         struct berval bv;
2308         int     rc;
2309
2310         bv.bv_len = sizeof(newbuf) - 1;
2311         bv.bv_val = newbuf;
2312
2313         if(str == NULL) str = "";
2314
2315         string_expand(&bv, pat, buf, matches);
2316         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
2317                 char error[ACL_BUF_SIZE];
2318                 regerror(rc, &re, error, sizeof(error));
2319
2320 #ifdef NEW_LOGGING
2321                 LDAP_LOG( ACL, ERR, 
2322                            "regex_matches: compile( \"%s\", \"%s\") failed %s\n",
2323                            pat->bv_val, str, error  );
2324 #else
2325                 Debug( LDAP_DEBUG_TRACE,
2326                     "compile( \"%s\", \"%s\") failed %s\n",
2327                         pat->bv_val, str, error );
2328 #endif
2329                 return( 0 );
2330         }
2331
2332         rc = regexec(&re, str, 0, NULL, 0);
2333         regfree( &re );
2334
2335 #ifdef NEW_LOGGING
2336         LDAP_LOG( ACL, DETAIL2, "regex_matches: string:   %s\n", str, 0, 0 );
2337         LDAP_LOG( ACL, DETAIL2, "regex_matches: rc:     %d  %s\n",
2338                    rc, rc ? "matches" : "no matches", 0  );
2339 #else
2340         Debug( LDAP_DEBUG_TRACE,
2341             "=> regex_matches: string:   %s\n", str, 0, 0 );
2342         Debug( LDAP_DEBUG_TRACE,
2343             "=> regex_matches: rc: %d %s\n",
2344                 rc, !rc ? "matches" : "no matches", 0 );
2345 #endif
2346         return( !rc );
2347 }
2348