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