Back to index

openldap  2.4.31
aci.c
Go to the documentation of this file.
00001 /* aci.c - routines to parse and check acl's */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00017  * All rights reserved.
00018  *
00019  * Redistribution and use in source and binary forms are permitted
00020  * provided that this notice is preserved and that due credit is given
00021  * to the University of Michigan at Ann Arbor. The name of the University
00022  * may not be used to endorse or promote products derived from this
00023  * software without specific prior written permission. This software
00024  * is provided ``as is'' without express or implied warranty.
00025  */
00026 
00027 #include "portable.h"
00028 
00029 #ifdef SLAPD_ACI_ENABLED
00030 
00031 #include <stdio.h>
00032 
00033 #include <ac/ctype.h>
00034 #include <ac/regex.h>
00035 #include <ac/socket.h>
00036 #include <ac/string.h>
00037 #include <ac/unistd.h>
00038 
00039 #include "slap.h"
00040 #include "lber_pvt.h"
00041 #include "lutil.h"
00042 
00043 /* use most appropriate size */
00044 #define ACI_BUF_SIZE                      1024
00045 
00046 /* move to "stable" when no longer experimental */
00047 #define SLAPD_ACI_SYNTAX           "1.3.6.1.4.1.4203.666.2.1"
00048 
00049 /* change this to "OpenLDAPset" */
00050 #define SLAPD_ACI_SET_ATTR         "template"
00051 
00052 typedef enum slap_aci_scope_t {
00053        SLAP_ACI_SCOPE_ENTRY        = 0x1,
00054        SLAP_ACI_SCOPE_CHILDREN            = 0x2,
00055        SLAP_ACI_SCOPE_SUBTREE             = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
00056 } slap_aci_scope_t;
00057 
00058 enum {
00059        ACI_BV_ENTRY,
00060        ACI_BV_CHILDREN,
00061        ACI_BV_ONELEVEL,
00062        ACI_BV_SUBTREE,
00063 
00064        ACI_BV_BR_ENTRY,
00065        ACI_BV_BR_CHILDREN,
00066        ACI_BV_BR_ALL,
00067 
00068        ACI_BV_ACCESS_ID,
00069        ACI_BV_PUBLIC,
00070        ACI_BV_USERS,
00071        ACI_BV_SELF,
00072        ACI_BV_DNATTR,
00073        ACI_BV_GROUP,
00074        ACI_BV_ROLE,
00075        ACI_BV_SET,
00076        ACI_BV_SET_REF,
00077 
00078        ACI_BV_GRANT,
00079        ACI_BV_DENY,
00080 
00081        ACI_BV_GROUP_CLASS,
00082        ACI_BV_GROUP_ATTR,
00083        ACI_BV_ROLE_CLASS,
00084        ACI_BV_ROLE_ATTR,
00085 
00086        ACI_BV_SET_ATTR,
00087 
00088        ACI_BV_LAST
00089 };
00090 
00091 static const struct berval  aci_bv[] = {
00092        /* scope */
00093        BER_BVC("entry"),
00094        BER_BVC("children"),
00095        BER_BVC("onelevel"),
00096        BER_BVC("subtree"),
00097 
00098        /* */
00099        BER_BVC("[entry]"),
00100        BER_BVC("[children]"),
00101        BER_BVC("[all]"),
00102 
00103        /* type */
00104        BER_BVC("access-id"),
00105        BER_BVC("public"),
00106        BER_BVC("users"),
00107        BER_BVC("self"),
00108        BER_BVC("dnattr"),
00109        BER_BVC("group"),
00110        BER_BVC("role"),
00111        BER_BVC("set"),
00112        BER_BVC("set-ref"),
00113 
00114        /* actions */
00115        BER_BVC("grant"),
00116        BER_BVC("deny"),
00117 
00118        /* schema */
00119        BER_BVC(SLAPD_GROUP_CLASS),
00120        BER_BVC(SLAPD_GROUP_ATTR),
00121        BER_BVC(SLAPD_ROLE_CLASS),
00122        BER_BVC(SLAPD_ROLE_ATTR),
00123 
00124        BER_BVC(SLAPD_ACI_SET_ATTR),
00125 
00126        BER_BVNULL
00127 };
00128 
00129 static AttributeDescription *slap_ad_aci;
00130 
00131 static int
00132 OpenLDAPaciValidate(
00133        Syntax        *syntax,
00134        struct berval *val );
00135 
00136 static int
00137 OpenLDAPaciPretty(
00138        Syntax        *syntax,
00139        struct berval *val,
00140        struct berval *out,
00141        void          *ctx );
00142 
00143 static int
00144 OpenLDAPaciNormalize(
00145        slap_mask_t   use,
00146        Syntax        *syntax,
00147        MatchingRule  *mr,
00148        struct berval *val,
00149        struct berval *out,
00150        void          *ctx );
00151 
00152 #define       OpenLDAPaciMatch                   octetStringMatch
00153 
00154 static int
00155 aci_list_map_rights(
00156        struct berval *list )
00157 {
00158        struct berval bv;
00159        slap_access_t mask;
00160        int           i;
00161 
00162        ACL_INIT( mask );
00163        for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
00164               if ( bv.bv_len <= 0 ) {
00165                      continue;
00166               }
00167 
00168               switch ( *bv.bv_val ) {
00169               case 'x':
00170                      /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
00171                       * define any equivalent to the AUTH right, so I've just used
00172                       * 'x' for now.
00173                       */
00174                      ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
00175                      break;
00176               case 'd':
00177                      /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
00178                       * the right 'd' to mean "delete"; we hijack it to mean
00179                       * "disclose" for consistency wuith the rest of slapd.
00180                       */
00181                      ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
00182                      break;
00183               case 'c':
00184                      ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
00185                      break;
00186               case 's':
00187                      /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
00188                       * the right 's' to mean "set", but in the examples states
00189                       * that the right 's' means "search".  The latter definition
00190                       * is used here.
00191                       */
00192                      ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
00193                      break;
00194               case 'r':
00195                      ACL_PRIV_SET(mask, ACL_PRIV_READ);
00196                      break;
00197               case 'w':
00198                      ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
00199                      break;
00200               default:
00201                      break;
00202               }
00203 
00204        }
00205 
00206        return mask;
00207 }
00208 
00209 static int
00210 aci_list_has_attr(
00211        struct berval        *list,
00212        const struct berval  *attr,
00213        struct berval        *val )
00214 {
00215        struct berval bv, left, right;
00216        int           i;
00217 
00218        for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
00219               if ( acl_get_part(&bv, 0, '=', &left ) < 0
00220                      || acl_get_part( &bv, 1, '=', &right ) < 0 )
00221               {
00222                      if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
00223                             return(1);
00224                      }
00225 
00226               } else if ( val == NULL ) {
00227                      if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
00228                             return(1);
00229                      }
00230 
00231               } else {
00232                      if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
00233                             /* FIXME: this is also totally undocumented! */
00234                             /* this is experimental code that implements a
00235                              * simple (prefix) match of the attribute value.
00236                              * the ACI draft does not provide for aci's that
00237                              * apply to specific values, but it would be
00238                              * nice to have.  If the <attr> part of an aci's
00239                              * rights list is of the form <attr>=<value>,
00240                              * that means the aci applies only to attrs with
00241                              * the given value.  Furthermore, if the attr is
00242                              * of the form <attr>=<value>*, then <value> is
00243                              * treated as a prefix, and the aci applies to 
00244                              * any value with that prefix.
00245                              *
00246                              * Ideally, this would allow r.e. matches.
00247                              */
00248                             if ( acl_get_part( &right, 0, '*', &left ) < 0
00249                                    || right.bv_len <= left.bv_len )
00250                             {
00251                                    if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
00252                                           return 1;
00253                                    }
00254 
00255                             } else if ( val->bv_len >= left.bv_len ) {
00256                                    if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
00257                                           return(1);
00258                                    }
00259                             }
00260                      }
00261               }
00262        }
00263 
00264        return 0;
00265 }
00266 
00267 static slap_access_t
00268 aci_list_get_attr_rights(
00269        struct berval        *list,
00270        const struct berval  *attr,
00271        struct berval        *val )
00272 {
00273        struct berval bv;
00274        slap_access_t mask;
00275        int           i;
00276 
00277        /* loop through each rights/attr pair, skip first part (action) */
00278        ACL_INIT(mask);
00279        for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
00280               if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
00281                      Debug( LDAP_DEBUG_ACL,
00282                             "        <= aci_list_get_attr_rights "
00283                             "test %s for %s -> failed\n",
00284                             bv.bv_val, attr->bv_val, 0 );
00285                      continue;
00286               }
00287 
00288               Debug( LDAP_DEBUG_ACL,
00289                      "        <= aci_list_get_attr_rights "
00290                      "test %s for %s -> ok\n",
00291                      bv.bv_val, attr->bv_val, 0 );
00292 
00293               if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
00294                      Debug( LDAP_DEBUG_ACL,
00295                             "        <= aci_list_get_attr_rights "
00296                             "test no rights\n",
00297                             0, 0, 0 );
00298                      continue;
00299               }
00300 
00301               mask |= aci_list_map_rights( &bv );
00302               Debug( LDAP_DEBUG_ACL,
00303                      "        <= aci_list_get_attr_rights "
00304                      "rights %s to mask 0x%x\n",
00305                      bv.bv_val, mask, 0 );
00306        }
00307 
00308        return mask;
00309 }
00310 
00311 static int
00312 aci_list_get_rights(
00313        struct berval *list,
00314        struct berval *attr,
00315        struct berval *val,
00316        slap_access_t *grant,
00317        slap_access_t *deny )
00318 {
00319        struct berval perm, actn, baseattr;
00320        slap_access_t *mask;
00321        int           i, found;
00322 
00323        if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
00324               attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
00325 
00326        } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
00327               attr = &baseattr;
00328        }
00329        found = 0;
00330        ACL_INIT(*grant);
00331        ACL_INIT(*deny);
00332        /* loop through each permissions clause */
00333        for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
00334               if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
00335                      continue;
00336               }
00337 
00338               if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
00339                      mask = grant;
00340 
00341               } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
00342                      mask = deny;
00343 
00344               } else {
00345                      continue;
00346               }
00347 
00348               *mask |= aci_list_get_attr_rights( &perm, attr, val );
00349               *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
00350 
00351               if ( *mask != ACL_PRIV_NONE ) { 
00352                      found = 1;
00353               }
00354        }
00355 
00356        return found;
00357 }
00358 
00359 static int
00360 aci_group_member (
00361        struct berval        *subj,
00362        const struct berval  *defgrpoc,
00363        const struct berval  *defgrpat,
00364        Operation            *op,
00365        Entry                *e,
00366        int                  nmatch,
00367        regmatch_t           *matches
00368 )
00369 {
00370        struct berval        subjdn;
00371        struct berval        grpoc;
00372        struct berval        grpat;
00373        ObjectClass          *grp_oc = NULL;
00374        AttributeDescription *grp_ad = NULL;
00375        const char           *text;
00376        int                  rc;
00377 
00378        /* format of string is "{group|role}/objectClassValue/groupAttrName" */
00379        if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
00380               return 0;
00381        }
00382 
00383        if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
00384               grpoc = *defgrpoc;
00385        }
00386 
00387        if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
00388               grpat = *defgrpat;
00389        }
00390 
00391        rc = slap_bv2ad( &grpat, &grp_ad, &text );
00392        if ( rc != LDAP_SUCCESS ) {
00393               rc = 0;
00394               goto done;
00395        }
00396        rc = 0;
00397 
00398        grp_oc = oc_bvfind( &grpoc );
00399 
00400        if ( grp_oc != NULL && grp_ad != NULL ) {
00401               char          buf[ ACI_BUF_SIZE ];
00402               struct berval bv, ndn;
00403               AclRegexMatches amatches = { 0 };
00404 
00405               amatches.dn_count = nmatch;
00406               AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
00407 
00408               bv.bv_len = sizeof( buf ) - 1;
00409               bv.bv_val = (char *)&buf;
00410               if ( acl_string_expand( &bv, &subjdn,
00411                             &e->e_nname, NULL, &amatches ) )
00412               {
00413                      rc = LDAP_OTHER;
00414                      goto done;
00415               }
00416 
00417               if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
00418               {
00419                      rc = ( backend_group( op, e, &ndn, &op->o_ndn,
00420                             grp_oc, grp_ad ) == 0 );
00421                      slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
00422               }
00423        }
00424 
00425 done:
00426        return rc;
00427 }
00428 
00429 static int
00430 aci_mask(
00431        Operation            *op,
00432        Entry                *e,
00433        AttributeDescription *desc,
00434        struct berval        *val,
00435        struct berval        *aci,
00436        int                  nmatch,
00437        regmatch_t           *matches,
00438        slap_access_t        *grant,
00439        slap_access_t        *deny,
00440        slap_aci_scope_t     asserted_scope )
00441 {
00442        struct berval        bv,
00443                             scope,
00444                             perms,
00445                             type,
00446                             opts,
00447                             sdn;
00448        int                  rc;
00449 
00450        ACL_INIT( *grant );
00451        ACL_INIT( *deny );
00452 
00453        assert( !BER_BVISNULL( &desc->ad_cname ) );
00454 
00455        /* parse an aci of the form:
00456               oid # scope # action;rights;attr;rights;attr 
00457                      $ action;rights;attr;rights;attr # type # subject
00458 
00459           [NOTE: the following comment is very outdated,
00460           as the draft version it refers to (Ando, 2004-11-20)].
00461 
00462           See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
00463           a full description of the format for this attribute.
00464           Differences: "this" in the draft is "self" here, and
00465           "self" and "public" is in the position of type.
00466 
00467           <scope> = {entry|children|subtree}
00468           <type> = {public|users|access-id|subtree|onelevel|children|
00469                     self|dnattr|group|role|set|set-ref}
00470 
00471           This routine now supports scope={ENTRY,CHILDREN}
00472           with the semantics:
00473             - ENTRY applies to "entry" and "subtree";
00474             - CHILDREN applies to "children" and "subtree"
00475         */
00476 
00477        /* check that the aci has all 5 components */
00478        if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
00479               return 0;
00480        }
00481 
00482        /* check that the aci family is supported */
00483        /* FIXME: the OID is ignored? */
00484        if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
00485               return 0;
00486        }
00487 
00488        /* check that the scope matches */
00489        if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
00490               return 0;
00491        }
00492 
00493        /* note: scope can be either ENTRY or CHILDREN;
00494         * they respectively match "entry" and "children" in bv
00495         * both match "subtree" */
00496        switch ( asserted_scope ) {
00497        case SLAP_ACI_SCOPE_ENTRY:
00498               if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
00499                             && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
00500               {
00501                      return 0;
00502               }
00503               break;
00504 
00505        case SLAP_ACI_SCOPE_CHILDREN:
00506               if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
00507                             && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
00508               {
00509                      return 0;
00510               }
00511               break;
00512 
00513        case SLAP_ACI_SCOPE_SUBTREE:
00514               /* TODO: add assertion? */
00515               return 0;
00516        }
00517 
00518        /* get the list of permissions clauses, bail if empty */
00519        if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
00520               assert( 0 );
00521               return 0;
00522        }
00523 
00524        /* check if any permissions allow desired access */
00525        if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
00526               return 0;
00527        }
00528 
00529        /* see if we have a DN match */
00530        if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
00531               assert( 0 );
00532               return 0;
00533        }
00534 
00535        /* see if we have a public (i.e. anonymous) access */
00536        if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
00537               return 1;
00538        }
00539        
00540        /* otherwise require an identity */
00541        if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
00542               return 0;
00543        }
00544 
00545        /* see if we have a users access */
00546        if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
00547               return 1;
00548        }
00549        
00550        /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
00551         * just grab all the berval up to its end (ITS#3303).
00552         * NOTE: the problem could be solved by providing the DN with
00553         * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would 
00554         * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
00555 #if 0
00556        if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
00557               return 0;
00558        }
00559 #endif
00560        sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
00561        sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
00562 
00563        /* get the type options, if any */
00564        if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
00565               opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
00566               type.bv_len = opts.bv_val - type.bv_val - 1;
00567 
00568        } else {
00569               BER_BVZERO( &opts );
00570        }
00571 
00572        if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
00573               return dn_match( &op->o_ndn, &sdn );
00574 
00575        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
00576               return dnIsSuffix( &op->o_ndn, &sdn );
00577 
00578        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
00579               struct berval pdn;
00580               
00581               dnParent( &sdn, &pdn );
00582 
00583               return dn_match( &op->o_ndn, &pdn );
00584 
00585        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
00586               return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
00587 
00588        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
00589               return dn_match( &op->o_ndn, &e->e_nname );
00590 
00591        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
00592               Attribute            *at;
00593               AttributeDescription *ad = NULL;
00594               const char           *text;
00595 
00596               rc = slap_bv2ad( &sdn, &ad, &text );
00597               assert( rc == LDAP_SUCCESS );
00598 
00599               rc = 0;
00600               for ( at = attrs_find( e->e_attrs, ad );
00601                             at != NULL;
00602                             at = attrs_find( at->a_next, ad ) )
00603               {
00604                      if ( attr_valfind( at, 
00605                             SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
00606                                    SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
00607                             &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 )
00608                      {
00609                             rc = 1;
00610                             break;
00611                      }
00612               }
00613 
00614               return rc;
00615 
00616        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
00617               struct berval oc,
00618                             at;
00619 
00620               if ( BER_BVISNULL( &opts ) ) {
00621                      oc = aci_bv[ ACI_BV_GROUP_CLASS ];
00622                      at = aci_bv[ ACI_BV_GROUP_ATTR ];
00623 
00624               } else {
00625                      if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
00626                             assert( 0 );
00627                      }
00628 
00629                      if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
00630                             at = aci_bv[ ACI_BV_GROUP_ATTR ];
00631                      }
00632               }
00633 
00634               if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
00635               {
00636                      return 1;
00637               }
00638 
00639        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
00640               struct berval oc,
00641                             at;
00642 
00643               if ( BER_BVISNULL( &opts ) ) {
00644                      oc = aci_bv[ ACI_BV_ROLE_CLASS ];
00645                      at = aci_bv[ ACI_BV_ROLE_ATTR ];
00646 
00647               } else {
00648                      if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
00649                             assert( 0 );
00650                      }
00651 
00652                      if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
00653                             at = aci_bv[ ACI_BV_ROLE_ATTR ];
00654                      }
00655               }
00656 
00657               if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
00658               {
00659                      return 1;
00660               }
00661 
00662        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
00663               if ( acl_match_set( &sdn, op, e, NULL ) ) {
00664                      return 1;
00665               }
00666 
00667        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
00668               if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
00669                      return 1;
00670               }
00671 
00672        } else {
00673               /* it passed normalization! */
00674               assert( 0 );
00675        }
00676 
00677        return 0;
00678 }
00679 
00680 static int
00681 aci_init( void )
00682 {
00683        /* OpenLDAP eXperimental Syntax */
00684        static slap_syntax_defs_rec aci_syntax_def = {
00685               "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
00686                      SLAP_SYNTAX_HIDE,
00687                      NULL,
00688                      OpenLDAPaciValidate,
00689                      OpenLDAPaciPretty
00690        };
00691        static slap_mrule_defs_rec aci_mr_def = {
00692               "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
00693                      "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
00694                      SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
00695                      NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
00696                      NULL, NULL,
00697                      NULL
00698        };
00699        static struct {
00700               char                 *name;
00701               char                 *desc;
00702               slap_mask_t          flags;
00703               AttributeDescription **ad;
00704        }             aci_at = {
00705               "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
00706                      "NAME 'OpenLDAPaci' "
00707                      "DESC 'OpenLDAP access control information (experimental)' "
00708                      "EQUALITY OpenLDAPaciMatch "
00709                      "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
00710                      "USAGE directoryOperation )",
00711               SLAP_AT_HIDE,
00712               &slap_ad_aci
00713        };
00714 
00715        int                  rc;
00716 
00717        /* ACI syntax */
00718        rc = register_syntax( &aci_syntax_def );
00719        if ( rc != 0 ) {
00720               return rc;
00721        }
00722        
00723        /* ACI equality rule */
00724        rc = register_matching_rule( &aci_mr_def );
00725        if ( rc != 0 ) {
00726               return rc;
00727        }
00728 
00729        /* ACI attribute */
00730        rc = register_at( aci_at.desc, aci_at.ad, 0 );
00731        if ( rc != LDAP_SUCCESS ) {
00732               Debug( LDAP_DEBUG_ANY,
00733                      "aci_init: at_register failed\n", 0, 0, 0 );
00734               return rc;
00735        }
00736 
00737        /* install flags */
00738        (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
00739 
00740        return rc;
00741 }
00742 
00743 static int
00744 dynacl_aci_parse(
00745        const char *fname,
00746        int lineno,
00747        const char *opts,
00748        slap_style_t sty,
00749        const char *right,
00750        void **privp )
00751 {
00752        AttributeDescription *ad = NULL;
00753        const char           *text = NULL;
00754 
00755        if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
00756               fprintf( stderr, "%s: line %d: "
00757                      "inappropriate style \"%s\" in \"aci\" by clause\n",
00758                      fname, lineno, style_strings[sty] );
00759               return -1;
00760        }
00761 
00762        if ( right != NULL && *right != '\0' ) {
00763               if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
00764                      fprintf( stderr,
00765                             "%s: line %d: aci \"%s\": %s\n",
00766                             fname, lineno, right, text );
00767                      return -1;
00768               }
00769 
00770        } else {
00771               ad = slap_ad_aci;
00772        }
00773 
00774        if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
00775               fprintf( stderr, "%s: line %d: "
00776                      "aci \"%s\": inappropriate syntax: %s\n",
00777                      fname, lineno, right,
00778                      ad->ad_type->sat_syntax_oid );
00779               return -1;
00780        }
00781 
00782        *privp = (void *)ad;
00783 
00784        return 0;
00785 }
00786 
00787 static int
00788 dynacl_aci_unparse( void *priv, struct berval *bv )
00789 {
00790        AttributeDescription *ad = ( AttributeDescription * )priv;
00791        char                 *ptr;
00792 
00793        assert( ad != NULL );
00794 
00795        bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
00796        ptr = lutil_strcopy( bv->bv_val, " aci=" );
00797        ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
00798        bv->bv_len = ptr - bv->bv_val;
00799 
00800        return 0;
00801 }
00802 
00803 static int
00804 dynacl_aci_mask(
00805        void                 *priv,
00806        Operation            *op,
00807        Entry                *e,
00808        AttributeDescription *desc,
00809        struct berval        *val,
00810        int                  nmatch,
00811        regmatch_t           *matches,
00812        slap_access_t        *grantp,
00813        slap_access_t        *denyp )
00814 {
00815        AttributeDescription *ad = ( AttributeDescription * )priv;
00816        Attribute            *at;
00817        slap_access_t        tgrant, tdeny, grant, deny;
00818 #ifdef LDAP_DEBUG
00819        char                 accessmaskbuf[ACCESSMASK_MAXLEN];
00820        char                 accessmaskbuf1[ACCESSMASK_MAXLEN];
00821 #endif /* LDAP_DEBUG */
00822 
00823        if ( BER_BVISEMPTY( &e->e_nname ) ) {
00824               /* no ACIs in the root DSE */
00825               return -1;
00826        }
00827 
00828        /* start out with nothing granted, nothing denied */
00829        ACL_INIT(tgrant);
00830        ACL_INIT(tdeny);
00831 
00832        /* get the aci attribute */
00833        at = attr_find( e->e_attrs, ad );
00834        if ( at != NULL ) {
00835               int           i;
00836 
00837               /* the aci is an multi-valued attribute.  The
00838                * rights are determined by OR'ing the individual
00839                * rights given by the acis.
00840                */
00841               for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
00842                      if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
00843                                    nmatch, matches, &grant, &deny,
00844                                    SLAP_ACI_SCOPE_ENTRY ) != 0 )
00845                      {
00846                             tgrant |= grant;
00847                             tdeny |= deny;
00848                      }
00849               }
00850               
00851               Debug( LDAP_DEBUG_ACL, "        <= aci_mask grant %s deny %s\n",
00852                        accessmask2str( tgrant, accessmaskbuf, 1 ), 
00853                        accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
00854        }
00855 
00856        /* If the entry level aci didn't contain anything valid for the 
00857         * current operation, climb up the tree and evaluate the
00858         * acis with scope set to subtree
00859         */
00860        if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
00861               struct berval parent_ndn;
00862 
00863               dnParent( &e->e_nname, &parent_ndn );
00864               while ( !BER_BVISEMPTY( &parent_ndn ) ){
00865                      int           i;
00866                      BerVarray     bvals = NULL;
00867                      int           ret, stop;
00868 
00869                      /* to solve the chicken'n'egg problem of accessing
00870                       * the OpenLDAPaci attribute, the direct access
00871                       * to the entry's attribute is unchecked; however,
00872                       * further accesses to OpenLDAPaci values in the 
00873                       * ancestors occur through backend_attribute(), i.e.
00874                       * with the identity of the operation, requiring
00875                       * further access checking.  For uniformity, this
00876                       * makes further requests occur as the rootdn, if
00877                       * any, i.e. searching for the OpenLDAPaci attribute
00878                       * is considered an internal search.  If this is not
00879                       * acceptable, then the same check needs be performed
00880                       * when accessing the entry's attribute. */
00881                      struct berval save_o_dn, save_o_ndn;
00882        
00883                      if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
00884                             save_o_dn = op->o_dn;
00885                             save_o_ndn = op->o_ndn;
00886 
00887                             op->o_dn = op->o_bd->be_rootdn;
00888                             op->o_ndn = op->o_bd->be_rootndn;
00889                      }
00890 
00891                      Debug( LDAP_DEBUG_ACL, "        checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
00892                      ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
00893 
00894                      if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
00895                             op->o_dn = save_o_dn;
00896                             op->o_ndn = save_o_ndn;
00897                      }
00898 
00899                      switch ( ret ) {
00900                      case LDAP_SUCCESS :
00901                             stop = 0;
00902                             if ( !bvals ) {
00903                                    break;
00904                             }
00905 
00906                             for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
00907                                    if ( aci_mask( op, e, desc, val,
00908                                                  &bvals[i],
00909                                                  nmatch, matches,
00910                                                  &grant, &deny,
00911                                                  SLAP_ACI_SCOPE_CHILDREN ) != 0 )
00912                                    {
00913                                           tgrant |= grant;
00914                                           tdeny |= deny;
00915                                           /* evaluation stops as soon as either a "deny" or a 
00916                                            * "grant" directive matches.
00917                                            */
00918                                           if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
00919                                                  stop = 1;
00920                                           }
00921                                    }
00922                                    Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n", 
00923                                           accessmask2str( tgrant, accessmaskbuf, 1 ),
00924                                           accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
00925                             }
00926                             break;
00927 
00928                      case LDAP_NO_SUCH_ATTRIBUTE:
00929                             /* just go on if the aci-Attribute is not present in
00930                              * the current entry 
00931                              */
00932                             Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
00933                             stop = 0;
00934                             break;
00935 
00936                      case LDAP_NO_SUCH_OBJECT:
00937                             /* We have reached the base object */
00938                             Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
00939                             stop = 1;
00940                             break;
00941 
00942                      default:
00943                             stop = 1;
00944                             break;
00945                      }
00946 
00947                      if ( stop ) {
00948                             break;
00949                      }
00950                      dnParent( &parent_ndn, &parent_ndn );
00951               }
00952        }
00953 
00954        *grantp = tgrant;
00955        *denyp = tdeny;
00956 
00957        return 0;
00958 }
00959 
00960 /* need to register this at some point */
00961 static slap_dynacl_t dynacl_aci = {
00962        "aci",
00963        dynacl_aci_parse,
00964        dynacl_aci_unparse,
00965        dynacl_aci_mask,
00966        NULL,
00967        NULL,
00968        NULL
00969 };
00970 
00971 int
00972 dynacl_aci_init( void )
00973 {
00974        int    rc;
00975 
00976        rc = aci_init();
00977 
00978        if ( rc == 0 ) {
00979               rc = slap_dynacl_register( &dynacl_aci );
00980        }
00981        
00982        return rc;
00983 }
00984 
00985 
00986 /* ACI syntax validation */
00987 
00988 /*
00989  * Matches given berval to array of bervals
00990  * Returns:
00991  *      >=0 if one if the array elements equals to this berval
00992  *       -1 if string was not found in array
00993  */
00994 static int 
00995 bv_getcaseidx(
00996        struct berval *bv, 
00997        const struct berval *arr[] )
00998 {
00999        int i;
01000 
01001        if ( BER_BVISEMPTY( bv ) ) {
01002               return -1;
01003        }
01004 
01005        for ( i = 0; arr[ i ] != NULL ; i++ ) {
01006               if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
01007                      return i;
01008               }
01009        }
01010 
01011        return -1;
01012 }
01013 
01014 
01015 /* Returns what have left in input berval after current sub */
01016 static void
01017 bv_get_tail(
01018        struct berval *val,
01019        struct berval *sub,
01020        struct berval *tail )
01021 {
01022        int           head_len;
01023 
01024        tail->bv_val = sub->bv_val + sub->bv_len;
01025        head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
01026        tail->bv_len = val->bv_len - head_len;
01027 }
01028 
01029 
01030 /*
01031  * aci is accepted in following form:
01032  *    oid#scope#rights#type#subject
01033  * Where:
01034  *    oid       := numeric OID (currently ignored)
01035  *    scope     := entry|children|subtree
01036  *    rights    := right[[$right]...]
01037  *    right     := (grant|deny);action
01038  *    action    := perms;attrs[[;perms;attrs]...]
01039  *    perms     := perm[[,perm]...]
01040  *    perm      := c|s|r|w|x
01041  *    attrs     := attribute[[,attribute]..]|"[all]"
01042  *    attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
01043  *    type      := public|users|self|dnattr|group|role|set|set-ref|
01044  *                 access_id|subtree|onelevel|children
01045  */
01046 static int 
01047 OpenLDAPaciValidatePerms(
01048        struct berval *perms ) 
01049 {
01050        ber_len_t     i;
01051 
01052        for ( i = 0; i < perms->bv_len; ) {
01053               switch ( perms->bv_val[ i ] ) {
01054               case 'x':
01055               case 'd':
01056               case 'c':
01057               case 's':
01058               case 'r':
01059               case 'w':
01060                      break;
01061 
01062               default:
01063                       Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val, 0, 0 );
01064                      return LDAP_INVALID_SYNTAX;
01065               }
01066 
01067               if ( ++i == perms->bv_len ) {
01068                      return LDAP_SUCCESS;
01069               }
01070 
01071               while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
01072                      i++;
01073 
01074               assert( i != perms->bv_len );
01075 
01076               if ( perms->bv_val[ i ] != ',' ) {
01077                       Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val, 0, 0 );
01078                      return LDAP_INVALID_SYNTAX;
01079               }
01080 
01081               do {
01082                      i++;
01083               } while ( perms->bv_val[ i ] == ' ' );
01084        }
01085 
01086        return LDAP_SUCCESS;
01087 }
01088 
01089 static const struct berval *ACIgrantdeny[] = {
01090        &aci_bv[ ACI_BV_GRANT ],
01091        &aci_bv[ ACI_BV_DENY ],
01092        NULL
01093 };
01094 
01095 static int 
01096 OpenLDAPaciValidateRight(
01097        struct berval *action )
01098 {
01099        struct berval bv = BER_BVNULL;
01100        int           i;
01101 
01102        /* grant|deny */
01103        if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
01104               bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
01105        {
01106               Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val, 0, 0 );
01107               return LDAP_INVALID_SYNTAX;
01108        }
01109 
01110        for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
01111               if ( i & 1 ) {
01112                      /* perms */
01113                      if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
01114                      {
01115                             return LDAP_INVALID_SYNTAX;
01116                      }
01117 
01118               } else {
01119                      /* attr */
01120                      AttributeDescription *ad;
01121                      const char           *text;
01122                      struct berval        attr, left, right;
01123                      int                  j;
01124 
01125                      /* could be "[all]" or an attribute description */
01126                      if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
01127                             continue;
01128                      }
01129 
01130 
01131                      for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 
01132                      {
01133                             ad = NULL;
01134                             text = NULL;
01135                             if ( acl_get_part( &attr, 0, '=', &left ) < 0
01136                                    || acl_get_part( &attr, 1, '=', &right ) < 0 ) 
01137                             {
01138                                    if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 
01139                                    {
01140                                           Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
01141                                           return LDAP_INVALID_SYNTAX;
01142                                    }
01143                             } else {
01144                                    if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 
01145                                    {
01146                                           Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
01147                                           return LDAP_INVALID_SYNTAX;
01148                                    }
01149                             }
01150                      }
01151               }
01152        }
01153        
01154        /* "perms;attr" go in pairs */
01155        if ( i > 0 && ( i & 1 ) == 0 ) {
01156               return LDAP_SUCCESS;
01157 
01158        } else {
01159               Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
01160               return LDAP_INVALID_SYNTAX;
01161        }
01162 
01163        return LDAP_SUCCESS;
01164 }
01165 
01166 static int
01167 OpenLDAPaciNormalizeRight(
01168        struct berval *action,
01169        struct berval *naction,
01170        void          *ctx )
01171 {
01172        struct berval grantdeny,
01173                      perms = BER_BVNULL,
01174                      bv = BER_BVNULL;
01175        int           idx,
01176                      i;
01177 
01178        /* grant|deny */
01179        if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
01180                Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val, 0, 0 );
01181               return LDAP_INVALID_SYNTAX;
01182        }
01183        idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
01184        if ( idx == -1 ) {
01185                Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val, 0, 0 );
01186               return LDAP_INVALID_SYNTAX;
01187        }
01188 
01189        ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
01190 
01191        for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
01192               struct berval nattrs = BER_BVNULL;
01193               int           freenattrs = 1;
01194               if ( i & 1 ) {
01195                      /* perms */
01196                      if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
01197                      {
01198                             return LDAP_INVALID_SYNTAX;
01199                      }
01200                      perms = bv;
01201 
01202               } else {
01203                      /* attr */
01204                      char          *ptr;
01205 
01206                      /* could be "[all]" or an attribute description */
01207                      if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
01208                             nattrs = aci_bv[ ACI_BV_BR_ALL ];
01209                             freenattrs = 0;
01210 
01211                      } else {
01212                             AttributeDescription *ad = NULL;
01213                             AttributeDescription adstatic= { 0 };
01214                             const char           *text = NULL;
01215                             struct berval        attr, left, right;
01216                             int                  j;
01217                             int                  len;
01218 
01219                             for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 
01220                             {
01221                                    ad = NULL;
01222                                    text = NULL;
01223                                    /* openldap 2.1 aci compabitibility [entry] -> entry */
01224                                    if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
01225                                           ad = &adstatic;
01226                                           adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
01227 
01228                                    /* openldap 2.1 aci compabitibility [children] -> children */
01229                                    } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
01230                                           ad = &adstatic;
01231                                           adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
01232 
01233                                    /* openldap 2.1 aci compabitibility [all] -> only [all] */
01234                                    } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
01235                                           ber_memfree_x( nattrs.bv_val, ctx );
01236                                           nattrs = aci_bv[ ACI_BV_BR_ALL ];
01237                                           freenattrs = 0;
01238                                           break;
01239 
01240                                    } else if ( acl_get_part( &attr, 0, '=', &left ) < 0
01241                                           || acl_get_part( &attr, 1, '=', &right ) < 0 ) 
01242                                    {
01243                                           if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 
01244                                           {
01245                                                  ber_memfree_x( nattrs.bv_val, ctx );
01246                                                  Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
01247                                                  return LDAP_INVALID_SYNTAX;
01248                                           }
01249 
01250                                    } else {
01251                                           if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 
01252                                           {
01253                                                  ber_memfree_x( nattrs.bv_val, ctx );
01254                                                  Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
01255                                                  return LDAP_INVALID_SYNTAX;
01256                                           }
01257                                    }
01258                                    
01259                             
01260                                    len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
01261                                           + ad->ad_cname.bv_len;
01262                                    nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx );
01263                                    ptr = &nattrs.bv_val[ nattrs.bv_len ];
01264                                    if ( !BER_BVISEMPTY( &nattrs ) ) {
01265                                           *ptr++ = ',';
01266                                    }
01267                                    ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
01268                                    ptr[ 0 ] = '\0';
01269                                    nattrs.bv_len = len;
01270                             }
01271 
01272                      }
01273 
01274                      naction->bv_val = ber_memrealloc_x( naction->bv_val,
01275                             naction->bv_len + STRLENOF( ";" )
01276                             + perms.bv_len + STRLENOF( ";" )
01277                             + nattrs.bv_len + 1,
01278                             ctx );
01279 
01280                      ptr = &naction->bv_val[ naction->bv_len ];
01281                      ptr[ 0 ] = ';';
01282                      ptr++;
01283                      ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
01284                      ptr[ 0 ] = ';';
01285                      ptr++;
01286                      ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
01287                      ptr[ 0 ] = '\0';
01288                      naction->bv_len += STRLENOF( ";" ) + perms.bv_len
01289                             + STRLENOF( ";" ) + nattrs.bv_len;
01290                      if ( freenattrs ) {
01291                             ber_memfree_x( nattrs.bv_val, ctx );
01292                      }
01293               }
01294        }
01295        
01296        /* perms;attr go in pairs */
01297        if ( i > 1 && ( i & 1 ) ) {
01298               return LDAP_SUCCESS;
01299 
01300        } else {
01301               Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
01302               return LDAP_INVALID_SYNTAX;
01303        }
01304 }
01305 
01306 static int 
01307 OpenLDAPaciValidateRights(
01308        struct berval *actions )
01309 
01310 {
01311        struct berval bv = BER_BVNULL;
01312        int           i;
01313 
01314        for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
01315               if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
01316                      return LDAP_INVALID_SYNTAX;
01317               }
01318        }
01319 
01320        return LDAP_SUCCESS;
01321 }
01322 
01323 static int 
01324 OpenLDAPaciNormalizeRights(
01325        struct berval *actions,
01326        struct berval *nactions,
01327        void          *ctx )
01328 
01329 {
01330        struct berval bv = BER_BVNULL;
01331        int           i;
01332 
01333        BER_BVZERO( nactions );
01334        for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
01335               int           rc;
01336               struct berval nbv;
01337 
01338               rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
01339               if ( rc != LDAP_SUCCESS ) {
01340                      ber_memfree_x( nactions->bv_val, ctx );
01341                      BER_BVZERO( nactions );
01342                      return LDAP_INVALID_SYNTAX;
01343               }
01344 
01345               if ( i == 0 ) {
01346                      *nactions = nbv;
01347 
01348               } else {
01349                      nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
01350                             nactions->bv_len + STRLENOF( "$" )
01351                             + nbv.bv_len + 1,
01352                             ctx );
01353                      nactions->bv_val[ nactions->bv_len ] = '$';
01354                      AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
01355                             nbv.bv_val, nbv.bv_len + 1 );
01356                      ber_memfree_x( nbv.bv_val, ctx );
01357                      nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
01358               }
01359               BER_BVZERO( &nbv );
01360        }
01361 
01362        return LDAP_SUCCESS;
01363 }
01364 
01365 static const struct berval *OpenLDAPaciscopes[] = {
01366        &aci_bv[ ACI_BV_ENTRY ],
01367        &aci_bv[ ACI_BV_CHILDREN ],
01368        &aci_bv[ ACI_BV_SUBTREE ],
01369 
01370        NULL
01371 };
01372 
01373 static const struct berval *OpenLDAPacitypes[] = {
01374        /* DN-valued */
01375        &aci_bv[ ACI_BV_GROUP ], 
01376        &aci_bv[ ACI_BV_ROLE ],
01377 
01378 /* set to one past the last DN-valued type with options (/) */
01379 #define       LAST_OPTIONAL 2
01380 
01381        &aci_bv[ ACI_BV_ACCESS_ID ],
01382        &aci_bv[ ACI_BV_SUBTREE ],
01383        &aci_bv[ ACI_BV_ONELEVEL ],
01384        &aci_bv[ ACI_BV_CHILDREN ],
01385 
01386 /* set to one past the last DN-valued type */
01387 #define LAST_DNVALUED       6
01388 
01389        /* non DN-valued */
01390        &aci_bv[ ACI_BV_DNATTR ],
01391        &aci_bv[ ACI_BV_PUBLIC ],
01392        &aci_bv[ ACI_BV_USERS ],
01393        &aci_bv[ ACI_BV_SELF ],
01394        &aci_bv[ ACI_BV_SET ],
01395        &aci_bv[ ACI_BV_SET_REF ],
01396 
01397        NULL
01398 };
01399 
01400 static int
01401 OpenLDAPaciValidate(
01402        Syntax        *syntax,
01403        struct berval *val )
01404 {
01405        struct berval oid = BER_BVNULL,
01406                      scope = BER_BVNULL,
01407                      rights = BER_BVNULL,
01408                      type = BER_BVNULL,
01409                      subject = BER_BVNULL;
01410        int           idx;
01411        int           rc;
01412        
01413        if ( BER_BVISEMPTY( val ) ) {
01414               Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n", 0, 0, 0 );
01415               return LDAP_INVALID_SYNTAX;
01416        }
01417 
01418        /* oid */
01419        if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
01420               numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
01421        {
01422               /* NOTE: the numericoidValidate() is rather pedantic;
01423                * I'd replace it with X-ORDERED VALUES so that
01424                * it's guaranteed values are maintained and used
01425                * in the desired order */
01426               Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val, 0, 0 );
01427               return LDAP_INVALID_SYNTAX;
01428        }
01429 
01430        /* scope */
01431        if ( acl_get_part( val, 1, '#', &scope ) < 0 || 
01432               bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
01433        {
01434               Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val, 0, 0 );
01435               return LDAP_INVALID_SYNTAX;
01436        }
01437 
01438        /* rights */
01439        if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
01440               OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) 
01441        {
01442               return LDAP_INVALID_SYNTAX;
01443        }
01444 
01445        /* type */
01446        if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
01447               Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val, 0, 0 );
01448               return LDAP_INVALID_SYNTAX;
01449        }
01450        idx = bv_getcaseidx( &type, OpenLDAPacitypes );
01451        if ( idx == -1 ) {
01452               struct berval isgr;
01453 
01454               if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
01455                      Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val, 0, 0 );
01456                      return LDAP_INVALID_SYNTAX;
01457               }
01458 
01459               idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
01460               if ( idx == -1 || idx >= LAST_OPTIONAL ) {
01461                      Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val, 0, 0 );
01462                      return LDAP_INVALID_SYNTAX;
01463               }
01464        }
01465 
01466        /* subject */
01467        bv_get_tail( val, &type, &subject );
01468        if ( subject.bv_val[ 0 ] != '#' ) {
01469               Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val, 0, 0 );
01470               return LDAP_INVALID_SYNTAX;
01471        }
01472 
01473        if ( idx >= LAST_DNVALUED ) {
01474               if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
01475                      AttributeDescription *ad = NULL;
01476                      const char           *text = NULL;
01477 
01478                      rc = slap_bv2ad( &subject, &ad, &text );
01479                      if ( rc != LDAP_SUCCESS ) {
01480                             Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
01481                             return LDAP_INVALID_SYNTAX;
01482                      }
01483 
01484                      if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
01485                             /* FIXME: allow nameAndOptionalUID? */
01486                             Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
01487                             return LDAP_INVALID_SYNTAX;
01488                      }
01489               }
01490 
01491               /* not a DN */
01492               return LDAP_SUCCESS;
01493 
01494        } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
01495                      || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
01496        {
01497               /* do {group|role}/oc/at check */
01498               struct berval ocbv = BER_BVNULL,
01499                             atbv = BER_BVNULL;
01500 
01501               ocbv.bv_val = ber_bvchr( &type, '/' );
01502               if ( ocbv.bv_val != NULL ) {
01503                      ocbv.bv_val++;
01504                      ocbv.bv_len = type.bv_len
01505                                    - ( ocbv.bv_val - type.bv_val );
01506 
01507                      atbv.bv_val = ber_bvchr( &ocbv, '/' );
01508                      if ( atbv.bv_val != NULL ) {
01509                             AttributeDescription *ad = NULL;
01510                             const char           *text = NULL;
01511                             int                  rc;
01512 
01513                             atbv.bv_val++;
01514                             atbv.bv_len = type.bv_len
01515                                    - ( atbv.bv_val - type.bv_val );
01516                             ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
01517 
01518                             rc = slap_bv2ad( &atbv, &ad, &text );
01519                             if ( rc != LDAP_SUCCESS ) {
01520                                     Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
01521                                    return LDAP_INVALID_SYNTAX;
01522                             }
01523                      }
01524 
01525                      if ( oc_bvfind( &ocbv ) == NULL ) {
01526                              Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val, 0, 0 );
01527                             return LDAP_INVALID_SYNTAX;
01528                      }
01529               }
01530        }
01531 
01532        if ( BER_BVISEMPTY( &subject ) ) {
01533               /* empty DN invalid */
01534                Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val, 0, 0 );
01535               return LDAP_INVALID_SYNTAX;
01536        }
01537 
01538        subject.bv_val++;
01539        subject.bv_len--;
01540 
01541        /* FIXME: pass DN syntax? */
01542        rc = dnValidate( NULL, &subject );
01543        if ( rc != LDAP_SUCCESS ) {
01544                Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val, 0, 0 );
01545        }
01546        return rc;
01547 }
01548 
01549 static int
01550 OpenLDAPaciPrettyNormal(
01551        struct berval *val,
01552        struct berval *out,
01553        void          *ctx,
01554        int           normalize )
01555 {
01556        struct berval oid = BER_BVNULL,
01557                      scope = BER_BVNULL,
01558                      rights = BER_BVNULL,
01559                      nrights = BER_BVNULL,
01560                      type = BER_BVNULL,
01561                      ntype = BER_BVNULL,
01562                      subject = BER_BVNULL,
01563                      nsubject = BER_BVNULL;
01564        int           idx,
01565                      rc = LDAP_SUCCESS,
01566                      freesubject = 0,
01567                      freetype = 0;
01568        char          *ptr;
01569 
01570        BER_BVZERO( out );
01571 
01572        if ( BER_BVISEMPTY( val ) ) {
01573               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n", 0, 0, 0 );
01574               return LDAP_INVALID_SYNTAX;
01575        }
01576 
01577        /* oid: if valid, it's already normalized */
01578        if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
01579               numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
01580        {
01581               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val, 0, 0 );
01582               return LDAP_INVALID_SYNTAX;
01583        }
01584 
01585        /* scope: normalize by replacing with OpenLDAPaciscopes */
01586        if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
01587               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val, 0, 0 );
01588               return LDAP_INVALID_SYNTAX;
01589        }
01590        idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
01591        if ( idx == -1 ) {
01592               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val, 0, 0 );
01593               return LDAP_INVALID_SYNTAX;
01594        }
01595        scope = *OpenLDAPaciscopes[ idx ];
01596 
01597        /* rights */
01598        if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
01599               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val, 0, 0 );
01600               return LDAP_INVALID_SYNTAX;
01601        }
01602        if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
01603               != LDAP_SUCCESS ) 
01604        {
01605               return LDAP_INVALID_SYNTAX;
01606        }
01607 
01608        /* type */
01609        if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
01610               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val, 0, 0 );
01611               rc = LDAP_INVALID_SYNTAX;
01612               goto cleanup;
01613        }
01614        idx = bv_getcaseidx( &type, OpenLDAPacitypes );
01615        if ( idx == -1 ) {
01616               struct berval isgr;
01617 
01618               if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
01619                       Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val, 0, 0 );
01620                      rc = LDAP_INVALID_SYNTAX;
01621                      goto cleanup;
01622               }
01623 
01624               idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
01625               if ( idx == -1 || idx >= LAST_OPTIONAL ) {
01626                       Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val, 0, 0 );
01627                      rc = LDAP_INVALID_SYNTAX;
01628                      goto cleanup;
01629               }
01630        }
01631        ntype = *OpenLDAPacitypes[ idx ];
01632 
01633        /* subject */
01634        bv_get_tail( val, &type, &subject );
01635 
01636        if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
01637                Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val, 0, 0 );
01638               rc = LDAP_INVALID_SYNTAX;
01639               goto cleanup;
01640        }
01641 
01642        subject.bv_val++;
01643        subject.bv_len--;
01644 
01645        if ( idx < LAST_DNVALUED ) {
01646               /* FIXME: pass DN syntax? */
01647               if ( normalize ) {
01648                      rc = dnNormalize( 0, NULL, NULL,
01649                             &subject, &nsubject, ctx );
01650               } else {
01651                      rc = dnPretty( NULL, &subject, &nsubject, ctx );
01652               }
01653 
01654               if ( rc == LDAP_SUCCESS ) {
01655                      freesubject = 1;
01656 
01657               } else {
01658                        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val, 0, 0 );
01659                      goto cleanup;
01660               }
01661 
01662               if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
01663                      || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
01664               {
01665                      /* do {group|role}/oc/at check */
01666                      struct berval ocbv = BER_BVNULL,
01667                                    atbv = BER_BVNULL;
01668 
01669                      ocbv.bv_val = ber_bvchr( &type, '/' );
01670                      if ( ocbv.bv_val != NULL ) {
01671                             ObjectClass          *oc = NULL;
01672                             AttributeDescription *ad = NULL;
01673                             const char           *text = NULL;
01674                             int                  rc;
01675                             struct berval        bv;
01676 
01677                             bv.bv_len = ntype.bv_len;
01678 
01679                             ocbv.bv_val++;
01680                             ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
01681 
01682                             atbv.bv_val = ber_bvchr( &ocbv, '/' );
01683                             if ( atbv.bv_val != NULL ) {
01684                                    atbv.bv_val++;
01685                                    atbv.bv_len = type.bv_len
01686                                           - ( atbv.bv_val - type.bv_val );
01687                                    ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
01688        
01689                                    rc = slap_bv2ad( &atbv, &ad, &text );
01690                                    if ( rc != LDAP_SUCCESS ) {
01691                                                Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
01692                                           rc = LDAP_INVALID_SYNTAX;
01693                                           goto cleanup;
01694                                    }
01695 
01696                                    bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
01697                             }
01698 
01699                             oc = oc_bvfind( &ocbv );
01700                             if ( oc == NULL ) {
01701                                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val, 0, 0 );
01702                                    rc = LDAP_INVALID_SYNTAX;
01703                                    goto cleanup;
01704                             }
01705 
01706                             bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
01707                             bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
01708 
01709                             ptr = bv.bv_val;
01710                             ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
01711                             ptr[ 0 ] = '/';
01712                             ptr++;
01713                             ptr = lutil_strncopy( ptr,
01714                                    oc->soc_cname.bv_val,
01715                                    oc->soc_cname.bv_len );
01716                             if ( ad != NULL ) {
01717                                    ptr[ 0 ] = '/';
01718                                    ptr++;
01719                                    ptr = lutil_strncopy( ptr,
01720                                           ad->ad_cname.bv_val,
01721                                           ad->ad_cname.bv_len );
01722                             }
01723                             ptr[ 0 ] = '\0';
01724 
01725                             ntype = bv;
01726                             freetype = 1;
01727                      }
01728               }
01729 
01730        } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
01731               AttributeDescription *ad = NULL;
01732               const char           *text = NULL;
01733               int                  rc;
01734 
01735               rc = slap_bv2ad( &subject, &ad, &text );
01736               if ( rc != LDAP_SUCCESS ) {
01737                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
01738                      rc = LDAP_INVALID_SYNTAX;
01739                      goto cleanup;
01740               }
01741 
01742               if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
01743                      /* FIXME: allow nameAndOptionalUID? */
01744                         Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
01745                      rc = LDAP_INVALID_SYNTAX;
01746                      goto cleanup;
01747               }
01748 
01749               nsubject = ad->ad_cname;
01750 
01751        } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ]
01752               || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] )
01753        {
01754               /* NOTE: dunno how to normalize it... */
01755               nsubject = subject;
01756        }
01757 
01758 
01759        out->bv_len = 
01760               oid.bv_len + STRLENOF( "#" )
01761               + scope.bv_len + STRLENOF( "#" )
01762               + nrights.bv_len + STRLENOF( "#" )
01763               + ntype.bv_len + STRLENOF( "#" )
01764               + nsubject.bv_len;
01765 
01766        out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
01767        ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
01768        ptr[ 0 ] = '#';
01769        ptr++;
01770        ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
01771        ptr[ 0 ] = '#';
01772        ptr++;
01773        ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
01774        ptr[ 0 ] = '#';
01775        ptr++;
01776        ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
01777        ptr[ 0 ] = '#';
01778        ptr++;
01779        if ( !BER_BVISNULL( &nsubject ) ) {
01780               ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
01781        }
01782        ptr[ 0 ] = '\0';
01783 
01784 cleanup:;
01785        if ( freesubject ) {
01786               ber_memfree_x( nsubject.bv_val, ctx );
01787        }
01788 
01789        if ( freetype ) {
01790               ber_memfree_x( ntype.bv_val, ctx );
01791        }
01792 
01793        if ( !BER_BVISNULL( &nrights ) ) {
01794               ber_memfree_x( nrights.bv_val, ctx );
01795        }
01796 
01797        return rc;
01798 }
01799 
01800 static int
01801 OpenLDAPaciPretty(
01802        Syntax        *syntax,
01803        struct berval *val,
01804        struct berval *out,
01805        void          *ctx )
01806 {
01807        return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
01808 }
01809 
01810 static int
01811 OpenLDAPaciNormalize(
01812        slap_mask_t   use,
01813        Syntax        *syntax,
01814        MatchingRule  *mr,
01815        struct berval *val,
01816        struct berval *out,
01817        void          *ctx )
01818 {
01819        return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
01820 }
01821 
01822 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
01823 /*
01824  * FIXME: need config and Makefile.am code to ease building
01825  * as dynamic module
01826  */
01827 int
01828 init_module( int argc, char *argv[] )
01829 {
01830        return dynacl_aci_init();
01831 }
01832 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
01833 
01834 #endif /* SLAPD_ACI_ENABLED */
01835