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