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