xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/aclparse.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* aclparse.c - routines to parse and check acl's */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/aclparse.c,v 1.198.2.6 2008/02/11 23:26:43 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26 
27 #include "portable.h"
28 
29 #include <stdio.h>
30 
31 #include <ac/ctype.h>
32 #include <ac/regex.h>
33 #include <ac/socket.h>
34 #include <ac/string.h>
35 #include <ac/unistd.h>
36 
37 #include "slap.h"
38 #include "lber_pvt.h"
39 #include "lutil.h"
40 
41 static const char style_base[] = "base";
42 const char *style_strings[] = {
43 	"regex",
44 	"expand",
45 	"exact",
46 	"one",
47 	"subtree",
48 	"children",
49 	"level",
50 	"attrof",
51 	"anonymous",
52 	"users",
53 	"self",
54 	"ip",
55 	"ipv6",
56 	"path",
57 	NULL
58 };
59 
60 static void		split(char *line, int splitchar, char **left, char **right);
61 static void		access_append(Access **l, Access *a);
62 static void		access_free( Access *a );
63 static int		acl_usage(void);
64 
65 static void		acl_regex_normalized_dn(const char *src, struct berval *pat);
66 
67 #ifdef LDAP_DEBUG
68 static void		print_acl(Backend *be, AccessControl *a);
69 #endif
70 
71 static int		check_scope( BackendDB *be, AccessControl *a );
72 
73 #ifdef SLAP_DYNACL
74 static int
75 slap_dynacl_config(
76 	const char *fname,
77 	int lineno,
78 	Access *b,
79 	const char *name,
80 	const char *opts,
81 	slap_style_t sty,
82 	const char *right )
83 {
84 	slap_dynacl_t	*da, *tmp;
85 	int		rc = 0;
86 
87 	for ( da = b->a_dynacl; da; da = da->da_next ) {
88 		if ( strcasecmp( da->da_name, name ) == 0 ) {
89 			Debug( LDAP_DEBUG_ANY,
90 				"%s: line %d: dynacl \"%s\" already specified.\n",
91 				fname, lineno, name );
92 			return acl_usage();
93 		}
94 	}
95 
96 	da = slap_dynacl_get( name );
97 	if ( da == NULL ) {
98 		return -1;
99 	}
100 
101 	tmp = ch_malloc( sizeof( slap_dynacl_t ) );
102 	*tmp = *da;
103 
104 	if ( tmp->da_parse ) {
105 		rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
106 		if ( rc ) {
107 			ch_free( tmp );
108 			return rc;
109 		}
110 	}
111 
112 	tmp->da_next = b->a_dynacl;
113 	b->a_dynacl = tmp;
114 
115 	return 0;
116 }
117 #endif /* SLAP_DYNACL */
118 
119 static void
120 regtest(const char *fname, int lineno, char *pat) {
121 	int e;
122 	regex_t re;
123 
124 	char		buf[ SLAP_TEXT_BUFLEN ];
125 	unsigned	size;
126 
127 	char *sp;
128 	char *dp;
129 	int  flag;
130 
131 	sp = pat;
132 	dp = buf;
133 	size = 0;
134 	buf[0] = '\0';
135 
136 	for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
137 		if (flag) {
138 			if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
139 				*dp++ = *sp;
140 				size++;
141 			}
142 			flag = 0;
143 
144 		} else {
145 			if (*sp == '$') {
146 				flag = 1;
147 			} else {
148 				*dp++ = *sp;
149 				size++;
150 			}
151 		}
152 	}
153 
154 	*dp = '\0';
155 	if ( size >= (sizeof(buf) - 1) ) {
156 		Debug( LDAP_DEBUG_ANY,
157 			"%s: line %d: regular expression \"%s\" too large\n",
158 			fname, lineno, pat );
159 		(void)acl_usage();
160 		exit( EXIT_FAILURE );
161 	}
162 
163 	if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
164 		char error[ SLAP_TEXT_BUFLEN ];
165 
166 		regerror(e, &re, error, sizeof(error));
167 
168 		snprintf( buf, sizeof( buf ),
169 			"regular expression \"%s\" bad because of %s",
170 			pat, error );
171 		Debug( LDAP_DEBUG_ANY,
172 			"%s: line %d: %s\n",
173 			fname, lineno, buf );
174 		acl_usage();
175 		exit( EXIT_FAILURE );
176 	}
177 	regfree(&re);
178 }
179 
180 /*
181  * Experimental
182  *
183  * Check if the pattern of an ACL, if any, matches the scope
184  * of the backend it is defined within.
185  */
186 #define	ACL_SCOPE_UNKNOWN	(-2)
187 #define	ACL_SCOPE_ERR		(-1)
188 #define	ACL_SCOPE_OK		(0)
189 #define	ACL_SCOPE_PARTIAL	(1)
190 #define	ACL_SCOPE_WARN		(2)
191 
192 static int
193 check_scope( BackendDB *be, AccessControl *a )
194 {
195 	ber_len_t	patlen;
196 	struct berval	dn;
197 
198 	dn = be->be_nsuffix[0];
199 
200 	if ( BER_BVISEMPTY( &dn ) ) {
201 		return ACL_SCOPE_OK;
202 	}
203 
204 	if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
205 			a->acl_dn_style != ACL_STYLE_REGEX )
206 	{
207 		slap_style_t	style = a->acl_dn_style;
208 
209 		if ( style == ACL_STYLE_REGEX ) {
210 			char		dnbuf[SLAP_LDAPDN_MAXLEN + 2];
211 			char		rebuf[SLAP_LDAPDN_MAXLEN + 1];
212 			ber_len_t	rebuflen;
213 			regex_t		re;
214 			int		rc;
215 
216 			/* add trailing '$' to database suffix to form
217 			 * a simple trial regex pattern "<suffix>$" */
218 			AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
219 				be->be_nsuffix[0].bv_len );
220 			dnbuf[be->be_nsuffix[0].bv_len] = '$';
221 			dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
222 
223 			if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
224 				return ACL_SCOPE_WARN;
225 			}
226 
227 			/* remove trailing ')$', if any, from original
228 			 * regex pattern */
229 			rebuflen = a->acl_dn_pat.bv_len;
230 			AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
231 			if ( rebuf[rebuflen - 1] == '$' ) {
232 				rebuf[--rebuflen] = '\0';
233 			}
234 			while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
235 				rebuf[--rebuflen] = '\0';
236 			}
237 			if ( rebuflen == be->be_nsuffix[0].bv_len ) {
238 				rc = ACL_SCOPE_WARN;
239 				goto regex_done;
240 			}
241 
242 			/* not a clear indication of scoping error, though */
243 			rc = regexec( &re, rebuf, 0, NULL, 0 )
244 				? ACL_SCOPE_WARN : ACL_SCOPE_OK;
245 
246 regex_done:;
247 			regfree( &re );
248 			return rc;
249 		}
250 
251 		patlen = a->acl_dn_pat.bv_len;
252 		/* If backend suffix is longer than pattern,
253 		 * it is a potential mismatch (in the sense
254 		 * that a superior naming context could
255 		 * match */
256 		if ( dn.bv_len > patlen ) {
257 			/* base is blatantly wrong */
258 			if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
259 
260 			/* a style of one can be wrong if there is
261 			 * more than one level between the suffix
262 			 * and the pattern */
263 			if ( style == ACL_STYLE_ONE ) {
264 				ber_len_t	rdnlen = 0;
265 				int		sep = 0;
266 
267 				if ( patlen > 0 ) {
268 					if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
269 						return ACL_SCOPE_ERR;
270 					}
271 					sep = 1;
272 				}
273 
274 				rdnlen = dn_rdnlen( NULL, &dn );
275 				if ( rdnlen != dn.bv_len - patlen - sep )
276 					return ACL_SCOPE_ERR;
277 			}
278 
279 			/* if the trailing part doesn't match,
280 			 * then it's an error */
281 			if ( strcmp( a->acl_dn_pat.bv_val,
282 				&dn.bv_val[dn.bv_len - patlen] ) != 0 )
283 			{
284 				return ACL_SCOPE_ERR;
285 			}
286 
287 			return ACL_SCOPE_PARTIAL;
288 		}
289 
290 		switch ( style ) {
291 		case ACL_STYLE_BASE:
292 		case ACL_STYLE_ONE:
293 		case ACL_STYLE_CHILDREN:
294 		case ACL_STYLE_SUBTREE:
295 			break;
296 
297 		default:
298 			assert( 0 );
299 			break;
300 		}
301 
302 		if ( dn.bv_len < patlen &&
303 			!DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
304 		{
305 			return ACL_SCOPE_ERR;
306 		}
307 
308 		if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
309 			!= 0 )
310 		{
311 			return ACL_SCOPE_ERR;
312 		}
313 
314 		return ACL_SCOPE_OK;
315 	}
316 
317 	return ACL_SCOPE_UNKNOWN;
318 }
319 
320 int
321 parse_acl(
322 	Backend	*be,
323 	const char	*fname,
324 	int		lineno,
325 	int		argc,
326 	char		**argv,
327 	int		pos )
328 {
329 	int		i;
330 	char		*left, *right, *style;
331 	struct berval	bv;
332 	AccessControl	*a = NULL;
333 	Access	*b = NULL;
334 	int rc;
335 	const char *text;
336 
337 	for ( i = 1; i < argc; i++ ) {
338 		/* to clause - select which entries are protected */
339 		if ( strcasecmp( argv[i], "to" ) == 0 ) {
340 			if ( a != NULL ) {
341 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
342 					"only one to clause allowed in access line\n",
343 				    fname, lineno, 0 );
344 				goto fail;
345 			}
346 			a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
347 			for ( ++i; i < argc; i++ ) {
348 				if ( strcasecmp( argv[i], "by" ) == 0 ) {
349 					i--;
350 					break;
351 				}
352 
353 				if ( strcasecmp( argv[i], "*" ) == 0 ) {
354 					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
355 						a->acl_dn_style != ACL_STYLE_REGEX )
356 					{
357 						Debug( LDAP_DEBUG_ANY,
358 							"%s: line %d: dn pattern"
359 							" already specified in to clause.\n",
360 							fname, lineno, 0 );
361 						goto fail;
362 					}
363 
364 					ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
365 					continue;
366 				}
367 
368 				split( argv[i], '=', &left, &right );
369 				split( left, '.', &left, &style );
370 
371 				if ( right == NULL ) {
372 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
373 						"missing \"=\" in \"%s\" in to clause\n",
374 					    fname, lineno, left );
375 					goto fail;
376 				}
377 
378 				if ( strcasecmp( left, "dn" ) == 0 ) {
379 					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
380 						a->acl_dn_style != ACL_STYLE_REGEX )
381 					{
382 						Debug( LDAP_DEBUG_ANY,
383 							"%s: line %d: dn pattern"
384 							" already specified in to clause.\n",
385 							fname, lineno, 0 );
386 						goto fail;
387 					}
388 
389 					if ( style == NULL || *style == '\0' ||
390 						strcasecmp( style, "baseObject" ) == 0 ||
391 						strcasecmp( style, "base" ) == 0 ||
392 						strcasecmp( style, "exact" ) == 0 )
393 					{
394 						a->acl_dn_style = ACL_STYLE_BASE;
395 						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
396 
397 					} else if ( strcasecmp( style, "oneLevel" ) == 0 ||
398 						strcasecmp( style, "one" ) == 0 )
399 					{
400 						a->acl_dn_style = ACL_STYLE_ONE;
401 						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
402 
403 					} else if ( strcasecmp( style, "subtree" ) == 0 ||
404 						strcasecmp( style, "sub" ) == 0 )
405 					{
406 						if( *right == '\0' ) {
407 							ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
408 
409 						} else {
410 							a->acl_dn_style = ACL_STYLE_SUBTREE;
411 							ber_str2bv( right, 0, 1, &a->acl_dn_pat );
412 						}
413 
414 					} else if ( strcasecmp( style, "children" ) == 0 ) {
415 						a->acl_dn_style = ACL_STYLE_CHILDREN;
416 						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
417 
418 					} else if ( strcasecmp( style, "regex" ) == 0 ) {
419 						a->acl_dn_style = ACL_STYLE_REGEX;
420 
421 						if ( *right == '\0' ) {
422 							/* empty regex should match empty DN */
423 							a->acl_dn_style = ACL_STYLE_BASE;
424 							ber_str2bv( right, 0, 1, &a->acl_dn_pat );
425 
426 						} else if ( strcmp(right, "*") == 0
427 							|| strcmp(right, ".*") == 0
428 							|| strcmp(right, ".*$") == 0
429 							|| strcmp(right, "^.*") == 0
430 							|| strcmp(right, "^.*$") == 0
431 							|| strcmp(right, ".*$$") == 0
432 							|| strcmp(right, "^.*$$") == 0 )
433 						{
434 							ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
435 
436 						} else {
437 							acl_regex_normalized_dn( right, &a->acl_dn_pat );
438 						}
439 
440 					} else {
441 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
442 							"unknown dn style \"%s\" in to clause\n",
443 						    fname, lineno, style );
444 						goto fail;
445 					}
446 
447 					continue;
448 				}
449 
450 				if ( strcasecmp( left, "filter" ) == 0 ) {
451 					if ( (a->acl_filter = str2filter( right )) == NULL ) {
452 						Debug( LDAP_DEBUG_ANY,
453 				"%s: line %d: bad filter \"%s\" in to clause\n",
454 						    fname, lineno, right );
455 						goto fail;
456 					}
457 
458 				} else if ( strcasecmp( left, "attr" ) == 0		/* TOLERATED */
459 						|| strcasecmp( left, "attrs" ) == 0 )	/* DOCUMENTED */
460 				{
461 					if ( strcasecmp( left, "attr" ) == 0 ) {
462 						Debug( LDAP_DEBUG_ANY,
463 							"%s: line %d: \"attr\" "
464 							"is deprecated (and undocumented); "
465 							"use \"attrs\" instead.\n",
466 							fname, lineno, 0 );
467 					}
468 
469 					a->acl_attrs = str2anlist( a->acl_attrs,
470 						right, "," );
471 					if ( a->acl_attrs == NULL ) {
472 						Debug( LDAP_DEBUG_ANY,
473 				"%s: line %d: unknown attr \"%s\" in to clause\n",
474 						    fname, lineno, right );
475 						goto fail;
476 					}
477 
478 				} else if ( strncasecmp( left, "val", 3 ) == 0 ) {
479 					struct berval	bv;
480 					char		*mr;
481 
482 					if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
483 						Debug( LDAP_DEBUG_ANY,
484 				"%s: line %d: attr val already specified in to clause.\n",
485 							fname, lineno, 0 );
486 						goto fail;
487 					}
488 					if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
489 					{
490 						Debug( LDAP_DEBUG_ANY,
491 				"%s: line %d: attr val requires a single attribute.\n",
492 							fname, lineno, 0 );
493 						goto fail;
494 					}
495 
496 					ber_str2bv( right, 0, 0, &bv );
497 					a->acl_attrval_style = ACL_STYLE_BASE;
498 
499 					mr = strchr( left, '/' );
500 					if ( mr != NULL ) {
501 						mr[ 0 ] = '\0';
502 						mr++;
503 
504 						a->acl_attrval_mr = mr_find( mr );
505 						if ( a->acl_attrval_mr == NULL ) {
506 							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
507 								"invalid matching rule \"%s\".\n",
508 								fname, lineno, mr );
509 							goto fail;
510 						}
511 
512 						if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
513 						{
514 							char	buf[ SLAP_TEXT_BUFLEN ];
515 
516 							snprintf( buf, sizeof( buf ),
517 								"matching rule \"%s\" use "
518 								"with attr \"%s\" not appropriate.",
519 								mr, a->acl_attrs[ 0 ].an_name.bv_val );
520 
521 
522 							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
523 								fname, lineno, buf );
524 							goto fail;
525 						}
526 					}
527 
528 					if ( style != NULL ) {
529 						if ( strcasecmp( style, "regex" ) == 0 ) {
530 							int e = regcomp( &a->acl_attrval_re, bv.bv_val,
531 								REG_EXTENDED | REG_ICASE | REG_NOSUB );
532 							if ( e ) {
533 								char	err[SLAP_TEXT_BUFLEN],
534 									buf[ SLAP_TEXT_BUFLEN ];
535 
536 								regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
537 
538 								snprintf( buf, sizeof( buf ),
539 									"regular expression \"%s\" bad because of %s",
540 									right, err );
541 
542 								Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
543 									fname, lineno, buf );
544 								goto fail;
545 							}
546 							a->acl_attrval_style = ACL_STYLE_REGEX;
547 
548 						} else {
549 							/* FIXME: if the attribute has DN syntax, we might
550 							 * allow one, subtree and children styles as well */
551 							if ( !strcasecmp( style, "base" ) ||
552 								!strcasecmp( style, "exact" ) ) {
553 								a->acl_attrval_style = ACL_STYLE_BASE;
554 
555 							} else if ( a->acl_attrs[0].an_desc->ad_type->
556 								sat_syntax == slap_schema.si_syn_distinguishedName )
557 							{
558 								if ( !strcasecmp( style, "baseObject" ) ||
559 									!strcasecmp( style, "base" ) )
560 								{
561 									a->acl_attrval_style = ACL_STYLE_BASE;
562 								} else if ( !strcasecmp( style, "onelevel" ) ||
563 									!strcasecmp( style, "one" ) )
564 								{
565 									a->acl_attrval_style = ACL_STYLE_ONE;
566 								} else if ( !strcasecmp( style, "subtree" ) ||
567 									!strcasecmp( style, "sub" ) )
568 								{
569 									a->acl_attrval_style = ACL_STYLE_SUBTREE;
570 								} else if ( !strcasecmp( style, "children" ) ) {
571 									a->acl_attrval_style = ACL_STYLE_CHILDREN;
572 								} else {
573 									char	buf[ SLAP_TEXT_BUFLEN ];
574 
575 									snprintf( buf, sizeof( buf ),
576 										"unknown val.<style> \"%s\" for attributeType \"%s\" "
577 											"with DN syntax.",
578 										style,
579 										a->acl_attrs[0].an_desc->ad_cname.bv_val );
580 
581 									Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
582 										"%s: line %d: %s\n",
583 										fname, lineno, buf );
584 									goto fail;
585 								}
586 
587 								rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
588 								if ( rc != LDAP_SUCCESS ) {
589 									char	buf[ SLAP_TEXT_BUFLEN ];
590 
591 									snprintf( buf, sizeof( buf ),
592 										"unable to normalize DN \"%s\" "
593 										"for attributeType \"%s\" (%d).",
594 										bv.bv_val,
595 										a->acl_attrs[0].an_desc->ad_cname.bv_val,
596 										rc );
597 									Debug( LDAP_DEBUG_ANY,
598 										"%s: line %d: %s\n",
599 										fname, lineno, buf );
600 									goto fail;
601 								}
602 
603 							} else {
604 								char	buf[ SLAP_TEXT_BUFLEN ];
605 
606 								snprintf( buf, sizeof( buf ),
607 									"unknown val.<style> \"%s\" for attributeType \"%s\".",
608 									style, a->acl_attrs[0].an_desc->ad_cname.bv_val );
609 								Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
610 									"%s: line %d: %s\n",
611 									fname, lineno, buf );
612 								goto fail;
613 							}
614 						}
615 					}
616 
617 					/* Check for appropriate matching rule */
618 					if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
619 						ber_dupbv( &a->acl_attrval, &bv );
620 
621 					} else if ( BER_BVISNULL( &a->acl_attrval ) ) {
622 						int		rc;
623 						const char	*text;
624 
625 						if ( a->acl_attrval_mr == NULL ) {
626 							a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
627 						}
628 
629 						if ( a->acl_attrval_mr == NULL ) {
630 							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
631 								"attr \"%s\" does not have an EQUALITY matching rule.\n",
632 								fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
633 							goto fail;
634 						}
635 
636 						rc = asserted_value_validate_normalize(
637 							a->acl_attrs[ 0 ].an_desc,
638 							a->acl_attrval_mr,
639 							SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
640 							&bv,
641 							&a->acl_attrval,
642 							&text,
643 							NULL );
644 						if ( rc != LDAP_SUCCESS ) {
645 							char	buf[ SLAP_TEXT_BUFLEN ];
646 
647 							snprintf( buf, sizeof( buf ), "%s: line %d: "
648 								" attr \"%s\" normalization failed (%d: %s)",
649 								fname, lineno,
650 								a->acl_attrs[ 0 ].an_name.bv_val, rc, text );
651 							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s.\n",
652 								fname, lineno, buf );
653 							goto fail;
654 						}
655 					}
656 
657 				} else {
658 					Debug( LDAP_DEBUG_ANY,
659 						"%s: line %d: expecting <what> got \"%s\"\n",
660 					    fname, lineno, left );
661 					goto fail;
662 				}
663 			}
664 
665 			if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
666 					ber_bvccmp( &a->acl_dn_pat, '*' ) )
667 			{
668 				free( a->acl_dn_pat.bv_val );
669 				BER_BVZERO( &a->acl_dn_pat );
670 				a->acl_dn_style = ACL_STYLE_REGEX;
671 			}
672 
673 			if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
674 					a->acl_dn_style != ACL_STYLE_REGEX )
675 			{
676 				if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
677 					struct berval bv;
678 					rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
679 					if ( rc != LDAP_SUCCESS ) {
680 						Debug( LDAP_DEBUG_ANY,
681 							"%s: line %d: bad DN \"%s\" in to DN clause\n",
682 							fname, lineno, a->acl_dn_pat.bv_val );
683 						goto fail;
684 					}
685 					free( a->acl_dn_pat.bv_val );
686 					a->acl_dn_pat = bv;
687 
688 				} else {
689 					int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
690 						REG_EXTENDED | REG_ICASE );
691 					if ( e ) {
692 						char	err[ SLAP_TEXT_BUFLEN ],
693 							buf[ SLAP_TEXT_BUFLEN ];
694 
695 						regerror( e, &a->acl_dn_re, err, sizeof( err ) );
696 						snprintf( buf, sizeof( buf ),
697 							"regular expression \"%s\" bad because of %s",
698 							right, err );
699 						Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
700 							fname, lineno, buf );
701 						goto fail;
702 					}
703 				}
704 			}
705 
706 		/* by clause - select who has what access to entries */
707 		} else if ( strcasecmp( argv[i], "by" ) == 0 ) {
708 			if ( a == NULL ) {
709 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
710 					"to clause required before by clause in access line\n",
711 					fname, lineno, 0 );
712 				goto fail;
713 			}
714 
715 			/*
716 			 * by clause consists of <who> and <access>
717 			 */
718 
719 			if ( ++i == argc ) {
720 				Debug( LDAP_DEBUG_ANY,
721 					"%s: line %d: premature EOL: expecting <who>\n",
722 					fname, lineno, 0 );
723 				goto fail;
724 			}
725 
726 			b = (Access *) ch_calloc( 1, sizeof(Access) );
727 
728 			ACL_INVALIDATE( b->a_access_mask );
729 
730 			/* get <who> */
731 			for ( ; i < argc; i++ ) {
732 				slap_style_t	sty = ACL_STYLE_REGEX;
733 				char		*style_modifier = NULL;
734 				char		*style_level = NULL;
735 				int		level = 0;
736 				int		expand = 0;
737 				slap_dn_access	*bdn = &b->a_dn;
738 				int		is_realdn = 0;
739 
740 				split( argv[i], '=', &left, &right );
741 				split( left, '.', &left, &style );
742 				if ( style ) {
743 					split( style, ',', &style, &style_modifier );
744 
745 					if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
746 						split( style, '{', &style, &style_level );
747 						if ( style_level != NULL ) {
748 							char *p = strchr( style_level, '}' );
749 							if ( p == NULL ) {
750 								Debug( LDAP_DEBUG_ANY,
751 									"%s: line %d: premature eol: "
752 									"expecting closing '}' in \"level{n}\"\n",
753 									fname, lineno, 0 );
754 								goto fail;
755 							} else if ( p == style_level ) {
756 								Debug( LDAP_DEBUG_ANY,
757 									"%s: line %d: empty level "
758 									"in \"level{n}\"\n",
759 									fname, lineno, 0 );
760 								goto fail;
761 							}
762 							p[0] = '\0';
763 						}
764 					}
765 				}
766 
767 				if ( style == NULL || *style == '\0' ||
768 					strcasecmp( style, "exact" ) == 0 ||
769 					strcasecmp( style, "baseObject" ) == 0 ||
770 					strcasecmp( style, "base" ) == 0 )
771 				{
772 					sty = ACL_STYLE_BASE;
773 
774 				} else if ( strcasecmp( style, "onelevel" ) == 0 ||
775 					strcasecmp( style, "one" ) == 0 )
776 				{
777 					sty = ACL_STYLE_ONE;
778 
779 				} else if ( strcasecmp( style, "subtree" ) == 0 ||
780 					strcasecmp( style, "sub" ) == 0 )
781 				{
782 					sty = ACL_STYLE_SUBTREE;
783 
784 				} else if ( strcasecmp( style, "children" ) == 0 ) {
785 					sty = ACL_STYLE_CHILDREN;
786 
787 				} else if ( strcasecmp( style, "level" ) == 0 )
788 				{
789 					if ( lutil_atoi( &level, style_level ) != 0 ) {
790 						Debug( LDAP_DEBUG_ANY,
791 							"%s: line %d: unable to parse level "
792 							"in \"level{n}\"\n",
793 							fname, lineno, 0 );
794 						goto fail;
795 					}
796 
797 					sty = ACL_STYLE_LEVEL;
798 
799 				} else if ( strcasecmp( style, "regex" ) == 0 ) {
800 					sty = ACL_STYLE_REGEX;
801 
802 				} else if ( strcasecmp( style, "expand" ) == 0 ) {
803 					sty = ACL_STYLE_EXPAND;
804 
805 				} else if ( strcasecmp( style, "ip" ) == 0 ) {
806 					sty = ACL_STYLE_IP;
807 
808 				} else if ( strcasecmp( style, "ipv6" ) == 0 ) {
809 #ifndef LDAP_PF_INET6
810 					Debug( LDAP_DEBUG_ANY,
811 						"%s: line %d: IPv6 not supported\n",
812 						fname, lineno, 0 );
813 #endif /* ! LDAP_PF_INET6 */
814 					sty = ACL_STYLE_IPV6;
815 
816 				} else if ( strcasecmp( style, "path" ) == 0 ) {
817 					sty = ACL_STYLE_PATH;
818 #ifndef LDAP_PF_LOCAL
819 					Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
820 						"%s: line %d: "
821 						"\"path\" style modifier is useless without local.\n",
822 						fname, lineno, 0 );
823 					goto fail;
824 #endif /* LDAP_PF_LOCAL */
825 
826 				} else {
827 					Debug( LDAP_DEBUG_ANY,
828 						"%s: line %d: unknown style \"%s\" in by clause\n",
829 						fname, lineno, style );
830 					goto fail;
831 				}
832 
833 				if ( style_modifier &&
834 					strcasecmp( style_modifier, "expand" ) == 0 )
835 				{
836 					switch ( sty ) {
837 					case ACL_STYLE_REGEX:
838 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
839 							"\"regex\" style implies \"expand\" modifier.\n",
840 							fname, lineno, 0 );
841 						goto fail;
842 						break;
843 
844 					case ACL_STYLE_EXPAND:
845 						break;
846 
847 					default:
848 						/* we'll see later if it's pertinent */
849 						expand = 1;
850 						break;
851 					}
852 				}
853 
854 				/* expand in <who> needs regex in <what> */
855 				if ( ( sty == ACL_STYLE_EXPAND || expand )
856 						&& a->acl_dn_style != ACL_STYLE_REGEX )
857 				{
858 					Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: line %d: \"expand\" style "
859 						"or modifier used in conjunction with a non-regex <what> clause.\n",
860 						fname, lineno, 0 );
861 						goto fail;
862 				}
863 
864 				if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
865 					is_realdn = 1;
866 					bdn = &b->a_realdn;
867 					left += STRLENOF( "real" );
868 				}
869 
870 				if ( strcasecmp( left, "*" ) == 0 ) {
871 					if ( is_realdn ) {
872 						goto fail;
873 					}
874 
875 					ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
876 					sty = ACL_STYLE_REGEX;
877 
878 				} else if ( strcasecmp( left, "anonymous" ) == 0 ) {
879 					ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
880 					sty = ACL_STYLE_ANONYMOUS;
881 
882 				} else if ( strcasecmp( left, "users" ) == 0 ) {
883 					ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
884 					sty = ACL_STYLE_USERS;
885 
886 				} else if ( strcasecmp( left, "self" ) == 0 ) {
887 					ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
888 					sty = ACL_STYLE_SELF;
889 
890 				} else if ( strcasecmp( left, "dn" ) == 0 ) {
891 					if ( sty == ACL_STYLE_REGEX ) {
892 						bdn->a_style = ACL_STYLE_REGEX;
893 						if ( right == NULL ) {
894 							/* no '=' */
895 							ber_str2bv("users",
896 								STRLENOF( "users" ),
897 								1, &bv);
898 							bdn->a_style = ACL_STYLE_USERS;
899 
900 						} else if (*right == '\0' ) {
901 							/* dn="" */
902 							ber_str2bv("anonymous",
903 								STRLENOF( "anonymous" ),
904 								1, &bv);
905 							bdn->a_style = ACL_STYLE_ANONYMOUS;
906 
907 						} else if ( strcmp( right, "*" ) == 0 ) {
908 							/* dn=* */
909 							/* any or users?  users for now */
910 							ber_str2bv("users",
911 								STRLENOF( "users" ),
912 								1, &bv);
913 							bdn->a_style = ACL_STYLE_USERS;
914 
915 						} else if ( strcmp( right, ".+" ) == 0
916 							|| strcmp( right, "^.+" ) == 0
917 							|| strcmp( right, ".+$" ) == 0
918 							|| strcmp( right, "^.+$" ) == 0
919 							|| strcmp( right, ".+$$" ) == 0
920 							|| strcmp( right, "^.+$$" ) == 0 )
921 						{
922 							ber_str2bv("users",
923 								STRLENOF( "users" ),
924 								1, &bv);
925 							bdn->a_style = ACL_STYLE_USERS;
926 
927 						} else if ( strcmp( right, ".*" ) == 0
928 							|| strcmp( right, "^.*" ) == 0
929 							|| strcmp( right, ".*$" ) == 0
930 							|| strcmp( right, "^.*$" ) == 0
931 							|| strcmp( right, ".*$$" ) == 0
932 							|| strcmp( right, "^.*$$" ) == 0 )
933 						{
934 							ber_str2bv("*",
935 								STRLENOF( "*" ),
936 								1, &bv);
937 
938 						} else {
939 							acl_regex_normalized_dn( right, &bv );
940 							if ( !ber_bvccmp( &bv, '*' ) ) {
941 								regtest( fname, lineno, bv.bv_val );
942 							}
943 						}
944 
945 					} else if ( right == NULL || *right == '\0' ) {
946 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
947 							"missing \"=\" in (or value after) \"%s\" "
948 							"in by clause\n",
949 							fname, lineno, left );
950 						goto fail;
951 
952 					} else {
953 						ber_str2bv( right, 0, 1, &bv );
954 					}
955 
956 				} else {
957 					BER_BVZERO( &bv );
958 				}
959 
960 				if ( !BER_BVISNULL( &bv ) ) {
961 					if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
962 						Debug( LDAP_DEBUG_ANY,
963 							"%s: line %d: dn pattern already specified.\n",
964 							fname, lineno, 0 );
965 						goto fail;
966 					}
967 
968 					if ( sty != ACL_STYLE_REGEX &&
969 							sty != ACL_STYLE_ANONYMOUS &&
970 							sty != ACL_STYLE_USERS &&
971 							sty != ACL_STYLE_SELF &&
972 							expand == 0 )
973 					{
974 						rc = dnNormalize(0, NULL, NULL,
975 							&bv, &bdn->a_pat, NULL);
976 						if ( rc != LDAP_SUCCESS ) {
977 							Debug( LDAP_DEBUG_ANY,
978 								"%s: line %d: bad DN \"%s\" in by DN clause\n",
979 								fname, lineno, bv.bv_val );
980 							goto fail;
981 						}
982 						free( bv.bv_val );
983 						if ( sty == ACL_STYLE_BASE
984 							&& be != NULL
985 							&& !BER_BVISNULL( &be->be_rootndn )
986 							&& dn_match( &bdn->a_pat, &be->be_rootndn ) )
987 						{
988 							Debug( LDAP_DEBUG_ANY,
989 								"%s: line %d: rootdn is always granted "
990 								"unlimited privileges.\n",
991 								fname, lineno, 0 );
992 						}
993 
994 					} else {
995 						bdn->a_pat = bv;
996 					}
997 					bdn->a_style = sty;
998 					if ( expand ) {
999 						char	*exp;
1000 						int	gotit = 0;
1001 
1002 						for ( exp = strchr( bdn->a_pat.bv_val, '$' );
1003 							exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
1004 								< bdn->a_pat.bv_len;
1005 							exp = strchr( exp, '$' ) )
1006 						{
1007 							if ( isdigit( (unsigned char) exp[ 1 ] ) ) {
1008 								gotit = 1;
1009 								break;
1010 							}
1011 						}
1012 
1013 						if ( gotit == 1 ) {
1014 							bdn->a_expand = expand;
1015 
1016 						} else {
1017 							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1018 								"\"expand\" used with no expansions in \"pattern\".\n",
1019 								fname, lineno, 0 );
1020 							goto fail;
1021 						}
1022 					}
1023 					if ( sty == ACL_STYLE_SELF ) {
1024 						bdn->a_self_level = level;
1025 
1026 					} else {
1027 						if ( level < 0 ) {
1028 							Debug( LDAP_DEBUG_ANY,
1029 								"%s: line %d: bad negative level \"%d\" "
1030 								"in by DN clause\n",
1031 								fname, lineno, level );
1032 							goto fail;
1033 						} else if ( level == 1 ) {
1034 							Debug( LDAP_DEBUG_ANY,
1035 								"%s: line %d: \"onelevel\" should be used "
1036 								"instead of \"level{1}\" in by DN clause\n",
1037 								fname, lineno, 0 );
1038 						} else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
1039 							Debug( LDAP_DEBUG_ANY,
1040 								"%s: line %d: \"base\" should be used "
1041 								"instead of \"level{0}\" in by DN clause\n",
1042 								fname, lineno, 0 );
1043 						}
1044 
1045 						bdn->a_level = level;
1046 					}
1047 					continue;
1048 				}
1049 
1050 				if ( strcasecmp( left, "dnattr" ) == 0 ) {
1051 					if ( right == NULL || right[0] == '\0' ) {
1052 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1053 							"missing \"=\" in (or value after) \"%s\" "
1054 							"in by clause\n",
1055 							fname, lineno, left );
1056 						goto fail;
1057 					}
1058 
1059 					if( bdn->a_at != NULL ) {
1060 						Debug( LDAP_DEBUG_ANY,
1061 							"%s: line %d: dnattr already specified.\n",
1062 							fname, lineno, 0 );
1063 						goto fail;
1064 					}
1065 
1066 					rc = slap_str2ad( right, &bdn->a_at, &text );
1067 
1068 					if( rc != LDAP_SUCCESS ) {
1069 						char	buf[ SLAP_TEXT_BUFLEN ];
1070 
1071 						snprintf( buf, sizeof( buf ),
1072 							"dnattr \"%s\": %s",
1073 							right, text );
1074 						Debug( LDAP_DEBUG_ANY,
1075 							"%s: line %d: %s\n",
1076 							fname, lineno, buf );
1077 						goto fail;
1078 					}
1079 
1080 
1081 					if( !is_at_syntax( bdn->a_at->ad_type,
1082 						SLAPD_DN_SYNTAX ) &&
1083 						!is_at_syntax( bdn->a_at->ad_type,
1084 						SLAPD_NAMEUID_SYNTAX ))
1085 					{
1086 						char	buf[ SLAP_TEXT_BUFLEN ];
1087 
1088 						snprintf( buf, sizeof( buf ),
1089 							"dnattr \"%s\": "
1090 							"inappropriate syntax: %s\n",
1091 							right,
1092 							bdn->a_at->ad_type->sat_syntax_oid );
1093 						Debug( LDAP_DEBUG_ANY,
1094 							"%s: line %d: %s\n",
1095 							fname, lineno, buf );
1096 						goto fail;
1097 					}
1098 
1099 					if( bdn->a_at->ad_type->sat_equality == NULL ) {
1100 						Debug( LDAP_DEBUG_ANY,
1101 							"%s: line %d: dnattr \"%s\": "
1102 							"inappropriate matching (no EQUALITY)\n",
1103 							fname, lineno, right );
1104 						goto fail;
1105 					}
1106 
1107 					continue;
1108 				}
1109 
1110 				if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
1111 					char *name = NULL;
1112 					char *value = NULL;
1113 					char *attr_name = SLAPD_GROUP_ATTR;
1114 
1115 					switch ( sty ) {
1116 					case ACL_STYLE_REGEX:
1117 						/* legacy, tolerated */
1118 						Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1119 							"%s: line %d: "
1120 							"deprecated group style \"regex\"; "
1121 							"use \"expand\" instead.\n",
1122 							fname, lineno, 0 );
1123 						sty = ACL_STYLE_EXPAND;
1124 						break;
1125 
1126 					case ACL_STYLE_BASE:
1127 						/* legal, traditional */
1128 					case ACL_STYLE_EXPAND:
1129 						/* legal, substring expansion; supersedes regex */
1130 						break;
1131 
1132 					default:
1133 						/* unknown */
1134 						Debug( LDAP_DEBUG_ANY,
1135 							"%s: line %d: "
1136 							"inappropriate style \"%s\" in by clause.\n",
1137 							fname, lineno, style );
1138 						goto fail;
1139 					}
1140 
1141 					if ( right == NULL || right[0] == '\0' ) {
1142 						Debug( LDAP_DEBUG_ANY,
1143 							"%s: line %d: "
1144 							"missing \"=\" in (or value after) \"%s\" "
1145 							"in by clause.\n",
1146 							fname, lineno, left );
1147 						goto fail;
1148 					}
1149 
1150 					if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1151 						Debug( LDAP_DEBUG_ANY,
1152 							"%s: line %d: group pattern already specified.\n",
1153 							fname, lineno, 0 );
1154 						goto fail;
1155 					}
1156 
1157 					/* format of string is
1158 						"group/objectClassValue/groupAttrName" */
1159 					if ( ( value = strchr(left, '/') ) != NULL ) {
1160 						*value++ = '\0';
1161 						if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
1162 							*name++ = '\0';
1163 						}
1164 					}
1165 
1166 					b->a_group_style = sty;
1167 					if ( sty == ACL_STYLE_EXPAND ) {
1168 						acl_regex_normalized_dn( right, &bv );
1169 						if ( !ber_bvccmp( &bv, '*' ) ) {
1170 							regtest( fname, lineno, bv.bv_val );
1171 						}
1172 						b->a_group_pat = bv;
1173 
1174 					} else {
1175 						ber_str2bv( right, 0, 0, &bv );
1176 						rc = dnNormalize( 0, NULL, NULL, &bv,
1177 							&b->a_group_pat, NULL );
1178 						if ( rc != LDAP_SUCCESS ) {
1179 							Debug( LDAP_DEBUG_ANY,
1180 								"%s: line %d: bad DN \"%s\".\n",
1181 								fname, lineno, right );
1182 							goto fail;
1183 						}
1184 					}
1185 
1186 					if ( value && *value ) {
1187 						b->a_group_oc = oc_find( value );
1188 						*--value = '/';
1189 
1190 						if ( b->a_group_oc == NULL ) {
1191 							Debug( LDAP_DEBUG_ANY,
1192 								"%s: line %d: group objectclass "
1193 								"\"%s\" unknown.\n",
1194 								fname, lineno, value );
1195 							goto fail;
1196 						}
1197 
1198 					} else {
1199 						b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
1200 
1201 						if( b->a_group_oc == NULL ) {
1202 							Debug( LDAP_DEBUG_ANY,
1203 								"%s: line %d: group default objectclass "
1204 								"\"%s\" unknown.\n",
1205 								fname, lineno, SLAPD_GROUP_CLASS );
1206 							goto fail;
1207 						}
1208 					}
1209 
1210 					if ( is_object_subclass( slap_schema.si_oc_referral,
1211 						b->a_group_oc ) )
1212 					{
1213 						Debug( LDAP_DEBUG_ANY,
1214 							"%s: line %d: group objectclass \"%s\" "
1215 							"is subclass of referral.\n",
1216 							fname, lineno, value );
1217 						goto fail;
1218 					}
1219 
1220 					if ( is_object_subclass( slap_schema.si_oc_alias,
1221 						b->a_group_oc ) )
1222 					{
1223 						Debug( LDAP_DEBUG_ANY,
1224 							"%s: line %d: group objectclass \"%s\" "
1225 							"is subclass of alias.\n",
1226 							fname, lineno, value );
1227 						goto fail;
1228 					}
1229 
1230 					if ( name && *name ) {
1231 						attr_name = name;
1232 						*--name = '/';
1233 
1234 					}
1235 
1236 					rc = slap_str2ad( attr_name, &b->a_group_at, &text );
1237 					if ( rc != LDAP_SUCCESS ) {
1238 						char	buf[ SLAP_TEXT_BUFLEN ];
1239 
1240 						snprintf( buf, sizeof( buf ),
1241 							"group \"%s\": %s.",
1242 							right, text );
1243 						Debug( LDAP_DEBUG_ANY,
1244 							"%s: line %d: %s\n",
1245 							fname, lineno, buf );
1246 						goto fail;
1247 					}
1248 
1249 					if ( !is_at_syntax( b->a_group_at->ad_type,
1250 							SLAPD_DN_SYNTAX ) /* e.g. "member" */
1251 						&& !is_at_syntax( b->a_group_at->ad_type,
1252 							SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
1253 						&& !is_at_subtype( b->a_group_at->ad_type,
1254 							slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
1255 					{
1256 						char	buf[ SLAP_TEXT_BUFLEN ];
1257 
1258 						snprintf( buf, sizeof( buf ),
1259 							"group \"%s\" attr \"%s\": inappropriate syntax: %s; "
1260 							"must be " SLAPD_DN_SYNTAX " (DN), "
1261 							SLAPD_NAMEUID_SYNTAX " (NameUID) "
1262 							"or a subtype of labeledURI.",
1263 							right,
1264 							attr_name,
1265 							at_syntax( b->a_group_at->ad_type ) );
1266 						Debug( LDAP_DEBUG_ANY,
1267 							"%s: line %d: %s\n",
1268 							fname, lineno, buf );
1269 						goto fail;
1270 					}
1271 
1272 
1273 					{
1274 						int rc;
1275 						ObjectClass *ocs[2];
1276 
1277 						ocs[0] = b->a_group_oc;
1278 						ocs[1] = NULL;
1279 
1280 						rc = oc_check_allowed( b->a_group_at->ad_type,
1281 							ocs, NULL );
1282 
1283 						if( rc != 0 ) {
1284 							char	buf[ SLAP_TEXT_BUFLEN ];
1285 
1286 							snprintf( buf, sizeof( buf ),
1287 								"group: \"%s\" not allowed by \"%s\".",
1288 								b->a_group_at->ad_cname.bv_val,
1289 								b->a_group_oc->soc_oid );
1290 							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
1291 								fname, lineno, buf );
1292 							goto fail;
1293 						}
1294 					}
1295 					continue;
1296 				}
1297 
1298 				if ( strcasecmp( left, "peername" ) == 0 ) {
1299 					switch ( sty ) {
1300 					case ACL_STYLE_REGEX:
1301 					case ACL_STYLE_BASE:
1302 						/* legal, traditional */
1303 					case ACL_STYLE_EXPAND:
1304 						/* cheap replacement to regex for simple expansion */
1305 					case ACL_STYLE_IP:
1306 					case ACL_STYLE_IPV6:
1307 					case ACL_STYLE_PATH:
1308 						/* legal, peername specific */
1309 						break;
1310 
1311 					default:
1312 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1313 							"inappropriate style \"%s\" in by clause.\n",
1314 						    fname, lineno, style );
1315 						goto fail;
1316 					}
1317 
1318 					if ( right == NULL || right[0] == '\0' ) {
1319 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1320 							"missing \"=\" in (or value after) \"%s\" "
1321 							"in by clause.\n",
1322 							fname, lineno, left );
1323 						goto fail;
1324 					}
1325 
1326 					if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1327 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1328 							"peername pattern already specified.\n",
1329 							fname, lineno, 0 );
1330 						goto fail;
1331 					}
1332 
1333 					b->a_peername_style = sty;
1334 					if ( sty == ACL_STYLE_REGEX ) {
1335 						acl_regex_normalized_dn( right, &bv );
1336 						if ( !ber_bvccmp( &bv, '*' ) ) {
1337 							regtest( fname, lineno, bv.bv_val );
1338 						}
1339 						b->a_peername_pat = bv;
1340 
1341 					} else {
1342 						ber_str2bv( right, 0, 1, &b->a_peername_pat );
1343 
1344 						if ( sty == ACL_STYLE_IP ) {
1345 							char		*addr = NULL,
1346 									*mask = NULL,
1347 									*port = NULL;
1348 
1349 							split( right, '{', &addr, &port );
1350 							split( addr, '%', &addr, &mask );
1351 
1352 							b->a_peername_addr = inet_addr( addr );
1353 							if ( b->a_peername_addr == (unsigned long)(-1) ) {
1354 								/* illegal address */
1355 								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1356 									"illegal peername address \"%s\".\n",
1357 									fname, lineno, addr );
1358 								goto fail;
1359 							}
1360 
1361 							b->a_peername_mask = (unsigned long)(-1);
1362 							if ( mask != NULL ) {
1363 								b->a_peername_mask = inet_addr( mask );
1364 								if ( b->a_peername_mask ==
1365 									(unsigned long)(-1) )
1366 								{
1367 									/* illegal mask */
1368 									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1369 										"illegal peername address mask "
1370 										"\"%s\".\n",
1371 										fname, lineno, mask );
1372 									goto fail;
1373 								}
1374 							}
1375 
1376 							b->a_peername_port = -1;
1377 							if ( port ) {
1378 								char	*end = NULL;
1379 
1380 								b->a_peername_port = strtol( port, &end, 10 );
1381 								if ( end == port || end[0] != '}' ) {
1382 									/* illegal port */
1383 									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1384 										"illegal peername port specification "
1385 										"\"{%s}\".\n",
1386 										fname, lineno, port );
1387 									goto fail;
1388 								}
1389 							}
1390 
1391 #ifdef LDAP_PF_INET6
1392 						} else if ( sty == ACL_STYLE_IPV6 ) {
1393 							char		*addr = NULL,
1394 									*mask = NULL,
1395 									*port = NULL;
1396 
1397 							split( right, '{', &addr, &port );
1398 							split( addr, '%', &addr, &mask );
1399 
1400 							if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
1401 								/* illegal address */
1402 								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1403 									"illegal peername address \"%s\".\n",
1404 									fname, lineno, addr );
1405 								goto fail;
1406 							}
1407 
1408 							if ( mask == NULL ) {
1409 								mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
1410 							}
1411 
1412 							if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
1413 								/* illegal mask */
1414 								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1415 									"illegal peername address mask "
1416 									"\"%s\".\n",
1417 									fname, lineno, mask );
1418 								goto fail;
1419 							}
1420 
1421 							b->a_peername_port = -1;
1422 							if ( port ) {
1423 								char	*end = NULL;
1424 
1425 								b->a_peername_port = strtol( port, &end, 10 );
1426 								if ( end == port || end[0] != '}' ) {
1427 									/* illegal port */
1428 									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1429 										"illegal peername port specification "
1430 										"\"{%s}\".\n",
1431 										fname, lineno, port );
1432 									goto fail;
1433 								}
1434 							}
1435 #endif /* LDAP_PF_INET6 */
1436 						}
1437 					}
1438 					continue;
1439 				}
1440 
1441 				if ( strcasecmp( left, "sockname" ) == 0 ) {
1442 					switch ( sty ) {
1443 					case ACL_STYLE_REGEX:
1444 					case ACL_STYLE_BASE:
1445 						/* legal, traditional */
1446 					case ACL_STYLE_EXPAND:
1447 						/* cheap replacement to regex for simple expansion */
1448 						break;
1449 
1450 					default:
1451 						/* unknown */
1452 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1453 							"inappropriate style \"%s\" in by clause\n",
1454 						    fname, lineno, style );
1455 						goto fail;
1456 					}
1457 
1458 					if ( right == NULL || right[0] == '\0' ) {
1459 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1460 							"missing \"=\" in (or value after) \"%s\" "
1461 							"in by clause\n",
1462 							fname, lineno, left );
1463 						goto fail;
1464 					}
1465 
1466 					if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
1467 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1468 							"sockname pattern already specified.\n",
1469 							fname, lineno, 0 );
1470 						goto fail;
1471 					}
1472 
1473 					b->a_sockname_style = sty;
1474 					if ( sty == ACL_STYLE_REGEX ) {
1475 						acl_regex_normalized_dn( right, &bv );
1476 						if ( !ber_bvccmp( &bv, '*' ) ) {
1477 							regtest( fname, lineno, bv.bv_val );
1478 						}
1479 						b->a_sockname_pat = bv;
1480 
1481 					} else {
1482 						ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1483 					}
1484 					continue;
1485 				}
1486 
1487 				if ( strcasecmp( left, "domain" ) == 0 ) {
1488 					switch ( sty ) {
1489 					case ACL_STYLE_REGEX:
1490 					case ACL_STYLE_BASE:
1491 					case ACL_STYLE_SUBTREE:
1492 						/* legal, traditional */
1493 						break;
1494 
1495 					case ACL_STYLE_EXPAND:
1496 						/* tolerated: means exact,expand */
1497 						if ( expand ) {
1498 							Debug( LDAP_DEBUG_ANY,
1499 								"%s: line %d: "
1500 								"\"expand\" modifier "
1501 								"with \"expand\" style.\n",
1502 								fname, lineno, 0 );
1503 						}
1504 						sty = ACL_STYLE_BASE;
1505 						expand = 1;
1506 						break;
1507 
1508 					default:
1509 						/* unknown */
1510 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1511 							"inappropriate style \"%s\" in by clause.\n",
1512 						    fname, lineno, style );
1513 						goto fail;
1514 					}
1515 
1516 					if ( right == NULL || right[0] == '\0' ) {
1517 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1518 							"missing \"=\" in (or value after) \"%s\" "
1519 							"in by clause.\n",
1520 							fname, lineno, left );
1521 						goto fail;
1522 					}
1523 
1524 					if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1525 						Debug( LDAP_DEBUG_ANY,
1526 							"%s: line %d: domain pattern already specified.\n",
1527 							fname, lineno, 0 );
1528 						goto fail;
1529 					}
1530 
1531 					b->a_domain_style = sty;
1532 					b->a_domain_expand = expand;
1533 					if ( sty == ACL_STYLE_REGEX ) {
1534 						acl_regex_normalized_dn( right, &bv );
1535 						if ( !ber_bvccmp( &bv, '*' ) ) {
1536 							regtest( fname, lineno, bv.bv_val );
1537 						}
1538 						b->a_domain_pat = bv;
1539 
1540 					} else {
1541 						ber_str2bv( right, 0, 1, &b->a_domain_pat );
1542 					}
1543 					continue;
1544 				}
1545 
1546 				if ( strcasecmp( left, "sockurl" ) == 0 ) {
1547 					switch ( sty ) {
1548 					case ACL_STYLE_REGEX:
1549 					case ACL_STYLE_BASE:
1550 						/* legal, traditional */
1551 					case ACL_STYLE_EXPAND:
1552 						/* cheap replacement to regex for simple expansion */
1553 						break;
1554 
1555 					default:
1556 						/* unknown */
1557 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1558 							"inappropriate style \"%s\" in by clause.\n",
1559 						    fname, lineno, style );
1560 						goto fail;
1561 					}
1562 
1563 					if ( right == NULL || right[0] == '\0' ) {
1564 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1565 							"missing \"=\" in (or value after) \"%s\" "
1566 							"in by clause.\n",
1567 							fname, lineno, left );
1568 						goto fail;
1569 					}
1570 
1571 					if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1572 						Debug( LDAP_DEBUG_ANY,
1573 							"%s: line %d: sockurl pattern already specified.\n",
1574 							fname, lineno, 0 );
1575 						goto fail;
1576 					}
1577 
1578 					b->a_sockurl_style = sty;
1579 					if ( sty == ACL_STYLE_REGEX ) {
1580 						acl_regex_normalized_dn( right, &bv );
1581 						if ( !ber_bvccmp( &bv, '*' ) ) {
1582 							regtest( fname, lineno, bv.bv_val );
1583 						}
1584 						b->a_sockurl_pat = bv;
1585 
1586 					} else {
1587 						ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1588 					}
1589 					continue;
1590 				}
1591 
1592 				if ( strcasecmp( left, "set" ) == 0 ) {
1593 					switch ( sty ) {
1594 						/* deprecated */
1595 					case ACL_STYLE_REGEX:
1596 						Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1597 							"%s: line %d: "
1598 							"deprecated set style "
1599 							"\"regex\" in <by> clause; "
1600 							"use \"expand\" instead.\n",
1601 							fname, lineno, 0 );
1602 						sty = ACL_STYLE_EXPAND;
1603 						/* FALLTHRU */
1604 
1605 					case ACL_STYLE_BASE:
1606 					case ACL_STYLE_EXPAND:
1607 						break;
1608 
1609 					default:
1610 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1611 							"inappropriate style \"%s\" in by clause.\n",
1612 							fname, lineno, style );
1613 						goto fail;
1614 					}
1615 
1616 					if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1617 						Debug( LDAP_DEBUG_ANY,
1618 							"%s: line %d: set attribute already specified.\n",
1619 							fname, lineno, 0 );
1620 						goto fail;
1621 					}
1622 
1623 					if ( right == NULL || *right == '\0' ) {
1624 						Debug( LDAP_DEBUG_ANY,
1625 							"%s: line %d: no set is defined.\n",
1626 							fname, lineno, 0 );
1627 						goto fail;
1628 					}
1629 
1630 					b->a_set_style = sty;
1631 					ber_str2bv( right, 0, 1, &b->a_set_pat );
1632 
1633 					continue;
1634 				}
1635 
1636 #ifdef SLAP_DYNACL
1637 				{
1638 					char		*name = NULL,
1639 							*opts = NULL;
1640 
1641 #if 1 /* tolerate legacy "aci" <who> */
1642 					if ( strcasecmp( left, "aci" ) == 0 ) {
1643 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1644 							"undocumented deprecated \"aci\" directive "
1645 							"is superseded by \"dynacl/aci\".\n",
1646 							fname, lineno, 0 );
1647 						name = "aci";
1648 
1649 					} else
1650 #endif /* tolerate legacy "aci" <who> */
1651 					if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
1652 						name = &left[ STRLENOF( "dynacl/" ) ];
1653 						opts = strchr( name, '/' );
1654 						if ( opts ) {
1655 							opts[ 0 ] = '\0';
1656 							opts++;
1657 						}
1658 					}
1659 
1660 					if ( name ) {
1661 						if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
1662 							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1663 								"unable to configure dynacl \"%s\".\n",
1664 								fname, lineno, name );
1665 							goto fail;
1666 						}
1667 
1668 						continue;
1669 					}
1670 				}
1671 #endif /* SLAP_DYNACL */
1672 
1673 				if ( strcasecmp( left, "ssf" ) == 0 ) {
1674 					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1675 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1676 							"inappropriate style \"%s\" in by clause.\n",
1677 						    fname, lineno, style );
1678 						goto fail;
1679 					}
1680 
1681 					if ( b->a_authz.sai_ssf ) {
1682 						Debug( LDAP_DEBUG_ANY,
1683 							"%s: line %d: ssf attribute already specified.\n",
1684 							fname, lineno, 0 );
1685 						goto fail;
1686 					}
1687 
1688 					if ( right == NULL || *right == '\0' ) {
1689 						Debug( LDAP_DEBUG_ANY,
1690 							"%s: line %d: no ssf is defined.\n",
1691 							fname, lineno, 0 );
1692 						goto fail;
1693 					}
1694 
1695 					if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
1696 						Debug( LDAP_DEBUG_ANY,
1697 							"%s: line %d: unable to parse ssf value (%s).\n",
1698 							fname, lineno, right );
1699 						goto fail;
1700 					}
1701 
1702 					if ( !b->a_authz.sai_ssf ) {
1703 						Debug( LDAP_DEBUG_ANY,
1704 							"%s: line %d: invalid ssf value (%s).\n",
1705 							fname, lineno, right );
1706 						goto fail;
1707 					}
1708 					continue;
1709 				}
1710 
1711 				if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1712 					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1713 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1714 							"inappropriate style \"%s\" in by clause.\n",
1715 							fname, lineno, style );
1716 						goto fail;
1717 					}
1718 
1719 					if ( b->a_authz.sai_transport_ssf ) {
1720 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1721 							"transport_ssf attribute already specified.\n",
1722 							fname, lineno, 0 );
1723 						goto fail;
1724 					}
1725 
1726 					if ( right == NULL || *right == '\0' ) {
1727 						Debug( LDAP_DEBUG_ANY,
1728 							"%s: line %d: no transport_ssf is defined.\n",
1729 							fname, lineno, 0 );
1730 						goto fail;
1731 					}
1732 
1733 					if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
1734 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1735 							"unable to parse transport_ssf value (%s).\n",
1736 							fname, lineno, right );
1737 						goto fail;
1738 					}
1739 
1740 					if ( !b->a_authz.sai_transport_ssf ) {
1741 						Debug( LDAP_DEBUG_ANY,
1742 							"%s: line %d: invalid transport_ssf value (%s).\n",
1743 							fname, lineno, right );
1744 						goto fail;
1745 					}
1746 					continue;
1747 				}
1748 
1749 				if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1750 					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1751 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1752 							"inappropriate style \"%s\" in by clause.\n",
1753 							fname, lineno, style );
1754 						goto fail;
1755 					}
1756 
1757 					if ( b->a_authz.sai_tls_ssf ) {
1758 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1759 							"tls_ssf attribute already specified.\n",
1760 							fname, lineno, 0 );
1761 						goto fail;
1762 					}
1763 
1764 					if ( right == NULL || *right == '\0' ) {
1765 						Debug( LDAP_DEBUG_ANY,
1766 							"%s: line %d: no tls_ssf is defined\n",
1767 							fname, lineno, 0 );
1768 						goto fail;
1769 					}
1770 
1771 					if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
1772 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1773 							"unable to parse tls_ssf value (%s).\n",
1774 							fname, lineno, right );
1775 						goto fail;
1776 					}
1777 
1778 					if ( !b->a_authz.sai_tls_ssf ) {
1779 						Debug( LDAP_DEBUG_ANY,
1780 							"%s: line %d: invalid tls_ssf value (%s).\n",
1781 							fname, lineno, right );
1782 						goto fail;
1783 					}
1784 					continue;
1785 				}
1786 
1787 				if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1788 					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1789 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1790 							"inappropriate style \"%s\" in by clause.\n",
1791 							fname, lineno, style );
1792 						goto fail;
1793 					}
1794 
1795 					if ( b->a_authz.sai_sasl_ssf ) {
1796 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1797 							"sasl_ssf attribute already specified.\n",
1798 							fname, lineno, 0 );
1799 						goto fail;
1800 					}
1801 
1802 					if ( right == NULL || *right == '\0' ) {
1803 						Debug( LDAP_DEBUG_ANY,
1804 							"%s: line %d: no sasl_ssf is defined.\n",
1805 							fname, lineno, 0 );
1806 						goto fail;
1807 					}
1808 
1809 					if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
1810 						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1811 							"unable to parse sasl_ssf value (%s).\n",
1812 							fname, lineno, right );
1813 						goto fail;
1814 					}
1815 
1816 					if ( !b->a_authz.sai_sasl_ssf ) {
1817 						Debug( LDAP_DEBUG_ANY,
1818 							"%s: line %d: invalid sasl_ssf value (%s).\n",
1819 							fname, lineno, right );
1820 						goto fail;
1821 					}
1822 					continue;
1823 				}
1824 
1825 				if ( right != NULL ) {
1826 					/* unsplit */
1827 					right[-1] = '=';
1828 				}
1829 				break;
1830 			}
1831 
1832 			if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
1833 				/* out of arguments or plain stop */
1834 
1835 				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1836 				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1837 				b->a_type = ACL_STOP;
1838 
1839 				access_append( &a->acl_access, b );
1840 				continue;
1841 			}
1842 
1843 			if ( strcasecmp( left, "continue" ) == 0 ) {
1844 				/* plain continue */
1845 
1846 				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1847 				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1848 				b->a_type = ACL_CONTINUE;
1849 
1850 				access_append( &a->acl_access, b );
1851 				continue;
1852 			}
1853 
1854 			if ( strcasecmp( left, "break" ) == 0 ) {
1855 				/* plain continue */
1856 
1857 				ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1858 				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1859 				b->a_type = ACL_BREAK;
1860 
1861 				access_append( &a->acl_access, b );
1862 				continue;
1863 			}
1864 
1865 			if ( strcasecmp( left, "by" ) == 0 ) {
1866 				/* we've gone too far */
1867 				--i;
1868 				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1869 				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1870 				b->a_type = ACL_STOP;
1871 
1872 				access_append( &a->acl_access, b );
1873 				continue;
1874 			}
1875 
1876 			/* get <access> */
1877 			{
1878 				char	*lleft = left;
1879 
1880 				if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
1881 					b->a_dn_self = 1;
1882 					lleft = &left[ STRLENOF( "self" ) ];
1883 
1884 				} else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
1885 					b->a_realdn_self = 1;
1886 					lleft = &left[ STRLENOF( "realself" ) ];
1887 				}
1888 
1889 				ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
1890 			}
1891 
1892 			if ( ACL_IS_INVALID( b->a_access_mask ) ) {
1893 				Debug( LDAP_DEBUG_ANY,
1894 					"%s: line %d: expecting <access> got \"%s\".\n",
1895 					fname, lineno, left );
1896 				goto fail;
1897 			}
1898 
1899 			b->a_type = ACL_STOP;
1900 
1901 			if ( ++i == argc ) {
1902 				/* out of arguments or plain stop */
1903 				access_append( &a->acl_access, b );
1904 				continue;
1905 			}
1906 
1907 			if ( strcasecmp( argv[i], "continue" ) == 0 ) {
1908 				/* plain continue */
1909 				b->a_type = ACL_CONTINUE;
1910 
1911 			} else if ( strcasecmp( argv[i], "break" ) == 0 ) {
1912 				/* plain continue */
1913 				b->a_type = ACL_BREAK;
1914 
1915 			} else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1916 				/* gone to far */
1917 				i--;
1918 			}
1919 
1920 			access_append( &a->acl_access, b );
1921 			b = NULL;
1922 
1923 		} else {
1924 			Debug( LDAP_DEBUG_ANY,
1925 				"%s: line %d: expecting \"to\" "
1926 				"or \"by\" got \"%s\"\n",
1927 				fname, lineno, argv[i] );
1928 			goto fail;
1929 		}
1930 	}
1931 
1932 	/* if we have no real access clause, complain and do nothing */
1933 	if ( a == NULL ) {
1934 		Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1935 			"warning: no access clause(s) specified in access line.\n",
1936 			fname, lineno, 0 );
1937 		goto fail;
1938 
1939 	} else {
1940 #ifdef LDAP_DEBUG
1941 		if ( slap_debug & LDAP_DEBUG_ACL ) {
1942 			print_acl( be, a );
1943 		}
1944 #endif
1945 
1946 		if ( a->acl_access == NULL ) {
1947 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1948 				"warning: no by clause(s) specified in access line.\n",
1949 				fname, lineno, 0 );
1950 			goto fail;
1951 		}
1952 
1953 		if ( be != NULL ) {
1954 			if ( be->be_nsuffix == NULL ) {
1955 				Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1956 					"scope checking needs suffix before ACLs.\n",
1957 					fname, lineno, 0 );
1958 				/* go ahead, since checking is not authoritative */
1959 			} else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
1960 				Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1961 					"scope checking only applies to single-valued "
1962 					"suffix databases\n",
1963 					fname, lineno, 0 );
1964 				/* go ahead, since checking is not authoritative */
1965 			} else {
1966 				switch ( check_scope( be, a ) ) {
1967 				case ACL_SCOPE_UNKNOWN:
1968 					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1969 						"cannot assess the validity of the ACL scope within "
1970 						"backend naming context\n",
1971 						fname, lineno, 0 );
1972 					break;
1973 
1974 				case ACL_SCOPE_WARN:
1975 					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1976 						"ACL could be out of scope within backend naming context\n",
1977 						fname, lineno, 0 );
1978 					break;
1979 
1980 				case ACL_SCOPE_PARTIAL:
1981 					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1982 						"ACL appears to be partially out of scope within "
1983 						"backend naming context\n",
1984 						fname, lineno, 0 );
1985 					break;
1986 
1987 				case ACL_SCOPE_ERR:
1988 					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1989 						"ACL appears to be out of scope within "
1990 						"backend naming context\n",
1991 						fname, lineno, 0 );
1992 					break;
1993 
1994 				default:
1995 					break;
1996 				}
1997 			}
1998 			acl_append( &be->be_acl, a, pos );
1999 
2000 		} else {
2001 			acl_append( &frontendDB->be_acl, a, pos );
2002 		}
2003 	}
2004 
2005 	return 0;
2006 
2007 fail:
2008 	if ( b ) access_free( b );
2009 	if ( a ) acl_free( a );
2010 	return acl_usage();
2011 }
2012 
2013 char *
2014 accessmask2str( slap_mask_t mask, char *buf, int debug )
2015 {
2016 	int	none = 1;
2017 	char	*ptr = buf;
2018 
2019 	assert( buf != NULL );
2020 
2021 	if ( ACL_IS_INVALID( mask ) ) {
2022 		return "invalid";
2023 	}
2024 
2025 	buf[0] = '\0';
2026 
2027 	if ( ACL_IS_LEVEL( mask ) ) {
2028 		if ( ACL_LVL_IS_NONE(mask) ) {
2029 			ptr = lutil_strcopy( ptr, "none" );
2030 
2031 		} else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
2032 			ptr = lutil_strcopy( ptr, "disclose" );
2033 
2034 		} else if ( ACL_LVL_IS_AUTH(mask) ) {
2035 			ptr = lutil_strcopy( ptr, "auth" );
2036 
2037 		} else if ( ACL_LVL_IS_COMPARE(mask) ) {
2038 			ptr = lutil_strcopy( ptr, "compare" );
2039 
2040 		} else if ( ACL_LVL_IS_SEARCH(mask) ) {
2041 			ptr = lutil_strcopy( ptr, "search" );
2042 
2043 		} else if ( ACL_LVL_IS_READ(mask) ) {
2044 			ptr = lutil_strcopy( ptr, "read" );
2045 
2046 		} else if ( ACL_LVL_IS_WRITE(mask) ) {
2047 			ptr = lutil_strcopy( ptr, "write" );
2048 
2049 		} else if ( ACL_LVL_IS_WADD(mask) ) {
2050 			ptr = lutil_strcopy( ptr, "add" );
2051 
2052 		} else if ( ACL_LVL_IS_WDEL(mask) ) {
2053 			ptr = lutil_strcopy( ptr, "delete" );
2054 
2055 		} else if ( ACL_LVL_IS_MANAGE(mask) ) {
2056 			ptr = lutil_strcopy( ptr, "manage" );
2057 
2058 		} else {
2059 			ptr = lutil_strcopy( ptr, "unknown" );
2060 		}
2061 
2062 		if ( !debug ) {
2063 			*ptr = '\0';
2064 			return buf;
2065 		}
2066 		*ptr++ = '(';
2067 	}
2068 
2069 	if( ACL_IS_ADDITIVE( mask ) ) {
2070 		*ptr++ = '+';
2071 
2072 	} else if( ACL_IS_SUBTRACTIVE( mask ) ) {
2073 		*ptr++ = '-';
2074 
2075 	} else {
2076 		*ptr++ = '=';
2077 	}
2078 
2079 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
2080 		none = 0;
2081 		*ptr++ = 'm';
2082 	}
2083 
2084 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
2085 		none = 0;
2086 		*ptr++ = 'w';
2087 
2088 	} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
2089 		none = 0;
2090 		*ptr++ = 'a';
2091 
2092 	} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
2093 		none = 0;
2094 		*ptr++ = 'z';
2095 	}
2096 
2097 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
2098 		none = 0;
2099 		*ptr++ = 'r';
2100 	}
2101 
2102 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
2103 		none = 0;
2104 		*ptr++ = 's';
2105 	}
2106 
2107 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
2108 		none = 0;
2109 		*ptr++ = 'c';
2110 	}
2111 
2112 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
2113 		none = 0;
2114 		*ptr++ = 'x';
2115 	}
2116 
2117 	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
2118 		none = 0;
2119 		*ptr++ = 'd';
2120 	}
2121 
2122 	if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
2123 		none = 0;
2124 		*ptr++ = '0';
2125 	}
2126 
2127 	if ( none ) {
2128 		ptr = buf;
2129 	}
2130 
2131 	if ( ACL_IS_LEVEL( mask ) ) {
2132 		*ptr++ = ')';
2133 	}
2134 
2135 	*ptr = '\0';
2136 
2137 	return buf;
2138 }
2139 
2140 slap_mask_t
2141 str2accessmask( const char *str )
2142 {
2143 	slap_mask_t	mask;
2144 
2145 	if( !ASCII_ALPHA(str[0]) ) {
2146 		int i;
2147 
2148 		if ( str[0] == '=' ) {
2149 			ACL_INIT(mask);
2150 
2151 		} else if( str[0] == '+' ) {
2152 			ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
2153 
2154 		} else if( str[0] == '-' ) {
2155 			ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
2156 
2157 		} else {
2158 			ACL_INVALIDATE(mask);
2159 			return mask;
2160 		}
2161 
2162 		for( i=1; str[i] != '\0'; i++ ) {
2163 			if( TOLOWER((unsigned char) str[i]) == 'm' ) {
2164 				ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
2165 
2166 			} else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
2167 				ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
2168 
2169 			} else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
2170 				ACL_PRIV_SET(mask, ACL_PRIV_WADD);
2171 
2172 			} else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
2173 				ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
2174 
2175 			} else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
2176 				ACL_PRIV_SET(mask, ACL_PRIV_READ);
2177 
2178 			} else if( TOLOWER((unsigned char) str[i]) == 's' ) {
2179 				ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
2180 
2181 			} else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
2182 				ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
2183 
2184 			} else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
2185 				ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
2186 
2187 			} else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
2188 				ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
2189 
2190 			} else if( str[i] == '0' ) {
2191 				ACL_PRIV_SET(mask, ACL_PRIV_NONE);
2192 
2193 			} else {
2194 				ACL_INVALIDATE(mask);
2195 				return mask;
2196 			}
2197 		}
2198 
2199 		return mask;
2200 	}
2201 
2202 	if ( strcasecmp( str, "none" ) == 0 ) {
2203 		ACL_LVL_ASSIGN_NONE(mask);
2204 
2205 	} else if ( strcasecmp( str, "disclose" ) == 0 ) {
2206 		ACL_LVL_ASSIGN_DISCLOSE(mask);
2207 
2208 	} else if ( strcasecmp( str, "auth" ) == 0 ) {
2209 		ACL_LVL_ASSIGN_AUTH(mask);
2210 
2211 	} else if ( strcasecmp( str, "compare" ) == 0 ) {
2212 		ACL_LVL_ASSIGN_COMPARE(mask);
2213 
2214 	} else if ( strcasecmp( str, "search" ) == 0 ) {
2215 		ACL_LVL_ASSIGN_SEARCH(mask);
2216 
2217 	} else if ( strcasecmp( str, "read" ) == 0 ) {
2218 		ACL_LVL_ASSIGN_READ(mask);
2219 
2220 	} else if ( strcasecmp( str, "add" ) == 0 ) {
2221 		ACL_LVL_ASSIGN_WADD(mask);
2222 
2223 	} else if ( strcasecmp( str, "delete" ) == 0 ) {
2224 		ACL_LVL_ASSIGN_WDEL(mask);
2225 
2226 	} else if ( strcasecmp( str, "write" ) == 0 ) {
2227 		ACL_LVL_ASSIGN_WRITE(mask);
2228 
2229 	} else if ( strcasecmp( str, "manage" ) == 0 ) {
2230 		ACL_LVL_ASSIGN_MANAGE(mask);
2231 
2232 	} else {
2233 		ACL_INVALIDATE( mask );
2234 	}
2235 
2236 	return mask;
2237 }
2238 
2239 static int
2240 acl_usage( void )
2241 {
2242 	char *access =
2243 		"<access clause> ::= access to <what> "
2244 				"[ by <who> [ <access> ] [ <control> ] ]+ \n";
2245 	char *what =
2246 		"<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
2247 		"<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
2248 		"<attrlist> ::= <attr> [ , <attrlist> ]\n"
2249 		"<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
2250 
2251 	char *who =
2252 		"<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
2253 			"\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
2254 			"\t[dnattr=<attrname>]\n"
2255 			"\t[realdnattr=<attrname>]\n"
2256 			"\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
2257 			"\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
2258 			"\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
2259 #ifdef SLAP_DYNACL
2260 			"\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
2261 #endif /* SLAP_DYNACL */
2262 			"\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
2263 		"<style> ::= exact | regex | base(Object)\n"
2264 		"<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
2265 			"exact | regex\n"
2266 		"<attrstyle> ::= exact | regex | base(Object) | one(level) | "
2267 			"sub(tree) | children\n"
2268 		"<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
2269 		"<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
2270 		"<access> ::= [[real]self]{<level>|<priv>}\n"
2271 		"<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
2272 		"<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
2273 		"<control> ::= [ stop | continue | break ]\n"
2274 #ifdef SLAP_DYNACL
2275 #ifdef SLAPD_ACI_ENABLED
2276 		"dynacl:\n"
2277 		"\t<name>=ACI\t<pattern>=<attrname>\n"
2278 #endif /* SLAPD_ACI_ENABLED */
2279 #endif /* ! SLAP_DYNACL */
2280 		"";
2281 
2282 	Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );
2283 
2284 	return 1;
2285 }
2286 
2287 /*
2288  * Set pattern to a "normalized" DN from src.
2289  * At present it simply eats the (optional) space after
2290  * a RDN separator (,)
2291  * Eventually will evolve in a more complete normalization
2292  */
2293 static void
2294 acl_regex_normalized_dn(
2295 	const char *src,
2296 	struct berval *pattern )
2297 {
2298 	char *str, *p;
2299 	ber_len_t len;
2300 
2301 	str = ch_strdup( src );
2302 	len = strlen( src );
2303 
2304 	for ( p = str; p && p[0]; p++ ) {
2305 		/* escape */
2306 		if ( p[0] == '\\' && p[1] ) {
2307 			/*
2308 			 * if escaping a hex pair we should
2309 			 * increment p twice; however, in that
2310 			 * case the second hex number does
2311 			 * no harm
2312 			 */
2313 			p++;
2314 		}
2315 
2316 		if ( p[0] == ',' && p[1] == ' ' ) {
2317 			char *q;
2318 
2319 			/*
2320 			 * too much space should be an error if we are pedantic
2321 			 */
2322 			for ( q = &p[2]; q[0] == ' '; q++ ) {
2323 				/* DO NOTHING */ ;
2324 			}
2325 			AC_MEMCPY( p+1, q, len-(q-str)+1);
2326 		}
2327 	}
2328 	pattern->bv_val = str;
2329 	pattern->bv_len = p - str;
2330 
2331 	return;
2332 }
2333 
2334 static void
2335 split(
2336     char	*line,
2337     int		splitchar,
2338     char	**left,
2339     char	**right )
2340 {
2341 	*left = line;
2342 	if ( (*right = strchr( line, splitchar )) != NULL ) {
2343 		*((*right)++) = '\0';
2344 	}
2345 }
2346 
2347 static void
2348 access_append( Access **l, Access *a )
2349 {
2350 	for ( ; *l != NULL; l = &(*l)->a_next ) {
2351 		;	/* Empty */
2352 	}
2353 
2354 	*l = a;
2355 }
2356 
2357 void
2358 acl_append( AccessControl **l, AccessControl *a, int pos )
2359 {
2360 	int i;
2361 
2362 	for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
2363 		;	/* Empty */
2364 	}
2365 	if ( *l && a )
2366 		a->acl_next = *l;
2367 	*l = a;
2368 }
2369 
2370 static void
2371 access_free( Access *a )
2372 {
2373 	if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
2374 		free( a->a_dn_pat.bv_val );
2375 	}
2376 	if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
2377 		free( a->a_realdn_pat.bv_val );
2378 	}
2379 	if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
2380 		free( a->a_peername_pat.bv_val );
2381 	}
2382 	if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
2383 		free( a->a_sockname_pat.bv_val );
2384 	}
2385 	if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
2386 		free( a->a_domain_pat.bv_val );
2387 	}
2388 	if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
2389 		free( a->a_sockurl_pat.bv_val );
2390 	}
2391 	if ( !BER_BVISNULL( &a->a_set_pat ) ) {
2392 		free( a->a_set_pat.bv_val );
2393 	}
2394 	if ( !BER_BVISNULL( &a->a_group_pat ) ) {
2395 		free( a->a_group_pat.bv_val );
2396 	}
2397 #ifdef SLAP_DYNACL
2398 	if ( a->a_dynacl != NULL ) {
2399 		slap_dynacl_t	*da;
2400 		for ( da = a->a_dynacl; da; ) {
2401 			slap_dynacl_t	*tmp = da;
2402 
2403 			da = da->da_next;
2404 
2405 			if ( tmp->da_destroy ) {
2406 				tmp->da_destroy( tmp->da_private );
2407 			}
2408 
2409 			ch_free( tmp );
2410 		}
2411 	}
2412 #endif /* SLAP_DYNACL */
2413 	free( a );
2414 }
2415 
2416 void
2417 acl_free( AccessControl *a )
2418 {
2419 	Access *n;
2420 	AttributeName *an;
2421 
2422 	if ( a->acl_filter ) {
2423 		filter_free( a->acl_filter );
2424 	}
2425 	if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2426 		if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
2427 			regfree( &a->acl_dn_re );
2428 		}
2429 		free ( a->acl_dn_pat.bv_val );
2430 	}
2431 	if ( a->acl_attrs ) {
2432 		for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
2433 			free( an->an_name.bv_val );
2434 		}
2435 		free( a->acl_attrs );
2436 
2437 		if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
2438 			regfree( &a->acl_attrval_re );
2439 		}
2440 
2441 		if ( !BER_BVISNULL( &a->acl_attrval ) ) {
2442 			ber_memfree( a->acl_attrval.bv_val );
2443 		}
2444 	}
2445 	for ( ; a->acl_access; a->acl_access = n ) {
2446 		n = a->acl_access->a_next;
2447 		access_free( a->acl_access );
2448 	}
2449 	free( a );
2450 }
2451 
2452 /* Because backend_startup uses acl_append to tack on the global_acl to
2453  * the end of each backend's acl, we cannot just take one argument and
2454  * merrily free our way to the end of the list. backend_destroy calls us
2455  * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
2456  * point. config_destroy calls us with global_acl in arg1 and NULL in
2457  * arg2, so we then proceed to polish off the global_acl.
2458  */
2459 void
2460 acl_destroy( AccessControl *a, AccessControl *end )
2461 {
2462 	AccessControl *n;
2463 
2464 	for ( ; a && a != end; a = n ) {
2465 		n = a->acl_next;
2466 		acl_free( a );
2467 	}
2468 }
2469 
2470 char *
2471 access2str( slap_access_t access )
2472 {
2473 	if ( access == ACL_NONE ) {
2474 		return "none";
2475 
2476 	} else if ( access == ACL_DISCLOSE ) {
2477 		return "disclose";
2478 
2479 	} else if ( access == ACL_AUTH ) {
2480 		return "auth";
2481 
2482 	} else if ( access == ACL_COMPARE ) {
2483 		return "compare";
2484 
2485 	} else if ( access == ACL_SEARCH ) {
2486 		return "search";
2487 
2488 	} else if ( access == ACL_READ ) {
2489 		return "read";
2490 
2491 	} else if ( access == ACL_WRITE ) {
2492 		return "write";
2493 
2494 	} else if ( access == ACL_WADD ) {
2495 		return "add";
2496 
2497 	} else if ( access == ACL_WDEL ) {
2498 		return "delete";
2499 
2500 	} else if ( access == ACL_MANAGE ) {
2501 		return "manage";
2502 
2503 	}
2504 
2505 	return "unknown";
2506 }
2507 
2508 slap_access_t
2509 str2access( const char *str )
2510 {
2511 	if ( strcasecmp( str, "none" ) == 0 ) {
2512 		return ACL_NONE;
2513 
2514 	} else if ( strcasecmp( str, "disclose" ) == 0 ) {
2515 		return ACL_DISCLOSE;
2516 
2517 	} else if ( strcasecmp( str, "auth" ) == 0 ) {
2518 		return ACL_AUTH;
2519 
2520 	} else if ( strcasecmp( str, "compare" ) == 0 ) {
2521 		return ACL_COMPARE;
2522 
2523 	} else if ( strcasecmp( str, "search" ) == 0 ) {
2524 		return ACL_SEARCH;
2525 
2526 	} else if ( strcasecmp( str, "read" ) == 0 ) {
2527 		return ACL_READ;
2528 
2529 	} else if ( strcasecmp( str, "write" ) == 0 ) {
2530 		return ACL_WRITE;
2531 
2532 	} else if ( strcasecmp( str, "add" ) == 0 ) {
2533 		return ACL_WADD;
2534 
2535 	} else if ( strcasecmp( str, "delete" ) == 0 ) {
2536 		return ACL_WDEL;
2537 
2538 	} else if ( strcasecmp( str, "manage" ) == 0 ) {
2539 		return ACL_MANAGE;
2540 	}
2541 
2542 	return( ACL_INVALID_ACCESS );
2543 }
2544 
2545 #define ACLBUF_MAXLEN	8192
2546 
2547 static char aclbuf[ACLBUF_MAXLEN];
2548 
2549 static char *
2550 dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
2551 {
2552 	*ptr++ = ' ';
2553 
2554 	if ( is_realdn ) {
2555 		ptr = lutil_strcopy( ptr, "real" );
2556 	}
2557 
2558 	if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
2559 		bdn->a_style == ACL_STYLE_ANONYMOUS ||
2560 		bdn->a_style == ACL_STYLE_USERS ||
2561 		bdn->a_style == ACL_STYLE_SELF )
2562 	{
2563 		if ( is_realdn ) {
2564 			assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
2565 		}
2566 
2567 		ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2568 		if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
2569 			int n = sprintf( ptr, ".level{%d}", bdn->a_self_level );
2570 			if ( n > 0 ) {
2571 				ptr += n;
2572 			} /* else ? */
2573 		}
2574 
2575 	} else {
2576 		ptr = lutil_strcopy( ptr, "dn." );
2577 		if ( bdn->a_style == ACL_STYLE_BASE )
2578 			ptr = lutil_strcopy( ptr, style_base );
2579 		else
2580 			ptr = lutil_strcopy( ptr, style_strings[bdn->a_style] );
2581 		if ( bdn->a_style == ACL_STYLE_LEVEL ) {
2582 			int n = sprintf( ptr, "{%d}", bdn->a_level );
2583 			if ( n > 0 ) {
2584 				ptr += n;
2585 			} /* else ? */
2586 		}
2587 		if ( bdn->a_expand ) {
2588 			ptr = lutil_strcopy( ptr, ",expand" );
2589 		}
2590 		*ptr++ = '=';
2591 		*ptr++ = '"';
2592 		ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2593 		*ptr++ = '"';
2594 	}
2595 	return ptr;
2596 }
2597 
2598 static char *
2599 access2text( Access *b, char *ptr )
2600 {
2601 	char maskbuf[ACCESSMASK_MAXLEN];
2602 
2603 	ptr = lutil_strcopy( ptr, "\tby" );
2604 
2605 	if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
2606 		ptr = dnaccess2text( &b->a_dn, ptr, 0 );
2607 	}
2608 	if ( b->a_dn_at ) {
2609 		ptr = lutil_strcopy( ptr, " dnattr=" );
2610 		ptr = lutil_strcopy( ptr, b->a_dn_at->ad_cname.bv_val );
2611 	}
2612 
2613 	if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
2614 		ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
2615 	}
2616 	if ( b->a_realdn_at ) {
2617 		ptr = lutil_strcopy( ptr, " realdnattr=" );
2618 		ptr = lutil_strcopy( ptr, b->a_realdn_at->ad_cname.bv_val );
2619 	}
2620 
2621 	if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
2622 		ptr = lutil_strcopy( ptr, " group/" );
2623 		ptr = lutil_strcopy( ptr, b->a_group_oc ?
2624 			b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
2625 		*ptr++ = '/';
2626 		ptr = lutil_strcopy( ptr, b->a_group_at ?
2627 			b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
2628 		*ptr++ = '.';
2629 		ptr = lutil_strcopy( ptr, style_strings[b->a_group_style] );
2630 		*ptr++ = '=';
2631 		*ptr++ = '"';
2632 		ptr = lutil_strcopy( ptr, b->a_group_pat.bv_val );
2633 		*ptr++ = '"';
2634 	}
2635 
2636 	if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
2637 		ptr = lutil_strcopy( ptr, " peername" );
2638 		*ptr++ = '.';
2639 		ptr = lutil_strcopy( ptr, style_strings[b->a_peername_style] );
2640 		*ptr++ = '=';
2641 		*ptr++ = '"';
2642 		ptr = lutil_strcopy( ptr, b->a_peername_pat.bv_val );
2643 		*ptr++ = '"';
2644 	}
2645 
2646 	if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
2647 		ptr = lutil_strcopy( ptr, " sockname" );
2648 		*ptr++ = '.';
2649 		ptr = lutil_strcopy( ptr, style_strings[b->a_sockname_style] );
2650 		*ptr++ = '=';
2651 		*ptr++ = '"';
2652 		ptr = lutil_strcopy( ptr, b->a_sockname_pat.bv_val );
2653 		*ptr++ = '"';
2654 	}
2655 
2656 	if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
2657 		ptr = lutil_strcopy( ptr, " domain" );
2658 		*ptr++ = '.';
2659 		ptr = lutil_strcopy( ptr, style_strings[b->a_domain_style] );
2660 		if ( b->a_domain_expand ) {
2661 			ptr = lutil_strcopy( ptr, ",expand" );
2662 		}
2663 		*ptr++ = '=';
2664 		ptr = lutil_strcopy( ptr, b->a_domain_pat.bv_val );
2665 	}
2666 
2667 	if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
2668 		ptr = lutil_strcopy( ptr, " sockurl" );
2669 		*ptr++ = '.';
2670 		ptr = lutil_strcopy( ptr, style_strings[b->a_sockurl_style] );
2671 		*ptr++ = '=';
2672 		*ptr++ = '"';
2673 		ptr = lutil_strcopy( ptr, b->a_sockurl_pat.bv_val );
2674 		*ptr++ = '"';
2675 	}
2676 
2677 	if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
2678 		ptr = lutil_strcopy( ptr, " set" );
2679 		*ptr++ = '.';
2680 		ptr = lutil_strcopy( ptr, style_strings[b->a_set_style] );
2681 		*ptr++ = '=';
2682 		*ptr++ = '"';
2683 		ptr = lutil_strcopy( ptr, b->a_set_pat.bv_val );
2684 		*ptr++ = '"';
2685 	}
2686 
2687 #ifdef SLAP_DYNACL
2688 	if ( b->a_dynacl ) {
2689 		slap_dynacl_t	*da;
2690 
2691 		for ( da = b->a_dynacl; da; da = da->da_next ) {
2692 			if ( da->da_unparse ) {
2693 				struct berval bv = BER_BVNULL;
2694 				(void)( *da->da_unparse )( da->da_private, &bv );
2695 				assert( !BER_BVISNULL( &bv ) );
2696 				ptr = lutil_strcopy( ptr, bv.bv_val );
2697 				ch_free( bv.bv_val );
2698 			}
2699 		}
2700 	}
2701 #endif /* SLAP_DYNACL */
2702 
2703 	/* Security Strength Factors */
2704 	if ( b->a_authz.sai_ssf ) {
2705 		ptr += sprintf( ptr, " ssf=%u",
2706 			b->a_authz.sai_ssf );
2707 	}
2708 	if ( b->a_authz.sai_transport_ssf ) {
2709 		ptr += sprintf( ptr, " transport_ssf=%u",
2710 			b->a_authz.sai_transport_ssf );
2711 	}
2712 	if ( b->a_authz.sai_tls_ssf ) {
2713 		ptr += sprintf( ptr, " tls_ssf=%u",
2714 			b->a_authz.sai_tls_ssf );
2715 	}
2716 	if ( b->a_authz.sai_sasl_ssf ) {
2717 		ptr += sprintf( ptr, " sasl_ssf=%u",
2718 			b->a_authz.sai_sasl_ssf );
2719 	}
2720 
2721 	*ptr++ = ' ';
2722 	if ( b->a_dn_self ) {
2723 		ptr = lutil_strcopy( ptr, "self" );
2724 	} else if ( b->a_realdn_self ) {
2725 		ptr = lutil_strcopy( ptr, "realself" );
2726 	}
2727 	ptr = lutil_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
2728 	if ( !maskbuf[0] ) ptr--;
2729 
2730 	if( b->a_type == ACL_BREAK ) {
2731 		ptr = lutil_strcopy( ptr, " break" );
2732 
2733 	} else if( b->a_type == ACL_CONTINUE ) {
2734 		ptr = lutil_strcopy( ptr, " continue" );
2735 
2736 	} else if( b->a_type != ACL_STOP ) {
2737 		ptr = lutil_strcopy( ptr, " unknown-control" );
2738 	} else {
2739 		if ( !maskbuf[0] ) ptr = lutil_strcopy( ptr, " stop" );
2740 	}
2741 	*ptr++ = '\n';
2742 
2743 	return ptr;
2744 }
2745 
2746 void
2747 acl_unparse( AccessControl *a, struct berval *bv )
2748 {
2749 	Access	*b;
2750 	char	*ptr;
2751 	int	to = 0;
2752 
2753 	bv->bv_val = aclbuf;
2754 	bv->bv_len = 0;
2755 
2756 	ptr = bv->bv_val;
2757 
2758 	ptr = lutil_strcopy( ptr, "to" );
2759 	if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2760 		to++;
2761 		ptr = lutil_strcopy( ptr, " dn." );
2762 		if ( a->acl_dn_style == ACL_STYLE_BASE )
2763 			ptr = lutil_strcopy( ptr, style_base );
2764 		else
2765 			ptr = lutil_strcopy( ptr, style_strings[a->acl_dn_style] );
2766 		*ptr++ = '=';
2767 		*ptr++ = '"';
2768 		ptr = lutil_strcopy( ptr, a->acl_dn_pat.bv_val );
2769 		ptr = lutil_strcopy( ptr, "\"\n" );
2770 	}
2771 
2772 	if ( a->acl_filter != NULL ) {
2773 		struct berval	bv = BER_BVNULL;
2774 
2775 		to++;
2776 		filter2bv( a->acl_filter, &bv );
2777 		ptr = lutil_strcopy( ptr, " filter=\"" );
2778 		ptr = lutil_strcopy( ptr, bv.bv_val );
2779 		*ptr++ = '"';
2780 		*ptr++ = '\n';
2781 		ch_free( bv.bv_val );
2782 	}
2783 
2784 	if ( a->acl_attrs != NULL ) {
2785 		int	first = 1;
2786 		AttributeName *an;
2787 		to++;
2788 
2789 		ptr = lutil_strcopy( ptr, " attrs=" );
2790 		for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
2791 			if ( ! first ) *ptr++ = ',';
2792 			if (an->an_oc) {
2793 				*ptr++ = an->an_oc_exclude ? '!' : '@';
2794 				ptr = lutil_strcopy( ptr, an->an_oc->soc_cname.bv_val );
2795 
2796 			} else {
2797 				ptr = lutil_strcopy( ptr, an->an_name.bv_val );
2798 			}
2799 			first = 0;
2800 		}
2801 		*ptr++ = '\n';
2802 	}
2803 
2804 	if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
2805 		to++;
2806 		ptr = lutil_strcopy( ptr, " val." );
2807 		if ( a->acl_attrval_style == ACL_STYLE_BASE &&
2808 			a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
2809 				slap_schema.si_syn_distinguishedName )
2810 			ptr = lutil_strcopy( ptr, style_base );
2811 		else
2812 			ptr = lutil_strcopy( ptr, style_strings[a->acl_attrval_style] );
2813 		*ptr++ = '=';
2814 		*ptr++ = '"';
2815 		ptr = lutil_strcopy( ptr, a->acl_attrval.bv_val );
2816 		*ptr++ = '"';
2817 		*ptr++ = '\n';
2818 	}
2819 
2820 	if( !to ) {
2821 		ptr = lutil_strcopy( ptr, " *\n" );
2822 	}
2823 
2824 	for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2825 		ptr = access2text( b, ptr );
2826 	}
2827 	*ptr = '\0';
2828 	bv->bv_len = ptr - bv->bv_val;
2829 }
2830 
2831 #ifdef LDAP_DEBUG
2832 static void
2833 print_acl( Backend *be, AccessControl *a )
2834 {
2835 	struct berval bv;
2836 
2837 	acl_unparse( a, &bv );
2838 	fprintf( stderr, "%s ACL: access %s\n",
2839 		be == NULL ? "Global" : "Backend", bv.bv_val );
2840 }
2841 #endif /* LDAP_DEBUG */
2842