acl.c 82.5 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* acl.c - routines to parse and check acl's */
2
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 1998-2005 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
25
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
26

Kurt Zeilenga's avatar
Kurt Zeilenga committed
27
28
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
29
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
30
31
32
33

#include <ac/regex.h>
#include <ac/socket.h>
#include <ac/string.h>
34

Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
#include "slap.h"
36
#include "sets.h"
37
#include "lber_pvt.h"
38
#include "lutil.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
39

40
41
#define ACL_BUF_SIZE 	1024	/* use most appropriate size */

42
43
44
45
/*
 * speed up compares
 */
static struct berval 
46
	aci_bv_entry 		= BER_BVC("entry"),
Kurt Zeilenga's avatar
Kurt Zeilenga committed
47
	aci_bv_children 	= BER_BVC("children"),
48
	aci_bv_onelevel 	= BER_BVC("onelevel"),
49
	aci_bv_subtree 		= BER_BVC("subtree"),
50
51
52
	aci_bv_br_entry		= BER_BVC("[entry]"),
	aci_bv_br_all		= BER_BVC("[all]"),
	aci_bv_access_id 	= BER_BVC("access-id"),
53
#if 0
54
	aci_bv_anonymous	= BER_BVC("anonymous"),
55
#endif
56
	aci_bv_public		= BER_BVC("public"),
57
58
59
60
61
62
63
64
	aci_bv_users		= BER_BVC("users"),
	aci_bv_self 		= BER_BVC("self"),
	aci_bv_dnattr 		= BER_BVC("dnattr"),
	aci_bv_group		= BER_BVC("group"),
	aci_bv_role		= BER_BVC("role"),
	aci_bv_set		= BER_BVC("set"),
	aci_bv_set_ref		= BER_BVC("set-ref"),
	aci_bv_grant		= BER_BVC("grant"),
65
	aci_bv_deny		= BER_BVC("deny"),
66
67
68
69

	aci_bv_ip_eq		= BER_BVC("IP="),
#ifdef LDAP_PF_LOCAL
	aci_bv_path_eq		= BER_BVC("PATH="),
70
#if 0
71
	aci_bv_dirsep		= BER_BVC(LDAP_DIRSEP),
72
#endif
73
#endif /* LDAP_PF_LOCAL */
74
75
76
77
	
	aci_bv_group_class 	= BER_BVC(SLAPD_GROUP_CLASS),
	aci_bv_group_attr 	= BER_BVC(SLAPD_GROUP_ATTR),
	aci_bv_role_class	= BER_BVC(SLAPD_ROLE_CLASS),
Pierangelo Masarati's avatar
Pierangelo Masarati committed
78
79
	aci_bv_role_attr	= BER_BVC(SLAPD_ROLE_ATTR),
	aci_bv_set_attr		= BER_BVC(SLAPD_ACI_SET_ATTR);
80

81
82
83
84
85
typedef enum slap_aci_scope_t {
	SLAP_ACI_SCOPE_ENTRY		= 0x1,
	SLAP_ACI_SCOPE_CHILDREN		= 0x2,
	SLAP_ACI_SCOPE_SUBTREE		= ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
} slap_aci_scope_t;
86

87
static AccessControl * slap_acl_get(
88
	AccessControl *ac, int *count,
89
	Operation *op, Entry *e,
90
	AttributeDescription *desc,
91
	struct berval *val,
92
	int nmatch, regmatch_t *matches,
Howard Chu's avatar
Howard Chu committed
93
	AccessControlState *state );
94

95
static slap_control_t slap_acl_mask(
96
	AccessControl *ac, slap_mask_t *mask,
97
	Operation *op, Entry *e,
98
	AttributeDescription *desc,
99
	struct berval *val,
100
	int nmatch,
101
102
103
	regmatch_t *matches,
	int count,
	AccessControlState *state );
104

105
#ifdef SLAPD_ACI_ENABLED
106
static int aci_mask(
107
	Operation *op, Entry *e,
108
	AttributeDescription *desc,
109
110
	struct berval *val,
	struct berval *aci,
111
	int nmatch,
112
113
	regmatch_t *matches,
	slap_access_t *grant,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
114
	slap_access_t *deny,
115
	slap_aci_scope_t scope);
Pierangelo Masarati's avatar
Pierangelo Masarati committed
116
#endif /* SLAPD_ACI_ENABLED */
117

118
static int	regex_matches(
119
120
	struct berval *pat, char *str, char *buf,
	int nmatch, regmatch_t *matches);
121
static int	string_expand(
122
	struct berval *newbuf, struct berval *pattern,
123
	char *match, int nmatch, regmatch_t *matches);
124

125
126
typedef	struct AciSetCookie {
	Operation *op;
127
	Entry *e;
128
129
} AciSetCookie;

130
SLAP_SET_GATHER aci_set_gather;
131
SLAP_SET_GATHER aci_set_gather2;
132
133
static int aci_match_set ( struct berval *subj, Operation *op,
    Entry *e, int setref );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
134
135

/*
136
 * access_allowed - check whether op->o_ndn is allowed the requested access
Kurt Zeilenga's avatar
Kurt Zeilenga committed
137
 * to entry e, attribute attr, value val.  if val is null, access to
138
 * the whole attribute is assumed (all values).
Kurt Zeilenga's avatar
Kurt Zeilenga committed
139
 *
140
 * This routine loops through all access controls and calls
141
 * slap_acl_mask() on each applicable access control.
142
143
144
145
146
147
 * The loop exits when a definitive answer is reached or
 * or no more controls remain.
 *
 * returns:
 *		0	access denied
 *		1	access granted
148
149
150
151
 *
 * Notes:
 * - can be legally called with op == NULL
 * - can be legally called with op->o_bd == NULL
Kurt Zeilenga's avatar
Kurt Zeilenga committed
152
153
 */

154
155
156
157
158
159
160
161
162
163
164
#ifdef SLAP_OVERLAY_ACCESS
int
slap_access_always_allowed(
	Operation		*op,
	Entry			*e,
	AttributeDescription	*desc,
	struct berval		*val,
	slap_access_t		access,
	AccessControlState	*state,
	slap_mask_t		*maskp )
{
165
	assert( maskp != NULL );
166
167
168
169
170

	ACL_PRIV_SET( *maskp, ACL_ACCESS2PRIV( access ) );

	return 1;
}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
171

Pierangelo Masarati's avatar
Pierangelo Masarati committed
172
int
Pierangelo Masarati's avatar
Pierangelo Masarati committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
slap_access_allowed(
	Operation		*op,
	Entry			*e,
	AttributeDescription	*desc,
	struct berval		*val,
	slap_access_t		access,
	AccessControlState	*state,
	slap_mask_t		*maskp )
{
	int				ret = 1;
	int				count;
	AccessControl			*a = NULL;

#ifdef LDAP_DEBUG
	char				accessmaskbuf[ACCESSMASK_MAXLEN];
#endif
	slap_mask_t			mask;
	slap_control_t			control;
	slap_access_t			access_level;
	const char			*attr;
	regmatch_t			matches[MAXREMATCHES];
	int				st_same_attr = 0;

	assert( op != NULL );
	assert( e != NULL );
	assert( desc != NULL );
	assert( maskp != NULL );

	access_level = ACL_LEVEL( access );
	attr = desc->ad_cname.bv_val;

	assert( attr != NULL );

	/* grant database root access */
	if ( be_isroot( op ) ) {
		Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
		mask = ACL_LVL_MANAGE;
		goto done;
	}

	/*
	 * no-user-modification operational attributes are ignored
	 * by ACL_WRITE checking as any found here are not provided
	 * by the user
	 */
	if ( access_level >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
		&& desc != slap_schema.si_ad_entry
		&& desc != slap_schema.si_ad_children )
	{
		Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
			" %s access granted\n",
			attr, 0, 0 );
		goto done;
	}

	/* use backend default access if no backend acls */
	if ( op->o_bd->be_acl == NULL ) {
		int	i;

		Debug( LDAP_DEBUG_ACL,
			"=> slap_access_allowed: backend default %s "
			"access %s to \"%s\"\n",
			access2str( access ),
			op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
			op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
		ret = op->o_bd->be_dfltaccess >= access_level;

		mask = ACL_PRIV_LEVEL;
		for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
			ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
		}

		goto done;
	}

	ret = 0;
	control = ACL_BREAK;

	if ( st_same_attr ) {
		assert( state->as_vd_acl != NULL );

		a = state->as_vd_acl;
		count = state->as_vd_acl_count;
		if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
			mask = state->as_vd_acl_mask;
			AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
			goto vd_access;
		}

	} else {
		if ( state ) state->as_vi_acl = NULL;
		a = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
265
		ACL_PRIV_ASSIGN( mask, *maskp );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
266
267
268
269
		count = 0;
		memset( matches, '\0', sizeof( matches ) );
	}

270
	while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
		MAXREMATCHES, matches, state ) ) != NULL )
	{
		int i;

		for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
			Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
				(int)matches[i].rm_so, (int)matches[i].rm_eo );
			if ( matches[i].rm_so <= matches[0].rm_eo ) {
				int n;
				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
					Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
				}
			}
			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
		}

		if ( state ) {
			if ( state->as_vi_acl == a &&
				( state->as_recorded & ACL_STATE_RECORDED_NV ) )
			{
				Debug( LDAP_DEBUG_ACL,
292
					"=> slap_access_allowed: result from state (%s)\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
293
294
295
296
297
					attr, 0, 0 );
				ret = state->as_result;
				goto done;
			} else {
				Debug( LDAP_DEBUG_ACL,
298
					"=> slap_access_allowed: no res from state (%s)\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
299
300
301
302
303
					attr, 0, 0 );
			}
		}

vd_access:
304
		control = slap_acl_mask( a, &mask, op,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
305
306
307
308
309
310
311
312
313
314
315
316
317
			e, desc, val, MAXREMATCHES, matches, count, state );

		if ( control != ACL_BREAK ) {
			break;
		}

		memset( matches, '\0', sizeof( matches ) );
	}

	if ( ACL_IS_INVALID( mask ) ) {
		Debug( LDAP_DEBUG_ACL,
			"=> slap_access_allowed: \"%s\" (%s) invalid!\n",
			e->e_dn, attr, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
318
		ACL_PRIV_ASSIGN( mask, *maskp );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
319
320
321
322
323
324
325
326

	} else if ( control == ACL_BREAK ) {
		Debug( LDAP_DEBUG_ACL,
			"=> slap_access_allowed: no more rules\n", 0, 0, 0 );

		goto done;
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
327
	ret = ACL_GRANT( mask, access );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
328
329
330
331
332
333
334

	Debug( LDAP_DEBUG_ACL,
		"=> slap_access_allowed: %s access %s by %s\n",
		access2str( access ), ret ? "granted" : "denied",
		accessmask2str( mask, accessmaskbuf, 1 ) );

done:
Pierangelo Masarati's avatar
Pierangelo Masarati committed
335
	ACL_PRIV_ASSIGN( *maskp, mask );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
336
337
338
	return ret;
}

339
340
341
342
343
344
345
346
347
348
349
350
351
int
fe_access_allowed(
	Operation		*op,
	Entry			*e,
	AttributeDescription	*desc,
	struct berval		*val,
	slap_access_t		access,
	AccessControlState	*state,
	slap_mask_t		*maskp )
{
	BackendDB		*be_orig;
	int			rc;

352
353
354
355
356
357
358
359
	/*
	 * NOTE: control gets here if FIXME
	 * if an appropriate backend cannot be selected for the operation,
	 * we assume that the frontend should handle this
	 * FIXME: should select_backend() take care of this,
	 * and return frontendDB instead of NULL?  maybe for some value
	 * of the flags?
	 */
360
361
362
	be_orig = op->o_bd;

	op->o_bd = select_backend( &op->o_req_ndn, 0, 0 );
363
364
365
	if ( op->o_bd == NULL ) {
		op->o_bd = frontendDB;
	}
366
367
368
369
370
371
	rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
	op->o_bd = be_orig;

	return rc;
}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
int
access_allowed_mask(
	Operation		*op,
	Entry			*e,
	AttributeDescription	*desc,
	struct berval		*val,
	slap_access_t		access,
	AccessControlState	*state,
	slap_mask_t		*maskp )
{
	int				ret = 1;
	AccessControl			*a = NULL;
	int				be_null = 0;

#ifdef LDAP_DEBUG
	char				accessmaskbuf[ACCESSMASK_MAXLEN];
#endif
	slap_mask_t			mask;
	slap_control_t			control;
	slap_access_t			access_level;
	const char			*attr;
	int				st_same_attr = 0;
	static AccessControlState	state_init = ACL_STATE_INIT;

	assert( e != NULL );
	assert( desc != NULL );

	access_level = ACL_LEVEL( access );

	assert( access_level > ACL_NONE );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
403
	ACL_INIT( mask );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
	if ( maskp ) ACL_INVALIDATE( *maskp );

	attr = desc->ad_cname.bv_val;

	assert( attr != NULL );

	if ( op && op->o_is_auth_check &&
		( access_level == ACL_SEARCH || access_level == ACL_READ ) )
	{
		access = ACL_AUTH;
	}

	if ( state ) {
		if ( state->as_vd_ad == desc ) {
			if ( state->as_recorded ) {
				if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
					val == NULL )
				{
					return state->as_result;

				} else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
					val != NULL && state->as_vd_acl == NULL )
				{
					return state->as_result;
				}
			}
			st_same_attr = 1;
		} else {
			*state = state_init;
		}

		state->as_vd_ad = desc;
	}

	Debug( LDAP_DEBUG_ACL,
		"=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
		access2str( access ), e->e_dn, attr );

	if ( op == NULL ) {
		/* no-op call */
		goto done;
	}

	if ( op->o_bd == NULL ) {
448
		op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
449
		be_null = 1;
450

Pierangelo Masarati's avatar
Pierangelo Masarati committed
451
452
453
454
455
456
457
#ifdef LDAP_DEVEL
		/*
		 * FIXME: experimental; use first backend rules
		 * iff there is no global_acl (ITS#3100) */
		if ( frontendDB->be_acl != NULL ) {
			op->o_bd = frontendDB;
		}
458
#endif /* LDAP_DEVEL */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
459
460
461
	}
	assert( op->o_bd != NULL );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
462
	/* this is enforced in backend_add() */
463
464
	if ( op->o_bd->bd_info->bi_access_allowed ) {
		/* delegate to backend */
465
466
		ret = op->o_bd->bd_info->bi_access_allowed( op, e,
				desc, val, access, state, &mask );
467
468

	} else {
469
		BackendDB	*be_orig = op->o_bd;
470

471
472
		/* use default (but pass through frontend
		 * for global ACL overlays) */
473
		op->o_bd = frontendDB;
474
475
		ret = frontendDB->bd_info->bi_access_allowed( op, e,
				desc, val, access, state, &mask );
476
		op->o_bd = be_orig;
477
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
478

Pierangelo Masarati's avatar
Pierangelo Masarati committed
479
480
481
482
483
	if ( !ret ) {
		if ( ACL_IS_INVALID( mask ) ) {
			Debug( LDAP_DEBUG_ACL,
				"=> access_allowed: \"%s\" (%s) invalid!\n",
				e->e_dn, attr, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
484
			ACL_INIT( mask );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

		} else if ( control == ACL_BREAK ) {
			Debug( LDAP_DEBUG_ACL,
				"=> access_allowed: no more rules\n", 0, 0, 0 );

			goto done;
		}

		ret = ACL_GRANT( mask, access );
	}

	Debug( LDAP_DEBUG_ACL,
		"=> access_allowed: %s access %s by %s\n",
		access2str( access ), ret ? "granted" : "denied",
		accessmask2str( mask, accessmaskbuf, 1 ) );

done:
	if ( state != NULL ) {
		/* If not value-dependent, save ACL in case of more attrs */
		if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
			state->as_vi_acl = a;
			state->as_result = ret;
		}
		state->as_recorded |= ACL_STATE_RECORDED;
	}
	if ( be_null ) op->o_bd = NULL;
	if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
	return ret;
}

515
516
#else /* !SLAP_OVERLAY_ACCESS */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
517
int
Pierangelo Masarati's avatar
Pierangelo Masarati committed
518
access_allowed_mask(
519
	Operation		*op,
520
	Entry			*e,
521
	AttributeDescription	*desc,
522
523
524
525
	struct berval		*val,
	slap_access_t		access,
	AccessControlState	*state,
	slap_mask_t		*maskp )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
526
{
527
	int				ret = 1;
528
	int				count;
529
	AccessControl			*a = NULL;
530
531
	Backend				*be;
	int				be_null = 0;
Julius Enarusai's avatar
   
Julius Enarusai committed
532

533
#ifdef LDAP_DEBUG
534
	char				accessmaskbuf[ACCESSMASK_MAXLEN];
535
#endif
536
537
538
539
540
541
542
	slap_mask_t			mask;
	slap_control_t			control;
	slap_access_t			access_level;
	const char			*attr;
	regmatch_t			matches[MAXREMATCHES];
	int				st_same_attr = 0;
	static AccessControlState	state_init = ACL_STATE_INIT;
543

544
545
	assert( e != NULL );
	assert( desc != NULL );
546
547
548
549

	access_level = ACL_LEVEL( access );

	assert( access_level > ACL_NONE );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
550
	if ( maskp ) ACL_INVALIDATE( *maskp );
551

552
	attr = desc->ad_cname.bv_val;
553
554

	assert( attr != NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
555

Pierangelo Masarati's avatar
Pierangelo Masarati committed
556
557
	if ( op && op->o_is_auth_check &&
		( access_level == ACL_SEARCH || access_level == ACL_READ ) )
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
558
	{
559
560
		access = ACL_AUTH;
	}
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
561

Pierangelo Masarati's avatar
Pierangelo Masarati committed
562
563
	if ( state ) {
		if ( state->as_vd_ad == desc ) {
564
			if ( state->as_recorded ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
565
				if ( ( state->as_recorded & ACL_STATE_RECORDED_NV ) &&
566
567
568
					val == NULL )
				{
					return state->as_result;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
569
570

				} else if ( ( state->as_recorded & ACL_STATE_RECORDED_VD ) &&
571
572
573
574
575
576
577
578
					val != NULL && state->as_vd_acl == NULL )
				{
					return state->as_result;
				}
			}
			st_same_attr = 1;
		} else {
			*state = state_init;
579
		}
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
580

Howard Chu's avatar
Howard Chu committed
581
		state->as_vd_ad=desc;
582
583
	}

584
585
	Debug( LDAP_DEBUG_ACL,
		"=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
586
		access2str( access ), e->e_dn, attr );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
587

588
589
	if ( op == NULL ) {
		/* no-op call */
590
		goto done;
591
592
	}

593
	be = op->o_bd;
594
	if ( be == NULL ) {
595
		be = LDAP_STAILQ_FIRST(&backendDB);
596
		be_null = 1;
597
598
599
600
#ifdef LDAP_DEVEL
		/*
		 * FIXME: experimental; use first backend rules
		 * iff there is no global_acl (ITS#3100) */
601
		if ( frontendDB->be_acl == NULL ) 
602
603
604
605
#endif
		{
			op->o_bd = be;
		}
606
607
608
	}
	assert( be != NULL );

609
	/* grant database root access */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
610
611
	if ( be_isroot( op ) ) {
		Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
612
		if ( maskp ) {
613
			mask = ACL_LVL_MANAGE;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
614
615
		}

616
		goto done;
617
	}
618

619
620
621
622
623
	/*
	 * no-user-modification operational attributes are ignored
	 * by ACL_WRITE checking as any found here are not provided
	 * by the user
	 */
624
	if ( access_level >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
625
626
		&& desc != slap_schema.si_ad_entry
		&& desc != slap_schema.si_ad_children )
627
	{
Gary Williams's avatar
Gary Williams committed
628
		Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
629
			" %s access granted\n",
630
			attr, 0, 0 );
631
		goto done;
632
633
634
	}

	/* use backend default access if no backend acls */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
635
	if ( be->be_acl == NULL ) {
636
		Debug( LDAP_DEBUG_ACL,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
637
638
			"=> access_allowed: backend default %s "
			"access %s to \"%s\"\n",
639
			access2str( access ),
640
			be->be_dfltaccess >= access_level ? "granted" : "denied",
Howard Chu's avatar
Howard Chu committed
641
			op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
642
		ret = be->be_dfltaccess >= access_level;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
643
644
645
646
647

		if ( maskp ) {
			int	i;

			mask = ACL_PRIV_LEVEL;
648
			for ( i = ACL_NONE; i <= be->be_dfltaccess; i++ ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
649
650
651
652
				mask |= ACL_ACCESS2PRIV( i );
			}
		}

653
		goto done;
654
655
656
657

#ifdef notdef
	/* be is always non-NULL */
	/* use global default access if no global acls */
658
	} else if ( be == NULL && frontendDB->be_acl == NULL ) {
659
660
661
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: global default %s access %s to \"%s\"\n",
			access2str( access ),
Pierangelo Masarati's avatar
Pierangelo Masarati committed
662
663
			frontendDB->be_dfltaccess >= access_level ?
				"granted" : "denied", op->o_dn.bv_val );
664
		ret = frontendDB->be_dfltaccess >= access_level;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
665
666
667
668
669
670
671
672
673
674

		if ( maskp ) {
			int	i;

			mask = ACL_PRIV_LEVEL;
			for ( i = ACL_NONE; i <= global_default_access; i++ ) {
				mask |= ACL_ACCESS2PRIV( i );
			}
		}

675
		goto done;
676
#endif
677
678
	}

679
	ret = 0;
680
	control = ACL_BREAK;
681

Pierangelo Masarati's avatar
Pierangelo Masarati committed
682
	if ( st_same_attr ) {
683
684
685
686
		assert( state->as_vd_acl != NULL );

		a = state->as_vd_acl;
		count = state->as_vd_acl_count;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
687
		if ( !ACL_IS_INVALID( state->as_vd_acl_mask ) ) {
Howard Chu's avatar
Howard Chu committed
688
689
690
691
			mask = state->as_vd_acl_mask;
			AC_MEMCPY( matches, state->as_vd_acl_matches, sizeof(matches) );
			goto vd_access;
		}
692
693

	} else {
694
		if ( state ) state->as_vi_acl = NULL;
695
696
697
		a = NULL;
		ACL_INIT(mask);
		count = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
698
		memset( matches, '\0', sizeof(matches) );
699
700
	}

701
	while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
702
		MAXREMATCHES, matches, state ) ) != NULL )
703
704
	{
		int i;
705

Pierangelo Masarati's avatar
Pierangelo Masarati committed
706
		for ( i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++ ) {
707
			Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
708
709
				(int)matches[i].rm_so, (int)matches[i].rm_eo );
			if ( matches[i].rm_so <= matches[0].rm_eo ) {
710
				int n;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
711
				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++ ) {
712
					Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
713
714
715
716
				}
			}
			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
		}
717

Pierangelo Masarati's avatar
Pierangelo Masarati committed
718
719
720
721
722
723
724
		if ( state ) {
			if ( state->as_vi_acl == a &&
				( state->as_recorded & ACL_STATE_RECORDED_NV ) )
			{
				Debug( LDAP_DEBUG_ACL,
					"access_allowed: result from state (%s)\n",
					attr, 0, 0 );
725
726
				ret = state->as_result;
				goto done;
727
			} else {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
728
729
730
				Debug( LDAP_DEBUG_ACL,
					"access_allowed: no res from state (%s)\n",
					attr, 0, 0 );
Howard Chu's avatar
Howard Chu committed
731
732
733
			}
		}

734
vd_access:
735
		control = slap_acl_mask( a, &mask, op,
736
			e, desc, val, MAXREMATCHES, matches, count, state );
737
738
739
740
741

		if ( control != ACL_BREAK ) {
			break;
		}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
742
		memset( matches, '\0', sizeof(matches) );
743
744
	}

745
746
747
748
	if ( ACL_IS_INVALID( mask ) ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: \"%s\" (%s) invalid!\n",
			e->e_dn, attr, 0 );
749
		ACL_INIT(mask);
750
751
752

	} else if ( control == ACL_BREAK ) {
		Debug( LDAP_DEBUG_ACL,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
753
			"=> access_allowed: no more rules\n", 0, 0, 0 );
754
755

		goto done;
756
	}
757

758
	Debug( LDAP_DEBUG_ACL,
759
		"=> access_allowed: %s access %s by %s\n",
760
		access2str( access ),
761
		ACL_GRANT(mask, access) ? "granted" : "denied",
762
		accessmask2str( mask, accessmaskbuf, 1 ) );
763
764
765
766

	ret = ACL_GRANT(mask, access);

done:
Pierangelo Masarati's avatar
Pierangelo Masarati committed
767
	if ( state != NULL ) {
Howard Chu's avatar
Howard Chu committed
768
		/* If not value-dependent, save ACL in case of more attrs */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
769
		if ( !( state->as_recorded & ACL_STATE_RECORDED_VD ) ) {
Howard Chu's avatar
Howard Chu committed
770
			state->as_vi_acl = a;
771
772
			state->as_result = ret;
		}
773
774
		state->as_recorded |= ACL_STATE_RECORDED;
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
775
	if ( be_null ) op->o_bd = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
776
	if ( maskp ) *maskp = mask;
777
	return ret;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
778
779
}

780
#endif /* SLAP_OVERLAY_ACCESS */
Howard Chu's avatar
Howard Chu committed
781

Kurt Zeilenga's avatar
Kurt Zeilenga committed
782
/*
783
 * slap_acl_get - return the acl applicable to entry e, attribute
Kurt Zeilenga's avatar
Kurt Zeilenga committed
784
785
786
787
 * attr.  the acl returned is suitable for use in subsequent calls to
 * acl_access_allowed().
 */

788
static AccessControl *
789
slap_acl_get(
790
791
	AccessControl *a,
	int			*count,
792
793
	Operation	*op,
	Entry		*e,
794
	AttributeDescription *desc,
795
	struct berval	*val,
796
	int			nmatch,
Howard Chu's avatar
Howard Chu committed
797
798
	regmatch_t	*matches,
	AccessControlState *state )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
799
{
800
	const char *attr;
801
	int dnlen, patlen;
802
	AccessControl *prev;
803

804
805
	assert( e != NULL );
	assert( count != NULL );
806
	assert( desc != NULL );
807

808
	attr = desc->ad_cname.bv_val;
809
810

	assert( attr != NULL );
811

812
	if( a == NULL ) {
813
		if( op->o_bd == NULL ) {
814
			a = frontendDB->be_acl;
815
		} else {
816
			a = op->o_bd->be_acl;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
817
		}
818
		prev = NULL;
819

820
		assert( a != NULL );
821

822
	} else {
823
		prev = a;
824
		a = a->acl_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
825
826
	}

Howard Chu's avatar
Howard Chu committed
827
	dnlen = e->e_nname.bv_len;
828

829
830
831
	for ( ; a != NULL; a = a->acl_next ) {
		(*count) ++;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
832
		if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
833
834
			if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
				Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
835
					*count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
836
837
				if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
					continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
838

839
			} else {
840
				Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
841
					*count, a->acl_dn_pat.bv_val, 0 );
842
				patlen = a->acl_dn_pat.bv_len;
843
844
845
846
847
848
849
850
851
				if ( dnlen < patlen )
					continue;

				if ( a->acl_dn_style == ACL_STYLE_BASE ) {
					/* base dn -- entire object DN must match */
					if ( dnlen != patlen )
						continue;

				} else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
852
					int	rdnlen = -1, sep = 0;
853
854
855
856

					if ( dnlen <= patlen )
						continue;

Pierangelo Masarati's avatar
Pierangelo Masarati committed
857
					if ( patlen > 0 ) {
858
						if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
859
860
861
							continue;
						sep = 1;
					}
862

863
					rdnlen = dn_rdnlen( NULL, &e->e_nname );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
864
					if ( rdnlen != dnlen - patlen - sep )
865
866
867
						continue;

				} else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
868
					if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
869
870
871
872
873
						continue;

				} else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
					if ( dnlen <= patlen )
						continue;
874
					if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
875
876
877
						continue;
				}

878
				if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
879
					continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
880
			}
881
882
883

			Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
				*count, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
884
		}
885

Howard Chu's avatar
Howard Chu committed
886
887
888
889
890
891
892
893
894
895
		if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
			matches[0].rm_so = matches[0].rm_eo = -1;
			continue;
		}

		/* Is this ACL only for a specific value? */
		if ( a->acl_attrval.bv_len ) {
			if ( val == NULL ) {
				continue;
			}
Howard Chu's avatar
Howard Chu committed
896
897
898

			if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) {
				state->as_recorded |= ACL_STATE_RECORDED_VD;
Howard Chu's avatar
Howard Chu committed
899
				state->as_vd_acl = a;
Howard Chu's avatar
Howard Chu committed
900
901
902
903
904
905
				state->as_vd_acl_count = *count;
				state->as_vd_access = a->acl_access;
				state->as_vd_access_count = 1;
				ACL_INVALIDATE( state->as_vd_acl_mask );
			}

Howard Chu's avatar
Howard Chu committed
906
907
908
909
			if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
				Debug( LDAP_DEBUG_ACL,
					"acl_get: valpat %s\n",
					a->acl_attrval.bv_val, 0, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
910
911
				if ( regexec( &a->acl_attrval_re, val->bv_val, 0, NULL, 0 ) )
				{
Howard Chu's avatar
Howard Chu committed
912
					continue;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
913
914
				}

Howard Chu's avatar
Howard Chu committed
915
916
917
918
919
920
921
922
923
			} else {
				int match = 0;
				const char *text;
				Debug( LDAP_DEBUG_ACL,
					"acl_get: val %s\n",
					a->acl_attrval.bv_val, 0, 0 );
	
				if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
					if (value_match( &match, desc,
924
						/* desc->ad_type->sat_equality */ a->acl_attrval_mr, 0,
Howard Chu's avatar
Howard Chu committed
925
926
927
928
929
930
931
932
933
934
935
936
937
						val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
							match )
						continue;
					
				} else {
					int		patlen, vdnlen;
	
					patlen = a->acl_attrval.bv_len;
					vdnlen = val->bv_len;
	
					if ( vdnlen < patlen )
						continue;
	
Howard Chu's avatar
Howard Chu committed
938
					if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
Howard Chu's avatar
Howard Chu committed
939
940
941
						if ( vdnlen > patlen )
							continue;
	
Howard Chu's avatar
Howard Chu committed
942
					} else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
Howard Chu's avatar
Howard Chu committed
943
944
						int rdnlen = -1;
	
945
						if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
Howard Chu's avatar
Howard Chu committed
946
947
948
949
950
951
							continue;
	
						rdnlen = dn_rdnlen( NULL, val );
						if ( rdnlen != vdnlen - patlen - 1 )
							continue;
	
Howard Chu's avatar
Howard Chu committed
952
					} else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
953
						if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
Howard Chu's avatar
Howard Chu committed
954
955
							continue;
	
Howard Chu's avatar
Howard Chu committed
956
					} else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
Howard Chu's avatar
Howard Chu committed
957
958
959
						if ( vdnlen <= patlen )
							continue;
	
960
						if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
Howard Chu's avatar
Howard Chu committed
961
962
963
964
965
966
967
968
969
							continue;
					}
	
					if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ))
						continue;
				}
			}
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
970
		if ( a->acl_filter != NULL ) {
971
			ber_int_t rc = test_filter( NULL, e, a->acl_filter );
972
			if ( rc != LDAP_COMPARE_TRUE ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
973
974
975
				continue;
			}
		}
976

Howard Chu's avatar
Howard Chu committed
977
		Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
Gary Williams's avatar
Gary Williams committed
978
		       *count, attr, 0);
Howard Chu's avatar
Howard Chu committed
979
		return a;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
980
981
	}

982
	Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
983
984
985
	return( NULL );
}

986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
static int
acl_mask_dn(
	Operation		*op,
	Entry			*e,
	AccessControl		*a,
	int			nmatch,
	regmatch_t		*matches,
	slap_dn_access		*b,
	struct berval		*opndn )
{
	/*
	 * if access applies to the entry itself, and the
	 * user is bound as somebody in the same namespace as
	 * the entry, OR the given dn matches the dn pattern
	 */
For faster browsing, not all history is shown. View entire blame