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