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