acl.c 14.1 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
2
/* acl.c - routines to parse and check acl's */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
6
7
8
9
10
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
11
12
#include <regex.h>

Kurt Zeilenga's avatar
Kurt Zeilenga committed
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "slap.h"

extern Attribute	*attr_find();
extern struct acl	*global_acl;
extern int		global_default_access;
extern char		*access2str();
extern char		*dn_normalize_case();

int		acl_access_allowed();
int		access_allowed();
struct acl	*acl_get_applicable();

static int	regex_matches();

Kurt Zeilenga's avatar
Kurt Zeilenga committed
27
28
29
static string_expand(char *newbuf, int bufsiz, char *pattern,
	char *match, regmatch_t *matches);

Kurt Zeilenga's avatar
Kurt Zeilenga committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

/*
 * access_allowed - check whether dn is allowed the requested access
 * to entry e, attribute attr, value val.  if val is null, access to
 * the whole attribute is assumed (all values).  this routine finds
 * the applicable acl and calls acl_access_allowed() to make the
 * decision.
 *
 * returns	0	access NOT allowed
 *		1	access allowed
 */

int
access_allowed(
    Backend		*be,
    Connection		*conn,
    Operation		*op,
    Entry		*e,
    char		*attr,
    struct berval	*val,
    char		*dn,
    int			access
)
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
54
55
56
57
58
59
60
	int				rc;
	struct acl		*a;
	char            *edn;

	regmatch_t       matches[MAXREMATCHES];
	int              i;
	int              n;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
61
62
63
64
65

	if ( be == NULL ) {
		return( 0 );
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
	edn = dn_normalize_case( strdup( e->e_dn ) );
	Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n",
		e->e_dn, attr, 0 );

	/* the lastmod attributes are ignored by ACL checking */
	if ( strcasecmp( attr, "modifiersname" ) == 0 ||
		strcasecmp( attr, "modifytimestamp" ) == 0 ||
		strcasecmp( attr, "creatorsname" ) == 0 ||
		strcasecmp( attr, "createtimestamp" ) == 0 )
	{
 		Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
			attr, 0, 0 );
		free( edn );
		return(1);
	}

	memset(matches, 0, sizeof(matches));

	a = acl_get_applicable( be, op, e, attr, edn, MAXREMATCHES, matches );

	if (a) {
		for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
			Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ",
				i, matches[i].rm_so, matches[i].rm_eo );

			if( matches[i].rm_so <= matches[0].rm_eo ) {
				for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
					Debug( LDAP_DEBUG_ARGS, "%c", edn[n], 0, 0 );
				}
			}
			Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
		}
	}

	rc = acl_access_allowed( a, be, conn, e, val, op, access, edn, matches );
	free( edn );

	Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n",
		e->e_dn, attr, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

	return( rc );
}

/*
 * acl_get_applicable - return the acl applicable to entry e, attribute
 * attr.  the acl returned is suitable for use in subsequent calls to
 * acl_access_allowed().
 */

struct acl *
acl_get_applicable(
    Backend		*be,
    Operation		*op,
    Entry		*e,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
120
121
122
123
    char		*attr,
    char		*edn,
    int			nmatch,
    regmatch_t	*matches
Kurt Zeilenga's avatar
Kurt Zeilenga committed
124
125
)
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
126
	int		i, j;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
127
128
	struct acl	*a;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
129
130
	Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n",
		e->e_dn, attr, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
131
132
133
134
135
136
137
138

	if ( be_isroot( be, op->o_dn ) ) {
		Debug( LDAP_DEBUG_ACL,
		    "<= acl_get: no acl applicable to database root\n", 0, 0,
		    0 );
		return( NULL );
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
139
140
	Debug( LDAP_DEBUG_ARGS, "=> acl_get: edn %s\n", edn, 0, 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
141
142
	/* check for a backend-specific acl that matches the entry */
	for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
143
144
145
146
147
		if (a->acl_dnpat != NULL) {
			Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
				i, a->acl_dnpat, a->acl_dnre.re_nsub);

			if (regexec(&a->acl_dnre, edn, nmatch, matches, 0))
Kurt Zeilenga's avatar
Kurt Zeilenga committed
148
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
149
150
151
			else
				Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d]  backend ACL match\n",
					i, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
152
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
153

Kurt Zeilenga's avatar
Kurt Zeilenga committed
154
		if ( a->acl_filter != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
155
			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
156
157
158
				continue;
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
159
160
161

        Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0);

Kurt Zeilenga's avatar
Kurt Zeilenga committed
162
		if ( attr == NULL || a->acl_attrs == NULL ||
Kurt Zeilenga's avatar
Kurt Zeilenga committed
163
164
165
166
			charray_inlist( a->acl_attrs, attr ) )
		{
			Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n",
				i, e->e_dn, attr );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
167
168
			return( a );
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
169
		matches[0].rm_so = matches[0].rm_eo = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
170
171
172
173
	}

	/* check for a global acl that matches the entry */
	for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
174
175
176
177
178
		if (a->acl_dnpat != NULL) {
			Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
				i, a->acl_dnpat, a->acl_dnre.re_nsub);

			if (regexec(&a->acl_dnre, edn, nmatch, matches, 0)) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
179
				continue;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
180
181
182
			} else {
				Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n",
					i, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
183
184
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
185

Kurt Zeilenga's avatar
Kurt Zeilenga committed
186
		if ( a->acl_filter != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
187
			if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
188
189
190
				continue;
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
191
192
193
194
195
196
197
198

		Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0);

		if ( attr == NULL || a->acl_attrs == NULL ||
			charray_inlist( a->acl_attrs, attr ) )
		{
			Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n",
				i, e->e_dn, attr );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
199
200
			return( a );
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
201
202

		matches[0].rm_so = matches[0].rm_eo = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
203
204
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
205
	Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
	return( NULL );
}

/*
 * acl_access_allowed - check whether the given acl allows dn the
 * 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
 */

int
acl_access_allowed(
    struct acl		*a,
    Backend		*be,
    Connection		*conn,
    Entry		*e,
    struct berval	*val,
    Operation		*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
226
227
228
    int			access,
	char		*edn,
	regmatch_t	*matches
Kurt Zeilenga's avatar
Kurt Zeilenga committed
229
230
231
)
{
	int		i;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
232
	char		*odn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
233
234
235
236
237
	struct access	*b;
	Attribute	*at;
	struct berval	bv;
	int		default_access;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
238
239
240
241
242
243
244
245
246
	Debug( LDAP_DEBUG_ACL,
		"\n=> acl_access_allowed: %s access to entry \"%s\"\n",
		access2str( access ), e->e_dn, 0 );

	Debug( LDAP_DEBUG_ACL,
		"\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n",
	    access2str( access ),
		val ? val->bv_val : "any",
		op->o_dn ?  op->o_dn : "" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
247
248

	if ( be_isroot( be, op->o_dn ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
249
250
		Debug( LDAP_DEBUG_ACL,
			"<= acl_access_allowed: granted to database root\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
251
252
253
254
		    0, 0, 0 );
		return( 1 );
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
255
256
	default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
257
258
	if ( a == NULL ) {
		Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
259
		    "<= acl_access_allowed: %s by default (no matching to)\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
260
261
262
263
264
265
266
267
268
269
270
271
		    default_access >= access ? "granted" : "denied", 0, 0 );
		return( default_access >= access );
	}

	odn = NULL;
	if ( op->o_dn != NULL ) {
		odn = dn_normalize_case( strdup( op->o_dn ) );
		bv.bv_val = odn;
		bv.bv_len = strlen( odn );
	}
	for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
		if ( b->a_dnpat != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
272
273
			Debug( LDAP_DEBUG_TRACE, "<= check a_dnpat: %s\n",
				b->a_dnpat, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
274
275
276
277
278
			/*
			 * 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
279
280
281
			if ( strcasecmp( b->a_dnpat, "self" ) == 0 && 
				op->o_dn != NULL && *(op->o_dn) && e->e_dn != NULL ) 
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
282
283
				if ( strcasecmp( edn, op->o_dn ) == 0 ) {
					Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
284
					"<= acl_access_allowed: matched by clause #%d access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
285
286
287
					    i, (b->a_access & ~ACL_SELF) >=
					    access ? "granted" : "denied", 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
288
289
					if ( odn ) free( odn );
					return( (b->a_access & ~ACL_SELF) >= access );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
290
291
				}
			} else {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
292
				if ( regex_matches( b->a_dnpat, odn, edn, matches ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
293
					Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
294
				    "<= acl_access_allowed: matched by clause #%d access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
295
296
297
				    i, (b->a_access & ~ACL_SELF) >= access ?
					    "granted" : "denied", 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
298
299
					if ( odn ) free( odn );
					return( (b->a_access & ~ACL_SELF) >= access );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
300
301
302
303
				}
			}
		}
		if ( b->a_addrpat != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
304
			if ( regex_matches( b->a_addrpat, conn->c_addr, edn, matches ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
305
				Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
306
				    "<= acl_access_allowed: matched by clause #%d access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
307
308
309
				    i, (b->a_access & ~ACL_SELF) >= access ?
				    "granted" : "denied", 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
310
				if ( odn ) free( odn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
311
312
313
314
				return( (b->a_access & ~ACL_SELF) >= access );
			}
		}
		if ( b->a_domainpat != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
315
316
317
318
			Debug( LDAP_DEBUG_ARGS, "<= check a_domainpath: %s\n",
				b->a_domainpat, 0, 0 );
			if ( regex_matches( b->a_domainpat, conn->c_domain, edn, matches ) ) 
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
319
				Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
320
				    "<= acl_access_allowed: matched by clause #%d access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
321
322
323
				    i, (b->a_access & ~ACL_SELF) >= access ?
				    "granted" : "denied", 0 );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
324
				if ( odn ) free( odn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
325
326
327
328
				return( (b->a_access & ~ACL_SELF) >= access );
			}
		}
		if ( b->a_dnattr != NULL && op->o_dn != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
329
330
			Debug( LDAP_DEBUG_ARGS, "<= check a_dnattr: %s\n",
				b->a_dnattr, 0, 0);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
331
			/* see if asker is listed in dnattr */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
332
333
			if ( (at = attr_find( e->e_attrs, b->a_dnattr )) != NULL && 
				value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
334
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
335
336
337
				if ( (b->a_access & ACL_SELF) && 
					(val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) )
				{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
338
339
340
341
342
					continue;
				}

				if ( odn ) free( odn );
				Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
343
				    "<= acl_acces_allowed: matched by clause #%d access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
344
345
346
347
348
349
350
351
				    i, (b->a_access & ~ACL_SELF) >= access ?
				    "granted" : "denied", 0 );

				return( (b->a_access & ~ACL_SELF) >= access );
			}

			/* asker not listed in dnattr - check for self access */
			if ( ! (b->a_access & ACL_SELF) || val == NULL ||
Kurt Zeilenga's avatar
Kurt Zeilenga committed
352
353
				value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
			{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
354
355
356
357
358
				continue;
			}

			if ( odn ) free( odn );
			Debug( LDAP_DEBUG_ACL,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
359
				"<= acl_access_allowed: matched by clause #%d (self) access %s\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
360
361
362
363
364
			    i, (b->a_access & ~ACL_SELF) >= access ? "granted"
			    : "denied", 0 );

			return( (b->a_access & ~ACL_SELF) >= access );
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#ifdef ACLGROUP
		if ( b->a_group != NULL && op->o_dn != NULL ) {
			char buf[512];

			/* 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
			 * the values in the attribute uniquegroup
			 */
			Debug( LDAP_DEBUG_ARGS, "<= check a_group: %s\n",
				b->a_group, 0, 0);
			Debug( LDAP_DEBUG_ARGS, "<= check a_group: odn: %s\n",
				odn, 0, 0);

			/* see if asker is listed in dnattr */
			string_expand(buf, sizeof(buf), b->a_group, edn, matches);

			if (be_group(be, buf, odn) == 0) {
				Debug( LDAP_DEBUG_ACL,
					"<= acl_access_allowed: matched by clause #%d (group) access granted\n",
					i, 0, 0 );
				if ( odn ) free( odn );
				return( (b->a_access & ~ACL_SELF) >= access );
			}
		}
#endif /* ACLGROUP */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
390
391
392
	}

	if ( odn ) free( odn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
393
394
	Debug( LDAP_DEBUG_ACL,
		"<= acl_access_allowed: %s by default (no matching by)\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
	    default_access >= access ? "granted" : "denied", 0, 0 );

	return( default_access >= access );
}

/*
 * acl_check_mods - check access control on the given entry to see if
 * it allows the given modifications by the user associated with op.
 * returns	LDAP_SUCCESS	mods allowed ok
 *		anything else	mods not allowed - return is an error
 *				code indicating the problem
 */

int
acl_check_mods(
    Backend	*be,
    Connection	*conn,
    Operation	*op,
    Entry	*e,
    LDAPMod	*mods
)
{
	int		i;
	struct acl	*a;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
419
420
421
	char            *edn;

	edn = dn_normalize_case( strdup( e->e_dn ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
422
423

	for ( ; mods != NULL; mods = mods->mod_next ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
424
425
426
		regmatch_t       matches[MAXREMATCHES];

		/* the lastmod attributes are ignored by ACL checking */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
427
		if ( strcasecmp( mods->mod_type, "modifiersname" ) == 0 ||
Kurt Zeilenga's avatar
Kurt Zeilenga committed
428
429
430
431
432
433
			strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ||
			strcasecmp( mods->mod_type, "creatorsname" ) == 0 ||
			strcasecmp( mods->mod_type, "createtimestamp" ) == 0 ) 
		{
			Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
				mods->mod_type, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
434
435
436
			continue;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
437
438
		a = acl_get_applicable( be, op, e, mods->mod_type, edn,
			MAXREMATCHES, matches );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
439
440
441
442
443
444
445
446

		switch ( mods->mod_op & ~LDAP_MOD_BVALUES ) {
		case LDAP_MOD_REPLACE:
		case LDAP_MOD_ADD:
			if ( mods->mod_bvalues == NULL ) {
				break;
			}
			for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
447
448
449
450
				if ( ! acl_access_allowed( a, be, conn, e, mods->mod_bvalues[i], 
					op, ACL_WRITE, edn, matches) ) 
				{
					free(edn);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
451
452
453
454
455
456
457
458
					return( LDAP_INSUFFICIENT_ACCESS );
				}
			}
			break;

		case LDAP_MOD_DELETE:
			if ( mods->mod_bvalues == NULL ) {
				if ( ! acl_access_allowed( a, be, conn, e,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
459
460
461
					NULL, op, ACL_WRITE, edn, matches) ) 
				{
					free(edn);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
462
463
464
465
466
					return( LDAP_INSUFFICIENT_ACCESS );
				}
				break;
			}
			for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
467
468
469
470
				if ( ! acl_access_allowed( a, be, conn, e, mods->mod_bvalues[i], 
					op, ACL_WRITE, edn, matches) ) 
				{
					free(edn);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
471
472
473
474
475
476
477
					return( LDAP_INSUFFICIENT_ACCESS );
				}
			}
			break;
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
478
	free(edn);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
479
480
481
	return( LDAP_SUCCESS );
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
482
483
484
485
486
487
static string_expand(
	char *newbuf,
	int bufsiz,
	char *pat,
	char *match,
	regmatch_t *matches)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
488
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	int     size;
	char   *sp;
	char   *dp;
	int     flag;

	size = 0;
	newbuf[0] = '\0';

	flag = 0;
	for ( dp = newbuf, sp = pat; size < 512 && *sp ; sp++) {
		/* did we previously see a $ */
		if (flag) {
			if (*sp == '$') {
				*dp++ = '$';
				size++;
			} else if (*sp >= '0' && *sp <= '9' ) {
				int     n;
				int     i;
				char   *ep;
				int     l;

				n = *sp - '0';
				*dp = '\0';
				i = matches[n].rm_so;
				l = matches[n].rm_eo; 
				for ( ; size < 512 && i < l; size++, i++ ) {
					*dp++ = match[i];
					size++;
				}
				*dp = '\0';
			}
			flag = 0;
		} else {
			if (*sp == '$') {
				flag = 1;
			} else {
				*dp++ = *sp;
				size++;
			}
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
529
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
530
	*dp = '\0';
Kurt Zeilenga's avatar
Kurt Zeilenga committed
531

Kurt Zeilenga's avatar
Kurt Zeilenga committed
532
533
	Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
	Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
534
535
536
}

static int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
537
538
539
540
541
542
regex_matches(
	char *pat,				/* pattern to expand and match against */
	char *str,				/* string to match against pattern */
	char *buf,				/* buffer with $N expansion variables */
	regmatch_t *matches		/* offsets in buffer for $N expansion variables */
)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
543
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
544
545
	regex_t re;
	char newbuf[512];
Kurt Zeilenga's avatar
Kurt Zeilenga committed
546
547
	int	rc;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
548
549
550
551
552
553
554
555
	string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
	if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
		char error[512];
		regerror(rc, &re, error, sizeof(error));

		Debug( LDAP_DEBUG_TRACE,
		    "compile( \"%s\", \"%s\") failed %s\n",
			pat, str, error );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
556
557
558
		return( 0 );
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
559
560
561
562
563
564
565
566
567
	rc = regexec(&re, str, 0, NULL, 0);
	regfree( &re );

	Debug( LDAP_DEBUG_TRACE,
	    "=> regex_matches: string:   %s\n", str, 0, 0 );
	Debug( LDAP_DEBUG_TRACE,
	    "=> regex_matches: rc: %d %s\n",
		rc, !rc ? "matches" : "no matches", 0 );
	return( !rc );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
568
569
}