Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: aci.c,v 1.4 2025/09/05 21:16:24 christos Exp $	*/
      2 
      3 /* aci.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: aci.c,v 1.4 2025/09/05 21:16:24 christos Exp $");
     31 
     32 #include "portable.h"
     33 
     34 #ifdef SLAPD_ACI_ENABLED
     35 
     36 #include <stdio.h>
     37 
     38 #include <ac/ctype.h>
     39 #include <ac/regex.h>
     40 #include <ac/socket.h>
     41 #include <ac/string.h>
     42 #include <ac/unistd.h>
     43 
     44 #include "slap.h"
     45 #include "lber_pvt.h"
     46 #include "lutil.h"
     47 #include "slap-config.h"
     48 
     49 /* use most appropriate size */
     50 #define ACI_BUF_SIZE 			1024
     51 
     52 /* move to "stable" when no longer experimental */
     53 #define SLAPD_ACI_SYNTAX		"1.3.6.1.4.1.4203.666.2.1"
     54 
     55 /* change this to "OpenLDAPset" */
     56 #define SLAPD_ACI_SET_ATTR		"template"
     57 
     58 typedef enum slap_aci_scope_t {
     59 	SLAP_ACI_SCOPE_ENTRY		= 0x1,
     60 	SLAP_ACI_SCOPE_CHILDREN		= 0x2,
     61 	SLAP_ACI_SCOPE_SUBTREE		= ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
     62 } slap_aci_scope_t;
     63 
     64 enum {
     65 	ACI_BV_ENTRY,
     66 	ACI_BV_CHILDREN,
     67 	ACI_BV_ONELEVEL,
     68 	ACI_BV_SUBTREE,
     69 
     70 	ACI_BV_BR_ENTRY,
     71 	ACI_BV_BR_CHILDREN,
     72 	ACI_BV_BR_ALL,
     73 
     74 	ACI_BV_ACCESS_ID,
     75 	ACI_BV_PUBLIC,
     76 	ACI_BV_USERS,
     77 	ACI_BV_SELF,
     78 	ACI_BV_DNATTR,
     79 	ACI_BV_GROUP,
     80 	ACI_BV_ROLE,
     81 	ACI_BV_SET,
     82 	ACI_BV_SET_REF,
     83 
     84 	ACI_BV_GRANT,
     85 	ACI_BV_DENY,
     86 
     87 	ACI_BV_GROUP_CLASS,
     88 	ACI_BV_GROUP_ATTR,
     89 	ACI_BV_ROLE_CLASS,
     90 	ACI_BV_ROLE_ATTR,
     91 
     92 	ACI_BV_SET_ATTR,
     93 
     94 	ACI_BV_LAST
     95 };
     96 
     97 static const struct berval	aci_bv[] = {
     98 	/* scope */
     99 	BER_BVC("entry"),
    100 	BER_BVC("children"),
    101 	BER_BVC("onelevel"),
    102 	BER_BVC("subtree"),
    103 
    104 	/* */
    105 	BER_BVC("[entry]"),
    106 	BER_BVC("[children]"),
    107 	BER_BVC("[all]"),
    108 
    109 	/* type */
    110 	BER_BVC("access-id"),
    111 	BER_BVC("public"),
    112 	BER_BVC("users"),
    113 	BER_BVC("self"),
    114 	BER_BVC("dnattr"),
    115 	BER_BVC("group"),
    116 	BER_BVC("role"),
    117 	BER_BVC("set"),
    118 	BER_BVC("set-ref"),
    119 
    120 	/* actions */
    121 	BER_BVC("grant"),
    122 	BER_BVC("deny"),
    123 
    124 	/* schema */
    125 	BER_BVC(SLAPD_GROUP_CLASS),
    126 	BER_BVC(SLAPD_GROUP_ATTR),
    127 	BER_BVC(SLAPD_ROLE_CLASS),
    128 	BER_BVC(SLAPD_ROLE_ATTR),
    129 
    130 	BER_BVC(SLAPD_ACI_SET_ATTR),
    131 
    132 	BER_BVNULL
    133 };
    134 
    135 static AttributeDescription	*slap_ad_aci;
    136 
    137 static int
    138 OpenLDAPaciValidate(
    139 	Syntax		*syntax,
    140 	struct berval	*val );
    141 
    142 static int
    143 OpenLDAPaciPretty(
    144 	Syntax		*syntax,
    145 	struct berval	*val,
    146 	struct berval	*out,
    147 	void		*ctx );
    148 
    149 static int
    150 OpenLDAPaciNormalize(
    151 	slap_mask_t	use,
    152 	Syntax		*syntax,
    153 	MatchingRule	*mr,
    154 	struct berval	*val,
    155 	struct berval	*out,
    156 	void		*ctx );
    157 
    158 #define	OpenLDAPaciMatch			octetStringMatch
    159 
    160 static int
    161 aci_list_map_rights(
    162 	struct berval	*list )
    163 {
    164 	struct berval	bv;
    165 	slap_access_t	mask;
    166 	int		i;
    167 
    168 	ACL_INIT( mask );
    169 	for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
    170 		if ( bv.bv_len <= 0 ) {
    171 			continue;
    172 		}
    173 
    174 		switch ( *bv.bv_val ) {
    175 		case 'x':
    176 			/* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
    177 			 * define any equivalent to the AUTH right, so I've just used
    178 			 * 'x' for now.
    179 			 */
    180 			ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
    181 			break;
    182 		case 'd':
    183 			/* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
    184 			 * the right 'd' to mean "delete"; we hijack it to mean
    185 			 * "disclose" for consistency wuith the rest of slapd.
    186 			 */
    187 			ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
    188 			break;
    189 		case 'c':
    190 			ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
    191 			break;
    192 		case 's':
    193 			/* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
    194 			 * the right 's' to mean "set", but in the examples states
    195 			 * that the right 's' means "search".  The latter definition
    196 			 * is used here.
    197 			 */
    198 			ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
    199 			break;
    200 		case 'r':
    201 			ACL_PRIV_SET(mask, ACL_PRIV_READ);
    202 			break;
    203 		case 'w':
    204 			ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
    205 			break;
    206 		default:
    207 			break;
    208 		}
    209 
    210 	}
    211 
    212 	return mask;
    213 }
    214 
    215 static int
    216 aci_list_has_attr(
    217 	struct berval		*list,
    218 	const struct berval	*attr,
    219 	struct berval		*val )
    220 {
    221 	struct berval	bv, left, right;
    222 	int		i;
    223 
    224 	for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
    225 		if ( acl_get_part(&bv, 0, '=', &left ) < 0
    226 			|| acl_get_part( &bv, 1, '=', &right ) < 0 )
    227 		{
    228 			if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
    229 				return(1);
    230 			}
    231 
    232 		} else if ( val == NULL ) {
    233 			if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
    234 				return(1);
    235 			}
    236 
    237 		} else {
    238 			if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
    239 				/* FIXME: this is also totally undocumented! */
    240 				/* this is experimental code that implements a
    241 				 * simple (prefix) match of the attribute value.
    242 				 * the ACI draft does not provide for aci's that
    243 				 * apply to specific values, but it would be
    244 				 * nice to have.  If the <attr> part of an aci's
    245 				 * rights list is of the form <attr>=<value>,
    246 				 * that means the aci applies only to attrs with
    247 				 * the given value.  Furthermore, if the attr is
    248 				 * of the form <attr>=<value>*, then <value> is
    249 				 * treated as a prefix, and the aci applies to
    250 				 * any value with that prefix.
    251 				 *
    252 				 * Ideally, this would allow r.e. matches.
    253 				 */
    254 				if ( acl_get_part( &right, 0, '*', &left ) < 0
    255 					|| right.bv_len <= left.bv_len )
    256 				{
    257 					if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
    258 						return 1;
    259 					}
    260 
    261 				} else if ( val->bv_len >= left.bv_len ) {
    262 					if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
    263 						return(1);
    264 					}
    265 				}
    266 			}
    267 		}
    268 	}
    269 
    270 	return 0;
    271 }
    272 
    273 static slap_access_t
    274 aci_list_get_attr_rights(
    275 	struct berval		*list,
    276 	const struct berval	*attr,
    277 	struct berval		*val )
    278 {
    279 	struct berval	bv;
    280 	slap_access_t	mask;
    281 	int		i;
    282 
    283 	/* loop through each rights/attr pair, skip first part (action) */
    284 	ACL_INIT(mask);
    285 	for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
    286 		if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
    287 			Debug( LDAP_DEBUG_ACL,
    288 				"        <= aci_list_get_attr_rights "
    289 				"test %s for %s -> failed\n",
    290 				bv.bv_val, attr->bv_val );
    291 			continue;
    292 		}
    293 
    294 		Debug( LDAP_DEBUG_ACL,
    295 			"        <= aci_list_get_attr_rights "
    296 			"test %s for %s -> ok\n",
    297 			bv.bv_val, attr->bv_val );
    298 
    299 		if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
    300 			Debug( LDAP_DEBUG_ACL,
    301 				"        <= aci_list_get_attr_rights "
    302 				"test no rights\n" );
    303 			continue;
    304 		}
    305 
    306 		mask |= aci_list_map_rights( &bv );
    307 		Debug( LDAP_DEBUG_ACL,
    308 			"        <= aci_list_get_attr_rights "
    309 			"rights %s to mask 0x%x\n",
    310 			bv.bv_val, mask );
    311 	}
    312 
    313 	return mask;
    314 }
    315 
    316 static int
    317 aci_list_get_rights(
    318 	struct berval	*list,
    319 	struct berval	*attr,
    320 	struct berval	*val,
    321 	slap_access_t	*grant,
    322 	slap_access_t	*deny )
    323 {
    324 	struct berval	perm, actn, baseattr;
    325 	slap_access_t	*mask;
    326 	int		i, found;
    327 
    328 	if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
    329 		attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
    330 
    331 	} else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
    332 		attr = &baseattr;
    333 	}
    334 	found = 0;
    335 	ACL_INIT(*grant);
    336 	ACL_INIT(*deny);
    337 	/* loop through each permissions clause */
    338 	for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
    339 		if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
    340 			continue;
    341 		}
    342 
    343 		if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
    344 			mask = grant;
    345 
    346 		} else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
    347 			mask = deny;
    348 
    349 		} else {
    350 			continue;
    351 		}
    352 
    353 		*mask |= aci_list_get_attr_rights( &perm, attr, val );
    354 		*mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
    355 
    356 		if ( *mask != ACL_PRIV_NONE ) {
    357 			found = 1;
    358 		}
    359 	}
    360 
    361 	return found;
    362 }
    363 
    364 static int
    365 aci_group_member (
    366 	struct berval		*subj,
    367 	const struct berval	*defgrpoc,
    368 	const struct berval	*defgrpat,
    369 	Operation		*op,
    370 	Entry			*e,
    371 	int			nmatch,
    372 	regmatch_t		*matches
    373 )
    374 {
    375 	struct berval		subjdn;
    376 	struct berval		grpoc;
    377 	struct berval		grpat;
    378 	ObjectClass		*grp_oc = NULL;
    379 	AttributeDescription	*grp_ad = NULL;
    380 	const char		*text;
    381 	int			rc;
    382 
    383 	/* format of string is "{group|role}/objectClassValue/groupAttrName" */
    384 	if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
    385 		return 0;
    386 	}
    387 
    388 	if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
    389 		grpoc = *defgrpoc;
    390 	}
    391 
    392 	if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
    393 		grpat = *defgrpat;
    394 	}
    395 
    396 	rc = slap_bv2ad( &grpat, &grp_ad, &text );
    397 	if ( rc != LDAP_SUCCESS ) {
    398 		rc = 0;
    399 		goto done;
    400 	}
    401 	rc = 0;
    402 
    403 	grp_oc = oc_bvfind( &grpoc );
    404 
    405 	if ( grp_oc != NULL && grp_ad != NULL ) {
    406 		char		buf[ ACI_BUF_SIZE ];
    407 		struct berval	bv, ndn;
    408 		AclRegexMatches amatches = { 0 };
    409 
    410 		amatches.dn_count = nmatch;
    411 		AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
    412 
    413 		bv.bv_len = sizeof( buf ) - 1;
    414 		bv.bv_val = (char *)&buf;
    415 		if ( acl_string_expand( &bv, &subjdn,
    416 				&e->e_nname, NULL, &amatches ) )
    417 		{
    418 			rc = LDAP_OTHER;
    419 			goto done;
    420 		}
    421 
    422 		if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
    423 		{
    424 			rc = ( backend_group( op, e, &ndn, &op->o_ndn,
    425 				grp_oc, grp_ad ) == 0 );
    426 			slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
    427 		}
    428 	}
    429 
    430 done:
    431 	return rc;
    432 }
    433 
    434 static int
    435 aci_mask(
    436 	Operation		*op,
    437 	Entry			*e,
    438 	AttributeDescription	*desc,
    439 	struct berval		*val,
    440 	struct berval		*aci,
    441 	int			nmatch,
    442 	regmatch_t		*matches,
    443 	slap_access_t		*grant,
    444 	slap_access_t		*deny,
    445 	slap_aci_scope_t	asserted_scope )
    446 {
    447 	struct berval		bv,
    448 				scope,
    449 				perms,
    450 				type,
    451 				opts,
    452 				sdn;
    453 	int			rc;
    454 
    455 	ACL_INIT( *grant );
    456 	ACL_INIT( *deny );
    457 
    458 	assert( !BER_BVISNULL( &desc->ad_cname ) );
    459 
    460 	/* parse an aci of the form:
    461 		oid # scope # action;rights;attr;rights;attr
    462 			$ action;rights;attr;rights;attr # type # subject
    463 
    464 	   [NOTE: the following comment is very outdated,
    465 	   as the draft version it refers to (Ando, 2004-11-20)].
    466 
    467 	   See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
    468 	   a full description of the format for this attribute.
    469 	   Differences: "this" in the draft is "self" here, and
    470 	   "self" and "public" is in the position of type.
    471 
    472 	   <scope> = {entry|children|subtree}
    473 	   <type> = {public|users|access-id|subtree|onelevel|children|
    474 	             self|dnattr|group|role|set|set-ref}
    475 
    476 	   This routine now supports scope={ENTRY,CHILDREN}
    477 	   with the semantics:
    478 	     - ENTRY applies to "entry" and "subtree";
    479 	     - CHILDREN applies to "children" and "subtree"
    480 	 */
    481 
    482 	/* check that the aci has all 5 components */
    483 	if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
    484 		return 0;
    485 	}
    486 
    487 	/* check that the aci family is supported */
    488 	/* FIXME: the OID is ignored? */
    489 	if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
    490 		return 0;
    491 	}
    492 
    493 	/* check that the scope matches */
    494 	if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
    495 		return 0;
    496 	}
    497 
    498 	/* note: scope can be either ENTRY or CHILDREN;
    499 	 * they respectively match "entry" and "children" in bv
    500 	 * both match "subtree" */
    501 	switch ( asserted_scope ) {
    502 	case SLAP_ACI_SCOPE_ENTRY:
    503 		if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
    504 				&& ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
    505 		{
    506 			return 0;
    507 		}
    508 		break;
    509 
    510 	case SLAP_ACI_SCOPE_CHILDREN:
    511 		if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
    512 				&& ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
    513 		{
    514 			return 0;
    515 		}
    516 		break;
    517 
    518 	case SLAP_ACI_SCOPE_SUBTREE:
    519 		/* TODO: add assertion? */
    520 		return 0;
    521 	}
    522 
    523 	/* get the list of permissions clauses, bail if empty */
    524 	if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
    525 		assert( 0 );
    526 		return 0;
    527 	}
    528 
    529 	/* check if any permissions allow desired access */
    530 	if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
    531 		return 0;
    532 	}
    533 
    534 	/* see if we have a DN match */
    535 	if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
    536 		assert( 0 );
    537 		return 0;
    538 	}
    539 
    540 	/* see if we have a public (i.e. anonymous) access */
    541 	if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
    542 		return 1;
    543 	}
    544 
    545 	/* otherwise require an identity */
    546 	if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
    547 		return 0;
    548 	}
    549 
    550 	/* see if we have a users access */
    551 	if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
    552 		return 1;
    553 	}
    554 
    555 	/* NOTE: this may fail if a DN contains a valid '#' (unescaped);
    556 	 * just grab all the berval up to its end (ITS#3303).
    557 	 * NOTE: the problem could be solved by providing the DN with
    558 	 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
    559 	 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
    560 #if 0
    561 	if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
    562 		return 0;
    563 	}
    564 #endif
    565 	sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
    566 	sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
    567 
    568 	/* get the type options, if any */
    569 	if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
    570 		opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
    571 		type.bv_len = opts.bv_val - type.bv_val - 1;
    572 
    573 	} else {
    574 		BER_BVZERO( &opts );
    575 	}
    576 
    577 	if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
    578 		return dn_match( &op->o_ndn, &sdn );
    579 
    580 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
    581 		return dnIsSuffix( &op->o_ndn, &sdn );
    582 
    583 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
    584 		struct berval pdn;
    585 
    586 		dnParent( &sdn, &pdn );
    587 
    588 		return dn_match( &op->o_ndn, &pdn );
    589 
    590 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
    591 		return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
    592 
    593 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
    594 		return dn_match( &op->o_ndn, &e->e_nname );
    595 
    596 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
    597 		Attribute		*at;
    598 		AttributeDescription	*ad = NULL;
    599 		const char		*text;
    600 
    601 		rc = slap_bv2ad( &sdn, &ad, &text );
    602 		assert( rc == LDAP_SUCCESS );
    603 
    604 		rc = 0;
    605 		for ( at = attrs_find( e->e_attrs, ad );
    606 				at != NULL;
    607 				at = attrs_find( at->a_next, ad ) )
    608 		{
    609 			if ( attr_valfind( at,
    610 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
    611 					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
    612 				&op->o_ndn, NULL, op->o_tmpmemctx ) == 0 )
    613 			{
    614 				rc = 1;
    615 				break;
    616 			}
    617 		}
    618 
    619 		return rc;
    620 
    621 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
    622 		struct berval	oc,
    623 				at;
    624 
    625 		if ( BER_BVISNULL( &opts ) ) {
    626 			oc = aci_bv[ ACI_BV_GROUP_CLASS ];
    627 			at = aci_bv[ ACI_BV_GROUP_ATTR ];
    628 
    629 		} else {
    630 			if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
    631 				assert( 0 );
    632 			}
    633 
    634 			if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
    635 				at = aci_bv[ ACI_BV_GROUP_ATTR ];
    636 			}
    637 		}
    638 
    639 		if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
    640 		{
    641 			return 1;
    642 		}
    643 
    644 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
    645 		struct berval	oc,
    646 				at;
    647 
    648 		if ( BER_BVISNULL( &opts ) ) {
    649 			oc = aci_bv[ ACI_BV_ROLE_CLASS ];
    650 			at = aci_bv[ ACI_BV_ROLE_ATTR ];
    651 
    652 		} else {
    653 			if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
    654 				assert( 0 );
    655 			}
    656 
    657 			if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
    658 				at = aci_bv[ ACI_BV_ROLE_ATTR ];
    659 			}
    660 		}
    661 
    662 		if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
    663 		{
    664 			return 1;
    665 		}
    666 
    667 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
    668 		if ( acl_match_set( &sdn, op, e, NULL ) ) {
    669 			return 1;
    670 		}
    671 
    672 	} else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
    673 		if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
    674 			return 1;
    675 		}
    676 
    677 	} else {
    678 		/* it passed normalization! */
    679 		assert( 0 );
    680 	}
    681 
    682 	return 0;
    683 }
    684 
    685 static int
    686 aci_init( void )
    687 {
    688 	/* OpenLDAP eXperimental Syntax */
    689 	static slap_syntax_defs_rec aci_syntax_def = {
    690 		"( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
    691 			SLAP_SYNTAX_HIDE,
    692 			NULL,
    693 			OpenLDAPaciValidate,
    694 			OpenLDAPaciPretty
    695 	};
    696 	static slap_mrule_defs_rec aci_mr_def = {
    697 		"( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
    698 			"SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
    699 			SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
    700 			NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
    701 			NULL, NULL,
    702 			NULL
    703 	};
    704 	static struct {
    705 		char			*name;
    706 		char			*desc;
    707 		slap_mask_t		flags;
    708 		AttributeDescription	**ad;
    709 	}		aci_at = {
    710 		"OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
    711 			"NAME 'OpenLDAPaci' "
    712 			"DESC 'OpenLDAP access control information (experimental)' "
    713 			"EQUALITY OpenLDAPaciMatch "
    714 			"SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
    715 			"USAGE directoryOperation )",
    716 		SLAP_AT_HIDE,
    717 		&slap_ad_aci
    718 	};
    719 
    720 	int			rc;
    721 
    722 	/* ACI syntax */
    723 	rc = register_syntax( &aci_syntax_def );
    724 	if ( rc != 0 ) {
    725 		return rc;
    726 	}
    727 
    728 	/* ACI equality rule */
    729 	rc = register_matching_rule( &aci_mr_def );
    730 	if ( rc != 0 ) {
    731 		return rc;
    732 	}
    733 
    734 	/* ACI attribute */
    735 	rc = register_at( aci_at.desc, aci_at.ad, 0 );
    736 	if ( rc != LDAP_SUCCESS ) {
    737 		Debug( LDAP_DEBUG_ANY,
    738 			"aci_init: at_register failed\n" );
    739 		return rc;
    740 	}
    741 
    742 	/* install flags */
    743 	(*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
    744 
    745 	return rc;
    746 }
    747 
    748 static int
    749 dynacl_aci_parse(
    750 	ConfigArgs *c,
    751 	const char *opts,
    752 	slap_style_t sty,
    753 	const char *right,
    754 	void **privp )
    755 {
    756 	AttributeDescription	*ad = NULL;
    757 	const char		*text = NULL;
    758 
    759 	if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
    760 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
    761 			"inappropriate style \"%s\" in \"aci\" by clause",
    762 			style_strings[sty] );
    763 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
    764 		return -1;
    765 	}
    766 
    767 	if ( right != NULL && *right != '\0' ) {
    768 		if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
    769 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
    770 				"aci \"%s\": %s",
    771 				right, text );
    772 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
    773 			return -1;
    774 		}
    775 
    776 	} else {
    777 		ad = slap_ad_aci;
    778 	}
    779 
    780 	if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
    781 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
    782 			"aci \"%s\": inappropriate syntax: %s",
    783 			right, ad->ad_type->sat_syntax_oid );
    784 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
    785 		return -1;
    786 	}
    787 
    788 	*privp = (void *)ad;
    789 
    790 	return 0;
    791 }
    792 
    793 static int
    794 dynacl_aci_unparse( void *priv, struct berval *bv )
    795 {
    796 	AttributeDescription	*ad = ( AttributeDescription * )priv;
    797 	char			*ptr;
    798 
    799 	assert( ad != NULL );
    800 
    801 	bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
    802 	ptr = lutil_strcopy( bv->bv_val, " aci=" );
    803 	ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
    804 	bv->bv_len = ptr - bv->bv_val;
    805 
    806 	return 0;
    807 }
    808 
    809 static int
    810 dynacl_aci_mask(
    811 	void			*priv,
    812 	Operation		*op,
    813 	Entry			*e,
    814 	AttributeDescription	*desc,
    815 	struct berval		*val,
    816 	int			nmatch,
    817 	regmatch_t		*matches,
    818 	slap_access_t		*grantp,
    819 	slap_access_t		*denyp )
    820 {
    821 	AttributeDescription	*ad = ( AttributeDescription * )priv;
    822 	Attribute		*at;
    823 	slap_access_t		tgrant, tdeny, grant, deny;
    824 #ifdef LDAP_DEBUG
    825 	char			accessmaskbuf[ACCESSMASK_MAXLEN];
    826 	char			accessmaskbuf1[ACCESSMASK_MAXLEN];
    827 #endif /* LDAP_DEBUG */
    828 
    829 	if ( BER_BVISEMPTY( &e->e_nname ) ) {
    830 		/* no ACIs in the root DSE */
    831 		return -1;
    832 	}
    833 
    834 	/* start out with nothing granted, nothing denied */
    835 	ACL_INIT(tgrant);
    836 	ACL_INIT(tdeny);
    837 
    838 	/* get the aci attribute */
    839 	at = attr_find( e->e_attrs, ad );
    840 	if ( at != NULL ) {
    841 		int		i;
    842 
    843 		/* the aci is an multi-valued attribute.  The
    844 		 * rights are determined by OR'ing the individual
    845 		 * rights given by the acis.
    846 		 */
    847 		for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
    848 			if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
    849 					nmatch, matches, &grant, &deny,
    850 					SLAP_ACI_SCOPE_ENTRY ) != 0 )
    851 			{
    852 				tgrant |= grant;
    853 				tdeny |= deny;
    854 			}
    855 		}
    856 
    857 		Debug( LDAP_DEBUG_ACL, "        <= aci_mask grant %s deny %s\n",
    858 			  accessmask2str( tgrant, accessmaskbuf, 1 ),
    859 			  accessmask2str( tdeny, accessmaskbuf1, 1 ) );
    860 	}
    861 
    862 	/* If the entry level aci didn't contain anything valid for the
    863 	 * current operation, climb up the tree and evaluate the
    864 	 * acis with scope set to subtree
    865 	 */
    866 	if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
    867 		struct berval	parent_ndn;
    868 
    869 		dnParent( &e->e_nname, &parent_ndn );
    870 		while ( !BER_BVISEMPTY( &parent_ndn ) ){
    871 			int		i;
    872 			BerVarray	bvals = NULL;
    873 			int		ret, stop;
    874 
    875 			/* to solve the chicken'n'egg problem of accessing
    876 			 * the OpenLDAPaci attribute, the direct access
    877 			 * to the entry's attribute is unchecked; however,
    878 			 * further accesses to OpenLDAPaci values in the
    879 			 * ancestors occur through backend_attribute(), i.e.
    880 			 * with the identity of the operation, requiring
    881 			 * further access checking.  For uniformity, this
    882 			 * makes further requests occur as the rootdn, if
    883 			 * any, i.e. searching for the OpenLDAPaci attribute
    884 			 * is considered an internal search.  If this is not
    885 			 * acceptable, then the same check needs be performed
    886 			 * when accessing the entry's attribute. */
    887 			struct berval	save_o_dn, save_o_ndn;
    888 
    889 			if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
    890 				save_o_dn = op->o_dn;
    891 				save_o_ndn = op->o_ndn;
    892 
    893 				op->o_dn = op->o_bd->be_rootdn;
    894 				op->o_ndn = op->o_bd->be_rootndn;
    895 			}
    896 
    897 			Debug( LDAP_DEBUG_ACL, "        checking ACI of \"%s\"\n", parent_ndn.bv_val );
    898 			ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
    899 
    900 			if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
    901 				op->o_dn = save_o_dn;
    902 				op->o_ndn = save_o_ndn;
    903 			}
    904 
    905 			switch ( ret ) {
    906 			case LDAP_SUCCESS :
    907 				stop = 0;
    908 				if ( !bvals ) {
    909 					break;
    910 				}
    911 
    912 				for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
    913 					if ( aci_mask( op, e, desc, val,
    914 							&bvals[i],
    915 							nmatch, matches,
    916 							&grant, &deny,
    917 							SLAP_ACI_SCOPE_CHILDREN ) != 0 )
    918 					{
    919 						tgrant |= grant;
    920 						tdeny |= deny;
    921 						/* evaluation stops as soon as either a "deny" or a
    922 						 * "grant" directive matches.
    923 						 */
    924 						if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
    925 							stop = 1;
    926 						}
    927 					}
    928 					Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
    929 						accessmask2str( tgrant, accessmaskbuf, 1 ),
    930 						accessmask2str( tdeny, accessmaskbuf1, 1 ) );
    931 				}
    932 				break;
    933 
    934 			case LDAP_NO_SUCH_ATTRIBUTE:
    935 				/* just go on if the aci-Attribute is not present in
    936 				 * the current entry
    937 				 */
    938 				Debug( LDAP_DEBUG_ACL, "no such attribute\n" );
    939 				stop = 0;
    940 				break;
    941 
    942 			case LDAP_NO_SUCH_OBJECT:
    943 				/* We have reached the base object */
    944 				Debug( LDAP_DEBUG_ACL, "no such object\n" );
    945 				stop = 1;
    946 				break;
    947 
    948 			default:
    949 				stop = 1;
    950 				break;
    951 			}
    952 
    953 			if ( stop ) {
    954 				break;
    955 			}
    956 			dnParent( &parent_ndn, &parent_ndn );
    957 		}
    958 	}
    959 
    960 	*grantp = tgrant;
    961 	*denyp = tdeny;
    962 
    963 	return 0;
    964 }
    965 
    966 /* need to register this at some point */
    967 static slap_dynacl_t	dynacl_aci = {
    968 	"aci",
    969 	dynacl_aci_parse,
    970 	dynacl_aci_unparse,
    971 	dynacl_aci_mask,
    972 	NULL,
    973 	NULL,
    974 	NULL
    975 };
    976 
    977 int
    978 dynacl_aci_init( void )
    979 {
    980 	int	rc;
    981 
    982 	rc = aci_init();
    983 
    984 	if ( rc == 0 ) {
    985 		rc = slap_dynacl_register( &dynacl_aci );
    986 	}
    987 
    988 	return rc;
    989 }
    990 
    991 
    992 /* ACI syntax validation */
    993 
    994 /*
    995  * Matches given berval to array of bervals
    996  * Returns:
    997  *      >=0 if one if the array elements equals to this berval
    998  *       -1 if string was not found in array
    999  */
   1000 static int
   1001 bv_getcaseidx(
   1002 	struct berval *bv,
   1003 	const struct berval *arr[] )
   1004 {
   1005 	int i;
   1006 
   1007 	if ( BER_BVISEMPTY( bv ) ) {
   1008 		return -1;
   1009 	}
   1010 
   1011 	for ( i = 0; arr[ i ] != NULL ; i++ ) {
   1012 		if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
   1013  			return i;
   1014 		}
   1015 	}
   1016 
   1017   	return -1;
   1018 }
   1019 
   1020 
   1021 /* Returns what have left in input berval after current sub */
   1022 static void
   1023 bv_get_tail(
   1024 	struct berval *val,
   1025 	struct berval *sub,
   1026 	struct berval *tail )
   1027 {
   1028 	int		head_len;
   1029 
   1030 	tail->bv_val = sub->bv_val + sub->bv_len;
   1031 	head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
   1032   	tail->bv_len = val->bv_len - head_len;
   1033 }
   1034 
   1035 
   1036 /*
   1037  * aci is accepted in following form:
   1038  *    oid#scope#rights#type#subject
   1039  * Where:
   1040  *    oid       := numeric OID (currently ignored)
   1041  *    scope     := entry|children|subtree
   1042  *    rights    := right[[$right]...]
   1043  *    right     := (grant|deny);action
   1044  *    action    := perms;attrs[[;perms;attrs]...]
   1045  *    perms     := perm[[,perm]...]
   1046  *    perm      := c|s|r|w|x
   1047  *    attrs     := attribute[[,attribute]..]|"[all]"
   1048  *    attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
   1049  *    type      := public|users|self|dnattr|group|role|set|set-ref|
   1050  *                 access_id|subtree|onelevel|children
   1051  */
   1052 static int
   1053 OpenLDAPaciValidatePerms(
   1054 	struct berval *perms )
   1055 {
   1056 	ber_len_t	i;
   1057 
   1058 	for ( i = 0; i < perms->bv_len; ) {
   1059 		switch ( perms->bv_val[ i ] ) {
   1060 		case 'x':
   1061 		case 'd':
   1062 		case 'c':
   1063 		case 's':
   1064 		case 'r':
   1065 		case 'w':
   1066 			break;
   1067 
   1068 		default:
   1069 		        Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val );
   1070 			return LDAP_INVALID_SYNTAX;
   1071 		}
   1072 
   1073 		if ( ++i == perms->bv_len ) {
   1074 			return LDAP_SUCCESS;
   1075 		}
   1076 
   1077 		while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
   1078 			i++;
   1079 
   1080 		assert( i != perms->bv_len );
   1081 
   1082 		if ( perms->bv_val[ i ] != ',' ) {
   1083 		        Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val );
   1084 			return LDAP_INVALID_SYNTAX;
   1085 		}
   1086 
   1087 		do {
   1088 			i++;
   1089 		} while ( perms->bv_val[ i ] == ' ' );
   1090 	}
   1091 
   1092 	return LDAP_SUCCESS;
   1093 }
   1094 
   1095 static const struct berval *ACIgrantdeny[] = {
   1096 	&aci_bv[ ACI_BV_GRANT ],
   1097 	&aci_bv[ ACI_BV_DENY ],
   1098 	NULL
   1099 };
   1100 
   1101 static int
   1102 OpenLDAPaciValidateRight(
   1103 	struct berval *action )
   1104 {
   1105 	struct berval	bv = BER_BVNULL;
   1106 	int		i;
   1107 
   1108 	/* grant|deny */
   1109 	if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
   1110 		bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
   1111 	{
   1112 		Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val );
   1113 		return LDAP_INVALID_SYNTAX;
   1114 	}
   1115 
   1116 	for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
   1117 		if ( i & 1 ) {
   1118 			/* perms */
   1119 			if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
   1120 			{
   1121 				return LDAP_INVALID_SYNTAX;
   1122 			}
   1123 
   1124 		} else {
   1125 			/* attr */
   1126 			AttributeDescription	*ad;
   1127 			const char		*text;
   1128 			struct berval		attr, left, right;
   1129 			int			j;
   1130 
   1131 			/* could be "[all]" or an attribute description */
   1132 			if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
   1133 				continue;
   1134 			}
   1135 
   1136 
   1137 			for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
   1138 			{
   1139 				ad = NULL;
   1140 				text = NULL;
   1141 				if ( acl_get_part( &attr, 0, '=', &left ) < 0
   1142 					|| acl_get_part( &attr, 1, '=', &right ) < 0 )
   1143 				{
   1144 					if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
   1145 					{
   1146 						Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val );
   1147 						return LDAP_INVALID_SYNTAX;
   1148 					}
   1149 				} else {
   1150 					if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
   1151 					{
   1152 						Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val );
   1153 						return LDAP_INVALID_SYNTAX;
   1154 					}
   1155 				}
   1156 			}
   1157 		}
   1158 	}
   1159 
   1160 	/* "perms;attr" go in pairs */
   1161 	if ( i > 0 && ( i & 1 ) == 0 ) {
   1162 		return LDAP_SUCCESS;
   1163 
   1164 	} else {
   1165 		Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
   1166 		return LDAP_INVALID_SYNTAX;
   1167 	}
   1168 
   1169 	return LDAP_SUCCESS;
   1170 }
   1171 
   1172 static int
   1173 OpenLDAPaciNormalizeRight(
   1174 	struct berval	*action,
   1175 	struct berval	*naction,
   1176 	void		*ctx )
   1177 {
   1178 	struct berval	grantdeny,
   1179 			perms = BER_BVNULL,
   1180 			bv = BER_BVNULL;
   1181 	int		idx,
   1182 			i;
   1183 
   1184 	/* grant|deny */
   1185 	if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
   1186 	        Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val );
   1187 		return LDAP_INVALID_SYNTAX;
   1188 	}
   1189 	idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
   1190 	if ( idx == -1 ) {
   1191 	        Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val );
   1192 		return LDAP_INVALID_SYNTAX;
   1193 	}
   1194 
   1195 	ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
   1196 
   1197 	for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
   1198 		struct berval	nattrs = BER_BVNULL;
   1199 		int		freenattrs = 1;
   1200 		if ( i & 1 ) {
   1201 			/* perms */
   1202 			if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
   1203 			{
   1204 				return LDAP_INVALID_SYNTAX;
   1205 			}
   1206 			perms = bv;
   1207 
   1208 		} else {
   1209 			/* attr */
   1210 			char		*ptr;
   1211 
   1212 			/* could be "[all]" or an attribute description */
   1213 			if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
   1214 				nattrs = aci_bv[ ACI_BV_BR_ALL ];
   1215 				freenattrs = 0;
   1216 
   1217 			} else {
   1218 				AttributeDescription	*ad = NULL;
   1219 				AttributeDescription	adstatic= { 0 };
   1220 				const char		*text = NULL;
   1221 				struct berval		attr, left, right;
   1222 				int			j;
   1223 				int			len;
   1224 
   1225 				for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
   1226 				{
   1227 					ad = NULL;
   1228 					text = NULL;
   1229 					/* openldap 2.1 aci compatibility [entry] -> entry */
   1230 					if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
   1231 						ad = &adstatic;
   1232 						adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
   1233 
   1234 					/* openldap 2.1 aci compatibility [children] -> children */
   1235 					} else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
   1236 						ad = &adstatic;
   1237 						adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
   1238 
   1239 					/* openldap 2.1 aci compatibility [all] -> only [all] */
   1240 					} else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
   1241 						ber_memfree_x( nattrs.bv_val, ctx );
   1242 						nattrs = aci_bv[ ACI_BV_BR_ALL ];
   1243 						freenattrs = 0;
   1244 						break;
   1245 
   1246 					} else if ( acl_get_part( &attr, 0, '=', &left ) < 0
   1247 				     		|| acl_get_part( &attr, 1, '=', &right ) < 0 )
   1248 					{
   1249 						if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
   1250 						{
   1251 							ber_memfree_x( nattrs.bv_val, ctx );
   1252 							Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val );
   1253 							return LDAP_INVALID_SYNTAX;
   1254 						}
   1255 
   1256 					} else {
   1257 						if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
   1258 						{
   1259 							ber_memfree_x( nattrs.bv_val, ctx );
   1260 							Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val );
   1261 							return LDAP_INVALID_SYNTAX;
   1262 						}
   1263 					}
   1264 
   1265 
   1266 					len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
   1267 				      		+ ad->ad_cname.bv_len;
   1268 					nattrs.bv_val = slap_sl_realloc( nattrs.bv_val, len + 1, ctx );
   1269 	                        	ptr = &nattrs.bv_val[ nattrs.bv_len ];
   1270 					if ( !BER_BVISEMPTY( &nattrs ) ) {
   1271 						*ptr++ = ',';
   1272 					}
   1273 					ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
   1274                                 	ptr[ 0 ] = '\0';
   1275                                 	nattrs.bv_len = len;
   1276 				}
   1277 
   1278 			}
   1279 
   1280 			naction->bv_val = slap_sl_realloc( naction->bv_val,
   1281 				naction->bv_len + STRLENOF( ";" )
   1282 				+ perms.bv_len + STRLENOF( ";" )
   1283 				+ nattrs.bv_len + 1,
   1284 				ctx );
   1285 
   1286 			ptr = &naction->bv_val[ naction->bv_len ];
   1287 			ptr[ 0 ] = ';';
   1288 			ptr++;
   1289 			ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
   1290 			ptr[ 0 ] = ';';
   1291 			ptr++;
   1292 			ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
   1293 			ptr[ 0 ] = '\0';
   1294 			naction->bv_len += STRLENOF( ";" ) + perms.bv_len
   1295 				+ STRLENOF( ";" ) + nattrs.bv_len;
   1296 			if ( freenattrs ) {
   1297 				ber_memfree_x( nattrs.bv_val, ctx );
   1298 			}
   1299 		}
   1300 	}
   1301 
   1302 	/* perms;attr go in pairs */
   1303 	if ( i > 1 && ( i & 1 ) ) {
   1304 		return LDAP_SUCCESS;
   1305 
   1306 	} else {
   1307 		Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
   1308 		return LDAP_INVALID_SYNTAX;
   1309 	}
   1310 }
   1311 
   1312 static int
   1313 OpenLDAPaciValidateRights(
   1314 	struct berval *actions )
   1315 
   1316 {
   1317 	struct berval	bv = BER_BVNULL;
   1318 	int		i;
   1319 
   1320 	for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
   1321 		if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
   1322 			return LDAP_INVALID_SYNTAX;
   1323 		}
   1324 	}
   1325 
   1326 	return LDAP_SUCCESS;
   1327 }
   1328 
   1329 static int
   1330 OpenLDAPaciNormalizeRights(
   1331 	struct berval	*actions,
   1332 	struct berval	*nactions,
   1333 	void		*ctx )
   1334 
   1335 {
   1336 	struct berval	bv = BER_BVNULL;
   1337 	int		i;
   1338 
   1339 	BER_BVZERO( nactions );
   1340 	for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
   1341 		int		rc;
   1342 		struct berval	nbv;
   1343 
   1344 		rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
   1345 		if ( rc != LDAP_SUCCESS ) {
   1346 			ber_memfree_x( nactions->bv_val, ctx );
   1347 			BER_BVZERO( nactions );
   1348 			return LDAP_INVALID_SYNTAX;
   1349 		}
   1350 
   1351 		if ( i == 0 ) {
   1352 			*nactions = nbv;
   1353 
   1354 		} else {
   1355 			nactions->bv_val = slap_sl_realloc( nactions->bv_val,
   1356 				nactions->bv_len + STRLENOF( "$" )
   1357 				+ nbv.bv_len + 1,
   1358 				ctx );
   1359 			nactions->bv_val[ nactions->bv_len ] = '$';
   1360 			AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
   1361 				nbv.bv_val, nbv.bv_len + 1 );
   1362 			ber_memfree_x( nbv.bv_val, ctx );
   1363 			nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
   1364 		}
   1365 		BER_BVZERO( &nbv );
   1366 	}
   1367 
   1368 	return LDAP_SUCCESS;
   1369 }
   1370 
   1371 static const struct berval *OpenLDAPaciscopes[] = {
   1372 	&aci_bv[ ACI_BV_ENTRY ],
   1373 	&aci_bv[ ACI_BV_CHILDREN ],
   1374 	&aci_bv[ ACI_BV_SUBTREE ],
   1375 
   1376 	NULL
   1377 };
   1378 
   1379 static const struct berval *OpenLDAPacitypes[] = {
   1380 	/* DN-valued */
   1381 	&aci_bv[ ACI_BV_GROUP ],
   1382 	&aci_bv[ ACI_BV_ROLE ],
   1383 
   1384 /* set to one past the last DN-valued type with options (/) */
   1385 #define	LAST_OPTIONAL	2
   1386 
   1387 	&aci_bv[ ACI_BV_ACCESS_ID ],
   1388 	&aci_bv[ ACI_BV_SUBTREE ],
   1389 	&aci_bv[ ACI_BV_ONELEVEL ],
   1390 	&aci_bv[ ACI_BV_CHILDREN ],
   1391 
   1392 /* set to one past the last DN-valued type */
   1393 #define LAST_DNVALUED	6
   1394 
   1395 	/* non DN-valued */
   1396 	&aci_bv[ ACI_BV_DNATTR ],
   1397 	&aci_bv[ ACI_BV_PUBLIC ],
   1398 	&aci_bv[ ACI_BV_USERS ],
   1399 	&aci_bv[ ACI_BV_SELF ],
   1400 	&aci_bv[ ACI_BV_SET ],
   1401 	&aci_bv[ ACI_BV_SET_REF ],
   1402 
   1403 	NULL
   1404 };
   1405 
   1406 static int
   1407 OpenLDAPaciValidate(
   1408 	Syntax		*syntax,
   1409 	struct berval	*val )
   1410 {
   1411 	struct berval	oid = BER_BVNULL,
   1412 			scope = BER_BVNULL,
   1413 			rights = BER_BVNULL,
   1414 			type = BER_BVNULL,
   1415 			subject = BER_BVNULL;
   1416 	int		idx;
   1417 	int		rc;
   1418 
   1419 	if ( BER_BVISEMPTY( val ) ) {
   1420 		Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n" );
   1421 		return LDAP_INVALID_SYNTAX;
   1422 	}
   1423 
   1424 	/* oid */
   1425 	if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
   1426 		numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
   1427 	{
   1428 		/* NOTE: the numericoidValidate() is rather pedantic;
   1429 		 * I'd replace it with X-ORDERED VALUES so that
   1430 		 * it's guaranteed values are maintained and used
   1431 		 * in the desired order */
   1432 		Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val );
   1433 		return LDAP_INVALID_SYNTAX;
   1434 	}
   1435 
   1436 	/* scope */
   1437 	if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
   1438 		bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
   1439 	{
   1440 		Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val );
   1441 		return LDAP_INVALID_SYNTAX;
   1442 	}
   1443 
   1444 	/* rights */
   1445 	if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
   1446 		OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
   1447 	{
   1448 		return LDAP_INVALID_SYNTAX;
   1449 	}
   1450 
   1451 	/* type */
   1452 	if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
   1453 		Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val );
   1454 		return LDAP_INVALID_SYNTAX;
   1455 	}
   1456 	idx = bv_getcaseidx( &type, OpenLDAPacitypes );
   1457 	if ( idx == -1 ) {
   1458 		struct berval	isgr;
   1459 
   1460 		if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
   1461 			Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val );
   1462 			return LDAP_INVALID_SYNTAX;
   1463 		}
   1464 
   1465 		idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
   1466 		if ( idx == -1 || idx >= LAST_OPTIONAL ) {
   1467 			Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val );
   1468 			return LDAP_INVALID_SYNTAX;
   1469 		}
   1470 	}
   1471 
   1472 	/* subject */
   1473 	bv_get_tail( val, &type, &subject );
   1474 	if ( subject.bv_val[ 0 ] != '#' ) {
   1475 		Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val );
   1476 		return LDAP_INVALID_SYNTAX;
   1477 	}
   1478 
   1479 	if ( idx >= LAST_DNVALUED ) {
   1480 		if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
   1481 			AttributeDescription	*ad = NULL;
   1482 			const char		*text = NULL;
   1483 
   1484 			rc = slap_bv2ad( &subject, &ad, &text );
   1485 			if ( rc != LDAP_SUCCESS ) {
   1486 				Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val );
   1487 				return LDAP_INVALID_SYNTAX;
   1488 			}
   1489 
   1490 			if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
   1491 				/* FIXME: allow nameAndOptionalUID? */
   1492 				Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val );
   1493 				return LDAP_INVALID_SYNTAX;
   1494 			}
   1495 		}
   1496 
   1497 		/* not a DN */
   1498 		return LDAP_SUCCESS;
   1499 
   1500 	} else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
   1501 			|| OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
   1502 	{
   1503 		/* do {group|role}/oc/at check */
   1504 		struct berval	ocbv = BER_BVNULL,
   1505 				atbv = BER_BVNULL;
   1506 
   1507 		ocbv.bv_val = ber_bvchr( &type, '/' );
   1508 		if ( ocbv.bv_val != NULL ) {
   1509 			ocbv.bv_val++;
   1510 			ocbv.bv_len = type.bv_len
   1511 					- ( ocbv.bv_val - type.bv_val );
   1512 
   1513 			atbv.bv_val = ber_bvchr( &ocbv, '/' );
   1514 			if ( atbv.bv_val != NULL ) {
   1515 				AttributeDescription	*ad = NULL;
   1516 				const char		*text = NULL;
   1517 				int			rc;
   1518 
   1519 				atbv.bv_val++;
   1520 				atbv.bv_len = type.bv_len
   1521 					- ( atbv.bv_val - type.bv_val );
   1522 				ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
   1523 
   1524 				rc = slap_bv2ad( &atbv, &ad, &text );
   1525 				if ( rc != LDAP_SUCCESS ) {
   1526 				        Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val );
   1527 					return LDAP_INVALID_SYNTAX;
   1528 				}
   1529 			}
   1530 
   1531 			if ( oc_bvfind( &ocbv ) == NULL ) {
   1532 			        Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val );
   1533 				return LDAP_INVALID_SYNTAX;
   1534 			}
   1535 		}
   1536 	}
   1537 
   1538 	if ( BER_BVISEMPTY( &subject ) ) {
   1539 		/* empty DN invalid */
   1540 	        Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val );
   1541 		return LDAP_INVALID_SYNTAX;
   1542 	}
   1543 
   1544 	subject.bv_val++;
   1545 	subject.bv_len--;
   1546 
   1547 	/* FIXME: pass DN syntax? */
   1548 	rc = dnValidate( NULL, &subject );
   1549 	if ( rc != LDAP_SUCCESS ) {
   1550 	        Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val );
   1551 	}
   1552 	return rc;
   1553 }
   1554 
   1555 static int
   1556 OpenLDAPaciPrettyNormal(
   1557 	struct berval	*val,
   1558 	struct berval	*out,
   1559 	void		*ctx,
   1560 	int		normalize )
   1561 {
   1562 	struct berval	oid = BER_BVNULL,
   1563 			scope = BER_BVNULL,
   1564 			rights = BER_BVNULL,
   1565 			nrights = BER_BVNULL,
   1566 			type = BER_BVNULL,
   1567 			ntype = BER_BVNULL,
   1568 			subject = BER_BVNULL,
   1569 			nsubject = BER_BVNULL;
   1570 	int		idx,
   1571 			rc = LDAP_SUCCESS,
   1572 			freesubject = 0,
   1573 			freetype = 0;
   1574 	char		*ptr;
   1575 
   1576 	BER_BVZERO( out );
   1577 
   1578 	if ( BER_BVISEMPTY( val ) ) {
   1579 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n" );
   1580 		return LDAP_INVALID_SYNTAX;
   1581 	}
   1582 
   1583 	/* oid: if valid, it's already normalized */
   1584 	if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
   1585 		numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
   1586 	{
   1587 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val );
   1588 		return LDAP_INVALID_SYNTAX;
   1589 	}
   1590 
   1591 	/* scope: normalize by replacing with OpenLDAPaciscopes */
   1592 	if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
   1593 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val );
   1594 		return LDAP_INVALID_SYNTAX;
   1595 	}
   1596 	idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
   1597 	if ( idx == -1 ) {
   1598 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val );
   1599 		return LDAP_INVALID_SYNTAX;
   1600 	}
   1601 	scope = *OpenLDAPaciscopes[ idx ];
   1602 
   1603 	/* rights */
   1604 	if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
   1605 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val );
   1606 		return LDAP_INVALID_SYNTAX;
   1607 	}
   1608 	if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
   1609 		!= LDAP_SUCCESS )
   1610 	{
   1611 		return LDAP_INVALID_SYNTAX;
   1612 	}
   1613 
   1614 	/* type */
   1615 	if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
   1616 		Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val );
   1617 		rc = LDAP_INVALID_SYNTAX;
   1618 		goto cleanup;
   1619 	}
   1620 	idx = bv_getcaseidx( &type, OpenLDAPacitypes );
   1621 	if ( idx == -1 ) {
   1622 		struct berval	isgr;
   1623 
   1624 		if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
   1625 		        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val );
   1626 			rc = LDAP_INVALID_SYNTAX;
   1627 			goto cleanup;
   1628 		}
   1629 
   1630 		idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
   1631 		if ( idx == -1 || idx >= LAST_OPTIONAL ) {
   1632 		        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val );
   1633 			rc = LDAP_INVALID_SYNTAX;
   1634 			goto cleanup;
   1635 		}
   1636 	}
   1637 	ntype = *OpenLDAPacitypes[ idx ];
   1638 
   1639 	/* subject */
   1640 	bv_get_tail( val, &type, &subject );
   1641 
   1642 	if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
   1643 	        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val );
   1644 		rc = LDAP_INVALID_SYNTAX;
   1645 		goto cleanup;
   1646 	}
   1647 
   1648 	subject.bv_val++;
   1649 	subject.bv_len--;
   1650 
   1651 	if ( idx < LAST_DNVALUED ) {
   1652 		/* FIXME: pass DN syntax? */
   1653 		if ( normalize ) {
   1654 			rc = dnNormalize( 0, NULL, NULL,
   1655 				&subject, &nsubject, ctx );
   1656 		} else {
   1657 			rc = dnPretty( NULL, &subject, &nsubject, ctx );
   1658 		}
   1659 
   1660 		if ( rc == LDAP_SUCCESS ) {
   1661 			freesubject = 1;
   1662 
   1663 		} else {
   1664 	                Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val );
   1665 			goto cleanup;
   1666 		}
   1667 
   1668 		if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
   1669 			|| OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
   1670 		{
   1671 			/* do {group|role}/oc/at check */
   1672 			struct berval	ocbv = BER_BVNULL,
   1673 					atbv = BER_BVNULL;
   1674 
   1675 			ocbv.bv_val = ber_bvchr( &type, '/' );
   1676 			if ( ocbv.bv_val != NULL ) {
   1677 				ObjectClass		*oc = NULL;
   1678 				AttributeDescription	*ad = NULL;
   1679 				const char		*text = NULL;
   1680 				int			rc;
   1681 				struct berval		bv;
   1682 
   1683 				bv.bv_len = ntype.bv_len;
   1684 
   1685 				ocbv.bv_val++;
   1686 				ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
   1687 
   1688 				atbv.bv_val = ber_bvchr( &ocbv, '/' );
   1689 				if ( atbv.bv_val != NULL ) {
   1690 					atbv.bv_val++;
   1691 					atbv.bv_len = type.bv_len
   1692 						- ( atbv.bv_val - type.bv_val );
   1693 					ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
   1694 
   1695 					rc = slap_bv2ad( &atbv, &ad, &text );
   1696 					if ( rc != LDAP_SUCCESS ) {
   1697 	                                        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val );
   1698 						rc = LDAP_INVALID_SYNTAX;
   1699 						goto cleanup;
   1700 					}
   1701 
   1702 					bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
   1703 				}
   1704 
   1705 				oc = oc_bvfind( &ocbv );
   1706 				if ( oc == NULL ) {
   1707                                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val );
   1708 					rc = LDAP_INVALID_SYNTAX;
   1709 					goto cleanup;
   1710 				}
   1711 
   1712 				bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
   1713 				bv.bv_val = slap_sl_malloc( bv.bv_len + 1, ctx );
   1714 
   1715 				ptr = bv.bv_val;
   1716 				ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
   1717 				ptr[ 0 ] = '/';
   1718 				ptr++;
   1719 				ptr = lutil_strncopy( ptr,
   1720 					oc->soc_cname.bv_val,
   1721 					oc->soc_cname.bv_len );
   1722 				if ( ad != NULL ) {
   1723 					ptr[ 0 ] = '/';
   1724 					ptr++;
   1725 					ptr = lutil_strncopy( ptr,
   1726 						ad->ad_cname.bv_val,
   1727 						ad->ad_cname.bv_len );
   1728 				}
   1729 				ptr[ 0 ] = '\0';
   1730 
   1731 				ntype = bv;
   1732 				freetype = 1;
   1733 			}
   1734 		}
   1735 
   1736 	} else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
   1737 		AttributeDescription	*ad = NULL;
   1738 		const char		*text = NULL;
   1739 		int			rc;
   1740 
   1741 		rc = slap_bv2ad( &subject, &ad, &text );
   1742 		if ( rc != LDAP_SUCCESS ) {
   1743                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val );
   1744 			rc = LDAP_INVALID_SYNTAX;
   1745 			goto cleanup;
   1746 		}
   1747 
   1748 		if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
   1749 			/* FIXME: allow nameAndOptionalUID? */
   1750                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val );
   1751 			rc = LDAP_INVALID_SYNTAX;
   1752 			goto cleanup;
   1753 		}
   1754 
   1755 		nsubject = ad->ad_cname;
   1756 
   1757 	} else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ]
   1758 		|| OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] )
   1759 	{
   1760 		/* NOTE: dunno how to normalize it... */
   1761 		nsubject = subject;
   1762 	}
   1763 
   1764 
   1765 	out->bv_len =
   1766 		oid.bv_len + STRLENOF( "#" )
   1767 		+ scope.bv_len + STRLENOF( "#" )
   1768 		+ nrights.bv_len + STRLENOF( "#" )
   1769 		+ ntype.bv_len + STRLENOF( "#" )
   1770 		+ nsubject.bv_len;
   1771 
   1772 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
   1773 	ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
   1774 	ptr[ 0 ] = '#';
   1775 	ptr++;
   1776 	ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
   1777 	ptr[ 0 ] = '#';
   1778 	ptr++;
   1779 	ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
   1780 	ptr[ 0 ] = '#';
   1781 	ptr++;
   1782 	ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
   1783 	ptr[ 0 ] = '#';
   1784 	ptr++;
   1785 	if ( !BER_BVISNULL( &nsubject ) ) {
   1786 		ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
   1787 	}
   1788 	ptr[ 0 ] = '\0';
   1789 
   1790 cleanup:;
   1791 	if ( freesubject ) {
   1792 		ber_memfree_x( nsubject.bv_val, ctx );
   1793 	}
   1794 
   1795 	if ( freetype ) {
   1796 		ber_memfree_x( ntype.bv_val, ctx );
   1797 	}
   1798 
   1799 	if ( !BER_BVISNULL( &nrights ) ) {
   1800 		ber_memfree_x( nrights.bv_val, ctx );
   1801 	}
   1802 
   1803 	return rc;
   1804 }
   1805 
   1806 static int
   1807 OpenLDAPaciPretty(
   1808 	Syntax		*syntax,
   1809 	struct berval	*val,
   1810 	struct berval	*out,
   1811 	void		*ctx )
   1812 {
   1813 	return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
   1814 }
   1815 
   1816 static int
   1817 OpenLDAPaciNormalize(
   1818 	slap_mask_t	use,
   1819 	Syntax		*syntax,
   1820 	MatchingRule	*mr,
   1821 	struct berval	*val,
   1822 	struct berval	*out,
   1823 	void		*ctx )
   1824 {
   1825 	return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
   1826 }
   1827 
   1828 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
   1829 /*
   1830  * FIXME: need config and Makefile.am code to ease building
   1831  * as dynamic module
   1832  */
   1833 int
   1834 init_module( int argc, char *argv[] )
   1835 {
   1836 	return dynacl_aci_init();
   1837 }
   1838 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
   1839 
   1840 #endif /* SLAPD_ACI_ENABLED */
   1841 
   1842