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