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