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