acl.c 27.1 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
5
6
/*
 * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7

Kurt Zeilenga's avatar
Kurt Zeilenga committed
8
9
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
10
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
11
12
13
14

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
16
17
#include "slap.h"

18
19
20
static AccessControl * acl_get(
	AccessControl *ac, int *count,
	Backend *be, Operation *op,
21
22
23
24
25
26
	Entry *e,
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType *type,
#else
	const char *attr,
#endif
27
28
29
30
31
	int nmatches, regmatch_t *matches );

static slap_control_t acl_mask(
	AccessControl *ac, slap_access_mask_t *mask,
	Backend *be, Connection *conn, Operation *op,
32
33
34
35
36
37
38
	Entry *e,
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType *type,
#else
	const char *attr,
#endif
	struct berval *val,
39
40
	regmatch_t *matches );

41
#ifdef SLAPD_ACI_ENABLED
42
static int aci_mask(
43
44
	Backend *be,
	Operation *op,
45
46
47
48
49
50
51
	Entry *e,
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType *type,
#else
	const char *attr,
#endif
	struct berval *val, struct berval *aci,
52
	regmatch_t *matches, slap_access_t *grant, slap_access_t *deny );
53
54

char *supportedACIMechs[] = {
55
	"1.3.6.1.4.1.4203.666.7.1",	/* experimental IETF aci family */
56
57
58
	"1.3.6.1.4.1.4203.666.7.2",	/* experimental OpenLDAP aci family */
	NULL
};
59
60
#endif

61
62
63
64
static int	regex_matches(
	char *pat, char *str, char *buf, regmatch_t *matches);
static void	string_expand(
	char *newbuf, int bufsiz, char *pattern,
65
	char *match, regmatch_t *matches);
66

Kurt Zeilenga's avatar
Kurt Zeilenga committed
67
68

/*
69
 * access_allowed - check whether op->o_ndn is allowed the requested access
Kurt Zeilenga's avatar
Kurt Zeilenga committed
70
 * to entry e, attribute attr, value val.  if val is null, access to
71
 * the whole attribute is assumed (all values).
Kurt Zeilenga's avatar
Kurt Zeilenga committed
72
 *
73
74
75
76
77
78
79
80
 * This routine loops through all access controls and calls
 * acl_mask() on each applicable access control.
 * The loop exits when a definitive answer is reached or
 * or no more controls remain.
 *
 * returns:
 *		0	access denied
 *		1	access granted
Kurt Zeilenga's avatar
Kurt Zeilenga committed
81
82
83
84
85
86
87
88
 */

int
access_allowed(
    Backend		*be,
    Connection		*conn,
    Operation		*op,
    Entry		*e,
89
90
91
92
93
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType	*attr,
#else
    const char		*attr,
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
94
    struct berval	*val,
95
    slap_access_t	access )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
96
{
97
	int				count;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
98
	AccessControl	*a;
99
#ifdef LDAP_DEBUG
Kurt Zeilenga's avatar
Kurt Zeilenga committed
100
	char accessmaskbuf[ACCESSMASK_MAXLEN];
101
#endif
102
103
	slap_access_mask_t mask;
	slap_control_t control;
104
105

	regmatch_t       matches[MAXREMATCHES];
Kurt Zeilenga's avatar
Kurt Zeilenga committed
106

107
108
109
110
	Debug( LDAP_DEBUG_ACL,
		"=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
	    access2str( access ),
		e->e_dn, attr );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
111

112
113
114
115
	assert( be != NULL );
	assert( e != NULL );
	assert( attr != NULL );
	assert( access > ACL_NONE );
116

117
118
119
120
121
122
123
	/* grant database root access */
	if ( be != NULL && be_isroot( be, op->o_ndn ) ) {
		Debug( LDAP_DEBUG_ACL,
		    "<= root access granted\n",
			0, 0, 0 );
		return 1;
	}
124

125
126
127
128
129
	/*
	 * no-user-modification operational attributes are ignored
	 * by ACL_WRITE checking as any found here are not provided
	 * by the user
	 */
130
131
132
133
134
135
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	if ( access >= ACL_WRITE && is_at_no_user_mod( attr ) )
#else
	if ( access >= ACL_WRITE && oc_check_op_no_usermod_attr( attr ) )
#endif
	{
136
137
 		Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
			" %s access granted\n",
138
			attr, 0, 0 );
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
		return 1;
	}

	/* use backend default access if no backend acls */
	if( be != NULL && be->be_acl == NULL ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: backend default %s access %s to \"%s\"\n",
			access2str( access ),
			be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn );

		return be->be_dfltaccess >= access;

#ifdef notdef
	/* be is always non-NULL */
	/* use global default access if no global acls */
	} else if ( be == NULL && global_acl == NULL ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: global default %s access %s to \"%s\"\n",
			access2str( access ),
			global_default_access >= access ? "granted" : "denied", op->o_dn );

		return global_default_access >= access;
#endif
162
163
	}

164
	ACL_INIT(mask);
165
	memset(matches, 0, sizeof(matches));
166
167
168
169
	
	control = ACL_BREAK;
	a = NULL;
	count = 0;
170

171
172
173
	while( a = acl_get( a, &count, be, op, e, attr, MAXREMATCHES, matches ) )
	{
		int i;
174
175

		for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
176
			Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
Hallvard Furuseth's avatar
Hallvard Furuseth committed
177
			       (int)matches[i].rm_so, (int)matches[i].rm_eo );
178
179

			if( matches[i].rm_so <= matches[0].rm_eo ) {
180
				int n;
181
				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
182
					Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
183
184
185
186
				}
			}
			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
		}
187
188
189
190
191
192
193
194
195

		control = acl_mask( a, &mask, be, conn, op,
			e, attr, val, matches );

		if ( control != ACL_BREAK ) {
			break;
		}

		memset(matches, 0, sizeof(matches));
196
197
	}

198
199
200
201
202
203
204
205
206
207
208
	if ( ACL_IS_INVALID( mask ) ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: \"%s\" (%s) invalid!\n",
			e->e_dn, attr, 0 );
		ACL_INIT( mask );

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

210
	Debug( LDAP_DEBUG_ACL,
211
		"=> access_allowed: %s access %s by %s\n",
212
		access2str( access ),
213
		ACL_GRANT(mask, access) ? "granted" : "denied",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
214
		accessmask2str( mask, accessmaskbuf ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
215

216
	return ACL_GRANT(mask, access);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
217
218
219
}

/*
220
 * acl_get - return the acl applicable to entry e, attribute
Kurt Zeilenga's avatar
Kurt Zeilenga committed
221
222
223
224
 * attr.  the acl returned is suitable for use in subsequent calls to
 * acl_access_allowed().
 */

225
226
227
228
static AccessControl *
acl_get(
	AccessControl *a,
	int			*count,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
229
    Backend		*be,
230
    Operation	*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
231
    Entry		*e,
232
233
234
235
236
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType *attr,
#else
    const char	*attr,
#endif
237
    int			nmatch,
238
    regmatch_t	*matches )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
239
{
240
241
	assert( e != NULL );
	assert( count != NULL );
242

243
244
245
246
247
	if( a == NULL ) {
		if( be == NULL ) {
			a = global_acl;
		} else {
			a = be->be_acl;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
248
		}
249

250
		assert( a != NULL );
251

252
253
	} else {
		a = a->acl_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
254
255
	}

256
257
258
	for ( ; a != NULL; a = a->acl_next ) {
		(*count) ++;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
259
		if (a->acl_dn_pat != NULL) {
260
261
			Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
				*count, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub );
262

263
			if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0)) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
264
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
265

266
			} else {
267
				Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
268
					*count, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
269
270
			}
		}
271

Kurt Zeilenga's avatar
Kurt Zeilenga committed
272
		if ( a->acl_filter != NULL ) {
273
274
			ber_int_t rc = test_filter( NULL, NULL, NULL, e, a->acl_filter );
			if ( rc != LDAP_COMPARE_TRUE ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
275
276
277
				continue;
			}
		}
278

279
280
        Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
			*count, attr, 0);
281
282
283
284

		if ( attr == NULL || a->acl_attrs == NULL ||
			charray_inlist( a->acl_attrs, attr ) )
		{
285
286
287
288
			Debug( LDAP_DEBUG_ACL,
				"<= acl_get: [%d] acl %s attr: %s\n",
				*count, e->e_dn, attr );
			return a;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
289
		}
290
		matches[0].rm_so = matches[0].rm_eo = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
291
292
	}

293
	Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
294
295
296
	return( NULL );
}

297

Kurt Zeilenga's avatar
Kurt Zeilenga committed
298
/*
299
 * acl_mask - modifies mask based upon the given acl and the
Kurt Zeilenga's avatar
Kurt Zeilenga committed
300
301
302
303
304
305
306
 * requested access to entry e, attribute attr, value val.  if val
 * is null, access to the whole attribute is assumed (all values).
 *
 * returns	0	access NOT allowed
 *		1	access allowed
 */

307
308
static slap_control_t
acl_mask(
Kurt Zeilenga's avatar
Kurt Zeilenga committed
309
    AccessControl	*a,
310
	slap_access_mask_t *mask,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
311
    Backend		*be,
312
313
    Connection	*conn,
    Operation	*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
314
    Entry		*e,
315
316
317
318
319
#ifdef SLAPD_SCHEMA_NOT_COMPAT
	AttributeType *attr,
#else
    const char	*attr,
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
320
    struct berval	*val,
321
	regmatch_t	*matches
Kurt Zeilenga's avatar
Kurt Zeilenga committed
322
323
324
)
{
	int		i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
325
	Access	*b;
326
#ifdef LDAP_DEBUG
Kurt Zeilenga's avatar
Kurt Zeilenga committed
327
	char accessmaskbuf[ACCESSMASK_MAXLEN];
328
#endif
329
330
331

	assert( a != NULL );
	assert( mask != NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
332

333
	Debug( LDAP_DEBUG_ACL,
334
		"=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
335
		e->e_dn, attr, 0 );
336
337

	Debug( LDAP_DEBUG_ACL,
338
		"=> acl_mask: to value \"%s\" by \"%s\", (%s) \n",
339
		val ? val->bv_val : "*",
340
		op->o_ndn ?  op->o_ndn : "",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
341
		accessmask2str( *mask, accessmaskbuf ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
342

343
344
	for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
		slap_access_mask_t oldmask, modmask;
345

346
		ACL_INVALIDATE( modmask );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
347

Kurt Zeilenga's avatar
Kurt Zeilenga committed
348
349
		/* AND <who> clauses */
		if ( b->a_dn_pat != NULL ) {
350
			Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
351
				b->a_dn_pat, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
352
353
354
355
356
			/*
			 * 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
			 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
357
			if ( strcmp( b->a_dn_pat, "anonymous" ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
358
359
360
361
				if (op->o_ndn != NULL && op->o_ndn[0] != '\0' ) {
					continue;
				}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
362
363
364
365
366
367
			} else if ( strcmp( b->a_dn_pat, "users" ) == 0 ) {
				if (op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
					continue;
				}

			} else if ( strcmp( b->a_dn_pat, "self" ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
368
369
370
371
				if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
					continue;
				}
				
372
				if ( e->e_dn == NULL || strcmp( e->e_ndn, op->o_ndn ) != 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
373
374
375
					continue;
				}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
376
377
378
379
380
381
382
			} else if ( strcmp( b->a_dn_pat, "*" ) != 0 ) {
				int ret = regex_matches( b->a_dn_pat,
					op->o_ndn, e->e_ndn, matches );

				if( ret == 0 ) {
					continue;
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
383
384
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
385

386
		if ( b->a_sockurl_pat != NULL ) {
387
			Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
388
				b->a_sockurl_pat, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
389

Kurt Zeilenga's avatar
Kurt Zeilenga committed
390
			if ( strcmp( b->a_sockurl_pat, "*" ) != 0 &&
391
				!regex_matches( b->a_sockurl_pat, conn->c_listener_url,
392
				e->e_ndn, matches ) ) 
393
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
394
395
396
				continue;
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
397

Kurt Zeilenga's avatar
Kurt Zeilenga committed
398
		if ( b->a_domain_pat != NULL ) {
399
			Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
400
				b->a_domain_pat, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
401

Kurt Zeilenga's avatar
Kurt Zeilenga committed
402
			if ( strcmp( b->a_domain_pat, "*" ) != 0 &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
403
				!regex_matches( b->a_domain_pat, conn->c_peer_domain,
404
				e->e_ndn, matches ) ) 
Kurt Zeilenga's avatar
Kurt Zeilenga committed
405
406
			{
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
407
408
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
409
410

		if ( b->a_peername_pat != NULL ) {
411
			Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
412
413
				b->a_peername_pat, 0, 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
414
			if ( strcmp( b->a_peername_pat, "*" ) != 0 &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
415
				!regex_matches( b->a_peername_pat, conn->c_peer_name,
416
				e->e_ndn, matches ) )
417
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
418
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
419
420
421
			}
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
422
		if ( b->a_sockname_pat != NULL ) {
423
			Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
424
425
				b->a_sockname_pat, 0, 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
426
			if ( strcmp( b->a_sockname_pat, "*" ) != 0 &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
427
				!regex_matches( b->a_sockname_pat, conn->c_sock_name,
428
				e->e_ndn, matches ) )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
429
430
			{
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
431
432
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
433
434
435
436
437

		if ( b->a_dn_at != NULL && op->o_ndn != NULL ) {
			Attribute	*at;
			struct berval	bv;

438
			Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
439
440
441
442
443
444
				b->a_dn_at, 0, 0);

			bv.bv_val = op->o_ndn;
			bv.bv_len = strlen( bv.bv_val );

			/* see if asker is listed in dnattr */ 
445
			if ( (at = attr_find( e->e_attrs, b->a_dn_at )) != NULL
446
447
448
#ifdef SLAPD_SCHEMA_NOT_COMPAT
				/* not yet implemented */
#else
449
450
451
				&& value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0
#endif
			)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
452
			{
453
				if ( b->a_dn_self && 
454
					(val == NULL
455
456
457
#ifdef SLAPD_SCHEMA_NOT_COMPAT
					/* not yet implemented */
#else
458
459
460
					|| value_cmp( &bv, val, at->a_syntax, 2 )
#endif
					) )
461
				{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
462
463
464
465
					continue;
				}

			/* asker not listed in dnattr - check for self access */
466
			} else if ( ! b->a_dn_self || val == NULL
467
468
469
#ifdef SLAPD_SCHEMA_NOT_COMPAT
					/* not yet implemented */
#else
470
471
472
				|| value_cmp( &bv, val, at->a_syntax, 2 ) != 0
#endif
			)
473
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
474
475
476
				continue;
			}
		}
477

Kurt Zeilenga's avatar
Kurt Zeilenga committed
478
		if ( b->a_group_pat != NULL && op->o_ndn != NULL ) {
479
			char buf[1024];
480
481
482

			/* b->a_group is an unexpanded entry name, expanded it should be an 
			 * entry with objectclass group* and we test to see if odn is one of
483
			 * the values in the attribute group
484
485
			 */
			/* see if asker is listed in dnattr */
486
			string_expand(buf, sizeof(buf), b->a_group_pat, e->e_ndn, matches);
487
			if ( dn_normalize(buf) == NULL ) {
488
489
490
				/* did not expand to a valid dn */
				continue;
			}
491

492
			if (backend_group(be, e, buf, op->o_ndn,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
493
				b->a_group_oc, b->a_group_at) != 0)
494
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
495
				continue;
496
497
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
498

499
#ifdef SLAPD_ACI_ENABLED
500
		if ( b->a_aci_at != NULL ) {
501
			Attribute	*at;
502
			slap_access_t grant, deny, tgrant, tdeny;
503
504
505

			/* this case works different from the others above.
			 * since aci's themselves give permissions, we need
506
			 * to first check b->a_mask, the ACL's access level.
507
508
509
510
511
512
513
514
515
516
			 */

			if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
				continue;
			}

			if ( e->e_dn == NULL ) {
				continue;
			}

517
518
			/* first check if the right being requested
			 * is allowed by the ACL clause.
519
			 */
520
			if ( ! ACL_GRANT( b->a_mask, *mask ) ) {
521
522
523
524
525
526
527
528
529
				continue;
			}

			/* get the aci attribute */
			at = attr_find( e->e_attrs, b->a_aci_at );
			if ( at == NULL ) {
				continue;
			}

530
531
532
533
			/* start out with nothing granted, nothing denied */
			ACL_INIT(tgrant);
			ACL_INIT(tdeny);

534
535
536
537
538
			/* the aci is an multi-valued attribute.  The
			 * rights are determined by OR'ing the individual
			 * rights given by the acis.
			 */
			for ( i = 0; at->a_vals[i] != NULL; i++ ) {
539
540
541
				if (aci_mask( be, op,
					e, attr, val, at->a_vals[i],
					matches, &grant, &deny ) != 0)
542
				{
543
544
					tgrant |= grant;
					tdeny |= deny;
545
546
				}
			}
547

548
549
550
551
552
553
			/* remove anything that the ACL clause does not allow */
			tgrant &= b->a_mask & ACL_PRIV_MASK;
			tdeny &= ACL_PRIV_MASK;

			/* see if we have anything to contribute */
			if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
554
555
556
				continue;
			}

557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
			/* this could be improved by changing acl_mask so that it can deal with
			 * by clauses that return grant/deny pairs.  Right now, it does either
			 * additive or subtractive rights, but not both at the same time.  So,
			 * we need to combine the grant/deny pair into a single rights mask in
			 * a smart way:  if either grant or deny is "empty", then we use the
			 * opposite as is, otherwise we remove any denied rights from the grant
			 * rights mask and construct an additive mask.
			 */
			if (ACL_IS_INVALID(tdeny)) {
				modmask = tgrant | ACL_PRIV_ADDITIVE;

			} else if (ACL_IS_INVALID(tgrant)) {
				modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;

			} else {
				modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
			}

575
		} else
576
#endif
577
578
579
580
		{
			modmask = b->a_mask;
		}

581

Kurt Zeilenga's avatar
Kurt Zeilenga committed
582
		Debug( LDAP_DEBUG_ACL,
583
			"<= acl_mask: [%d] applying %s (%s)\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
584
			i, accessmask2str( modmask, accessmaskbuf ), 
585
586
587
588
589
590
591
			b->a_type == ACL_CONTINUE
				? "continue"
				: b->a_type == ACL_BREAK
					? "break"
					: "stop" );

		/* save old mask */
592
		oldmask = *mask;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
593

594
		if( ACL_IS_ADDITIVE(modmask) ) {
595
			/* add privs */
596
			ACL_PRIV_SET( *mask, modmask );
597
598
599
600

			/* cleanup */
			ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );

601
		} else if( ACL_IS_SUBTRACTIVE(modmask) ) {
602
			/* substract privs */
603
604
			ACL_PRIV_CLR( *mask, modmask );

605
606
607
			/* cleanup */
			ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );

608
		} else {
609
610
			/* assign privs */
			*mask = modmask;
611
612
613
		}

		Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
614
615
			"<= acl_mask: [%d] mask: %s\n",
			i, accessmask2str(*mask, accessmaskbuf), 0 );
616
617
618
619
620
621
622
623

		if( b->a_type == ACL_CONTINUE ) {
			continue;

		} else if ( b->a_type == ACL_BREAK ) {
			return ACL_BREAK;

		} else {
624
			return ACL_STOP;
625
626
		}
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
627

628
629
	Debug( LDAP_DEBUG_ACL,
		"<= acl_mask: no more <who> clauses, returning %s (stop)\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
630
		accessmask2str(*mask, accessmaskbuf), 0, 0 );
631
	return ACL_STOP;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
632
633
634
}

/*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
635
 * acl_check_modlist - check access control on the given entry to see if
Kurt Zeilenga's avatar
Kurt Zeilenga committed
636
 * it allows the given modifications by the user associated with op.
637
638
 * returns	1	if mods allowed ok
 *			0	mods not allowed
Kurt Zeilenga's avatar
Kurt Zeilenga committed
639
640
641
 */

int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
642
acl_check_modlist(
Kurt Zeilenga's avatar
Kurt Zeilenga committed
643
644
645
646
    Backend	*be,
    Connection	*conn,
    Operation	*op,
    Entry	*e,
647
    Modifications	*mlist
Kurt Zeilenga's avatar
Kurt Zeilenga committed
648
649
650
)
{
	int		i;
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682

	assert( be != NULL );

	/* short circuit root database access */
	if ( be_isroot( be, op->o_ndn ) ) {
		Debug( LDAP_DEBUG_ACL,
			"<= acl_access_allowed: granted to database root\n",
		    0, 0, 0 );
		return 1;
	}

	/* use backend default access if no backend acls */
	if( be != NULL && be->be_acl == NULL ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: backend default %s access %s to \"%s\"\n",
			access2str( ACL_WRITE ),
			be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn );

		return be->be_dfltaccess >= ACL_WRITE;

#ifdef notdef
	/* be is always non-NULL */
	/* use global default access if no global acls */
	} else if ( be == NULL && global_acl == NULL ) {
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: global default %s access %s to \"%s\"\n",
			access2str( ACL_WRITE ),
			global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn );

		return global_default_access >= ACL_WRITE;
#endif
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
683

Kurt Zeilenga's avatar
Kurt Zeilenga committed
684
	for ( ; mlist != NULL; mlist = mlist->ml_next ) {
685
686
687
688
689
		/*
		 * no-user-modification operational attributes are ignored
		 * by ACL_WRITE checking as any found here are not provided
		 * by the user
		 */
690
691
692
#ifdef SLAPD_SCHEMA_NOT_COMPAT
		/* not yet implemented */
#else
693
		if ( oc_check_op_no_usermod_attr( mlist->ml_type ) ) {
694
695
 			Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
				" modify access granted\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
696
				mlist->ml_type, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
697
698
699
			continue;
		}

700
		switch ( mlist->ml_op ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
701
702
		case LDAP_MOD_REPLACE:
		case LDAP_MOD_ADD:
Kurt Zeilenga's avatar
Kurt Zeilenga committed
703
			if ( mlist->ml_bvalues == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
704
705
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
706
			for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
707
708
709
				if ( ! access_allowed( be, conn, op, e,
					mlist->ml_type, mlist->ml_bvalues[i],
					ACL_WRITE ) )
710
				{
711
					return( 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
712
713
714
715
716
				}
			}
			break;

		case LDAP_MOD_DELETE:
Kurt Zeilenga's avatar
Kurt Zeilenga committed
717
			if ( mlist->ml_bvalues == NULL ) {
718
719
720
				if ( ! access_allowed( be, conn, op, e,
					mlist->ml_type, NULL, 
					ACL_WRITE ) )
721
				{
722
					return( 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
723
724
725
				}
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
726
			for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
727
728
729
				if ( ! access_allowed( be, conn, op, e,
					mlist->ml_type, mlist->ml_bvalues[i],
					ACL_WRITE ) )
730
				{
731
					return( 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
732
733
734
735
				}
			}
			break;
		}
736
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
737
738
	}

739
	return( 1 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
740
741
}

742
#ifdef SLAPD_ACI_ENABLED
Hallvard Furuseth's avatar
Hallvard Furuseth committed
743
static char *
744
aci_bvstrdup( struct berval *bv )
745
746
747
748
749
750
751
752
753
754
755
{
	char *s;

	s = (char *)ch_malloc(bv->bv_len + 1);
	if (s != NULL) {
		memcpy(s, bv->bv_val, bv->bv_len);
		s[bv->bv_len] = 0;
	}
	return(s);
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
756
static int
757
758
759
aci_strbvcmp(
	const char *s,
	struct berval *bv )
760
761
762
763
764
765
766
{
	int res, len;

	res = strncasecmp( s, bv->bv_val, bv->bv_len );
	if (res)
		return(res);
	len = strlen(s);
767
	if (len > (int)bv->bv_len)
768
		return(1);
769
	if (len < (int)bv->bv_len)
770
771
772
773
		return(-1);
	return(0);
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
774
static int
775
776
777
778
779
aci_get_part(
	struct berval *list,
	int ix,
	char sep,
	struct berval *bv )
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
{
	int len;
	char *p;

	if (bv) {
		bv->bv_len = 0;
		bv->bv_val = NULL;
	}
	len = list->bv_len;
	p = list->bv_val;
	while (len >= 0 && --ix >= 0) {
		while (--len >= 0 && *p++ != sep) ;
	}
	while (len >= 0 && *p == ' ') {
		len--;
		p++;
	}
	if (len < 0)
		return(-1);

	if (!bv)
		return(0);

	bv->bv_val = p;
	while (--len >= 0 && *p != sep) {
		bv->bv_len++;
		p++;
	}
	while (bv->bv_len > 0 && *--p == ' ')
		bv->bv_len--;
	return(bv->bv_len);
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
813
static int
814
815
aci_list_map_rights(
	struct berval *list )
816
817
{
	struct berval bv;
818
	slap_access_t mask;
819
	int i;
820

821
	ACL_INIT(mask);
822
823
824
825
826
	for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
		if (bv.bv_len <= 0)
			continue;
		switch (*bv.bv_val) {
		case 'c':
827
			ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
828
829
830
831
832
833
834
			break;
		case 's':
			/* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
			 * the right 's' to mean "set", but in the examples states
			 * that the right 's' means "search".  The latter definition
			 * is used here.
			 */
835
			ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
836
837
			break;
		case 'r':
838
			ACL_PRIV_SET(mask, ACL_PRIV_READ);
839
840
			break;
		case 'w':
841
			ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
842
843
844
845
846
847
			break;
		case 'x':
			/* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
			 * define any equivalent to the AUTH right, so I've just used
			 * 'x' for now.
			 */
848
			ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
849
850
851
852
			break;
		default:
			break;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
853

854
	}
855
	return(mask);
856
857
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
858
static int
859
860
861
862
aci_list_has_attr(
	struct berval *list,
	const char *attr,
	struct berval *val )
863
{
864
	struct berval bv, left, right;
865
866
867
	int i;

	for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
		if (aci_get_part(&bv, 0, '=', &left) < 0
			|| aci_get_part(&bv, 1, '=', &right) < 0)
		{
			if (aci_strbvcmp(attr, &bv) == 0)
				return(1);
		} else if (val == NULL) {
			if (aci_strbvcmp(attr, &left) == 0)
				return(1);
		} else {
			if (aci_strbvcmp(attr, &left) == 0) {
				/* this is experimental code that implements a
				 * simple (prefix) match of the attribute value.
				 * the ACI draft does not provide for aci's that
				 * apply to specific values, but it would be
				 * nice to have.  If the <attr> part of an aci's
				 * rights list is of the form <attr>=<value>,
				 * that means the aci applies only to attrs with
				 * the given value.  Furthermore, if the attr is
				 * of the form <attr>=<value>*, then <value> is
				 * treated as a prefix, and the aci applies to 
				 * any value with that prefix.
				 *
				 * Ideally, this would allow r.e. matches.
				 */
				if (aci_get_part(&right, 0, '*', &left) < 0
					|| right.bv_len <= left.bv_len)
				{
					if (aci_strbvcmp(val->bv_val, &right) == 0)
						return(1);
				} else if (val->bv_len >= left.bv_len) {
					if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
						return(1);
				}
			}
902
903
904
905
906
		}
	}
	return(0);
}

907
static slap_access_t
908
909
910
911
aci_list_get_attr_rights(
	struct berval *list,
	const char *attr,
	struct berval *val )
912
{
Hallvard Furuseth's avatar
Hallvard Furuseth committed
913
    struct berval bv;
914
915
    slap_access_t mask;
    int i;
916
917

	/* loop through each rights/attr pair, skip first part (action) */
918
	ACL_INIT(mask);
919
	for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
920
		if (aci_list_has_attr(&bv, attr, val) == 0)
921
922
923
			continue;
		if (aci_get_part(list, i, ';', &bv) < 0)
			continue;
924
		mask |= aci_list_map_rights(&bv);
925
	}
926
	return(mask);
927
928
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
929
static int
930
aci_list_get_rights(
931
	struct berval *list,
932
	const char *attr,
933
934
	struct berval *val,
	slap_access_t *grant,
935
	slap_access_t *deny )
936
937
{
    struct berval perm, actn;
938
939
    slap_access_t *mask;
    int i, found;
940
941
942
943
944

	if (attr == NULL || *attr == 0 || strcasecmp(attr, "entry") == 0) {
		attr = "[entry]";
	}

945
946
947
	found = 0;
	ACL_INIT(*grant);
	ACL_INIT(*deny);
948
949
950
951
952
	/* loop through each permissions clause */
	for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
		if (aci_get_part(&perm, 0, ';', &actn) < 0)
			continue;
		if (aci_strbvcmp( "grant", &actn ) == 0) {
953
			mask = grant;
954
		} else if (aci_strbvcmp( "deny", &actn ) == 0) {
955
			mask = deny;
956
957
958
959
		} else {
			continue;
		}

960
961
962
		found = 1;
		*mask |= aci_list_get_attr_rights(&perm, attr, val);
		*mask |= aci_list_get_attr_rights(&perm, "[all]", NULL);
963
	}
964
	return(found);
965
966
}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
967
static int
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
aci_group_member (
	struct berval *subj,
	char *grpoc,
	char *grpat,
    Backend		*be,
    Entry		*e,
    Operation		*op,
	regmatch_t	*matches
)
{
	struct berval bv;
	char *subjdn, *grpdn;
	int rc = 0;

	/* format of string is "group/objectClassValue/groupAttrName" */
	if (aci_get_part(subj, 0, '/', &bv) < 0)
		return(0);
	subjdn = aci_bvstrdup(&bv);
	if (subjdn == NULL)
		return(0);

	if (aci_get_part(subj, 1, '/', &bv) < 0)
		grpoc = ch_strdup(grpoc);
	else
		grpoc = aci_bvstrdup(&bv);

	if (aci_get_part(subj, 2, '/', &bv) < 0)
		grpat = ch_strdup(grpat);
	else
		grpat = aci_bvstrdup(&bv);

	grpdn = (char *)ch_malloc(1024);
	if (grpoc != NULL && grpat != NULL && grpdn != NULL) {
1001
		string_expand(grpdn, 1024, subjdn, e->e_ndn, matches);
1002
		if ( dn_normalize(grpdn) != NULL ) {
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
			rc = (backend_group(be, e, grpdn, op->o_ndn, grpoc, grpat) == 0);
		}
		ch_free(grpdn);
	}
	if (grpat != NULL)
		ch_free(grpat);
	if (grpoc != NULL)
		ch_free(grpoc);
	ch_free(subjdn);
	return(rc);
}

1015
static int
1016
aci_mask(
1017
1018
    Backend			*be,
    Operation		*op,
1019
    Entry			*e,
1020
    const char		*attr,
1021
1022
1023
1024
1025
    struct berval	*val,
    struct berval	*aci,
	regmatch_t		*matches,
	slap_access_t	*grant,
	slap_access_t	*deny
1026
1027
1028
1029
)
{
    struct berval bv, perms, sdn;
    char *subjdn;
1030
	int rc, i;
1031
1032
1033
1034

	/* parse an aci of the form:
		oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN

1035
	   See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
1036
1037
1038
1039
1040
1041
1042
1043
1044
	   a full description of the format for this attribute.

	   For now, this routine only supports scope=entry.
	 */

	/* check that the aci has all 5 components */
	if (aci_get_part(aci, 4, '#', NULL) < 0)
		return(0);

1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
	/* check that the aci family is supported */
	if (aci_get_part(aci, 0, '#', &bv) < 0)
		return(0);
	for (i = 0; supportedACIMechs[i] != NULL; i++) {
		if (aci_strbvcmp( supportedACIMechs[i], &bv ) == 0)
			break;
	}
	if (supportedACIMechs[i] == NULL)
		return(0);

1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
	/* check that the scope is "entry" */
	if (aci_get_part(aci, 1, '#', &bv) < 0
		|| aci_strbvcmp( "entry", &bv ) != 0)
	{
		return(0);
	}

	/* get the list of permissions clauses, bail if empty */
	if (aci_get_part(aci, 2, '#', &perms) <= 0)
		return(0);

	/* check if any permissions allow desired access */
1067
	if (aci_list_get_rights(&perms, attr, val, grant, deny) == 0)
1068
1069
1070
1071
1072
1073
1074
1075
		return(0);

	/* see if we have a DN match */
	if (aci_get_part(aci, 3, '#', &bv) < 0)
		return(0);

	if (aci_get_part(aci, 4, '#', &sdn) < 0)
		return(0);
1076

1077
1078
1079
1080
	if (aci_strbvcmp( "access-id", &bv ) == 0) {
		subjdn = aci_bvstrdup(&sdn);
		if (subjdn == NULL)
			return(0);