]> git.sur5r.net Git - openldap/blob - servers/slapd/aci.c
0311558ac736b53ddfcb9f79dec588d3ca3dc590
[openldap] / servers / slapd / aci.c
1 /* aci.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2006 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26
27 #include "portable.h"
28
29 #ifdef SLAPD_ACI_ENABLED
30
31 #include <stdio.h>
32
33 #include <ac/ctype.h>
34 #include <ac/regex.h>
35 #include <ac/socket.h>
36 #include <ac/string.h>
37 #include <ac/unistd.h>
38
39 #include "slap.h"
40 #include "lber_pvt.h"
41 #include "lutil.h"
42
43 /* use most appropriate size */
44 #define ACI_BUF_SIZE                    1024
45
46 /* move to "stable" when no longer experimental */
47 #define SLAPD_ACI_SYNTAX                "1.3.6.1.4.1.4203.666.2.1"
48
49 /* change this to "OpenLDAPset" */
50 #define SLAPD_ACI_SET_ATTR              "template"
51
52 enum {
53         ACI_BV_ENTRY,
54         ACI_BV_CHILDREN,
55         ACI_BV_ONELEVEL,
56         ACI_BV_SUBTREE,
57
58         ACI_BV_BR_ENTRY,
59         ACI_BV_BR_ALL,
60
61         ACI_BV_ACCESS_ID,
62         ACI_BV_PUBLIC,
63         ACI_BV_USERS,
64         ACI_BV_SELF,
65         ACI_BV_DNATTR,
66         ACI_BV_GROUP,
67         ACI_BV_ROLE,
68         ACI_BV_SET,
69         ACI_BV_SET_REF,
70
71         ACI_BV_GRANT,
72         ACI_BV_DENY,
73
74         ACI_BV_GROUP_CLASS,
75         ACI_BV_GROUP_ATTR,
76         ACI_BV_ROLE_CLASS,
77         ACI_BV_ROLE_ATTR,
78
79         ACI_BV_SET_ATTR,
80
81         ACI_BV_LAST
82 };
83
84 static const struct berval      aci_bv[] = {
85         /* scope */
86         BER_BVC("entry"),
87         BER_BVC("children"),
88         BER_BVC("onelevel"),
89         BER_BVC("subtree"),
90
91         /* */
92         BER_BVC("[entry]"),
93         BER_BVC("[all]"),
94
95         /* type */
96         BER_BVC("access-id"),
97         BER_BVC("public"),
98         BER_BVC("users"),
99         BER_BVC("self"),
100         BER_BVC("dnattr"),
101         BER_BVC("group"),
102         BER_BVC("role"),
103         BER_BVC("set"),
104         BER_BVC("set-ref"),
105
106         /* actions */
107         BER_BVC("grant"),
108         BER_BVC("deny"),
109
110         /* schema */
111         BER_BVC(SLAPD_GROUP_CLASS),
112         BER_BVC(SLAPD_GROUP_ATTR),
113         BER_BVC(SLAPD_ROLE_CLASS),
114         BER_BVC(SLAPD_ROLE_ATTR),
115
116         BER_BVC(SLAPD_ACI_SET_ATTR),
117
118         BER_BVNULL
119 };
120
121 static AttributeDescription     *slap_ad_aci;
122
123 static int
124 OpenLDAPaciValidate(
125         Syntax          *syntax,
126         struct berval   *val );
127
128 static int
129 OpenLDAPaciPretty(
130         Syntax          *syntax,
131         struct berval   *val,
132         struct berval   *out,
133         void            *ctx );
134
135 static int
136 OpenLDAPaciNormalize(
137         slap_mask_t     use,
138         Syntax          *syntax,
139         MatchingRule    *mr,
140         struct berval   *val,
141         struct berval   *out,
142         void            *ctx );
143
144 #define OpenLDAPaciMatch                        octetStringMatch
145
146 static int
147 aci_list_map_rights(
148         struct berval   *list )
149 {
150         struct berval   bv;
151         slap_access_t   mask;
152         int             i;
153
154         ACL_INIT( mask );
155         for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
156                 if ( bv.bv_len <= 0 ) {
157                         continue;
158                 }
159
160                 switch ( *bv.bv_val ) {
161                 case 'x':
162                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
163                          * define any equivalent to the AUTH right, so I've just used
164                          * 'x' for now.
165                          */
166                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
167                         break;
168                 case 'd':
169                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
170                          * the right 'd' to mean "delete"; we hijack it to mean
171                          * "disclose" for consistency wuith the rest of slapd.
172                          */
173                         ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
174                         break;
175                 case 'c':
176                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
177                         break;
178                 case 's':
179                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
180                          * the right 's' to mean "set", but in the examples states
181                          * that the right 's' means "search".  The latter definition
182                          * is used here.
183                          */
184                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
185                         break;
186                 case 'r':
187                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
188                         break;
189                 case 'w':
190                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
191                         break;
192                 default:
193                         break;
194                 }
195
196         }
197
198         return mask;
199 }
200
201 static int
202 aci_list_has_attr(
203         struct berval           *list,
204         const struct berval     *attr,
205         struct berval           *val )
206 {
207         struct berval   bv, left, right;
208         int             i;
209
210         for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
211                 if ( acl_get_part(&bv, 0, '=', &left ) < 0
212                         || acl_get_part( &bv, 1, '=', &right ) < 0 )
213                 {
214                         if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
215                                 return(1);
216                         }
217
218                 } else if ( val == NULL ) {
219                         if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
220                                 return(1);
221                         }
222
223                 } else {
224                         if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
225                                 /* FIXME: this is also totally undocumented! */
226                                 /* this is experimental code that implements a
227                                  * simple (prefix) match of the attribute value.
228                                  * the ACI draft does not provide for aci's that
229                                  * apply to specific values, but it would be
230                                  * nice to have.  If the <attr> part of an aci's
231                                  * rights list is of the form <attr>=<value>,
232                                  * that means the aci applies only to attrs with
233                                  * the given value.  Furthermore, if the attr is
234                                  * of the form <attr>=<value>*, then <value> is
235                                  * treated as a prefix, and the aci applies to 
236                                  * any value with that prefix.
237                                  *
238                                  * Ideally, this would allow r.e. matches.
239                                  */
240                                 if ( acl_get_part( &right, 0, '*', &left ) < 0
241                                         || right.bv_len <= left.bv_len )
242                                 {
243                                         if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
244                                                 return 1;
245                                         }
246
247                                 } else if ( val->bv_len >= left.bv_len ) {
248                                         if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
249                                                 return(1);
250                                         }
251                                 }
252                         }
253                 }
254         }
255
256         return 0;
257 }
258
259 static slap_access_t
260 aci_list_get_attr_rights(
261         struct berval           *list,
262         const struct berval     *attr,
263         struct berval           *val )
264 {
265         struct berval   bv;
266         slap_access_t   mask;
267         int             i;
268
269         /* loop through each rights/attr pair, skip first part (action) */
270         ACL_INIT(mask);
271         for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
272                 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
273                         continue;
274                 }
275
276                 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
277                         continue;
278                 }
279
280                 mask |= aci_list_map_rights( &bv );
281         }
282
283         return mask;
284 }
285
286 static int
287 aci_list_get_rights(
288         struct berval           *list,
289         const struct berval     *attr,
290         struct berval           *val,
291         slap_access_t           *grant,
292         slap_access_t           *deny )
293 {
294         struct berval   perm, actn;
295         slap_access_t   *mask;
296         int             i, found;
297
298         if ( attr == NULL || BER_BVISEMPTY( attr )
299                         || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
300         {
301                 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
302         }
303
304         found = 0;
305         ACL_INIT(*grant);
306         ACL_INIT(*deny);
307         /* loop through each permissions clause */
308         for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
309                 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
310                         continue;
311                 }
312
313                 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
314                         mask = grant;
315
316                 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
317                         mask = deny;
318
319                 } else {
320                         continue;
321                 }
322
323                 found = 1;
324                 *mask |= aci_list_get_attr_rights( &perm, attr, val );
325                 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
326         }
327
328         return found;
329 }
330
331 static int
332 aci_group_member (
333         struct berval           *subj,
334         const struct berval     *defgrpoc,
335         const struct berval     *defgrpat,
336         Operation               *op,
337         Entry                   *e,
338         int                     nmatch,
339         regmatch_t              *matches
340 )
341 {
342         struct berval           subjdn;
343         struct berval           grpoc;
344         struct berval           grpat;
345         ObjectClass             *grp_oc = NULL;
346         AttributeDescription    *grp_ad = NULL;
347         const char              *text;
348         int                     rc;
349
350         /* format of string is "{group|role}/objectClassValue/groupAttrName" */
351         if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
352                 return 0;
353         }
354
355         if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
356                 grpoc = *defgrpoc;
357         }
358
359         if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
360                 grpat = *defgrpat;
361         }
362
363         rc = slap_bv2ad( &grpat, &grp_ad, &text );
364         if ( rc != LDAP_SUCCESS ) {
365                 rc = 0;
366                 goto done;
367         }
368         rc = 0;
369
370         grp_oc = oc_bvfind( &grpoc );
371
372         if ( grp_oc != NULL && grp_ad != NULL ) {
373                 char            buf[ ACI_BUF_SIZE ];
374                 struct berval   bv, ndn;
375
376                 bv.bv_len = sizeof( buf ) - 1;
377                 bv.bv_val = (char *)&buf;
378                 if ( acl_string_expand( &bv, &subjdn,
379                                 e->e_ndn, nmatch, matches ) )
380                 {
381                         rc = LDAP_OTHER;
382                         goto done;
383                 }
384
385                 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
386                 {
387                         rc = ( backend_group( op, e, &ndn, &op->o_ndn,
388                                 grp_oc, grp_ad ) == 0 );
389                         slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
390                 }
391         }
392
393 done:
394         return rc;
395 }
396
397 static int
398 aci_mask(
399         Operation               *op,
400         Entry                   *e,
401         AttributeDescription    *desc,
402         struct berval           *val,
403         struct berval           *aci,
404         int                     nmatch,
405         regmatch_t              *matches,
406         slap_access_t           *grant,
407         slap_access_t           *deny,
408         slap_aci_scope_t        asserted_scope )
409 {
410         struct berval           bv,
411                                 scope,
412                                 perms,
413                                 type,
414                                 opts,
415                                 sdn;
416         int                     rc;
417                 
418
419         assert( !BER_BVISNULL( &desc->ad_cname ) );
420
421         /* parse an aci of the form:
422                 oid # scope # action;rights;attr;rights;attr 
423                         $ action;rights;attr;rights;attr # type # subject
424
425            [NOTE: the following comment is very outdated,
426            as the draft version it refers to (Ando, 2004-11-20)].
427
428            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
429            a full description of the format for this attribute.
430            Differences: "this" in the draft is "self" here, and
431            "self" and "public" is in the position of type.
432
433            <scope> = {entry|children|subtree}
434            <type> = {public|users|access-id|subtree|onelevel|children|
435                      self|dnattr|group|role|set|set-ref}
436
437            This routine now supports scope={ENTRY,CHILDREN}
438            with the semantics:
439              - ENTRY applies to "entry" and "subtree";
440              - CHILDREN aplies to "children" and "subtree"
441          */
442
443         /* check that the aci has all 5 components */
444         if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
445                 return 0;
446         }
447
448         /* check that the aci family is supported */
449         /* FIXME: the OID is ignored? */
450         if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
451                 return 0;
452         }
453
454         /* check that the scope matches */
455         if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
456                 return 0;
457         }
458
459         /* note: scope can be either ENTRY or CHILDREN;
460          * they respectively match "entry" and "children" in bv
461          * both match "subtree" */
462         switch ( asserted_scope ) {
463         case SLAP_ACI_SCOPE_ENTRY:
464                 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
465                                 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
466                 {
467                         return 0;
468                 }
469                 break;
470
471         case SLAP_ACI_SCOPE_CHILDREN:
472                 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
473                                 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
474                 {
475                         return 0;
476                 }
477                 break;
478
479         case SLAP_ACI_SCOPE_SUBTREE:
480                 /* TODO: add assertion? */
481                 return 0;
482         }
483
484         /* get the list of permissions clauses, bail if empty */
485         if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
486                 assert( 0 );
487                 return 0;
488         }
489
490         /* check if any permissions allow desired access */
491         if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
492                 return 0;
493         }
494
495         /* see if we have a DN match */
496         if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
497                 assert( 0 );
498                 return 0;
499         }
500
501         /* see if we have a public (i.e. anonymous) access */
502         if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
503                 return 1;
504         }
505         
506         /* otherwise require an identity */
507         if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
508                 return 0;
509         }
510
511         /* see if we have a users access */
512         if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
513                 return 1;
514         }
515         
516         /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
517          * just grab all the berval up to its end (ITS#3303).
518          * NOTE: the problem could be solved by providing the DN with
519          * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 
520          * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
521 #if 0
522         if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
523                 return 0;
524         }
525 #endif
526         sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
527         sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
528
529         /* get the type options, if any */
530         if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
531                 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
532                 type.bv_len = opts.bv_val - type.bv_val - 1;
533
534         } else {
535                 BER_BVZERO( &opts );
536         }
537
538         if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
539                 return dn_match( &op->o_ndn, &sdn );
540
541         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
542                 return dnIsSuffix( &op->o_ndn, &sdn );
543
544         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
545                 struct berval pdn;
546                 
547                 dnParent( &sdn, &pdn );
548
549                 return dn_match( &op->o_ndn, &pdn );
550
551         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
552                 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
553
554         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
555                 return dn_match( &op->o_ndn, &e->e_nname );
556
557         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
558                 Attribute               *at;
559                 AttributeDescription    *ad = NULL;
560                 const char              *text;
561
562                 rc = slap_bv2ad( &sdn, &ad, &text );
563                 assert( rc == LDAP_SUCCESS );
564
565                 rc = 0;
566                 for ( at = attrs_find( e->e_attrs, ad );
567                                 at != NULL;
568                                 at = attrs_find( at->a_next, ad ) )
569                 {
570                         if ( value_find_ex( ad,
571                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
572                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
573                                 at->a_nvals,
574                                 &op->o_ndn, op->o_tmpmemctx ) == 0 )
575                         {
576                                 rc = 1;
577                                 break;
578                         }
579                 }
580
581                 return rc;
582
583         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
584                 struct berval   oc,
585                                 at;
586
587                 if ( BER_BVISNULL( &opts ) ) {
588                         oc = aci_bv[ ACI_BV_GROUP_CLASS ];
589                         at = aci_bv[ ACI_BV_GROUP_ATTR ];
590
591                 } else {
592                         if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
593                                 assert( 0 );
594                         }
595
596                         if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
597                                 at = aci_bv[ ACI_BV_GROUP_ATTR ];
598                         }
599                 }
600
601                 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
602                 {
603                         return 1;
604                 }
605
606         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
607                 struct berval   oc,
608                                 at;
609
610                 if ( BER_BVISNULL( &opts ) ) {
611                         oc = aci_bv[ ACI_BV_ROLE_CLASS ];
612                         at = aci_bv[ ACI_BV_ROLE_ATTR ];
613
614                 } else {
615                         if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
616                                 assert( 0 );
617                         }
618
619                         if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
620                                 at = aci_bv[ ACI_BV_ROLE_ATTR ];
621                         }
622                 }
623
624                 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
625                 {
626                         return 1;
627                 }
628
629         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
630                 if ( acl_match_set( &sdn, op, e, NULL ) ) {
631                         return 1;
632                 }
633
634         } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
635                 if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
636                         return 1;
637                 }
638
639         } else {
640                 /* it passed normalization! */
641                 assert( 0 );
642         }
643
644         return 0;
645 }
646
647 static int
648 aci_init( void )
649 {
650         /* OpenLDAP eXperimental Syntax */
651         static slap_syntax_defs_rec aci_syntax_def = {
652                 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
653                         SLAP_SYNTAX_HIDE,
654                         OpenLDAPaciValidate,
655                         OpenLDAPaciPretty
656         };
657         static slap_mrule_defs_rec aci_mr_def = {
658                 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
659                         "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
660                         SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
661                         NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
662                         NULL, NULL,
663                         NULL
664         };
665         static struct {
666                 char                    *name;
667                 char                    *desc;
668                 slap_mask_t             flags;
669                 AttributeDescription    **ad;
670         }               aci_at = {
671                 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
672                         "NAME 'OpenLDAPaci' "
673                         "DESC 'OpenLDAP access control information (experimental)' "
674                         "EQUALITY OpenLDAPaciMatch "
675                         "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
676                         "USAGE directoryOperation )",
677                 SLAP_AT_HIDE,
678                 &slap_ad_aci
679         };
680
681         LDAPAttributeType       *at;
682         AttributeType           *sat;
683         int                     rc;
684         const char              *text;
685
686         /* ACI syntax */
687         rc = register_syntax( &aci_syntax_def );
688         if ( rc != 0 ) {
689                 return rc;
690         }
691         
692         /* ACI equality rule */
693         rc = register_matching_rule( &aci_mr_def );
694         if ( rc != 0 ) {
695                 return rc;
696         }
697
698         /* ACI attribute */
699         at = ldap_str2attributetype( aci_at.desc,
700                 &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
701         if ( !at ) {
702                 Debug( LDAP_DEBUG_ANY,
703                         "aci_init: AttributeType \"%s\" parse failed: %s %s\n",
704                         aci_at.name, ldap_scherr2str( rc ), text );
705                 return rc;
706         }
707
708         rc = at_add( at, 0, &sat, &text );
709         if ( rc != LDAP_SUCCESS ) {
710                 ldap_attributetype_free( at );
711                 Debug( LDAP_DEBUG_ANY,
712                         "aci_init: AttributeType \"%s\" load failed: %s %s\n",
713                         aci_at.name, scherr2str( rc ), text );
714                 return rc;
715         }
716         ldap_memfree( at );
717
718         rc = slap_str2ad( aci_at.name,
719                         aci_at.ad, &text );
720         if ( rc != LDAP_SUCCESS ) {
721                 Debug( LDAP_DEBUG_ANY,
722                         "aci_init: unable to find AttributeDescription "
723                         "\"%s\": %d (%s)\n",
724                         aci_at.name, rc, text );
725                 return 1;
726         }
727
728         /* install flags */
729         sat->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
1026  *    scope     := entry|children
1027  *    rights    := right[[$right]...]
1028  *    right     := (grant|deny);action
1029  *    action    := perms;attr[[;perms;attr]...]
1030  *    perms     := perm[[,perm]...]
1031  *    perm      := c|s|r|w|x
1032  *    attr      := attributeType|[all]
1033  *    type      :=  public|users|self|dnattr|group|role|set|set-ref|
1034  *                  access_id|subtree|onelevel|children
1035  */
1036 static int 
1037 OpenLDAPaciValidatePerms(
1038         struct berval *perms ) 
1039 {
1040         int             i;
1041
1042         for ( i = 0; i < perms->bv_len; ) {
1043                 switch ( perms->bv_val[ i ] ) {
1044                 case 'x':
1045                 case 'd':
1046                 case 'c':
1047                 case 's':
1048                 case 'r':
1049                 case 'w':
1050                         break;
1051
1052                 default:
1053                         return LDAP_INVALID_SYNTAX;
1054                 }
1055
1056                 if ( ++i == perms->bv_len ) {
1057                         return LDAP_SUCCESS;
1058                 }
1059
1060                 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
1061                         i++;
1062
1063                 assert( i != perms->bv_len );
1064
1065                 if ( perms->bv_val[ i ] != ',' ) {
1066                         return LDAP_INVALID_SYNTAX;
1067                 }
1068
1069                 do {
1070                         i++;
1071                 } while ( perms->bv_val[ i ] == ' ' );
1072         }
1073
1074         return LDAP_SUCCESS;
1075 }
1076
1077 static const struct berval *ACIgrantdeny[] = {
1078         &aci_bv[ ACI_BV_GRANT ],
1079         &aci_bv[ ACI_BV_DENY ],
1080         NULL
1081 };
1082
1083 static int 
1084 OpenLDAPaciValidateRight(
1085         struct berval *action )
1086 {
1087         struct berval   bv = BER_BVNULL;
1088         int             i;
1089
1090         /* grant|deny */
1091         if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
1092                 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
1093         {
1094                 return LDAP_INVALID_SYNTAX;
1095         }
1096
1097         for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
1098                 if ( i & 1 ) {
1099                         /* perms */
1100                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1101                         {
1102                                 return LDAP_INVALID_SYNTAX;
1103                         }
1104
1105                 } else {
1106                         /* attr */
1107                         AttributeDescription    *ad = NULL;
1108                         const char              *text = NULL;
1109
1110                         /* could be "[all]" or an attribute description */
1111                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1112                                 continue;
1113                         }
1114
1115                         if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
1116                                 return LDAP_INVALID_SYNTAX;
1117                         }
1118                 }
1119         }
1120         
1121         /* "perms;attr" go in pairs */
1122         if ( i > 0 && ( i & 1 ) == 0 ) {
1123                 return LDAP_SUCCESS;
1124
1125         } else {
1126                 return LDAP_INVALID_SYNTAX;
1127         }
1128
1129         return LDAP_SUCCESS;
1130 }
1131
1132 static int
1133 OpenLDAPaciNormalizeRight(
1134         struct berval   *action,
1135         struct berval   *naction,
1136         void            *ctx )
1137 {
1138         struct berval   grantdeny,
1139                         perms = BER_BVNULL,
1140                         bv = BER_BVNULL;
1141         int             idx,
1142                         i;
1143
1144         /* grant|deny */
1145         if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1146                 return LDAP_INVALID_SYNTAX;
1147         }
1148         idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1149         if ( idx == -1 ) {
1150                 return LDAP_INVALID_SYNTAX;
1151         }
1152
1153         ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1154
1155         for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1156                 if ( i & 1 ) {
1157                         /* perms */
1158                         if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1159                         {
1160                                 return LDAP_INVALID_SYNTAX;
1161                         }
1162                         perms = bv;
1163
1164                 } else {
1165                         /* attr */
1166                         char            *ptr;
1167
1168                         /* could be "[all]" or an attribute description */
1169                         if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1170                                 bv = aci_bv[ ACI_BV_BR_ALL ];
1171
1172                         } else {
1173                                 AttributeDescription    *ad = NULL;
1174                                 const char              *text = NULL;
1175                                 int                     rc;
1176
1177                                 rc = slap_bv2ad( &bv, &ad, &text );
1178                                 if ( rc != LDAP_SUCCESS ) {
1179                                         return LDAP_INVALID_SYNTAX;
1180                                 }
1181
1182                                 bv = ad->ad_cname;
1183                         }
1184
1185                         naction->bv_val = ber_memrealloc_x( naction->bv_val,
1186                                 naction->bv_len + STRLENOF( ";" )
1187                                 + perms.bv_len + STRLENOF( ";" )
1188                                 + bv.bv_len + 1,
1189                                 ctx );
1190
1191                         ptr = &naction->bv_val[ naction->bv_len ];
1192                         ptr[ 0 ] = ';';
1193                         ptr++;
1194                         ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1195                         ptr[ 0 ] = ';';
1196                         ptr++;
1197                         ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1198                         ptr[ 0 ] = '\0';
1199                         naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1200                                 + STRLENOF( ";" ) + bv.bv_len;
1201                 }
1202         }
1203         
1204         /* perms;attr go in pairs */
1205         if ( i > 1 && ( i & 1 ) ) {
1206                 return LDAP_SUCCESS;
1207
1208         } else {
1209                 return LDAP_INVALID_SYNTAX;
1210         }
1211 }
1212
1213 static int 
1214 OpenLDAPaciValidateRights(
1215         struct berval *actions )
1216
1217 {
1218         struct berval   bv = BER_BVNULL;
1219         int             i;
1220
1221         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1222                 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1223                         return LDAP_INVALID_SYNTAX;
1224                 }
1225         }
1226
1227         return LDAP_SUCCESS;
1228 }
1229
1230 static int 
1231 OpenLDAPaciNormalizeRights(
1232         struct berval   *actions,
1233         struct berval   *nactions,
1234         void            *ctx )
1235
1236 {
1237         struct berval   bv = BER_BVNULL;
1238         int             i;
1239
1240         BER_BVZERO( nactions );
1241         for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1242                 int             rc;
1243                 struct berval   nbv;
1244
1245                 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1246                 if ( rc != LDAP_SUCCESS ) {
1247                         ber_memfree_x( nactions->bv_val, ctx );
1248                         BER_BVZERO( nactions );
1249                         return LDAP_INVALID_SYNTAX;
1250                 }
1251
1252                 if ( i == 0 ) {
1253                         *nactions = nbv;
1254
1255                 } else {
1256                         nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1257                                 nactions->bv_len + STRLENOF( "$" )
1258                                 + nbv.bv_len + 1,
1259                                 ctx );
1260                         nactions->bv_val[ nactions->bv_len ] = '$';
1261                         AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1262                                 nbv.bv_val, nbv.bv_len + 1 );
1263                         ber_memfree_x( nbv.bv_val, ctx );
1264                         nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1265                 }
1266                 BER_BVZERO( &nbv );
1267         }
1268
1269         return LDAP_SUCCESS;
1270 }
1271
1272 static const struct berval *OpenLDAPaciscopes[] = {
1273         &aci_bv[ ACI_BV_ENTRY ],
1274         &aci_bv[ ACI_BV_CHILDREN ],
1275         &aci_bv[ ACI_BV_SUBTREE ],
1276
1277         NULL
1278 };
1279
1280 static const struct berval *OpenLDAPacitypes[] = {
1281         /* DN-valued */
1282         &aci_bv[ ACI_BV_GROUP ], 
1283         &aci_bv[ ACI_BV_ROLE ],
1284
1285 /* set to one past the last DN-valued type with options (/) */
1286 #define LAST_OPTIONAL   2
1287
1288         &aci_bv[ ACI_BV_ACCESS_ID ],
1289         &aci_bv[ ACI_BV_SUBTREE ],
1290         &aci_bv[ ACI_BV_ONELEVEL ],
1291         &aci_bv[ ACI_BV_CHILDREN ],
1292
1293 /* set to one past the last DN-valued type */
1294 #define LAST_DNVALUED   6
1295
1296         /* non DN-valued */
1297         &aci_bv[ ACI_BV_DNATTR ],
1298         &aci_bv[ ACI_BV_PUBLIC ],
1299         &aci_bv[ ACI_BV_USERS ],
1300         &aci_bv[ ACI_BV_SELF ],
1301         &aci_bv[ ACI_BV_SET ],
1302         &aci_bv[ ACI_BV_SET_REF ],
1303
1304         NULL
1305 };
1306
1307 static int
1308 OpenLDAPaciValidate(
1309         Syntax          *syntax,
1310         struct berval   *val )
1311 {
1312         struct berval   oid = BER_BVNULL,
1313                         scope = BER_BVNULL,
1314                         rights = BER_BVNULL,
1315                         type = BER_BVNULL,
1316                         subject = BER_BVNULL;
1317         int             idx;
1318
1319         if ( BER_BVISEMPTY( val ) ) {
1320                 return LDAP_INVALID_SYNTAX;
1321         }
1322
1323         /* oid */
1324         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1325                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1326         {
1327                 /* NOTE: the numericoidValidate() is rather pedantic;
1328                  * I'd replace it with X-ORDERED VALUES so that
1329                  * it's guaranteed values are maintained and used
1330                  * in the desired order */
1331                 return LDAP_INVALID_SYNTAX;
1332         }
1333
1334         /* scope */
1335         if ( acl_get_part( val, 1, '#', &scope ) < 0 || 
1336                 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1337         {
1338                 return LDAP_INVALID_SYNTAX;
1339         }
1340
1341         /* rights */
1342         if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1343                 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) 
1344         {
1345                 return LDAP_INVALID_SYNTAX;
1346         }
1347
1348         /* type */
1349         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1350                 return LDAP_INVALID_SYNTAX;
1351         }
1352         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1353         if ( idx == -1 ) {
1354                 struct berval   isgr;
1355
1356                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1357                         return LDAP_INVALID_SYNTAX;
1358                 }
1359
1360                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1361                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1362                         return LDAP_INVALID_SYNTAX;
1363                 }
1364         }
1365
1366         /* subject */
1367         bv_get_tail( val, &type, &subject );
1368         if ( subject.bv_val[ 0 ] != '#' ) {
1369                 return LDAP_INVALID_SYNTAX;
1370         }
1371
1372         if ( idx >= LAST_DNVALUED ) {
1373                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1374                         AttributeDescription    *ad = NULL;
1375                         const char              *text = NULL;
1376                         int                     rc;
1377
1378                         rc = slap_bv2ad( &subject, &ad, &text );
1379                         if ( rc != LDAP_SUCCESS ) {
1380                                 return LDAP_INVALID_SYNTAX;
1381                         }
1382
1383                         if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1384                                 /* FIXME: allow nameAndOptionalUID? */
1385                                 return LDAP_INVALID_SYNTAX;
1386                         }
1387                 }
1388
1389                 /* not a DN */
1390                 return LDAP_SUCCESS;
1391
1392         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1393                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1394         {
1395                 /* do {group|role}/oc/at check */
1396                 struct berval   ocbv = BER_BVNULL,
1397                                 atbv = BER_BVNULL;
1398
1399                 ocbv.bv_val = ber_bvchr( &type, '/' );
1400                 if ( ocbv.bv_val != NULL ) {
1401                         ocbv.bv_val++;
1402                         ocbv.bv_len = type.bv_len
1403                                         - ( ocbv.bv_val - type.bv_val );
1404
1405                         atbv.bv_val = ber_bvchr( &ocbv, '/' );
1406                         if ( atbv.bv_val != NULL ) {
1407                                 AttributeDescription    *ad = NULL;
1408                                 const char              *text = NULL;
1409                                 int                     rc;
1410
1411                                 atbv.bv_val++;
1412                                 atbv.bv_len = type.bv_len
1413                                         - ( atbv.bv_val - type.bv_val );
1414                                 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1415
1416                                 rc = slap_bv2ad( &atbv, &ad, &text );
1417                                 if ( rc != LDAP_SUCCESS ) {
1418                                         return LDAP_INVALID_SYNTAX;
1419                                 }
1420                         }
1421
1422                         if ( oc_bvfind( &ocbv ) == NULL ) {
1423                                 return LDAP_INVALID_SYNTAX;
1424                         }
1425                 }
1426         }
1427
1428         if ( BER_BVISEMPTY( &subject ) ) {
1429                 /* empty DN invalid */
1430                 return LDAP_INVALID_SYNTAX;
1431         }
1432
1433         subject.bv_val++;
1434         subject.bv_len--;
1435
1436         /* FIXME: pass DN syntax? */
1437         return dnValidate( NULL, &subject );
1438 }
1439
1440 static int
1441 OpenLDAPaciPrettyNormal(
1442         struct berval   *val,
1443         struct berval   *out,
1444         void            *ctx,
1445         int             normalize )
1446 {
1447         struct berval   oid = BER_BVNULL,
1448                         scope = BER_BVNULL,
1449                         rights = BER_BVNULL,
1450                         nrights = BER_BVNULL,
1451                         type = BER_BVNULL,
1452                         ntype = BER_BVNULL,
1453                         subject = BER_BVNULL,
1454                         nsubject = BER_BVNULL;
1455         int             idx,
1456                         rc = LDAP_SUCCESS,
1457                         freesubject = 0,
1458                         freetype = 0;
1459         char            *ptr;
1460
1461         if ( BER_BVISEMPTY( val ) ) {
1462                 return LDAP_INVALID_SYNTAX;
1463         }
1464
1465         /* oid: if valid, it's already normalized */
1466         if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
1467                 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1468         {
1469                 return LDAP_INVALID_SYNTAX;
1470         }
1471
1472         /* scope: normalize by replacing with OpenLDAPaciscopes */
1473         if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1474                 return LDAP_INVALID_SYNTAX;
1475         }
1476         idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1477         if ( idx == -1 ) {
1478                 return LDAP_INVALID_SYNTAX;
1479         }
1480         scope = *OpenLDAPaciscopes[ idx ];
1481
1482         /* rights */
1483         if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1484                 return LDAP_INVALID_SYNTAX;
1485         }
1486         if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1487                 != LDAP_SUCCESS ) 
1488         {
1489                 return LDAP_INVALID_SYNTAX;
1490         }
1491
1492         /* type */
1493         if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1494                 rc = LDAP_INVALID_SYNTAX;
1495                 goto cleanup;
1496         }
1497         idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1498         if ( idx == -1 ) {
1499                 struct berval   isgr;
1500
1501                 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1502                         rc = LDAP_INVALID_SYNTAX;
1503                         goto cleanup;
1504                 }
1505
1506                 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1507                 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1508                         rc = LDAP_INVALID_SYNTAX;
1509                         goto cleanup;
1510                 }
1511         }
1512         ntype = *OpenLDAPacitypes[ idx ];
1513
1514         /* subject */
1515         bv_get_tail( val, &type, &subject );
1516
1517         if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1518                 rc = LDAP_INVALID_SYNTAX;
1519                 goto cleanup;
1520         }
1521
1522         subject.bv_val++;
1523         subject.bv_len--;
1524
1525         if ( idx < LAST_DNVALUED ) {
1526                 /* FIXME: pass DN syntax? */
1527                 if ( normalize ) {
1528                         rc = dnNormalize( 0, NULL, NULL,
1529                                 &subject, &nsubject, ctx );
1530                 } else {
1531                         rc = dnPretty( NULL, &subject, &nsubject, ctx );
1532                 }
1533
1534                 if ( rc == LDAP_SUCCESS ) {
1535                         freesubject = 1;
1536
1537                 } else {
1538                         goto cleanup;
1539                 }
1540
1541                 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1542                         || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1543                 {
1544                         /* do {group|role}/oc/at check */
1545                         struct berval   ocbv = BER_BVNULL,
1546                                         atbv = BER_BVNULL;
1547
1548                         ocbv.bv_val = ber_bvchr( &type, '/' );
1549                         if ( ocbv.bv_val != NULL ) {
1550                                 ObjectClass             *oc = NULL;
1551                                 AttributeDescription    *ad = NULL;
1552                                 const char              *text = NULL;
1553                                 int                     rc;
1554                                 struct berval           bv;
1555
1556                                 bv.bv_len = ntype.bv_len;
1557
1558                                 ocbv.bv_val++;
1559                                 ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
1560
1561                                 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1562                                 if ( atbv.bv_val != NULL ) {
1563                                         atbv.bv_val++;
1564                                         atbv.bv_len = type.bv_len
1565                                                 - ( atbv.bv_val - type.bv_val );
1566                                         ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1567         
1568                                         rc = slap_bv2ad( &atbv, &ad, &text );
1569                                         if ( rc != LDAP_SUCCESS ) {
1570                                                 rc = LDAP_INVALID_SYNTAX;
1571                                                 goto cleanup;
1572                                         }
1573
1574                                         bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1575                                 }
1576
1577                                 oc = oc_bvfind( &ocbv );
1578                                 if ( oc == NULL ) {
1579                                         rc = LDAP_INVALID_SYNTAX;
1580                                         goto cleanup;
1581                                 }
1582
1583                                 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1584                                 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1585
1586                                 ptr = bv.bv_val;
1587                                 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1588                                 ptr[ 0 ] = '/';
1589                                 ptr++;
1590                                 ptr = lutil_strncopy( ptr,
1591                                         oc->soc_cname.bv_val,
1592                                         oc->soc_cname.bv_len );
1593                                 if ( ad != NULL ) {
1594                                         ptr[ 0 ] = '/';
1595                                         ptr++;
1596                                         ptr = lutil_strncopy( ptr,
1597                                                 ad->ad_cname.bv_val,
1598                                                 ad->ad_cname.bv_len );
1599                                 }
1600                                 ptr[ 0 ] = '\0';
1601
1602                                 ntype = bv;
1603                                 freetype = 1;
1604                         }
1605                 }
1606
1607         } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1608                 AttributeDescription    *ad = NULL;
1609                 const char              *text = NULL;
1610                 int                     rc;
1611
1612                 rc = slap_bv2ad( &subject, &ad, &text );
1613                 if ( rc != LDAP_SUCCESS ) {
1614                         rc = LDAP_INVALID_SYNTAX;
1615                         goto cleanup;
1616                 }
1617
1618                 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1619                         /* FIXME: allow nameAndOptionalUID? */
1620                         rc = LDAP_INVALID_SYNTAX;
1621                         goto cleanup;
1622                 }
1623
1624                 nsubject = ad->ad_cname;
1625         }
1626
1627
1628         out->bv_len = 
1629                 oid.bv_len + STRLENOF( "#" )
1630                 + scope.bv_len + STRLENOF( "#" )
1631                 + rights.bv_len + STRLENOF( "#" )
1632                 + ntype.bv_len + STRLENOF( "#" )
1633                 + nsubject.bv_len;
1634
1635         out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1636         ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1637         ptr[ 0 ] = '#';
1638         ptr++;
1639         ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1640         ptr[ 0 ] = '#';
1641         ptr++;
1642         ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1643         ptr[ 0 ] = '#';
1644         ptr++;
1645         ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1646         ptr[ 0 ] = '#';
1647         ptr++;
1648         if ( !BER_BVISNULL( &nsubject ) ) {
1649                 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1650         }
1651         ptr[ 0 ] = '\0';
1652
1653 cleanup:;
1654         if ( freesubject ) {
1655                 ber_memfree_x( nsubject.bv_val, ctx );
1656         }
1657
1658         if ( freetype ) {
1659                 ber_memfree_x( ntype.bv_val, ctx );
1660         }
1661
1662         if ( !BER_BVISNULL( &nrights ) ) {
1663                 ber_memfree_x( nrights.bv_val, ctx );
1664         }
1665
1666         return rc;
1667 }
1668
1669 static int
1670 OpenLDAPaciPretty(
1671         Syntax          *syntax,
1672         struct berval   *val,
1673         struct berval   *out,
1674         void            *ctx )
1675 {
1676         return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1677 }
1678
1679 static int
1680 OpenLDAPaciNormalize(
1681         slap_mask_t     use,
1682         Syntax          *syntax,
1683         MatchingRule    *mr,
1684         struct berval   *val,
1685         struct berval   *out,
1686         void            *ctx )
1687 {
1688         return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1689 }
1690
1691 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
1692 /*
1693  * FIXME: need config and Makefile.am code to ease building
1694  * as dynamic module
1695  */
1696 int
1697 init_module( int argc, char *argv[] )
1698 {
1699         return dynacl_aci_init();
1700 }
1701 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
1702
1703 #endif /* SLAPD_ACI_ENABLED */
1704