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