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