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