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