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