]> git.sur5r.net Git - openldap/blob - servers/slapd/aci.c
cleanup objectclass inheritance; don't proceed if initial lookup resulted in a hit...
[openldap] / servers / slapd / aci.c
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-2005 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 #define ACI_BUF_SIZE    1024    /* use most appropriate size */
44
45 static int
46 aci_list_map_rights(
47         struct berval   *list )
48 {
49         struct berval   bv;
50         slap_access_t   mask;
51         int             i;
52
53         ACL_INIT( mask );
54         for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
55                 if ( bv.bv_len <= 0 ) {
56                         continue;
57                 }
58
59                 switch ( *bv.bv_val ) {
60                 case 'c':
61                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
62                         break;
63                 case 's':
64                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
65                          * the right 's' to mean "set", but in the examples states
66                          * that the right 's' means "search".  The latter definition
67                          * is used here.
68                          */
69                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
70                         break;
71                 case 'r':
72                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
73                         break;
74                 case 'w':
75                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
76                         break;
77                 case 'x':
78                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
79                          * define any equivalent to the AUTH right, so I've just used
80                          * 'x' for now.
81                          */
82                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
83                         break;
84                 default:
85                         break;
86                 }
87
88         }
89
90         return mask;
91 }
92
93 static int
94 aci_list_has_attr(
95         struct berval           *list,
96         const struct berval     *attr,
97         struct berval           *val )
98 {
99         struct berval   bv, left, right;
100         int             i;
101
102         for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
103                 if ( acl_get_part(&bv, 0, '=', &left ) < 0
104                         || acl_get_part( &bv, 1, '=', &right ) < 0 )
105                 {
106                         if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
107                                 return(1);
108                         }
109
110                 } else if ( val == NULL ) {
111                         if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
112                                 return(1);
113                         }
114
115                 } else {
116                         if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
117                                 /* FIXME: this is also totally undocumented! */
118                                 /* this is experimental code that implements a
119                                  * simple (prefix) match of the attribute value.
120                                  * the ACI draft does not provide for aci's that
121                                  * apply to specific values, but it would be
122                                  * nice to have.  If the <attr> part of an aci's
123                                  * rights list is of the form <attr>=<value>,
124                                  * that means the aci applies only to attrs with
125                                  * the given value.  Furthermore, if the attr is
126                                  * of the form <attr>=<value>*, then <value> is
127                                  * treated as a prefix, and the aci applies to 
128                                  * any value with that prefix.
129                                  *
130                                  * Ideally, this would allow r.e. matches.
131                                  */
132                                 if ( acl_get_part( &right, 0, '*', &left ) < 0
133                                         || right.bv_len <= left.bv_len )
134                                 {
135                                         if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
136                                                 return 1;
137                                         }
138
139                                 } else if ( val->bv_len >= left.bv_len ) {
140                                         if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
141                                                 return(1);
142                                         }
143                                 }
144                         }
145                 }
146         }
147
148         return 0;
149 }
150
151 static slap_access_t
152 aci_list_get_attr_rights(
153         struct berval           *list,
154         const struct berval     *attr,
155         struct berval           *val )
156 {
157         struct berval   bv;
158         slap_access_t   mask;
159         int             i;
160
161         /* loop through each rights/attr pair, skip first part (action) */
162         ACL_INIT(mask);
163         for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
164                 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
165                         continue;
166                 }
167
168                 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
169                         continue;
170                 }
171
172                 mask |= aci_list_map_rights( &bv );
173         }
174
175         return mask;
176 }
177
178 static int
179 aci_list_get_rights(
180         struct berval           *list,
181         const struct berval     *attr,
182         struct berval           *val,
183         slap_access_t           *grant,
184         slap_access_t           *deny )
185 {
186         struct berval   perm, actn;
187         slap_access_t   *mask;
188         int             i, found;
189
190         if ( attr == NULL || BER_BVISEMPTY( attr )
191                         || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
192         {
193                 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
194         }
195
196         found = 0;
197         ACL_INIT(*grant);
198         ACL_INIT(*deny);
199         /* loop through each permissions clause */
200         for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
201                 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
202                         continue;
203                 }
204
205                 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
206                         mask = grant;
207
208                 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
209                         mask = deny;
210
211                 } else {
212                         continue;
213                 }
214
215                 found = 1;
216                 *mask |= aci_list_get_attr_rights( &perm, attr, val );
217                 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
218         }
219
220         return found;
221 }
222
223 static int
224 aci_group_member (
225         struct berval           *subj,
226         const struct berval     *defgrpoc,
227         const struct berval     *defgrpat,
228         Operation               *op,
229         Entry                   *e,
230         int                     nmatch,
231         regmatch_t              *matches
232 )
233 {
234         struct berval           subjdn;
235         struct berval           grpoc;
236         struct berval           grpat;
237         ObjectClass             *grp_oc = NULL;
238         AttributeDescription    *grp_ad = NULL;
239         const char              *text;
240         int                     rc;
241
242         /* format of string is "group/objectClassValue/groupAttrName" */
243         if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
244                 return 0;
245         }
246
247         if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
248                 grpoc = *defgrpoc;
249         }
250
251         if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
252                 grpat = *defgrpat;
253         }
254
255         rc = slap_bv2ad( &grpat, &grp_ad, &text );
256         if ( rc != LDAP_SUCCESS ) {
257                 rc = 0;
258                 goto done;
259         }
260         rc = 0;
261
262         grp_oc = oc_bvfind( &grpoc );
263
264         if ( grp_oc != NULL && grp_ad != NULL ) {
265                 char            buf[ ACI_BUF_SIZE ];
266                 struct berval   bv, ndn;
267
268                 bv.bv_len = sizeof( buf ) - 1;
269                 bv.bv_val = (char *)&buf;
270                 if ( acl_string_expand( &bv, &subjdn,
271                                 e->e_ndn, nmatch, matches ) )
272                 {
273                         rc = LDAP_OTHER;
274                         goto done;
275                 }
276
277                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
278                 {
279                         rc = ( backend_group( op, e, &ndn, &op->o_ndn,
280                                 grp_oc, grp_ad ) == 0 );
281                         slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
282                 }
283         }
284
285 done:
286         return rc;
287 }
288
289 int
290 aci_mask(
291         Operation               *op,
292         Entry                   *e,
293         AttributeDescription    *desc,
294         struct berval           *val,
295         struct berval           *aci,
296         int                     nmatch,
297         regmatch_t              *matches,
298         slap_access_t           *grant,
299         slap_access_t           *deny,
300         slap_aci_scope_t        asserted_scope )
301 {
302         struct berval           bv, scope, perms, type, sdn;
303         int                     rc;
304                 
305
306         assert( !BER_BVISNULL( &desc->ad_cname ) );
307
308         /* parse an aci of the form:
309                 oid # scope # action;rights;attr;rights;attr 
310                         $ action;rights;attr;rights;attr # type # subject
311
312            [NOTE: the following comment is very outdated,
313            as the draft version it refers to (Ando, 2004-11-20)].
314
315            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
316            a full description of the format for this attribute.
317            Differences: "this" in the draft is "self" here, and
318            "self" and "public" is in the position of type.
319
320            <scope> = {entry|children|subtree}
321            <type> = {public|users|access-id|subtree|onelevel|children|
322                      self|dnattr|group|role|set|set-ref}
323
324            This routine now supports scope={ENTRY,CHILDREN}
325            with the semantics:
326              - ENTRY applies to "entry" and "subtree";
327              - CHILDREN aplies to "children" and "subtree"
328          */
329
330         /* check that the aci has all 5 components */
331         if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
332                 return 0;
333         }
334
335         /* check that the aci family is supported */
336         /* FIXME: the OID is ignored? */
337         if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
338                 return 0;
339         }
340
341         /* check that the scope matches */
342         if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
343                 return 0;
344         }
345
346         /* note: scope can be either ENTRY or CHILDREN;
347          * they respectively match "entry" and "children" in bv
348          * both match "subtree" */
349         switch ( asserted_scope ) {
350         case SLAP_ACI_SCOPE_ENTRY:
351                 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
352                                 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
353                 {
354                         return 0;
355                 }
356                 break;
357
358         case SLAP_ACI_SCOPE_CHILDREN:
359                 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
360                                 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
361                 {
362                         return 0;
363                 }
364                 break;
365
366         case SLAP_ACI_SCOPE_SUBTREE:
367                 /* TODO: add assertion? */
368                 return 0;
369         }
370
371         /* get the list of permissions clauses, bail if empty */
372         if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
373                 assert( 0 );
374                 return 0;
375         }
376
377         /* check if any permissions allow desired access */
378         if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
379                 return 0;
380         }
381
382         /* see if we have a DN match */
383         if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
384                 assert( 0 );
385                 return 0;
386         }
387
388         /* see if we have a public (i.e. anonymous) access */
389         if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
390                 return 1;
391         }
392         
393         /* otherwise require an identity */
394         if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
395                 return 0;
396         }
397
398         /* see if we have a users access */
399         if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
400                 return 1;
401         }
402         
403         /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
404          * just grab all the berval up to its end (ITS#3303).
405          * NOTE: the problem could be solved by providing the DN with
406          * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 
407          * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
408 #if 0
409         if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
410                 return 0;
411         }
412 #endif
413         sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
414         sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
415
416         if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
417                 return dn_match( &op->o_ndn, &sdn );
418
419         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
420                 return dnIsSuffix( &op->o_ndn, &sdn );
421
422         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
423                 struct berval pdn;
424                 
425                 dnParent( &sdn, &pdn );
426
427                 return dn_match( &op->o_ndn, &pdn );
428
429         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
430                 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
431
432         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
433                 return dn_match( &op->o_ndn, &e->e_nname );
434
435         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
436                 Attribute               *at;
437                 AttributeDescription    *ad = NULL;
438                 const char              *text;
439
440                 rc = slap_bv2ad( &sdn, &ad, &text );
441                 assert( rc == LDAP_SUCCESS );
442
443                 rc = 0;
444                 for ( at = attrs_find( e->e_attrs, ad );
445                                 at != NULL;
446                                 at = attrs_find( at->a_next, ad ) )
447                 {
448                         if ( value_find_ex( ad,
449                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
450                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
451                                 at->a_nvals,
452                                 &op->o_ndn, op->o_tmpmemctx ) == 0 )
453                         {
454                                 rc = 1;
455                                 break;
456                         }
457                 }
458
459                 return rc;
460
461         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
462                 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
463                                 &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
464                 {
465                         return 1;
466                 }
467
468         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
469                 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
470                                 &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
471                 {
472                         return 1;
473                 }
474
475         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
476                 if ( acl_match_set( &sdn, op, e, 0 ) ) {
477                         return 1;
478                 }
479
480         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
481                 if ( acl_match_set( &sdn, op, e, 1 ) ) {
482                         return 1;
483                 }
484         }
485
486         return 0;
487 }
488
489 #ifdef SLAP_DYNACL
490 /*
491  * FIXME: there is a silly dependence that makes it difficult
492  * to move ACIs in a run-time loadable module under the "dynacl" 
493  * umbrella, because sets share some helpers with ACIs.
494  */
495 static int
496 dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
497 {
498         AttributeDescription    *ad = NULL;
499         const char              *text = NULL;
500
501         if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
502                 fprintf( stderr, "%s: line %d: "
503                         "inappropriate style \"%s\" in \"aci\" by clause\n",
504                         fname, lineno, style_strings[sty] );
505                 return -1;
506         }
507
508         if ( right != NULL && *right != '\0' ) {
509                 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
510                         fprintf( stderr,
511                                 "%s: line %d: aci \"%s\": %s\n",
512                                 fname, lineno, right, text );
513                         return -1;
514                 }
515
516         } else {
517                 ad = slap_schema.si_ad_aci;
518         }
519
520         if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
521                 fprintf( stderr, "%s: line %d: "
522                         "aci \"%s\": inappropriate syntax: %s\n",
523                         fname, lineno, right,
524                         ad->ad_type->sat_syntax_oid );
525                 return -1;
526         }
527
528         *privp = (void *)ad;
529
530         return 0;
531 }
532
533 static int
534 dynacl_aci_unparse( void *priv, struct berval *bv )
535 {
536         AttributeDescription    *ad = ( AttributeDescription * )priv;
537         char                    *ptr;
538
539         assert( ad != NULL );
540
541         bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
542         ptr = lutil_strcopy( bv->bv_val, " aci=" );
543         ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
544         bv->bv_len = ptr - bv->bv_val;
545
546         return 0;
547 }
548
549 static int
550 dynacl_aci_mask(
551         void                    *priv,
552         Operation               *op,
553         Entry                   *e,
554         AttributeDescription    *desc,
555         struct berval           *val,
556         int                     nmatch,
557         regmatch_t              *matches,
558         slap_access_t           *grantp,
559         slap_access_t           *denyp )
560 {
561         AttributeDescription    *ad = ( AttributeDescription * )priv;
562         Attribute               *at;
563         slap_access_t           tgrant, tdeny, grant, deny;
564 #ifdef LDAP_DEBUG
565         char                    accessmaskbuf[ACCESSMASK_MAXLEN];
566         char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
567 #endif /* LDAP_DEBUG */
568
569         /* start out with nothing granted, nothing denied */
570         ACL_INIT(tgrant);
571         ACL_INIT(tdeny);
572
573         /* get the aci attribute */
574         at = attr_find( e->e_attrs, ad );
575         if ( at != NULL ) {
576                 int             i;
577
578                 /* the aci is an multi-valued attribute.  The
579                  * rights are determined by OR'ing the individual
580                  * rights given by the acis.
581                  */
582                 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
583                         if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
584                                         nmatch, matches, &grant, &deny,
585                                         SLAP_ACI_SCOPE_ENTRY ) != 0 )
586                         {
587                                 tgrant |= grant;
588                                 tdeny |= deny;
589                         }
590                 }
591                 
592                 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
593                           accessmask2str( tgrant, accessmaskbuf, 1 ), 
594                           accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
595         }
596
597         /* If the entry level aci didn't contain anything valid for the 
598          * current operation, climb up the tree and evaluate the
599          * acis with scope set to subtree
600          */
601         if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
602                 struct berval   parent_ndn;
603
604 #if 1
605                 /* to solve the chicken'n'egg problem of accessing
606                  * the OpenLDAPaci attribute, the direct access
607                  * to the entry's attribute is unchecked; however,
608                  * further accesses to OpenLDAPaci values in the 
609                  * ancestors occur through backend_attribute(), i.e.
610                  * with the identity of the operation, requiring
611                  * further access checking.  For uniformity, this
612                  * makes further requests occur as the rootdn, if
613                  * any, i.e. searching for the OpenLDAPaci attribute
614                  * is considered an internal search.  If this is not
615                  * acceptable, then the same check needs be performed
616                  * when accessing the entry's attribute. */
617                 Operation       op2 = *op;
618
619                 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
620                         op2.o_dn = op->o_bd->be_rootdn;
621                         op2.o_ndn = op->o_bd->be_rootndn;
622                 }
623 #endif
624
625                 dnParent( &e->e_nname, &parent_ndn );
626                 while ( !BER_BVISEMPTY( &parent_ndn ) ){
627                         int             i;
628                         BerVarray       bvals = NULL;
629                         int             ret, stop;
630
631                         Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
632                         ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
633
634                         switch ( ret ) {
635                         case LDAP_SUCCESS :
636                                 stop = 0;
637                                 if ( !bvals ) {
638                                         break;
639                                 }
640
641                                 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
642                                         if ( aci_mask( op, e, desc, val,
643                                                         &bvals[i],
644                                                         nmatch, matches,
645                                                         &grant, &deny,
646                                                         SLAP_ACI_SCOPE_CHILDREN ) != 0 )
647                                         {
648                                                 tgrant |= grant;
649                                                 tdeny |= deny;
650                                                 /* evaluation stops as soon as either a "deny" or a 
651                                                  * "grant" directive matches.
652                                                  */
653                                                 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
654                                                         stop = 1;
655                                                 }
656                                         }
657                                         Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
658                                                 accessmask2str( tgrant, accessmaskbuf, 1 ),
659                                                 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
660                                 }
661                                 break;
662
663                         case LDAP_NO_SUCH_ATTRIBUTE:
664                                 /* just go on if the aci-Attribute is not present in
665                                  * the current entry 
666                                  */
667                                 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
668                                 stop = 0;
669                                 break;
670
671                         case LDAP_NO_SUCH_OBJECT:
672                                 /* We have reached the base object */
673                                 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
674                                 stop = 1;
675                                 break;
676
677                         default:
678                                 stop = 1;
679                                 break;
680                         }
681
682                         if ( stop ) {
683                                 break;
684                         }
685                         dnParent( &parent_ndn, &parent_ndn );
686                 }
687         }
688
689         *grantp = tgrant;
690         *denyp = tdeny;
691
692         return 0;
693 }
694
695 /* need to register this at some point */
696 static slap_dynacl_t    dynacl_aci = {
697         "aci",
698         dynacl_aci_parse,
699         dynacl_aci_unparse,
700         dynacl_aci_mask,
701         NULL,
702         NULL,
703         NULL
704 };
705
706 int
707 dynacl_aci_init( void )
708 {
709         return slap_dynacl_register( &dynacl_aci );
710 }
711
712 #endif /* SLAP_DYNACL */
713
714 /* ACI syntax validation */
715
716 /*
717  * Matches given berval to array of bervals
718  * Returns:
719  *      >=0 if one if the array elements equals to this berval
720  *       -1 if string was not found in array
721  */
722 static int 
723 bv_getcaseidx(
724         struct berval *bv, 
725         const struct berval *arr[] )
726 {
727         int i;
728
729         if ( BER_BVISEMPTY( bv ) ) {
730                 return -1;
731         }
732
733         for ( i = 0; arr[ i ] != NULL ; i++ ) {
734                 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
735                         return i;
736                 }
737         }
738
739         return -1;
740 }
741
742
743 /* Returns what have left in input berval after current sub */
744 static void
745 bv_get_tail(
746         struct berval *val,
747         struct berval *sub,
748         struct berval *tail )
749 {
750         int             head_len;
751
752         tail->bv_val = sub->bv_val + sub->bv_len;
753         head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
754         tail->bv_len = val->bv_len - head_len;
755 }
756
757
758 /*
759  * aci is accepted in following form:
760  *    oid#scope#rights#type#subject
761  * Where:
762  *    oid       := numeric OID
763  *    scope     := entry|children
764  *    rights    := right[[$right]...]
765  *    right     := (grant|deny);action
766  *    action    := perms;attr[[;perms;attr]...]
767  *    perms     := perm[[,perm]...]
768  *    perm      := c|s|r|w|x
769  *    attr      := attributeType|[all]
770  *    type      :=  public|users|self|dnattr|group|role|set|set-ref|
771  *                  access_id|subtree|onelevel|children
772  */
773 static int 
774 OpenLDAPaciValidatePerms(
775         struct berval *perms ) 
776 {
777         int             i;
778
779         for ( i = 0; i < perms->bv_len; ) {
780                 switch ( perms->bv_val[ i ] ) {
781                 case 'c':
782                 case 's':
783                 case 'r':
784                 case 'w':
785                 case 'x':
786                         break;
787
788                 default:
789                         return LDAP_INVALID_SYNTAX;
790                 }
791
792                 if ( ++i == perms->bv_len ) {
793                         return LDAP_SUCCESS;
794                 }
795
796                 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
797                         i++;
798
799                 assert( i != perms->bv_len );
800
801                 if ( perms->bv_val[ i ] != ',' ) {
802                         return LDAP_INVALID_SYNTAX;
803                 }
804
805                 do {
806                         i++;
807                 } while ( perms->bv_val[ i ] == ' ' );
808         }
809
810         return LDAP_SUCCESS;
811 }
812
813 static const struct berval *ACIgrantdeny[] = {
814         &aci_bv[ ACI_BV_GRANT ],
815         &aci_bv[ ACI_BV_DENY ],
816         NULL
817 };
818
819 static int 
820 OpenLDAPaciValidateRight(
821         struct berval *action )
822 {
823         struct berval   bv = BER_BVNULL;
824         int             i;
825
826         /* grant|deny */
827         if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
828                 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
829         {
830                 return LDAP_INVALID_SYNTAX;
831         }
832
833         for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
834                 if ( i & 1 ) {
835                         /* perms */
836                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
837                         {
838                                 return LDAP_INVALID_SYNTAX;
839                         }
840
841                 } else {
842                         /* attr */
843                         AttributeDescription    *ad = NULL;
844                         const char              *text = NULL;
845
846                         /* could be "[all]" or an attribute description */
847                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
848                                 continue;
849                         }
850
851                         if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
852                                 return LDAP_INVALID_SYNTAX;
853                         }
854                 }
855         }
856         
857         /* "perms;attr" go in pairs */
858         if ( i > 0 && ( i & 1 ) == 0 ) {
859                 return LDAP_SUCCESS;
860
861         } else {
862                 return LDAP_INVALID_SYNTAX;
863         }
864
865         return LDAP_SUCCESS;
866 }
867
868 static int
869 OpenLDAPaciNormalizeRight(
870         struct berval   *action,
871         struct berval   *naction,
872         void            *ctx )
873 {
874         struct berval   grantdeny,
875                         perms = BER_BVNULL,
876                         bv = BER_BVNULL;
877         int             idx,
878                         i;
879
880         /* grant|deny */
881         if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
882                 return LDAP_INVALID_SYNTAX;
883         }
884         idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
885         if ( idx == -1 ) {
886                 return LDAP_INVALID_SYNTAX;
887         }
888
889         ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
890
891         for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
892                 if ( i & 1 ) {
893                         /* perms */
894                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
895                         {
896                                 return LDAP_INVALID_SYNTAX;
897                         }
898                         perms = bv;
899
900                 } else {
901                         /* attr */
902                         char            *ptr;
903
904                         /* could be "[all]" or an attribute description */
905                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
906                                 bv = aci_bv[ ACI_BV_BR_ALL ];
907
908                         } else {
909                                 AttributeDescription    *ad = NULL;
910                                 const char              *text = NULL;
911                                 int                     rc;
912
913                                 rc = slap_bv2ad( &bv, &ad, &text );
914                                 if ( rc != LDAP_SUCCESS ) {
915                                         return LDAP_INVALID_SYNTAX;
916                                 }
917
918                                 bv = ad->ad_cname;
919                         }
920
921                         naction->bv_val = ber_memrealloc_x( naction->bv_val,
922                                 naction->bv_len + STRLENOF( ";" )
923                                 + perms.bv_len + STRLENOF( ";" )
924                                 + bv.bv_len + 1,
925                                 ctx );
926
927                         ptr = &naction->bv_val[ naction->bv_len ];
928                         ptr[ 0 ] = ';';
929                         ptr++;
930                         ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
931                         ptr[ 0 ] = ';';
932                         ptr++;
933                         ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
934                         ptr[ 0 ] = '\0';
935                         naction->bv_len += STRLENOF( ";" ) + perms.bv_len
936                                 + STRLENOF( ";" ) + bv.bv_len;
937                 }
938         }
939         
940         /* perms;attr go in pairs */
941         if ( i > 1 && ( i & 1 ) ) {
942                 return LDAP_SUCCESS;
943
944         } else {
945                 return LDAP_INVALID_SYNTAX;
946         }
947 }
948
949 static int 
950 OpenLDAPaciValidateRights(
951         struct berval *actions )
952
953 {
954         struct berval   bv = BER_BVNULL;
955         int             i;
956
957         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
958                 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
959                         return LDAP_INVALID_SYNTAX;
960                 }
961         }
962
963         return LDAP_SUCCESS;
964 }
965
966 static int 
967 OpenLDAPaciNormalizeRights(
968         struct berval   *actions,
969         struct berval   *nactions,
970         void            *ctx )
971
972 {
973         struct berval   bv = BER_BVNULL;
974         int             i;
975
976         BER_BVZERO( nactions );
977         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
978                 int             rc;
979                 struct berval   nbv;
980
981                 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
982                 if ( rc != LDAP_SUCCESS ) {
983                         ber_memfree_x( nactions->bv_val, ctx );
984                         BER_BVZERO( nactions );
985                         return LDAP_INVALID_SYNTAX;
986                 }
987
988                 if ( i == 0 ) {
989                         *nactions = nbv;
990
991                 } else {
992                         nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
993                                 nactions->bv_len + STRLENOF( "$" )
994                                 + nbv.bv_len + 1,
995                                 ctx );
996                         nactions->bv_val[ nactions->bv_len ] = '$';
997                         AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
998                                 nbv.bv_val, nbv.bv_len + 1 );
999                         ber_memfree_x( nbv.bv_val, ctx );
1000                         nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1001                 }
1002                 BER_BVZERO( &nbv );
1003         }
1004
1005         return LDAP_SUCCESS;
1006 }
1007
1008 static const struct berval *OpenLDAPaciscopes[] = {
1009         &aci_bv[ ACI_BV_ENTRY ],
1010         &aci_bv[ ACI_BV_CHILDREN ],
1011         &aci_bv[ ACI_BV_SUBTREE ],
1012
1013         NULL
1014 };
1015
1016 static const struct berval *OpenLDAPacitypes[] = {
1017         /* DN-valued */
1018         &aci_bv[ ACI_BV_GROUP ], 
1019         &aci_bv[ ACI_BV_ROLE ],
1020
1021 /* set to one past the last DN-valued type with options (/) */
1022 #define LAST_OPTIONAL   2
1023
1024         &aci_bv[ ACI_BV_ACCESS_ID ],
1025         &aci_bv[ ACI_BV_SUBTREE ],
1026         &aci_bv[ ACI_BV_ONELEVEL ],
1027         &aci_bv[ ACI_BV_CHILDREN ],
1028
1029 /* set to one past the last DN-valued type */
1030 #define LAST_DNVALUED   6
1031
1032         /* non DN-valued */
1033         &aci_bv[ ACI_BV_DNATTR ],
1034         &aci_bv[ ACI_BV_PUBLIC ],
1035         &aci_bv[ ACI_BV_USERS ],
1036         &aci_bv[ ACI_BV_SELF ],
1037         &aci_bv[ ACI_BV_SET ],
1038         &aci_bv[ ACI_BV_SET_REF ],
1039
1040         NULL
1041 };
1042
1043 int
1044 OpenLDAPaciValidate(
1045         Syntax          *syntax,
1046         struct berval   *val )
1047 {
1048         struct berval   oid = BER_BVNULL,
1049                         scope = BER_BVNULL,
1050                         rights = BER_BVNULL,
1051                         type = BER_BVNULL,
1052                         subject = BER_BVNULL;
1053         int             idx;
1054
1055         if ( BER_BVISEMPTY( val ) ) {
1056                 return LDAP_INVALID_SYNTAX;
1057         }
1058
1059         /* oid */
1060         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1061                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1062         {
1063                 /* NOTE: the numericoidValidate() is rather pedantic;
1064                  * I'd replace it with X-ORDERED VALUES so that
1065                  * it's guaranteed values are maintained and used
1066                  * in the desired order */
1067                 return LDAP_INVALID_SYNTAX;
1068         }
1069
1070         /* scope */
1071         if ( acl_get_part( val, 1, '#', &scope ) < 0 || 
1072                 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1073         {
1074                 return LDAP_INVALID_SYNTAX;
1075         }
1076
1077         /* rights */
1078         if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1079                 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) 
1080         {
1081                 return LDAP_INVALID_SYNTAX;
1082         }
1083
1084         /* type */
1085         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1086                 return LDAP_INVALID_SYNTAX;
1087         }
1088         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1089         if ( idx == -1 ) {
1090                 struct berval   isgr;
1091
1092                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1093                         return LDAP_INVALID_SYNTAX;
1094                 }
1095
1096                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1097                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1098                         return LDAP_INVALID_SYNTAX;
1099                 }
1100         }
1101
1102         /* subject */
1103         bv_get_tail( val, &type, &subject );
1104         if ( subject.bv_val[ 0 ] != '#' ) {
1105                 return LDAP_INVALID_SYNTAX;
1106         }
1107
1108         if ( idx >= LAST_DNVALUED ) {
1109                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1110                         AttributeDescription    *ad = NULL;
1111                         const char              *text = NULL;
1112                         int                     rc;
1113
1114                         rc = slap_bv2ad( &subject, &ad, &text );
1115                         if ( rc != LDAP_SUCCESS ) {
1116                                 return LDAP_INVALID_SYNTAX;
1117                         }
1118
1119                         if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1120                                 /* FIXME: allow nameAndOptionalUID? */
1121                                 return LDAP_INVALID_SYNTAX;
1122                         }
1123                 }
1124
1125                 /* not a DN */
1126                 return LDAP_SUCCESS;
1127
1128         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1129                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1130         {
1131                 /* do {group|role}/oc/at check */
1132                 struct berval   ocbv = BER_BVNULL,
1133                                 atbv = BER_BVNULL;
1134
1135                 ocbv.bv_val = strchr( type.bv_val, '/' );
1136                 if ( ocbv.bv_val != NULL ) {
1137                         ocbv.bv_val++;
1138
1139                         atbv.bv_val = strchr( ocbv.bv_val, '/' );
1140                         if ( atbv.bv_val != NULL ) {
1141                                 AttributeDescription    *ad = NULL;
1142                                 const char              *text = NULL;
1143                                 int                     rc;
1144
1145                                 atbv.bv_val++;
1146                                 atbv.bv_len = type.bv_len
1147                                         - ( atbv.bv_val - type.bv_val );
1148                                 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1149
1150                                 rc = slap_bv2ad( &atbv, &ad, &text );
1151                                 if ( rc != LDAP_SUCCESS ) {
1152                                         return LDAP_INVALID_SYNTAX;
1153                                 }
1154                                 
1155                         } else {
1156                                 ocbv.bv_len = type.bv_len
1157                                         - ( ocbv.bv_val - type.bv_val );
1158                         }
1159
1160                         if ( oc_bvfind( &ocbv ) == NULL ) {
1161                                 return LDAP_INVALID_SYNTAX;
1162                         }
1163                 }
1164         }
1165
1166         if ( BER_BVISEMPTY( &subject ) ) {
1167                 /* empty DN invalid */
1168                 return LDAP_INVALID_SYNTAX;
1169         }
1170
1171         subject.bv_val++;
1172         subject.bv_len--;
1173
1174         /* FIXME: pass DN syntax? */
1175         return dnValidate( NULL, &subject );
1176 }
1177
1178 static int
1179 OpenLDAPaciPrettyNormal(
1180         struct berval   *val,
1181         struct berval   *out,
1182         void            *ctx,
1183         int             normalize )
1184 {
1185         struct berval   oid = BER_BVNULL,
1186                         scope = BER_BVNULL,
1187                         rights = BER_BVNULL,
1188                         nrights = BER_BVNULL,
1189                         type = BER_BVNULL,
1190                         ntype = BER_BVNULL,
1191                         subject = BER_BVNULL,
1192                         nsubject = BER_BVNULL;
1193         int             idx,
1194                         rc,
1195                         freesubject = 0,
1196                         freetype = 0;
1197         char            *ptr;
1198
1199         if ( BER_BVISEMPTY( val ) ) {
1200                 return LDAP_INVALID_SYNTAX;
1201         }
1202
1203         /* oid: if valid, it's already normalized */
1204         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1205                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1206         {
1207                 return LDAP_INVALID_SYNTAX;
1208         }
1209
1210         /* scope: normalize by replacing with OpenLDAPaciscopes */
1211         if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1212                 return LDAP_INVALID_SYNTAX;
1213         }
1214         idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1215         if ( idx == -1 ) {
1216                 return LDAP_INVALID_SYNTAX;
1217         }
1218         scope = *OpenLDAPaciscopes[ idx ];
1219
1220         /* rights */
1221         if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1222                 return LDAP_INVALID_SYNTAX;
1223         }
1224         if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1225                 != LDAP_SUCCESS ) 
1226         {
1227                 return LDAP_INVALID_SYNTAX;
1228         }
1229
1230         /* type */
1231         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1232                 rc = LDAP_INVALID_SYNTAX;
1233                 goto cleanup;
1234         }
1235         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1236         if ( idx == -1 ) {
1237                 struct berval   isgr;
1238
1239                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1240                         rc = LDAP_INVALID_SYNTAX;
1241                         goto cleanup;
1242                 }
1243
1244                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1245                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1246                         rc = LDAP_INVALID_SYNTAX;
1247                         goto cleanup;
1248                 }
1249         }
1250         ntype = *OpenLDAPacitypes[ idx ];
1251
1252         /* subject */
1253         bv_get_tail( val, &type, &subject );
1254
1255         if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1256                 rc = LDAP_INVALID_SYNTAX;
1257                 goto cleanup;
1258         }
1259
1260         subject.bv_val++;
1261         subject.bv_len--;
1262
1263         if ( idx < LAST_DNVALUED ) {
1264                 /* FIXME: pass DN syntax? */
1265                 if ( normalize ) {
1266                         rc = dnNormalize( 0, NULL, NULL,
1267                                 &subject, &nsubject, ctx );
1268                 } else {
1269                         rc = dnPretty( NULL, &subject, &nsubject, ctx );
1270                 }
1271
1272                 if ( rc == LDAP_SUCCESS ) {
1273                         freesubject = 1;
1274
1275                 } else {
1276                         goto cleanup;
1277                 }
1278
1279                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1280                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1281                 {
1282                         /* do {group|role}/oc/at check */
1283                         struct berval   ocbv = BER_BVNULL,
1284                                         atbv = BER_BVNULL;
1285
1286                         ocbv.bv_val = strchr( type.bv_val, '/' );
1287                         if ( ocbv.bv_val != NULL ) {
1288                                 ObjectClass             *oc = NULL;
1289                                 AttributeDescription    *ad = NULL;
1290                                 const char              *text = NULL;
1291                                 int                     rc;
1292                                 struct berval           bv;
1293
1294                                 bv.bv_len = ntype.bv_len;
1295
1296                                 ocbv.bv_val++;
1297
1298                                 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1299                                 if ( atbv.bv_val != NULL ) {
1300                                         atbv.bv_val++;
1301                                         atbv.bv_len = type.bv_len
1302                                                 - ( atbv.bv_val - type.bv_val );
1303                                         ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1304         
1305                                         rc = slap_bv2ad( &atbv, &ad, &text );
1306                                         if ( rc != LDAP_SUCCESS ) {
1307                                                 rc = LDAP_INVALID_SYNTAX;
1308                                                 goto cleanup;
1309                                         }
1310
1311                                         bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1312                                         
1313                                 } else {
1314                                         ocbv.bv_len = type.bv_len
1315                                                 - ( ocbv.bv_val - type.bv_val );
1316                                 }
1317
1318                                 if ( oc_bvfind( &ocbv ) == NULL ) {
1319                                         rc = LDAP_INVALID_SYNTAX;
1320                                         goto cleanup;
1321                                 }
1322
1323                                 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1324                                 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1325
1326                                 ptr = bv.bv_val;
1327                                 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1328                                 ptr[ 0 ] = '/';
1329                                 ptr++;
1330                                 ptr = lutil_strncopy( ptr,
1331                                         oc->soc_cname.bv_val,
1332                                         oc->soc_cname.bv_len );
1333                                 if ( ad != NULL ) {
1334                                         ptr[ 0 ] = '/';
1335                                         ptr++;
1336                                         ptr = lutil_strncopy( ptr,
1337                                                 ad->ad_cname.bv_val,
1338                                                 ad->ad_cname.bv_len );
1339                                 }
1340                                 ptr[ 0 ] = '\0';
1341
1342                                 ntype = bv;
1343                                 freetype = 1;
1344                         }
1345                 }
1346
1347         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1348                 AttributeDescription    *ad = NULL;
1349                 const char              *text = NULL;
1350                 int                     rc;
1351
1352                 rc = slap_bv2ad( &subject, &ad, &text );
1353                 if ( rc != LDAP_SUCCESS ) {
1354                         rc = LDAP_INVALID_SYNTAX;
1355                         goto cleanup;
1356                 }
1357
1358                 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1359                         /* FIXME: allow nameAndOptionalUID? */
1360                         rc = LDAP_INVALID_SYNTAX;
1361                         goto cleanup;
1362                 }
1363
1364                 nsubject = ad->ad_cname;
1365         }
1366
1367
1368         out->bv_len = 
1369                 oid.bv_len + STRLENOF( "#" )
1370                 + scope.bv_len + STRLENOF( "#" )
1371                 + rights.bv_len + STRLENOF( "#" )
1372                 + ntype.bv_len + STRLENOF( "#" )
1373                 + nsubject.bv_len;
1374
1375         out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1376         ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1377         ptr[ 0 ] = '#';
1378         ptr++;
1379         ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1380         ptr[ 0 ] = '#';
1381         ptr++;
1382         ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1383         ptr[ 0 ] = '#';
1384         ptr++;
1385         ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1386         ptr[ 0 ] = '#';
1387         ptr++;
1388         if ( !BER_BVISNULL( &nsubject ) ) {
1389                 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1390         }
1391         ptr[ 0 ] = '\0';
1392
1393 cleanup:;
1394         if ( freesubject ) {
1395                 ber_memfree_x( nsubject.bv_val, ctx );
1396         }
1397
1398         if ( freetype ) {
1399                 ber_memfree_x( ntype.bv_val, ctx );
1400         }
1401
1402         if ( !BER_BVISNULL( &nrights ) ) {
1403                 ber_memfree_x( nrights.bv_val, ctx );
1404         }
1405
1406         return rc;
1407 }
1408
1409 int
1410 OpenLDAPaciPretty(
1411         Syntax          *syntax,
1412         struct berval   *val,
1413         struct berval   *out,
1414         void            *ctx )
1415 {
1416         return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1417 }
1418
1419 int
1420 OpenLDAPaciNormalize(
1421         slap_mask_t     use,
1422         Syntax          *syntax,
1423         MatchingRule    *mr,
1424         struct berval   *val,
1425         struct berval   *out,
1426         void            *ctx )
1427 {
1428         return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1429 }
1430
1431 #endif /* SLAPD_ACI_ENABLED */
1432