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