aclparse.c 61.5 KB
Newer Older
1
/* aclparse.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
#include "portable.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
28
29

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

#include <ac/ctype.h>
#include <ac/regex.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/unistd.h>
36

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

41
42
static char *style_strings[] = {
	"regex",
43
	"expand",
44
45
46
47
	"base",
	"one",
	"subtree",
	"children",
48
	"level",
49
	"attrof",
50
51
52
	"anonymous",
	"users",
	"self",
53
54
55
56
	"ip",
	"path",
	NULL
};
57

58
static void		split(char *line, int splitchar, char **left, char **right);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
59
static void		access_append(Access **l, Access *a);
60
static void		acl_usage(void) LDAP_GCCATTR((noreturn));
61

62
static void		acl_regex_normalized_dn(const char *src, struct berval *pat);
63

Kurt Zeilenga's avatar
Kurt Zeilenga committed
64
#ifdef LDAP_DEBUG
65
static void		print_acl(Backend *be, AccessControl *a);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
66
67
#endif

68
static int		check_scope( BackendDB *be, AccessControl *a );
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
105
106
107
108
#ifdef SLAP_DYNACL
static int
slap_dynacl_config( const char *fname, int lineno, Access *b, const char *name, slap_style_t sty, const char *right )
{
	slap_dynacl_t	*da, *tmp;
	int		rc = 0;

	for ( da = b->a_dynacl; da; da = da->da_next ) {
		if ( strcasecmp( da->da_name, name ) == 0 ) {
			fprintf( stderr,
				"%s: line %d: dynacl \"%s\" already specified.\n",
				fname, lineno, name );
			acl_usage();
		}
	}

	da = slap_dynacl_get( name );
	if ( da == NULL ) {
		return -1;
	}

	tmp = ch_malloc( sizeof( slap_dynacl_t ) );
	*tmp = *da;

	if ( tmp->da_parse ) {
		rc = ( *tmp->da_parse )( fname, lineno, sty, right, &tmp->da_private );
		if ( rc ) {
			ch_free( tmp );
			return rc;
		}
	}

	tmp->da_next = b->a_dynacl;
	b->a_dynacl = tmp;

	return 0;
}
#endif /* SLAP_DYNACL */

109
static void
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
regtest(const char *fname, int lineno, char *pat) {
111
112
113
114
	int e;
	regex_t re;

	char buf[512];
115
	unsigned size;
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

	char *sp;
	char *dp;
	int  flag;

	sp = pat;
	dp = buf;
	size = 0;
	buf[0] = '\0';

	for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
		if (flag) {
			if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
				*dp++ = *sp;
				size++;
			}
			flag = 0;

		} else {
			if (*sp == '$') {
				flag = 1;
			} else {
				*dp++ = *sp;
				size++;
			}
		}
	}

	*dp = '\0';
145
	if ( size >= (sizeof(buf) - 1) ) {
146
147
		fprintf( stderr,
			"%s: line %d: regular expression \"%s\" too large\n",
Hallvard Furuseth's avatar
Hallvard Furuseth committed
148
			fname, lineno, pat );
149
150
151
152
153
154
155
156
157
158
159
160
161
162
		acl_usage();
	}

	if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
		char error[512];
		regerror(e, &re, error, sizeof(error));
		fprintf( stderr,
			"%s: line %d: regular expression \"%s\" bad because of %s\n",
			fname, lineno, pat, error );
		acl_usage();
	}
	regfree(&re);
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 * Experimental
 *
 * Check if the pattern of an ACL, if any, matches the scope
 * of the backend it is defined within.
 */
#define	ACL_SCOPE_UNKNOWN	(-2)
#define	ACL_SCOPE_ERR		(-1)
#define	ACL_SCOPE_OK		(0)
#define	ACL_SCOPE_PARTIAL	(1)
#define	ACL_SCOPE_WARN		(2)

static int
check_scope( BackendDB *be, AccessControl *a )
{
	int		patlen;
	struct berval	dn;

181
	dn = be->be_nsuffix[0];
182

183
184
185
186
	if ( BER_BVISEMPTY( &dn ) ) {
		return ACL_SCOPE_OK;
	}

187
188
189
	if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
			a->acl_dn_style != ACL_STYLE_REGEX )
	{
190
191
192
		slap_style_t	style = a->acl_dn_style;

		if ( style == ACL_STYLE_REGEX ) {
193
194
195
196
197
			char		dnbuf[SLAP_LDAPDN_MAXLEN + 2];
			char		rebuf[SLAP_LDAPDN_MAXLEN + 1];
			ber_len_t	rebuflen;
			regex_t		re;
			int		rc;
198
			
199
200
			/* add trailing '$' to database suffix to form
			 * a simple trial regex pattern "<suffix>$" */
201
202
203
204
			AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
				be->be_nsuffix[0].bv_len );
			dnbuf[be->be_nsuffix[0].bv_len] = '$';
			dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
205
206
207
208
209

			if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
				return ACL_SCOPE_WARN;
			}

210
211
212
213
214
215
216
217
218
219
220
221
222
			/* remove trailing ')$', if any, from original
			 * regex pattern */
			rebuflen = a->acl_dn_pat.bv_len;
			AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
			if ( rebuf[rebuflen - 1] == '$' ) {
				rebuf[--rebuflen] = '\0';
			}
			while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
				rebuf[--rebuflen] = '\0';
			}
			if ( rebuflen == be->be_nsuffix[0].bv_len ) {
				rc = ACL_SCOPE_WARN;
				goto regex_done;
223
224
225
226
227
228
			}

			/* not a clear indication of scoping error, though */
			rc = regexec( &re, rebuf, 0, NULL, 0 )
				? ACL_SCOPE_WARN : ACL_SCOPE_OK;

229
regex_done:;
230
231
232
233
234
235
236
237
238
239
240
			regfree( &re );
			return rc;
		}

		patlen = a->acl_dn_pat.bv_len;
		/* If backend suffix is longer than pattern,
		 * it is a potential mismatch (in the sense
		 * that a superior naming context could
		 * match */
		if ( dn.bv_len > patlen ) {
			/* base is blatantly wrong */
241
			if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
242

243
244
			/* a style of one can be wrong if there is
			 * more than one level between the suffix
245
246
247
248
249
			 * and the pattern */
			if ( style == ACL_STYLE_ONE ) {
				int	rdnlen = -1, sep = 0;

				if ( patlen > 0 ) {
250
					if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
251
						return ACL_SCOPE_ERR;
252
					}
253
254
255
256
257
258
259
260
261
262
					sep = 1;
				}

				rdnlen = dn_rdnlen( NULL, &dn );
				if ( rdnlen != dn.bv_len - patlen - sep )
					return ACL_SCOPE_ERR;
			}

			/* if the trailing part doesn't match,
			 * then it's an error */
263
264
265
			if ( strcmp( a->acl_dn_pat.bv_val,
				&dn.bv_val[dn.bv_len - patlen] ) != 0 )
			{
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
				return ACL_SCOPE_ERR;
			}

			return ACL_SCOPE_PARTIAL;
		}

		switch ( style ) {
		case ACL_STYLE_BASE:
		case ACL_STYLE_ONE:
		case ACL_STYLE_CHILDREN:
		case ACL_STYLE_SUBTREE:
			break;

		default:
			assert( 0 );
			break;
		}

284
		if ( dn.bv_len < patlen &&
285
286
			!DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
		{
287
288
289
			return ACL_SCOPE_ERR;
		}

290
291
292
		if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
			!= 0 )
		{
293
294
295
296
297
298
299
300
301
			return ACL_SCOPE_ERR;
		}

		return ACL_SCOPE_OK;
	}

	return ACL_SCOPE_UNKNOWN;
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
302
303
304
void
parse_acl(
    Backend	*be,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
305
    const char	*fname,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
306
307
    int		lineno,
    int		argc,
308
309
    char	**argv,
	int		pos )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
310
311
{
	int		i;
312
	char		*left, *right, *style, *next;
313
	struct berval	bv;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
314
315
	AccessControl	*a;
	Access	*b;
316
	int rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
317
	const char *text;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
318
319
320
321
322
323

	a = NULL;
	for ( i = 1; i < argc; i++ ) {
		/* to clause - select which entries are protected */
		if ( strcasecmp( argv[i], "to" ) == 0 ) {
			if ( a != NULL ) {
324
325
				fprintf( stderr, "%s: line %d: "
					"only one to clause allowed in access line\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
326
327
328
				    fname, lineno );
				acl_usage();
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
329
			a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
330
331
332
333
334
335
			for ( ++i; i < argc; i++ ) {
				if ( strcasecmp( argv[i], "by" ) == 0 ) {
					i--;
					break;
				}

Hallvard Furuseth's avatar
Hallvard Furuseth committed
336
				if ( strcasecmp( argv[i], "*" ) == 0 ) {
337
338
					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
						a->acl_dn_style != ACL_STYLE_REGEX )
339
					{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
340
341
342
343
344
345
346
						fprintf( stderr,
							"%s: line %d: dn pattern"
							" already specified in to clause.\n",
							fname, lineno );
						acl_usage();
					}

347
					ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
348
349
350
351
					continue;
				}

				split( argv[i], '=', &left, &right );
352
353
				split( left, '.', &left, &style );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
354
				if ( right == NULL ) {
355
356
					fprintf( stderr, "%s: line %d: "
						"missing \"=\" in \"%s\" in to clause\n",
357
358
359
					    fname, lineno, left );
					acl_usage();
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
360
361

				if ( strcasecmp( left, "dn" ) == 0 ) {
362
363
					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
						a->acl_dn_style != ACL_STYLE_REGEX )
364
					{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
365
366
367
368
369
370
371
						fprintf( stderr,
							"%s: line %d: dn pattern"
							" already specified in to clause.\n",
							fname, lineno );
						acl_usage();
					}

372
					if ( style == NULL || *style == '\0' ||
373
374
375
						strcasecmp( style, "baseObject" ) == 0 ||
						strcasecmp( style, "base" ) == 0 ||
						strcasecmp( style, "exact" ) == 0 )
376
377
378
379
					{
						a->acl_dn_style = ACL_STYLE_BASE;
						ber_str2bv( right, 0, 1, &a->acl_dn_pat );

380
381
382
					} else if ( strcasecmp( style, "oneLevel" ) == 0 ||
						strcasecmp( style, "one" ) == 0 )
					{
383
384
385
						a->acl_dn_style = ACL_STYLE_ONE;
						ber_str2bv( right, 0, 1, &a->acl_dn_pat );

386
387
					} else if ( strcasecmp( style, "subtree" ) == 0 ||
						strcasecmp( style, "sub" ) == 0 )
388
					{
389
						if( *right == '\0' ) {
390
							ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
391
392
393
394
395

						} else {
							a->acl_dn_style = ACL_STYLE_SUBTREE;
							ber_str2bv( right, 0, 1, &a->acl_dn_pat );
						}
396
397
398
399
400
401

					} else if ( strcasecmp( style, "children" ) == 0 ) {
						a->acl_dn_style = ACL_STYLE_CHILDREN;
						ber_str2bv( right, 0, 1, &a->acl_dn_pat );

					} else if ( strcasecmp( style, "regex" ) == 0 ) {
402
						a->acl_dn_style = ACL_STYLE_REGEX;
403
404
405
406
407
408
409

						if ( *right == '\0' ) {
							/* empty regex should match empty DN */
							a->acl_dn_style = ACL_STYLE_BASE;
							ber_str2bv( right, 0, 1, &a->acl_dn_pat );

						} else if ( strcmp(right, "*") == 0 
410
411
412
							|| strcmp(right, ".*") == 0 
							|| strcmp(right, ".*$") == 0 
							|| strcmp(right, "^.*") == 0 
413
							|| strcmp(right, "^.*$") == 0
414
415
416
							|| strcmp(right, ".*$$") == 0 
							|| strcmp(right, "^.*$$") == 0 )
						{
417
							ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
418

419
						} else {
420
							acl_regex_normalized_dn( right, &a->acl_dn_pat );
421
						}
422

423
					} else {
424
425
						fprintf( stderr, "%s: line %d: "
							"unknown dn style \"%s\" in to clause\n",
426
427
						    fname, lineno, style );
						acl_usage();
Kurt Zeilenga's avatar
Kurt Zeilenga committed
428
429
430
431
432
					}

					continue;
				}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
433
				if ( strcasecmp( left, "filter" ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
434
					if ( (a->acl_filter = str2filter( right )) == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
435
436
437
438
439
						fprintf( stderr,
				"%s: line %d: bad filter \"%s\" in to clause\n",
						    fname, lineno, right );
						acl_usage();
					}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
440

441
442
443
				} else if ( strcasecmp( left, "attr" ) == 0		/* TOLERATED */
						|| strcasecmp( left, "attrs" ) == 0 )	/* DOCUMENTED */
				{
444
					a->acl_attrs = str2anlist( a->acl_attrs,
445
						right, "," );
446
447
448
449
450
451
					if ( a->acl_attrs == NULL ) {
						fprintf( stderr,
				"%s: line %d: unknown attr \"%s\" in to clause\n",
						    fname, lineno, right );
						acl_usage();
					}
452

453
				} else if ( strncasecmp( left, "val", 3 ) == 0 ) {
454
					if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
455
456
457
458
459
						fprintf( stderr,
				"%s: line %d: attr val already specified in to clause.\n",
							fname, lineno );
						acl_usage();
					}
460
461
					if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
					{
462
463
464
465
466
467
						fprintf( stderr,
				"%s: line %d: attr val requires a single attribute.\n",
							fname, lineno );
						acl_usage();
					}
					ber_str2bv( right, 0, 1, &a->acl_attrval );
468
469
470
471
472
473
474
475
476
477
478
479
480
481
					a->acl_attrval_style = ACL_STYLE_BASE;
					if ( style != NULL ) {
						if ( strcasecmp( style, "regex" ) == 0 ) {
							int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
								REG_EXTENDED | REG_ICASE | REG_NOSUB );
							if ( e ) {
								char buf[512];
								regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
								fprintf( stderr, "%s: line %d: "
									"regular expression \"%s\" bad because of %s\n",
									fname, lineno, right, buf );
								acl_usage();
							}
							a->acl_attrval_style = ACL_STYLE_REGEX;
482

483
484
485
						} else {
							/* FIXME: if the attribute has DN syntax, we might
							 * allow one, subtree and children styles as well */
486
487
							if ( !strcasecmp( style, "base" ) ||
								!strcasecmp( style, "exact" ) ) {
488
								a->acl_attrval_style = ACL_STYLE_BASE;
489
490
491

							} else if ( a->acl_attrs[0].an_desc->ad_type->
								sat_syntax == slap_schema.si_syn_distinguishedName )
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
								if ( !strcasecmp( style, "baseObject" ) ||
									!strcasecmp( style, "base" ) )
								{
									a->acl_attrval_style = ACL_STYLE_BASE;
								} else if ( !strcasecmp( style, "onelevel" ) ||
									!strcasecmp( style, "one" ) )
								{
									a->acl_attrval_style = ACL_STYLE_ONE;
								} else if ( !strcasecmp( style, "subtree" ) ||
									!strcasecmp( style, "sub" ) )
								{
									a->acl_attrval_style = ACL_STYLE_SUBTREE;
								} else if ( !strcasecmp( style, "children" ) ) {
									a->acl_attrval_style = ACL_STYLE_CHILDREN;
								} else {
									fprintf( stderr, 
										"%s: line %d: unknown val.<style> \"%s\" "
										"for attributeType \"%s\" with DN syntax; "
										"using \"base\"\n",
										fname, lineno, style,
										a->acl_attrs[0].an_desc->ad_cname.bv_val );
									a->acl_attrval_style = ACL_STYLE_BASE;
								}

517
518
							} else {
								fprintf( stderr, 
519
									"%s: line %d: unknown val.<style> \"%s\" "
520
									"for attributeType \"%s\"; using \"exact\"\n",
521
522
									fname, lineno, style,
									a->acl_attrs[0].an_desc->ad_cname.bv_val );
523
524
525
								a->acl_attrval_style = ACL_STYLE_BASE;
							}
						}
526
527
					}
					
Kurt Zeilenga's avatar
Kurt Zeilenga committed
528
529
				} else {
					fprintf( stderr,
530
						"%s: line %d: expecting <what> got \"%s\"\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
531
532
533
534
535
					    fname, lineno, left );
					acl_usage();
				}
			}

536
537
			if ( !BER_BVISNULL( &a->acl_dn_pat ) && 
					ber_bvccmp( &a->acl_dn_pat, '*' ) )
538
			{
539
				free( a->acl_dn_pat.bv_val );
540
				BER_BVZERO( &a->acl_dn_pat );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
541
542
			}
			
543
544
			if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
					a->acl_dn_style != ACL_STYLE_REGEX ) 
545
			{
546
				if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
547
					struct berval bv;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
548
					rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
549
550
					if ( rc != LDAP_SUCCESS ) {
						fprintf( stderr,
551
							"%s: line %d: bad DN \"%s\" in to DN clause\n",
552
553
554
							fname, lineno, a->acl_dn_pat.bv_val );
						acl_usage();
					}
555
					free( a->acl_dn_pat.bv_val );
556
					a->acl_dn_pat = bv;
557

558
				} else {
559
					int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
560
						REG_EXTENDED | REG_ICASE );
561
562
563
					if ( e ) {
						char buf[512];
						regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
564
565
566
						fprintf( stderr, "%s: line %d: "
							"regular expression \"%s\" bad because of %s\n",
							fname, lineno, right, buf );
567
568
						acl_usage();
					}
569
570
571
				}
			}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
572
573
574
		/* by clause - select who has what access to entries */
		} else if ( strcasecmp( argv[i], "by" ) == 0 ) {
			if ( a == NULL ) {
575
576
				fprintf( stderr, "%s: line %d: "
					"to clause required before by clause in access line\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
577
578
579
				    fname, lineno );
				acl_usage();
			}
580

Kurt Zeilenga's avatar
Kurt Zeilenga committed
581
582
583
584
			/*
			 * by clause consists of <who> and <access>
			 */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
585
			b = (Access *) ch_calloc( 1, sizeof(Access) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
586

587
			ACL_INVALIDATE( b->a_access_mask );
588

Kurt Zeilenga's avatar
Kurt Zeilenga committed
589
590
591
592
593
594
595
596
			if ( ++i == argc ) {
				fprintf( stderr,
			    "%s: line %d: premature eol: expecting <who>\n",
				    fname, lineno );
				acl_usage();
			}

			/* get <who> */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
597
			for ( ; i < argc; i++ ) {
598
599
600
601
602
603
604
				slap_style_t	sty = ACL_STYLE_REGEX;
				char		*style_modifier = NULL;
				char		*style_level = NULL;
				int		level = 0;
				int		expand = 0;
				slap_dn_access	*bdn = &b->a_dn;
				int		is_realdn = 0;
605

Kurt Zeilenga's avatar
Kurt Zeilenga committed
606
				split( argv[i], '=', &left, &right );
607
				split( left, '.', &left, &style );
608
				if ( style ) {
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
					split( style, ',', &style, &style_modifier );

					if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
						split( style, '{', &style, &style_level );
						if ( style_level != NULL ) {
							char *p = strchr( style_level, '}' );
							if ( p == NULL ) {
								fprintf( stderr,
									"%s: line %d: premature eol: "
									"expecting closing '}' in \"level{n}\"\n",
									fname, lineno );
								acl_usage();
							} else if ( p == style_level ) {
								fprintf( stderr,
									"%s: line %d: empty level "
									"in \"level{n}\"\n",
									fname, lineno );
								acl_usage();
							}
							p[0] = '\0';
						}
					}
631
				}
632
633
634

				if ( style == NULL || *style == '\0' ||
					strcasecmp( style, "exact" ) == 0 ||
635
					strcasecmp( style, "baseObject" ) == 0 ||
636
					strcasecmp( style, "base" ) == 0 )
637
638
				{
					sty = ACL_STYLE_BASE;
639

640
				} else if ( strcasecmp( style, "onelevel" ) == 0 ||
641
642
					strcasecmp( style, "one" ) == 0 )
				{
643
					sty = ACL_STYLE_ONE;
644
645
646
647

				} else if ( strcasecmp( style, "subtree" ) == 0 ||
					strcasecmp( style, "sub" ) == 0 )
				{
648
					sty = ACL_STYLE_SUBTREE;
649

650
651
				} else if ( strcasecmp( style, "children" ) == 0 ) {
					sty = ACL_STYLE_CHILDREN;
652

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
				} else if ( strcasecmp( style, "level" ) == 0 )
				{
					char	*next;

					level = strtol( style_level, &next, 10 );
					if ( next[0] != '\0' ) {
						fprintf( stderr,
							"%s: line %d: unable to parse level "
							"in \"level{n}\"\n",
							fname, lineno );
						acl_usage();
					}

					sty = ACL_STYLE_LEVEL;

668
669
670
				} else if ( strcasecmp( style, "regex" ) == 0 ) {
					sty = ACL_STYLE_REGEX;

671
672
673
				} else if ( strcasecmp( style, "expand" ) == 0 ) {
					sty = ACL_STYLE_EXPAND;

674
675
676
677
678
679
680
681
682
683
684
				} else if ( strcasecmp( style, "ip" ) == 0 ) {
					sty = ACL_STYLE_IP;

				} else if ( strcasecmp( style, "path" ) == 0 ) {
					sty = ACL_STYLE_PATH;
#ifndef LDAP_PF_LOCAL
					fprintf( stderr, "%s: line %d: "
						"path style modifier is useless without local\n",
						fname, lineno );
#endif /* LDAP_PF_LOCAL */

685
686
687
688
689
690
				} else {
					fprintf( stderr,
						"%s: line %d: unknown style \"%s\" in by clause\n",
					    fname, lineno, style );
					acl_usage();
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
691

692
693
694
				if ( style_modifier &&
					strcasecmp( style_modifier, "expand" ) == 0 )
				{
695
696
697
698
699
700
701
702
703
					switch ( sty ) {
					case ACL_STYLE_REGEX:
						fprintf( stderr, "%s: line %d: "
							"\"regex\" style implies "
							"\"expand\" modifier (ignored)\n",
							fname, lineno );
						break;

					case ACL_STYLE_EXPAND:
704
705
#if 0
						/* FIXME: now it's legal... */
706
707
708
709
710
						fprintf( stderr, "%s: line %d: "
							"\"expand\" style used "
							"in conjunction with "
							"\"expand\" modifier (ignored)\n",
							fname, lineno );
711
#endif
712
713
714
						break;

					default:
715
						/* we'll see later if it's pertinent */
716
717
718
719
720
						expand = 1;
						break;
					}
				}

721
				/* expand in <who> needs regex in <what> */
722
				if ( ( sty == ACL_STYLE_EXPAND || expand )
723
						&& a->acl_dn_style != ACL_STYLE_REGEX )
724
725
726
727
728
729
				{
					fprintf( stderr, "%s: line %d: "
						"\"expand\" style or modifier used "
						"in conjunction with "
						"a non-regex <what> clause\n",
						fname, lineno );
730
731
				}

732
733
734
735
736
737
738
739
740
741
742
				if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
					is_realdn = 1;
					bdn = &b->a_realdn;
					left += STRLENOF( "real" );
				}

				if ( strcasecmp( left, "*" ) == 0 ) {
					if ( is_realdn ) {
						acl_usage();
					}

743
					ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
744
					sty = ACL_STYLE_REGEX;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
745

746
				} else if ( strcasecmp( left, "anonymous" ) == 0 ) {
747
					ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
748
749
					sty = ACL_STYLE_ANONYMOUS;

750
				} else if ( strcasecmp( left, "users" ) == 0 ) {
751
752
					ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
					sty = ACL_STYLE_USERS;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
753

754
				} else if ( strcasecmp( left, "self" ) == 0 ) {
755
					ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
756
					sty = ACL_STYLE_SELF;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
757

Kurt Zeilenga's avatar
Kurt Zeilenga committed
758
				} else if ( strcasecmp( left, "dn" ) == 0 ) {
759
					if ( sty == ACL_STYLE_REGEX ) {
760
						bdn->a_style = ACL_STYLE_REGEX;
761
						if ( right == NULL ) {
762
							/* no '=' */
763
							ber_str2bv("users",
764
								STRLENOF( "users" ),
765
								1, &bv);
766
							bdn->a_style = ACL_STYLE_USERS;
767

768
769
						} else if (*right == '\0' ) {
							/* dn="" */
770
							ber_str2bv("anonymous",
771
								STRLENOF( "anonymous" ),
772
								1, &bv);
773
							bdn->a_style = ACL_STYLE_ANONYMOUS;
774

775
776
777
						} else if ( strcmp( right, "*" ) == 0 ) {
							/* dn=* */
							/* any or users?  users for now */
778
							ber_str2bv("users",
779
								STRLENOF( "users" ),
780
								1, &bv);
781
							bdn->a_style = ACL_STYLE_USERS;
782

783
784
785
786
787
788
789
						} else if ( strcmp( right, ".+" ) == 0
							|| strcmp( right, "^.+" ) == 0
							|| strcmp( right, ".+$" ) == 0
							|| strcmp( right, "^.+$" ) == 0
							|| strcmp( right, ".+$$" ) == 0
							|| strcmp( right, "^.+$$" ) == 0 )
						{
790
							ber_str2bv("users",
791
								STRLENOF( "users" ),
792
								1, &bv);
793
							bdn->a_style = ACL_STYLE_USERS;
794

795
796
797
798
799
800
801
						} else if ( strcmp( right, ".*" ) == 0
							|| strcmp( right, "^.*" ) == 0
							|| strcmp( right, ".*$" ) == 0
							|| strcmp( right, "^.*$" ) == 0
							|| strcmp( right, ".*$$" ) == 0
							|| strcmp( right, "^.*$$" ) == 0 )
						{
802
							ber_str2bv("*",
803
								STRLENOF( "*" ),
804
								1, &bv);
805
806

						} else {
807
							acl_regex_normalized_dn( right, &bv );
808
							if ( !ber_bvccmp( &bv, '*' ) ) {
809
								regtest( fname, lineno, bv.bv_val );
810
							}
811
						}
812

813
					} else if ( right == NULL || *right == '\0' ) {
814
815
816
						fprintf( stderr, "%s: line %d: "
							"missing \"=\" in (or value after) \"%s\" "
							"in by clause\n",
817
818
						    fname, lineno, left );
						acl_usage();
Kurt Zeilenga's avatar
Kurt Zeilenga committed
819
820

					} else {
821
						ber_str2bv( right, 0, 1, &bv );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
822
823
					}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
824
				} else {
825
					BER_BVZERO( &bv );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
826
827
				}

828
				if ( !BER_BVISNULL( &bv ) ) {
829
					if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
830
831
832
833
834
835
						fprintf( stderr,
						    "%s: line %d: dn pattern already specified.\n",
						    fname, lineno );
						acl_usage();
					}

836
837
838
839
840
841
					if ( sty != ACL_STYLE_REGEX &&
							sty != ACL_STYLE_ANONYMOUS &&
							sty != ACL_STYLE_USERS &&
							sty != ACL_STYLE_SELF &&
							expand == 0 )
					{
842
						rc = dnNormalize(0, NULL, NULL,
843
							&bv, &bdn->a_pat, NULL);
844
845
						if ( rc != LDAP_SUCCESS ) {
							fprintf( stderr,
846
								"%s: line %d: bad DN \"%s\" in by DN clause\n",
847
848
849
								fname, lineno, bv.bv_val );
							acl_usage();
						}
850
851
						free( bv.bv_val );

852
					} else {
853
						bdn->a_pat = bv;
854
					}
855
856
					bdn->a_style = sty;
					bdn->a_expand = expand;
857
					if ( sty == ACL_STYLE_SELF ) {
858
						bdn->a_self_level = level;
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878

					} else {
						if ( level < 0 ) {
							fprintf( stderr,
								"%s: line %d: bad negative level \"%d\" "
								"in by DN clause\n",
								fname, lineno, level );
							acl_usage();
						} else if ( level == 1 ) {
							fprintf( stderr,
								"%s: line %d: \"onelevel\" should be used "
								"instead of \"level{1}\" in by DN clause\n",
								fname, lineno, 0 );
						} else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
							fprintf( stderr,
								"%s: line %d: \"base\" should be used "
								"instead of \"level{0}\" in by DN clause\n",
								fname, lineno, 0 );
						}

879
						bdn->a_level = level;
880
					}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
881
882
883
884
					continue;
				}

				if ( strcasecmp( left, "dnattr" ) == 0 ) {
885
886
887
888
					if ( right == NULL || right[0] == '\0' ) {
						fprintf( stderr, "%s: line %d: "
							"missing \"=\" in (or value after) \"%s\" "
							"in by clause\n",
889
890
891
892
							fname, lineno, left );
						acl_usage();
					}

893
					if( bdn->a_at != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
894
						fprintf( stderr,
895
							"%s: line %d: dnattr already specified.\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
896
897
							fname, lineno );
						acl_usage();
898
					}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
899

900
					rc = slap_str2ad( right, &bdn->a_at, &text );
901

902
					if( rc != LDAP_SUCCESS ) {
903
						fprintf( stderr,
904
905
							"%s: line %d: dnattr \"%s\": %s\n",
							fname, lineno, right, text );
906
907
908
						acl_usage();
					}

909

910
					if( !is_at_syntax( bdn->a_at->ad_type,
911
						SLAPD_DN_SYNTAX ) &&
912
						!is_at_syntax( bdn->a_at->ad_type,
913
						SLAPD_NAMEUID_SYNTAX ))
914
915
					{
						fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
916
917
							"%s: line %d: dnattr \"%s\": "
							"inappropriate syntax: %s\n",
918
							fname, lineno, right,
919
							bdn->a_at->ad_type->sat_syntax_oid );
920
921
						acl_usage();
					}
922

923
					if( bdn->a_at->ad_type->sat_equality == NULL ) {
924
925
926
927
928
929
930
						fprintf( stderr,
							"%s: line %d: dnattr \"%s\": "
							"inappropriate matching (no EQUALITY)\n",
							fname, lineno, right );
						acl_usage();
					}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
931
					continue;
932
				}
933

934
				if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
935
936
					char *name = NULL;
					char *value = NULL;
937

938
939
					switch ( sty ) {
					case ACL_STYLE_REGEX:
940
						/* legacy, tolerated */
941
942
943
944
945
946
947
948
						fprintf( stderr, "%s: line %d: "
							"deprecated group style \"regex\"; "
							"use \"expand\" instead\n",
							fname, lineno, style );
						sty = ACL_STYLE_EXPAND;
						break;

					case ACL_STYLE_BASE:
949
950
951
						/* legal, traditional */
					case ACL_STYLE_EXPAND:
						/* legal, substring expansion; supersedes regex */
952
953
954
						break;

					default:
955
						/* unknown */
956
957
						fprintf( stderr, "%s: line %d: "
							"inappropriate style \"%s\" in by clause\n",
958
							fname, lineno, style );
959
960
961
						acl_usage();
					}

962
					if ( right == NULL || right[0] == '\0' ) {
963
964
965
						fprintf( stderr, "%s: line %d: "
							"missing \"=\" in (or value after) \"%s\" "
							"in by clause\n",
966
967
968
969
							fname, lineno, left );
						acl_usage();
					}

970
					if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
971
972
973
974
975
976
						fprintf( stderr,
							"%s: line %d: group pattern already specified.\n",
							fname, lineno );
						acl_usage();
					}

977
978
					/* format of string is
						"group/objectClassValue/groupAttrName" */
979
					if ( ( value = strchr(left, '/') ) != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
980
						*value++ = '\0';
981
						if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
982
983
984
							*name++ = '\0';
						}
					}
985

986
					b->a_group_style = sty;
987
					if ( sty == ACL_STYLE_EXPAND ) {
988
						acl_regex_normalized_dn( right, &bv );
989
						if ( !ber_bvccmp( &bv, '*' ) ) {
990
							regtest( fname, lineno, bv.bv_val );
991
						}
992
						b->a_group_pat = bv;
993

994
					} else {
995
						ber_str2bv( right, 0, 0, &bv );
996
997
						rc = dnNormalize( 0, NULL, NULL, &bv,
							&b->a_group_pat, NULL );
998
999
1000
						if ( rc != LDAP_SUCCESS ) {
							fprintf( stderr,
								"%s: line %d: bad DN \"%s\"\n",
For faster browsing, not all history is shown. View entire blame