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