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