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