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