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