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