]> git.sur5r.net Git - openldap/blob - servers/slapd/aci.c
012d829fcff25b82a75b8ac329f8a81ce62f10ff
[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(
612         const char *fname,
613         int lineno,
614         const char *opts,
615         slap_style_t sty,
616         const char *right,
617         void **privp )
618 {
619         AttributeDescription    *ad = NULL;
620         const char              *text = NULL;
621
622         if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
623                 fprintf( stderr, "%s: line %d: "
624                         "inappropriate style \"%s\" in \"aci\" by clause\n",
625                         fname, lineno, style_strings[sty] );
626                 return -1;
627         }
628
629         if ( right != NULL && *right != '\0' ) {
630                 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
631                         fprintf( stderr,
632                                 "%s: line %d: aci \"%s\": %s\n",
633                                 fname, lineno, right, text );
634                         return -1;
635                 }
636
637         } else {
638                 ad = slap_ad_aci;
639         }
640
641         if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
642                 fprintf( stderr, "%s: line %d: "
643                         "aci \"%s\": inappropriate syntax: %s\n",
644                         fname, lineno, right,
645                         ad->ad_type->sat_syntax_oid );
646                 return -1;
647         }
648
649         *privp = (void *)ad;
650
651         return 0;
652 }
653
654 static int
655 dynacl_aci_unparse( void *priv, struct berval *bv )
656 {
657         AttributeDescription    *ad = ( AttributeDescription * )priv;
658         char                    *ptr;
659
660         assert( ad != NULL );
661
662         bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
663         ptr = lutil_strcopy( bv->bv_val, " aci=" );
664         ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
665         bv->bv_len = ptr - bv->bv_val;
666
667         return 0;
668 }
669
670 static int
671 dynacl_aci_mask(
672         void                    *priv,
673         Operation               *op,
674         Entry                   *e,
675         AttributeDescription    *desc,
676         struct berval           *val,
677         int                     nmatch,
678         regmatch_t              *matches,
679         slap_access_t           *grantp,
680         slap_access_t           *denyp )
681 {
682         AttributeDescription    *ad = ( AttributeDescription * )priv;
683         Attribute               *at;
684         slap_access_t           tgrant, tdeny, grant, deny;
685 #ifdef LDAP_DEBUG
686         char                    accessmaskbuf[ACCESSMASK_MAXLEN];
687         char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
688 #endif /* LDAP_DEBUG */
689
690         /* start out with nothing granted, nothing denied */
691         ACL_INIT(tgrant);
692         ACL_INIT(tdeny);
693
694         /* get the aci attribute */
695         at = attr_find( e->e_attrs, ad );
696         if ( at != NULL ) {
697                 int             i;
698
699                 /* the aci is an multi-valued attribute.  The
700                  * rights are determined by OR'ing the individual
701                  * rights given by the acis.
702                  */
703                 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
704                         if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
705                                         nmatch, matches, &grant, &deny,
706                                         SLAP_ACI_SCOPE_ENTRY ) != 0 )
707                         {
708                                 tgrant |= grant;
709                                 tdeny |= deny;
710                         }
711                 }
712                 
713                 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
714                           accessmask2str( tgrant, accessmaskbuf, 1 ), 
715                           accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
716         }
717
718         /* If the entry level aci didn't contain anything valid for the 
719          * current operation, climb up the tree and evaluate the
720          * acis with scope set to subtree
721          */
722         if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
723                 struct berval   parent_ndn;
724
725 #if 1
726                 /* to solve the chicken'n'egg problem of accessing
727                  * the OpenLDAPaci attribute, the direct access
728                  * to the entry's attribute is unchecked; however,
729                  * further accesses to OpenLDAPaci values in the 
730                  * ancestors occur through backend_attribute(), i.e.
731                  * with the identity of the operation, requiring
732                  * further access checking.  For uniformity, this
733                  * makes further requests occur as the rootdn, if
734                  * any, i.e. searching for the OpenLDAPaci attribute
735                  * is considered an internal search.  If this is not
736                  * acceptable, then the same check needs be performed
737                  * when accessing the entry's attribute. */
738                 Operation       op2 = *op;
739
740                 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
741                         op2.o_dn = op->o_bd->be_rootdn;
742                         op2.o_ndn = op->o_bd->be_rootndn;
743                 }
744 #endif
745
746                 dnParent( &e->e_nname, &parent_ndn );
747                 while ( !BER_BVISEMPTY( &parent_ndn ) ){
748                         int             i;
749                         BerVarray       bvals = NULL;
750                         int             ret, stop;
751
752                         Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
753                         ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
754
755                         switch ( ret ) {
756                         case LDAP_SUCCESS :
757                                 stop = 0;
758                                 if ( !bvals ) {
759                                         break;
760                                 }
761
762                                 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
763                                         if ( aci_mask( op, e, desc, val,
764                                                         &bvals[i],
765                                                         nmatch, matches,
766                                                         &grant, &deny,
767                                                         SLAP_ACI_SCOPE_CHILDREN ) != 0 )
768                                         {
769                                                 tgrant |= grant;
770                                                 tdeny |= deny;
771                                                 /* evaluation stops as soon as either a "deny" or a 
772                                                  * "grant" directive matches.
773                                                  */
774                                                 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
775                                                         stop = 1;
776                                                 }
777                                         }
778                                         Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
779                                                 accessmask2str( tgrant, accessmaskbuf, 1 ),
780                                                 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
781                                 }
782                                 break;
783
784                         case LDAP_NO_SUCH_ATTRIBUTE:
785                                 /* just go on if the aci-Attribute is not present in
786                                  * the current entry 
787                                  */
788                                 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
789                                 stop = 0;
790                                 break;
791
792                         case LDAP_NO_SUCH_OBJECT:
793                                 /* We have reached the base object */
794                                 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
795                                 stop = 1;
796                                 break;
797
798                         default:
799                                 stop = 1;
800                                 break;
801                         }
802
803                         if ( stop ) {
804                                 break;
805                         }
806                         dnParent( &parent_ndn, &parent_ndn );
807                 }
808         }
809
810         *grantp = tgrant;
811         *denyp = tdeny;
812
813         return 0;
814 }
815
816 /* need to register this at some point */
817 static slap_dynacl_t    dynacl_aci = {
818         "aci",
819         dynacl_aci_parse,
820         dynacl_aci_unparse,
821         dynacl_aci_mask,
822         NULL,
823         NULL,
824         NULL
825 };
826
827 int
828 dynacl_aci_init( void )
829 {
830         int     rc;
831
832         rc = aci_init();
833
834         if ( rc == 0 ) {
835                 rc = slap_dynacl_register( &dynacl_aci );
836         }
837         
838         return rc;
839 }
840
841 #endif /* SLAP_DYNACL */
842
843 /* ACI syntax validation */
844
845 /*
846  * Matches given berval to array of bervals
847  * Returns:
848  *      >=0 if one if the array elements equals to this berval
849  *       -1 if string was not found in array
850  */
851 static int 
852 bv_getcaseidx(
853         struct berval *bv, 
854         const struct berval *arr[] )
855 {
856         int i;
857
858         if ( BER_BVISEMPTY( bv ) ) {
859                 return -1;
860         }
861
862         for ( i = 0; arr[ i ] != NULL ; i++ ) {
863                 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
864                         return i;
865                 }
866         }
867
868         return -1;
869 }
870
871
872 /* Returns what have left in input berval after current sub */
873 static void
874 bv_get_tail(
875         struct berval *val,
876         struct berval *sub,
877         struct berval *tail )
878 {
879         int             head_len;
880
881         tail->bv_val = sub->bv_val + sub->bv_len;
882         head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
883         tail->bv_len = val->bv_len - head_len;
884 }
885
886
887 /*
888  * aci is accepted in following form:
889  *    oid#scope#rights#type#subject
890  * Where:
891  *    oid       := numeric OID
892  *    scope     := entry|children
893  *    rights    := right[[$right]...]
894  *    right     := (grant|deny);action
895  *    action    := perms;attr[[;perms;attr]...]
896  *    perms     := perm[[,perm]...]
897  *    perm      := c|s|r|w|x
898  *    attr      := attributeType|[all]
899  *    type      :=  public|users|self|dnattr|group|role|set|set-ref|
900  *                  access_id|subtree|onelevel|children
901  */
902 static int 
903 OpenLDAPaciValidatePerms(
904         struct berval *perms ) 
905 {
906         int             i;
907
908         for ( i = 0; i < perms->bv_len; ) {
909                 switch ( perms->bv_val[ i ] ) {
910                 case 'c':
911                 case 's':
912                 case 'r':
913                 case 'w':
914                 case 'x':
915                         break;
916
917                 default:
918                         return LDAP_INVALID_SYNTAX;
919                 }
920
921                 if ( ++i == perms->bv_len ) {
922                         return LDAP_SUCCESS;
923                 }
924
925                 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
926                         i++;
927
928                 assert( i != perms->bv_len );
929
930                 if ( perms->bv_val[ i ] != ',' ) {
931                         return LDAP_INVALID_SYNTAX;
932                 }
933
934                 do {
935                         i++;
936                 } while ( perms->bv_val[ i ] == ' ' );
937         }
938
939         return LDAP_SUCCESS;
940 }
941
942 static const struct berval *ACIgrantdeny[] = {
943         &aci_bv[ ACI_BV_GRANT ],
944         &aci_bv[ ACI_BV_DENY ],
945         NULL
946 };
947
948 static int 
949 OpenLDAPaciValidateRight(
950         struct berval *action )
951 {
952         struct berval   bv = BER_BVNULL;
953         int             i;
954
955         /* grant|deny */
956         if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
957                 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
958         {
959                 return LDAP_INVALID_SYNTAX;
960         }
961
962         for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
963                 if ( i & 1 ) {
964                         /* perms */
965                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
966                         {
967                                 return LDAP_INVALID_SYNTAX;
968                         }
969
970                 } else {
971                         /* attr */
972                         AttributeDescription    *ad = NULL;
973                         const char              *text = NULL;
974
975                         /* could be "[all]" or an attribute description */
976                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
977                                 continue;
978                         }
979
980                         if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
981                                 return LDAP_INVALID_SYNTAX;
982                         }
983                 }
984         }
985         
986         /* "perms;attr" go in pairs */
987         if ( i > 0 && ( i & 1 ) == 0 ) {
988                 return LDAP_SUCCESS;
989
990         } else {
991                 return LDAP_INVALID_SYNTAX;
992         }
993
994         return LDAP_SUCCESS;
995 }
996
997 static int
998 OpenLDAPaciNormalizeRight(
999         struct berval   *action,
1000         struct berval   *naction,
1001         void            *ctx )
1002 {
1003         struct berval   grantdeny,
1004                         perms = BER_BVNULL,
1005                         bv = BER_BVNULL;
1006         int             idx,
1007                         i;
1008
1009         /* grant|deny */
1010         if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1011                 return LDAP_INVALID_SYNTAX;
1012         }
1013         idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1014         if ( idx == -1 ) {
1015                 return LDAP_INVALID_SYNTAX;
1016         }
1017
1018         ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1019
1020         for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1021                 if ( i & 1 ) {
1022                         /* perms */
1023                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1024                         {
1025                                 return LDAP_INVALID_SYNTAX;
1026                         }
1027                         perms = bv;
1028
1029                 } else {
1030                         /* attr */
1031                         char            *ptr;
1032
1033                         /* could be "[all]" or an attribute description */
1034                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1035                                 bv = aci_bv[ ACI_BV_BR_ALL ];
1036
1037                         } else {
1038                                 AttributeDescription    *ad = NULL;
1039                                 const char              *text = NULL;
1040                                 int                     rc;
1041
1042                                 rc = slap_bv2ad( &bv, &ad, &text );
1043                                 if ( rc != LDAP_SUCCESS ) {
1044                                         return LDAP_INVALID_SYNTAX;
1045                                 }
1046
1047                                 bv = ad->ad_cname;
1048                         }
1049
1050                         naction->bv_val = ber_memrealloc_x( naction->bv_val,
1051                                 naction->bv_len + STRLENOF( ";" )
1052                                 + perms.bv_len + STRLENOF( ";" )
1053                                 + bv.bv_len + 1,
1054                                 ctx );
1055
1056                         ptr = &naction->bv_val[ naction->bv_len ];
1057                         ptr[ 0 ] = ';';
1058                         ptr++;
1059                         ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1060                         ptr[ 0 ] = ';';
1061                         ptr++;
1062                         ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1063                         ptr[ 0 ] = '\0';
1064                         naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1065                                 + STRLENOF( ";" ) + bv.bv_len;
1066                 }
1067         }
1068         
1069         /* perms;attr go in pairs */
1070         if ( i > 1 && ( i & 1 ) ) {
1071                 return LDAP_SUCCESS;
1072
1073         } else {
1074                 return LDAP_INVALID_SYNTAX;
1075         }
1076 }
1077
1078 static int 
1079 OpenLDAPaciValidateRights(
1080         struct berval *actions )
1081
1082 {
1083         struct berval   bv = BER_BVNULL;
1084         int             i;
1085
1086         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1087                 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1088                         return LDAP_INVALID_SYNTAX;
1089                 }
1090         }
1091
1092         return LDAP_SUCCESS;
1093 }
1094
1095 static int 
1096 OpenLDAPaciNormalizeRights(
1097         struct berval   *actions,
1098         struct berval   *nactions,
1099         void            *ctx )
1100
1101 {
1102         struct berval   bv = BER_BVNULL;
1103         int             i;
1104
1105         BER_BVZERO( nactions );
1106         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1107                 int             rc;
1108                 struct berval   nbv;
1109
1110                 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1111                 if ( rc != LDAP_SUCCESS ) {
1112                         ber_memfree_x( nactions->bv_val, ctx );
1113                         BER_BVZERO( nactions );
1114                         return LDAP_INVALID_SYNTAX;
1115                 }
1116
1117                 if ( i == 0 ) {
1118                         *nactions = nbv;
1119
1120                 } else {
1121                         nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1122                                 nactions->bv_len + STRLENOF( "$" )
1123                                 + nbv.bv_len + 1,
1124                                 ctx );
1125                         nactions->bv_val[ nactions->bv_len ] = '$';
1126                         AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1127                                 nbv.bv_val, nbv.bv_len + 1 );
1128                         ber_memfree_x( nbv.bv_val, ctx );
1129                         nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1130                 }
1131                 BER_BVZERO( &nbv );
1132         }
1133
1134         return LDAP_SUCCESS;
1135 }
1136
1137 static const struct berval *OpenLDAPaciscopes[] = {
1138         &aci_bv[ ACI_BV_ENTRY ],
1139         &aci_bv[ ACI_BV_CHILDREN ],
1140         &aci_bv[ ACI_BV_SUBTREE ],
1141
1142         NULL
1143 };
1144
1145 static const struct berval *OpenLDAPacitypes[] = {
1146         /* DN-valued */
1147         &aci_bv[ ACI_BV_GROUP ], 
1148         &aci_bv[ ACI_BV_ROLE ],
1149
1150 /* set to one past the last DN-valued type with options (/) */
1151 #define LAST_OPTIONAL   2
1152
1153         &aci_bv[ ACI_BV_ACCESS_ID ],
1154         &aci_bv[ ACI_BV_SUBTREE ],
1155         &aci_bv[ ACI_BV_ONELEVEL ],
1156         &aci_bv[ ACI_BV_CHILDREN ],
1157
1158 /* set to one past the last DN-valued type */
1159 #define LAST_DNVALUED   6
1160
1161         /* non DN-valued */
1162         &aci_bv[ ACI_BV_DNATTR ],
1163         &aci_bv[ ACI_BV_PUBLIC ],
1164         &aci_bv[ ACI_BV_USERS ],
1165         &aci_bv[ ACI_BV_SELF ],
1166         &aci_bv[ ACI_BV_SET ],
1167         &aci_bv[ ACI_BV_SET_REF ],
1168
1169         NULL
1170 };
1171
1172 static int
1173 OpenLDAPaciValidate(
1174         Syntax          *syntax,
1175         struct berval   *val )
1176 {
1177         struct berval   oid = BER_BVNULL,
1178                         scope = BER_BVNULL,
1179                         rights = BER_BVNULL,
1180                         type = BER_BVNULL,
1181                         subject = BER_BVNULL;
1182         int             idx;
1183
1184         if ( BER_BVISEMPTY( val ) ) {
1185                 return LDAP_INVALID_SYNTAX;
1186         }
1187
1188         /* oid */
1189         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1190                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1191         {
1192                 /* NOTE: the numericoidValidate() is rather pedantic;
1193                  * I'd replace it with X-ORDERED VALUES so that
1194                  * it's guaranteed values are maintained and used
1195                  * in the desired order */
1196                 return LDAP_INVALID_SYNTAX;
1197         }
1198
1199         /* scope */
1200         if ( acl_get_part( val, 1, '#', &scope ) < 0 || 
1201                 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1202         {
1203                 return LDAP_INVALID_SYNTAX;
1204         }
1205
1206         /* rights */
1207         if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1208                 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) 
1209         {
1210                 return LDAP_INVALID_SYNTAX;
1211         }
1212
1213         /* type */
1214         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1215                 return LDAP_INVALID_SYNTAX;
1216         }
1217         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1218         if ( idx == -1 ) {
1219                 struct berval   isgr;
1220
1221                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1222                         return LDAP_INVALID_SYNTAX;
1223                 }
1224
1225                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1226                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1227                         return LDAP_INVALID_SYNTAX;
1228                 }
1229         }
1230
1231         /* subject */
1232         bv_get_tail( val, &type, &subject );
1233         if ( subject.bv_val[ 0 ] != '#' ) {
1234                 return LDAP_INVALID_SYNTAX;
1235         }
1236
1237         if ( idx >= LAST_DNVALUED ) {
1238                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1239                         AttributeDescription    *ad = NULL;
1240                         const char              *text = NULL;
1241                         int                     rc;
1242
1243                         rc = slap_bv2ad( &subject, &ad, &text );
1244                         if ( rc != LDAP_SUCCESS ) {
1245                                 return LDAP_INVALID_SYNTAX;
1246                         }
1247
1248                         if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1249                                 /* FIXME: allow nameAndOptionalUID? */
1250                                 return LDAP_INVALID_SYNTAX;
1251                         }
1252                 }
1253
1254                 /* not a DN */
1255                 return LDAP_SUCCESS;
1256
1257         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1258                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1259         {
1260                 /* do {group|role}/oc/at check */
1261                 struct berval   ocbv = BER_BVNULL,
1262                                 atbv = BER_BVNULL;
1263
1264                 ocbv.bv_val = strchr( type.bv_val, '/' );
1265                 if ( ocbv.bv_val != NULL ) {
1266                         ocbv.bv_val++;
1267
1268                         atbv.bv_val = strchr( ocbv.bv_val, '/' );
1269                         if ( atbv.bv_val != NULL ) {
1270                                 AttributeDescription    *ad = NULL;
1271                                 const char              *text = NULL;
1272                                 int                     rc;
1273
1274                                 atbv.bv_val++;
1275                                 atbv.bv_len = type.bv_len
1276                                         - ( atbv.bv_val - type.bv_val );
1277                                 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1278
1279                                 rc = slap_bv2ad( &atbv, &ad, &text );
1280                                 if ( rc != LDAP_SUCCESS ) {
1281                                         return LDAP_INVALID_SYNTAX;
1282                                 }
1283                                 
1284                         } else {
1285                                 ocbv.bv_len = type.bv_len
1286                                         - ( ocbv.bv_val - type.bv_val );
1287                         }
1288
1289                         if ( oc_bvfind( &ocbv ) == NULL ) {
1290                                 return LDAP_INVALID_SYNTAX;
1291                         }
1292                 }
1293         }
1294
1295         if ( BER_BVISEMPTY( &subject ) ) {
1296                 /* empty DN invalid */
1297                 return LDAP_INVALID_SYNTAX;
1298         }
1299
1300         subject.bv_val++;
1301         subject.bv_len--;
1302
1303         /* FIXME: pass DN syntax? */
1304         return dnValidate( NULL, &subject );
1305 }
1306
1307 static int
1308 OpenLDAPaciPrettyNormal(
1309         struct berval   *val,
1310         struct berval   *out,
1311         void            *ctx,
1312         int             normalize )
1313 {
1314         struct berval   oid = BER_BVNULL,
1315                         scope = BER_BVNULL,
1316                         rights = BER_BVNULL,
1317                         nrights = BER_BVNULL,
1318                         type = BER_BVNULL,
1319                         ntype = BER_BVNULL,
1320                         subject = BER_BVNULL,
1321                         nsubject = BER_BVNULL;
1322         int             idx,
1323                         rc,
1324                         freesubject = 0,
1325                         freetype = 0;
1326         char            *ptr;
1327
1328         if ( BER_BVISEMPTY( val ) ) {
1329                 return LDAP_INVALID_SYNTAX;
1330         }
1331
1332         /* oid: if valid, it's already normalized */
1333         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1334                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1335         {
1336                 return LDAP_INVALID_SYNTAX;
1337         }
1338
1339         /* scope: normalize by replacing with OpenLDAPaciscopes */
1340         if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1341                 return LDAP_INVALID_SYNTAX;
1342         }
1343         idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1344         if ( idx == -1 ) {
1345                 return LDAP_INVALID_SYNTAX;
1346         }
1347         scope = *OpenLDAPaciscopes[ idx ];
1348
1349         /* rights */
1350         if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1351                 return LDAP_INVALID_SYNTAX;
1352         }
1353         if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1354                 != LDAP_SUCCESS ) 
1355         {
1356                 return LDAP_INVALID_SYNTAX;
1357         }
1358
1359         /* type */
1360         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1361                 rc = LDAP_INVALID_SYNTAX;
1362                 goto cleanup;
1363         }
1364         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1365         if ( idx == -1 ) {
1366                 struct berval   isgr;
1367
1368                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1369                         rc = LDAP_INVALID_SYNTAX;
1370                         goto cleanup;
1371                 }
1372
1373                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1374                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1375                         rc = LDAP_INVALID_SYNTAX;
1376                         goto cleanup;
1377                 }
1378         }
1379         ntype = *OpenLDAPacitypes[ idx ];
1380
1381         /* subject */
1382         bv_get_tail( val, &type, &subject );
1383
1384         if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1385                 rc = LDAP_INVALID_SYNTAX;
1386                 goto cleanup;
1387         }
1388
1389         subject.bv_val++;
1390         subject.bv_len--;
1391
1392         if ( idx < LAST_DNVALUED ) {
1393                 /* FIXME: pass DN syntax? */
1394                 if ( normalize ) {
1395                         rc = dnNormalize( 0, NULL, NULL,
1396                                 &subject, &nsubject, ctx );
1397                 } else {
1398                         rc = dnPretty( NULL, &subject, &nsubject, ctx );
1399                 }
1400
1401                 if ( rc == LDAP_SUCCESS ) {
1402                         freesubject = 1;
1403
1404                 } else {
1405                         goto cleanup;
1406                 }
1407
1408                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1409                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1410                 {
1411                         /* do {group|role}/oc/at check */
1412                         struct berval   ocbv = BER_BVNULL,
1413                                         atbv = BER_BVNULL;
1414
1415                         ocbv.bv_val = strchr( type.bv_val, '/' );
1416                         if ( ocbv.bv_val != NULL ) {
1417                                 ObjectClass             *oc = NULL;
1418                                 AttributeDescription    *ad = NULL;
1419                                 const char              *text = NULL;
1420                                 int                     rc;
1421                                 struct berval           bv;
1422
1423                                 bv.bv_len = ntype.bv_len;
1424
1425                                 ocbv.bv_val++;
1426
1427                                 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1428                                 if ( atbv.bv_val != NULL ) {
1429                                         atbv.bv_val++;
1430                                         atbv.bv_len = type.bv_len
1431                                                 - ( atbv.bv_val - type.bv_val );
1432                                         ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1433         
1434                                         rc = slap_bv2ad( &atbv, &ad, &text );
1435                                         if ( rc != LDAP_SUCCESS ) {
1436                                                 rc = LDAP_INVALID_SYNTAX;
1437                                                 goto cleanup;
1438                                         }
1439
1440                                         bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1441                                         
1442                                 } else {
1443                                         ocbv.bv_len = type.bv_len
1444                                                 - ( ocbv.bv_val - type.bv_val );
1445                                 }
1446
1447                                 if ( oc_bvfind( &ocbv ) == NULL ) {
1448                                         rc = LDAP_INVALID_SYNTAX;
1449                                         goto cleanup;
1450                                 }
1451
1452                                 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1453                                 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1454
1455                                 ptr = bv.bv_val;
1456                                 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1457                                 ptr[ 0 ] = '/';
1458                                 ptr++;
1459                                 ptr = lutil_strncopy( ptr,
1460                                         oc->soc_cname.bv_val,
1461                                         oc->soc_cname.bv_len );
1462                                 if ( ad != NULL ) {
1463                                         ptr[ 0 ] = '/';
1464                                         ptr++;
1465                                         ptr = lutil_strncopy( ptr,
1466                                                 ad->ad_cname.bv_val,
1467                                                 ad->ad_cname.bv_len );
1468                                 }
1469                                 ptr[ 0 ] = '\0';
1470
1471                                 ntype = bv;
1472                                 freetype = 1;
1473                         }
1474                 }
1475
1476         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1477                 AttributeDescription    *ad = NULL;
1478                 const char              *text = NULL;
1479                 int                     rc;
1480
1481                 rc = slap_bv2ad( &subject, &ad, &text );
1482                 if ( rc != LDAP_SUCCESS ) {
1483                         rc = LDAP_INVALID_SYNTAX;
1484                         goto cleanup;
1485                 }
1486
1487                 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1488                         /* FIXME: allow nameAndOptionalUID? */
1489                         rc = LDAP_INVALID_SYNTAX;
1490                         goto cleanup;
1491                 }
1492
1493                 nsubject = ad->ad_cname;
1494         }
1495
1496
1497         out->bv_len = 
1498                 oid.bv_len + STRLENOF( "#" )
1499                 + scope.bv_len + STRLENOF( "#" )
1500                 + rights.bv_len + STRLENOF( "#" )
1501                 + ntype.bv_len + STRLENOF( "#" )
1502                 + nsubject.bv_len;
1503
1504         out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1505         ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1506         ptr[ 0 ] = '#';
1507         ptr++;
1508         ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1509         ptr[ 0 ] = '#';
1510         ptr++;
1511         ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1512         ptr[ 0 ] = '#';
1513         ptr++;
1514         ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1515         ptr[ 0 ] = '#';
1516         ptr++;
1517         if ( !BER_BVISNULL( &nsubject ) ) {
1518                 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1519         }
1520         ptr[ 0 ] = '\0';
1521
1522 cleanup:;
1523         if ( freesubject ) {
1524                 ber_memfree_x( nsubject.bv_val, ctx );
1525         }
1526
1527         if ( freetype ) {
1528                 ber_memfree_x( ntype.bv_val, ctx );
1529         }
1530
1531         if ( !BER_BVISNULL( &nrights ) ) {
1532                 ber_memfree_x( nrights.bv_val, ctx );
1533         }
1534
1535         return rc;
1536 }
1537
1538 static int
1539 OpenLDAPaciPretty(
1540         Syntax          *syntax,
1541         struct berval   *val,
1542         struct berval   *out,
1543         void            *ctx )
1544 {
1545         return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1546 }
1547
1548 static int
1549 OpenLDAPaciNormalize(
1550         slap_mask_t     use,
1551         Syntax          *syntax,
1552         MatchingRule    *mr,
1553         struct berval   *val,
1554         struct berval   *out,
1555         void            *ctx )
1556 {
1557         return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1558 }
1559
1560 #endif /* SLAPD_ACI_ENABLED */
1561