acl.c 42.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
/*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
6
 * 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
#include "slap.h"
17
#include "sets.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
18

19
20
21
static AccessControl * acl_get(
	AccessControl *ac, int *count,
	Backend *be, Operation *op,
22
	Entry *e,
23
	AttributeDescription *desc,
24
25
26
	int nmatches, regmatch_t *matches );

static slap_control_t acl_mask(
27
	AccessControl *ac, slap_mask_t *mask,
28
	Backend *be, Connection *conn, Operation *op,
29
	Entry *e,
30
	AttributeDescription *desc,
31
	struct berval *val,
32
33
	regmatch_t *matches );

34
#ifdef SLAPD_ACI_ENABLED
35
static int aci_mask(
36
	Backend *be,
37
    Connection *conn,
38
	Operation *op,
39
	Entry *e,
40
	AttributeDescription *desc,
41
42
43
44
45
	struct berval *val,
	struct berval *aci,
	regmatch_t *matches,
	slap_access_t *grant,
	slap_access_t *deny );
46
47
#endif

48
49
50
51
static int	regex_matches(
	char *pat, char *str, char *buf, regmatch_t *matches);
static void	string_expand(
	char *newbuf, int bufsiz, char *pattern,
52
	char *match, regmatch_t *matches);
53

54
55
56
char **aci_set_gather (void *cookie, char *name, char *attr);
static int aci_match_set ( struct berval *subj, Backend *be,
    Entry *e, Connection *conn, Operation *op, int setref );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
57
58

/*
59
 * access_allowed - check whether op->o_ndn is allowed the requested access
Kurt Zeilenga's avatar
Kurt Zeilenga committed
60
 * to entry e, attribute attr, value val.  if val is null, access to
61
 * the whole attribute is assumed (all values).
Kurt Zeilenga's avatar
Kurt Zeilenga committed
62
 *
63
64
65
66
67
68
69
70
 * 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
71
72
73
74
75
76
77
78
 */

int
access_allowed(
    Backend		*be,
    Connection		*conn,
    Operation		*op,
    Entry		*e,
79
	AttributeDescription	*desc,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
80
    struct berval	*val,
81
    slap_access_t	access )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
82
{
83
	int				count;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
84
	AccessControl	*a;
85
#ifdef LDAP_DEBUG
Kurt Zeilenga's avatar
Kurt Zeilenga committed
86
	char accessmaskbuf[ACCESSMASK_MAXLEN];
87
#endif
88
	slap_mask_t mask;
89
	slap_control_t control;
90
91
	const char *attr;
	regmatch_t matches[MAXREMATCHES];
92

93
94
95
	assert( e != NULL );
	assert( desc != NULL );
	assert( access > ACL_NONE );
96

97
	attr = desc->ad_cname.bv_val;
98
99

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

101
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
102
	LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
103
104
		"access_allowed: conn %d %s access to \"%s\" \"%s\" requested\n",
		conn ? conn->c_connid : -1, access2str( access ), e->e_dn, attr ));
105
#else
106
107
	Debug( LDAP_DEBUG_ACL,
		"=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
108
	    access2str( access ), e->e_dn, attr );
109
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110

111
112
113
114
115
116
117
118
	if ( op == NULL ) {
		/* no-op call */
		return 1;
	}

	if ( be == NULL ) be = &backends[0];
	assert( be != NULL );

119
120
	/* grant database root access */
	if ( be != NULL && be_isroot( be, op->o_ndn ) ) {
121
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
122
123
124
		LDAP_LOG(( "acl", LDAP_LEVEL_INFO,
		       "access_allowed: conn %d root access granted\n",
		       conn->c_connid));
125
#else
126
127
128
		Debug( LDAP_DEBUG_ACL,
		    "<= root access granted\n",
			0, 0, 0 );
129
#endif
130
131
		return 1;
	}
132

133
134
135
136
137
	/*
	 * no-user-modification operational attributes are ignored
	 * by ACL_WRITE checking as any found here are not provided
	 * by the user
	 */
138
139
140
	if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
		&& desc != slap_schema.si_ad_entry
		&& desc != slap_schema.si_ad_children )
141
	{
142
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
143
144
145
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
		       "access_allowed: conn %d NoUserMod Operational attribute: %s access granted\n",
		       conn->c_connid, attr ));
146
#else
Gary Williams's avatar
Gary Williams committed
147
		Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
148
			" %s access granted\n",
149
			attr, 0, 0 );
150
#endif
151
152
153
154
155
		return 1;
	}

	/* use backend default access if no backend acls */
	if( be != NULL && be->be_acl == NULL ) {
156
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
157
158
159
160
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
		       "access_allowed: conn %d backend default %s access %s to \"%s\"\n",
		       conn->c_connid, access2str( access ),
		       be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn ));
161
#else
162
163
164
165
		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 );
166
#endif
167
168
169
170
171
172
		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 ) {
173
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
174
175
176
177
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
		       "access_allowed: conn %d global default %s access %s to \"%s\"\n",
		       conn->c_connid, access2str( access ),
		       global_default_access >= access ? "granted" : "denied", op->o_dn ));
178
#else
179
180
181
182
		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 );
183
#endif
184
185
		return global_default_access >= access;
#endif
186
187
	}

188
	ACL_INIT(mask);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
189
	memset(matches, '\0', sizeof(matches));
190
191
192
193
	
	control = ACL_BREAK;
	a = NULL;
	count = 0;
194

195
	while((a = acl_get( a, &count, be, op, e, desc, MAXREMATCHES, matches )) != NULL)
196
197
	{
		int i;
198
199

		for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
200
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
201
202
203
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
			       "access_allowed: conn %d match[%d]:  %d %d ",
			       conn->c_connid, i, (int)matches[i].rm_so, (int)matches[i].rm_eo ));
204
#else
205
			Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
Hallvard Furuseth's avatar
Hallvard Furuseth committed
206
			       (int)matches[i].rm_so, (int)matches[i].rm_eo );
207
#endif
208
			if( matches[i].rm_so <= matches[0].rm_eo ) {
209
				int n;
210
				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
211
					Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
212
213
				}
			}
214
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
215
			LDAP_LOG(( "acl", LDAP_LEVEL_ARGS, "\n" ));
216
#else
217
			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
218
#endif
219
		}
220
221

		control = acl_mask( a, &mask, be, conn, op,
222
			e, desc, val, matches );
223
224
225
226
227

		if ( control != ACL_BREAK ) {
			break;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
228
		memset(matches, '\0', sizeof(matches));
229
230
	}

231
	if ( ACL_IS_INVALID( mask ) ) {
232
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
233
234
235
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
		       "access_allowed: conn %d	 \"%s\" (%s) invalid!\n",
		       conn->c_connid, e->e_dn, attr ));
236
#else
237
238
239
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: \"%s\" (%s) invalid!\n",
			e->e_dn, attr, 0 );
240
#endif
241
242
243
		ACL_INIT( mask );

	} else if ( control == ACL_BREAK ) {
244
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
245
246
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
		       "access_allowed: conn %d	 no more rules\n", conn->c_connid ));
247
#else
248
249
		Debug( LDAP_DEBUG_ACL,
			"=> access_allowed: no more rules\n", 0, 0, 0);
250
#endif
251
252
		ACL_INIT( mask );
	}
253

254
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
255
256
	LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
		   "access_allowed: conn %d  %s access %s by %s\n",
Gary Williams's avatar
Gary Williams committed
257
		   conn->c_connid,
Gary Williams's avatar
Gary Williams committed
258
259
260
		   access2str( access ),
		   ACL_GRANT( mask, access ) ? "granted" : "denied",
		   accessmask2str( mask, accessmaskbuf ) ));
261
#else
262
	Debug( LDAP_DEBUG_ACL,
263
		"=> access_allowed: %s access %s by %s\n",
264
		access2str( access ),
265
		ACL_GRANT(mask, access) ? "granted" : "denied",
266
		accessmask2str( mask, accessmaskbuf ) );
267
#endif
268
	return ACL_GRANT(mask, access);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
269
270
271
}

/*
272
 * acl_get - return the acl applicable to entry e, attribute
Kurt Zeilenga's avatar
Kurt Zeilenga committed
273
274
275
276
 * attr.  the acl returned is suitable for use in subsequent calls to
 * acl_access_allowed().
 */

277
278
279
280
static AccessControl *
acl_get(
	AccessControl *a,
	int			*count,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
281
    Backend		*be,
282
    Operation	*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
283
    Entry		*e,
284
	AttributeDescription *desc,
285
    int			nmatch,
286
    regmatch_t	*matches )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
287
{
288
	const char *attr;
289
290
	int dnlen, patlen;

291
292
	assert( e != NULL );
	assert( count != NULL );
293
	assert( desc != NULL );
294

295
	attr = desc->ad_cname.bv_val;
296
297

	assert( attr != NULL );
298

299
300
301
302
303
	if( a == NULL ) {
		if( be == NULL ) {
			a = global_acl;
		} else {
			a = be->be_acl;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
304
		}
305

306
		assert( a != NULL );
307

308
309
	} else {
		a = a->acl_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
310
311
	}

312
313
	dnlen = strlen(e->e_ndn);

314
315
316
	for ( ; a != NULL; a = a->acl_next ) {
		(*count) ++;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
317
		if (a->acl_dn_pat != NULL) {
318
			if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
319
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
320
321
322
				LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
					   "acl_get: dnpat [%d] %s nsub: %d\n",
					   *count, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub ));
323
#else
324
325
				Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
					*count, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub );
326
#endif
327
328
				if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
					continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
329

330
			} else {
331
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
332
333
334
				LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
					   "acl_get: dn [%d] %s\n",
					   *count, a->acl_dn_pat ));
335
#else
336
337
				Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n", 
					*count, a->acl_dn_pat, 0 );
338
#endif
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
				patlen = strlen( a->acl_dn_pat );
				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 ) {
					int rdnlen = -1;

					if ( dnlen <= patlen )
						continue;

354
					if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) )
355
356
						continue;

357
					rdnlen = dn_rdnlen( NULL, e->e_ndn );
358
359
360
361
					if ( rdnlen != dnlen - patlen - 1 )
						continue;

				} else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
362
					if ( dnlen > patlen && ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) ) )
363
364
365
366
367
						continue;

				} else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
					if ( dnlen <= patlen )
						continue;
368
					if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) || DN_ESCAPE( e->e_ndn[dnlen - patlen - 2] ) )
369
370
371
372
373
						continue;
				}

				if ( strcmp( a->acl_dn_pat, e->e_ndn + dnlen - patlen ) != 0 )
					continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
374
			}
375

376
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
377
378
379
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_get: [%d] matched\n",
				   *count ));
380
#else
381
382
			Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
				*count, 0, 0 );
383
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
384
		}
385

Kurt Zeilenga's avatar
Kurt Zeilenga committed
386
		if ( a->acl_filter != NULL ) {
387
388
			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
389
390
391
				continue;
			}
		}
392

393
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
394
395
396
		LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
			   "acl_get: [%d] check attr %s\n",
			   *count, attr ));
397
#else
Gary Williams's avatar
Gary Williams committed
398
399
		Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
		       *count, attr, 0);
400
#endif
401
		if ( attr == NULL || a->acl_attrs == NULL ||
402
			ad_inlist( desc, a->acl_attrs ) )
403
		{
404
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
405
406
407
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_get:  [%d] acl %s attr: %s\n",
				   *count, e->e_dn, attr ));
408
#else
409
410
411
			Debug( LDAP_DEBUG_ACL,
				"<= acl_get: [%d] acl %s attr: %s\n",
				*count, e->e_dn, attr );
412
#endif
413
			return a;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
414
		}
415
		matches[0].rm_so = matches[0].rm_eo = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
416
417
	}

418
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
419
420
	LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
		   "acl_get: done.\n" ));
421
#else
422
	Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
423
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
424
425
426
	return( NULL );
}

427

Kurt Zeilenga's avatar
Kurt Zeilenga committed
428
/*
429
 * acl_mask - modifies mask based upon the given acl and the
Kurt Zeilenga's avatar
Kurt Zeilenga committed
430
431
432
433
434
435
436
 * 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
 */

437
438
static slap_control_t
acl_mask(
Kurt Zeilenga's avatar
Kurt Zeilenga committed
439
    AccessControl	*a,
440
	slap_mask_t *mask,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
441
    Backend		*be,
442
443
    Connection	*conn,
    Operation	*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
444
    Entry		*e,
445
	AttributeDescription *desc,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
446
    struct berval	*val,
447
	regmatch_t	*matches
Kurt Zeilenga's avatar
Kurt Zeilenga committed
448
449
)
{
450
	int		i, odnlen, patlen;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
451
	Access	*b;
452
#ifdef LDAP_DEBUG
Kurt Zeilenga's avatar
Kurt Zeilenga committed
453
	char accessmaskbuf[ACCESSMASK_MAXLEN];
454
#endif
455
	const char *attr;
456
457
458

	assert( a != NULL );
	assert( mask != NULL );
459
460
	assert( desc != NULL );

461
	attr = desc->ad_cname.bv_val;
462
463

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

465
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
466
467
468
469
470
471
472
473
474
	LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
		   "acl_mask: conn %d  access to entry \"%s\", attr \"%s\" requested\n",
		   conn->c_connid, e->e_dn, attr ));

	LDAP_LOG(( "acl", LDAP_LEVEL_ARGS,
		   " to %s by \"%s\", (%s) \n",
		   val ? "value" : "all values",
		   op->o_ndn ? op->o_ndn : "",
		   accessmask2str( *mask, accessmaskbuf ) ));
475
#else
476
	Debug( LDAP_DEBUG_ACL,
477
		"=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
478
		e->e_dn, attr, 0 );
479
480

	Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
481
482
		"=> acl_mask: to %s by \"%s\", (%s) \n",
		val ? "value" : "all values",
483
		op->o_ndn ?  op->o_ndn : "",
484
		accessmask2str( *mask, accessmaskbuf ) );
485
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
486

487
	for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
488
		slap_mask_t oldmask, modmask;
489

490
		ACL_INVALIDATE( modmask );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
491

Kurt Zeilenga's avatar
Kurt Zeilenga committed
492
493
		/* AND <who> clauses */
		if ( b->a_dn_pat != NULL ) {
494
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
495
496
497
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_dn_pat: %s\n",
				   conn->c_connid, b->a_dn_pat ));
498
#else
499
			Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
500
				b->a_dn_pat, 0, 0);
501
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
502
503
504
505
506
			/*
			 * 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
507
			if ( strcmp( b->a_dn_pat, "anonymous" ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
508
509
510
511
				if (op->o_ndn != NULL && op->o_ndn[0] != '\0' ) {
					continue;
				}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
512
513
514
515
516
517
			} 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
518
519
520
521
				if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
					continue;
				}
				
522
				if ( e->e_dn == NULL || strcmp( e->e_ndn, op->o_ndn ) != 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
523
524
525
					continue;
				}

526
527
528
529
530
531
532
533
534
535
536
537
538
			} else if ( b->a_dn_style == ACL_STYLE_REGEX ) {
				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;
					}
				}

			} else {
				if ( e->e_dn == NULL )
					continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
539

540
541
542
				patlen = strlen( b->a_dn_pat );
				odnlen = strlen( op->o_ndn );
				if ( odnlen < patlen )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
543
					continue;
544
545
546
547
548
549
550
551
552
553
554
555

				if ( b->a_dn_style == ACL_STYLE_BASE ) {
					/* base dn -- entire object DN must match */
					if ( odnlen != patlen )
						continue;

				} else if ( b->a_dn_style == ACL_STYLE_ONE ) {
					int rdnlen = -1;

					if ( odnlen <= patlen )
						continue;

556
					if ( !DN_SEPARATOR( op->o_ndn[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn[odnlen - patlen - 2] ) )
557
558
						continue;

559
					rdnlen = dn_rdnlen( NULL, op->o_ndn );
560
561
562
563
					if ( rdnlen != odnlen - patlen - 1 )
						continue;

				} else if ( b->a_dn_style == ACL_STYLE_SUBTREE ) {
564
					if ( odnlen > patlen && ( !DN_SEPARATOR( op->o_ndn[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn[odnlen - patlen - 2] ) ) )
565
566
567
568
569
						continue;

				} else if ( b->a_dn_style == ACL_STYLE_CHILDREN ) {
					if ( odnlen <= patlen )
						continue;
570
					if ( !DN_SEPARATOR( op->o_ndn[odnlen - patlen - 1] ) || DN_ESCAPE( op->o_ndn[odnlen - patlen - 2] ) )
571
						continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
572
				}
573
574
575
576

				if ( strcmp( b->a_dn_pat, op->o_ndn + odnlen - patlen ) != 0 )
					continue;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
577
578
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
579

580
		if ( b->a_sockurl_pat != NULL ) {
581
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
582
583
584
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_sockurl_pat: %s\n",
				   conn->c_connid, b->a_sockurl_pat ));
585
#else
586
			Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
587
				b->a_sockurl_pat, 0, 0 );
588
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
589

590
591
592
593
594
595
596
597
598
599
600
			if ( strcmp( b->a_sockurl_pat, "*" ) != 0) {
				if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
					if (!regex_matches( b->a_sockurl_pat, conn->c_listener_url,
							e->e_ndn, matches ) ) 
					{
						continue;
					}
				} else {
					if ( strcasecmp( b->a_sockurl_pat, conn->c_listener_url ) == 0 )
						continue;
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
601
602
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
603

Kurt Zeilenga's avatar
Kurt Zeilenga committed
604
		if ( b->a_domain_pat != NULL ) {
605
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
606
607
608
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_domain_pat: %s\n",
				   conn->c_connid, b->a_domain_pat ));
609
#else
610
			Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
611
				b->a_domain_pat, 0, 0 );
612
#endif
613
614
615
616
617
618
619
620
621
622
623
			if ( strcmp( b->a_domain_pat, "*" ) != 0) {
				if ( b->a_domain_style == ACL_STYLE_REGEX) {
					if (!regex_matches( b->a_domain_pat, conn->c_peer_domain,
							e->e_ndn, matches ) ) 
					{
						continue;
					}
				} else {
					if ( strcasecmp( b->a_domain_pat, conn->c_peer_domain ) == 0 )
						continue;
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
624
625
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
626
627

		if ( b->a_peername_pat != NULL ) {
628
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
629
630
631
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_perrname_path: %s\n",
				   conn->c_connid, b->a_peername_pat ));
632
#else
633
			Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
634
				b->a_peername_pat, 0, 0 );
635
#endif
636
637
638
639
640
641
642
643
644
645
646
			if ( strcmp( b->a_peername_pat, "*" ) != 0) {
				if ( b->a_peername_style == ACL_STYLE_REGEX) {
					if (!regex_matches( b->a_peername_pat, conn->c_peer_name,
							e->e_ndn, matches ) ) 
					{
						continue;
					}
				} else {
					if ( strcasecmp( b->a_peername_pat, conn->c_peer_name ) == 0 )
						continue;
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
647
648
649
			}
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
650
		if ( b->a_sockname_pat != NULL ) {
651
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
652
653
654
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_sockname_path: %s\n",
				   conn->c_connid, b->a_sockname_pat ));
655
#else
656
			Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
657
				b->a_sockname_pat, 0, 0 );
658
#endif
659
660
661
662
663
664
665
666
667
668
669
			if ( strcmp( b->a_sockname_pat, "*" ) != 0) {
				if ( b->a_sockname_style == ACL_STYLE_REGEX) {
					if (!regex_matches( b->a_sockname_pat, conn->c_sock_name,
							e->e_ndn, matches ) ) 
					{
						continue;
					}
				} else {
					if ( strcasecmp( b->a_sockname_pat, conn->c_sock_name ) == 0 )
						continue;
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
670
671
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
672
673
674
675

		if ( b->a_dn_at != NULL && op->o_ndn != NULL ) {
			Attribute	*at;
			struct berval	bv;
676
			int rc, match = 0;
677
			const char *text;
678
			const char *attr = b->a_dn_at->ad_cname.bv_val;
679
680

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

682
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
683
684
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_dn_pat: %s\n",
685
				   conn->c_connid, attr ));
686
#else
687
			Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
688
				attr, 0, 0);
689
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
690
691
692
			bv.bv_val = op->o_ndn;
			bv.bv_len = strlen( bv.bv_val );

693
694
			/* see if asker is listed in dnattr */
			for( at = attrs_find( e->e_attrs, b->a_dn_at );
695
696
				at != NULL;
				at = attrs_find( at->a_next, b->a_dn_at ) )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
697
			{
698
				if( value_find( b->a_dn_at, at->a_vals, &bv ) == 0 ) {
699
700
701
					/* found it */
					match = 1;
					break;
702
703
704
				}
			}

705
			if( match ) {
706
707
708
709
710
711
712
713
714
715
				/* have a dnattr match. if this is a self clause then
				 * the target must also match the op dn.
				 */
				if ( b->a_dn_self ) {
					/* check if the target is an attribute. */
					if ( val == NULL )
						continue;
					/* target is attribute, check if the attribute value
					 * is the op dn.
					 */
716
717
718
					rc = value_match( &match, b->a_dn_at,
						b->a_dn_at->ad_type->sat_equality, 0,
						val, &bv, &text );
719
720
721
					/* on match error or no match, fail the ACL clause */
					if (rc != LDAP_SUCCESS || match != 0 )
						continue;
722
				}
723
724
725
726
727
728
729
730
731
732
733
734
			} else {
				/* no dnattr match, check if this is a self clause */
				if ( ! b->a_dn_self )
					continue;
				/* this is a self clause, check if the target is an
				 * attribute.
				 */
				if ( val == NULL )
					continue;
				/* target is attribute, check if the attribute value
				 * is the op dn.
				 */
735
736
737
738
				rc = value_match( &match, b->a_dn_at,
					b->a_dn_at->ad_type->sat_equality, 0,
					val, &bv, &text );

739
740
741
				/* on match error or no match, fail the ACL clause */
				if (rc != LDAP_SUCCESS || match != 0 )
					continue;
742
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
743
		}
744

Kurt Zeilenga's avatar
Kurt Zeilenga committed
745
		if ( b->a_group_pat != NULL && op->o_ndn != NULL ) {
746
			char buf[1024];
747
748
749

			/* 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
750
			 * the values in the attribute group
751
752
			 */
			/* see if asker is listed in dnattr */
753
			if ( b->a_group_style == ACL_STYLE_REGEX ) {
754
755
756
757
758
759
760
761
				string_expand(buf, sizeof(buf), b->a_group_pat, e->e_ndn, matches);
				if ( dn_normalize(buf) == NULL ) {
					/* did not expand to a valid dn */
					continue;
				}
			} else {
				strncpy( buf, b->a_group_pat, sizeof(buf) - 1 );
				buf[sizeof(buf) - 1] = 0;
762
			}
763

764
			if (backend_group(be, conn, op, e, buf, op->o_ndn,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
765
				b->a_group_oc, b->a_group_at) != 0)
766
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
767
				continue;
768
769
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
770

771
772
773
774
775
776
777
778
779
780
		if ( b->a_set_pat != NULL ) {
			struct berval bv;

			bv.bv_val = b->a_set_pat;
			bv.bv_len = strlen(b->a_set_pat);
			if (aci_match_set( &bv, be, e, conn, op, 0 ) == 0) {
				continue;
			}
		}

781
		if ( b->a_authz.sai_ssf ) {
782
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
783
784
785
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_authz.sai_ssf: ACL %u > OP %u\n",
				   conn->c_connid, b->a_authz.sai_ssf, op->o_ssf ));
786
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
787
788
			Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
				b->a_authz.sai_ssf, op->o_ssf, 0 );
789
#endif
790
791
792
793
794
795
			if ( b->a_authz.sai_ssf >  op->o_ssf ) {
				continue;
			}
		}

		if ( b->a_authz.sai_transport_ssf ) {
796
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
797
798
799
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
				   conn->c_connid, b->a_authz.sai_transport_ssf, op->o_transport_ssf ));
800
#else
801
			Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
802
803
				"<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
				b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
804
#endif
805
806
807
808
809
810
			if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
				continue;
			}
		}

		if ( b->a_authz.sai_tls_ssf ) {
811
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
812
813
814
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d  check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
				   conn->c_connid, b->a_authz.sai_tls_ssf, op->o_tls_ssf ));
815
#else
816
			Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
817
				"<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
818
				b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
819
#endif
820
821
822
823
824
825
			if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
				continue;
			}
		}

		if ( b->a_authz.sai_sasl_ssf ) {
826
#ifdef NEW_LOGGING
Gary Williams's avatar
Gary Williams committed
827
828
829
			LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
				   "acl_mask: conn %d check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
				   conn->c_connid, b->a_authz.sai_sasl_ssf, op->o_sasl_ssf ));
830
#else
831
			Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
832
				"<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
833
				b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
834
#endif
Gary Williams's avatar
Gary Williams committed
835
			if ( b->a_authz.sai_sasl_ssf >	op->o_sasl_ssf ) {
836
837
838
839
				continue;
			}
		}

Mark Valence's avatar
Mark Valence committed
840
#ifdef SLAPD_ACI_ENABLED
841
		if ( b->a_aci_at != NULL ) {
842
			Attribute	*at;
843
			slap_access_t grant, deny, tgrant, tdeny;
844
845
846

			/* this case works different from the others above.
			 * since aci's themselves give permissions, we need
847
			 * to first check b->a_access_mask, the ACL's access level.
848
849
850
851
852
853
854
855
856
857
			 */

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

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

858
859
			/* first check if the right being requested
			 * is allowed by the ACL clause.
860
			 */
861
			if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
862
863
864
865
866
867
868
869
870
				continue;
			}

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

871
872
873
874
			/* start out with nothing granted, nothing denied */
			ACL_INIT(tgrant);
			ACL_INIT(tdeny);

875
876
877
878
879
			/* 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++ ) {
880
				if (aci_mask( be, conn, op,
881
					e, desc, val, at->a_vals[i],
882
					matches, &grant, &deny ) != 0)
883
				{
884
885
					tgrant |= grant;
					tdeny |= deny;
886
887
				}
			}
888

889
			/* remove anything that the ACL clause does not allow */
890
			tgrant &= b->a_access_mask & ACL_PRIV_MASK;
891
892
893
894
			tdeny &= ACL_PRIV_MASK;

			/* see if we have anything to contribute */
			if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {