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