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