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