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