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