]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/aci.c
Import recent changes in 2.4 for release
[openldap] / servers / slapd / aci.c
index a77a9fb3ceefedb1667701975ef8556f5f6ac143..a86e3acd1fe1380a4bf31a9284b0c94bd3869eac 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2005 The OpenLDAP Foundation.
+ * Copyright 1998-2007 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "lber_pvt.h"
 #include "lutil.h"
 
-#define ACI_BUF_SIZE   1024    /* use most appropriate size */
+/* use most appropriate size */
+#define ACI_BUF_SIZE                   1024
 
-#ifdef SLAP_DYNACL
-static
-#endif /* SLAP_DYNACL */
-AttributeDescription *slap_ad_aci;
+/* move to "stable" when no longer experimental */
+#define SLAPD_ACI_SYNTAX               "1.3.6.1.4.1.4203.666.2.1"
+
+/* change this to "OpenLDAPset" */
+#define SLAPD_ACI_SET_ATTR             "template"
+
+typedef enum slap_aci_scope_t {
+       SLAP_ACI_SCOPE_ENTRY            = 0x1,
+       SLAP_ACI_SCOPE_CHILDREN         = 0x2,
+       SLAP_ACI_SCOPE_SUBTREE          = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
+} slap_aci_scope_t;
+
+enum {
+       ACI_BV_ENTRY,
+       ACI_BV_CHILDREN,
+       ACI_BV_ONELEVEL,
+       ACI_BV_SUBTREE,
+
+       ACI_BV_BR_ENTRY,
+       ACI_BV_BR_CHILDREN,
+       ACI_BV_BR_ALL,
+
+       ACI_BV_ACCESS_ID,
+       ACI_BV_PUBLIC,
+       ACI_BV_USERS,
+       ACI_BV_SELF,
+       ACI_BV_DNATTR,
+       ACI_BV_GROUP,
+       ACI_BV_ROLE,
+       ACI_BV_SET,
+       ACI_BV_SET_REF,
+
+       ACI_BV_GRANT,
+       ACI_BV_DENY,
+
+       ACI_BV_GROUP_CLASS,
+       ACI_BV_GROUP_ATTR,
+       ACI_BV_ROLE_CLASS,
+       ACI_BV_ROLE_ATTR,
+
+       ACI_BV_SET_ATTR,
+
+       ACI_BV_LAST
+};
+
+static const struct berval     aci_bv[] = {
+       /* scope */
+       BER_BVC("entry"),
+       BER_BVC("children"),
+       BER_BVC("onelevel"),
+       BER_BVC("subtree"),
+
+       /* */
+       BER_BVC("[entry]"),
+       BER_BVC("[children]"),
+       BER_BVC("[all]"),
+
+       /* type */
+       BER_BVC("access-id"),
+       BER_BVC("public"),
+       BER_BVC("users"),
+       BER_BVC("self"),
+       BER_BVC("dnattr"),
+       BER_BVC("group"),
+       BER_BVC("role"),
+       BER_BVC("set"),
+       BER_BVC("set-ref"),
+
+       /* actions */
+       BER_BVC("grant"),
+       BER_BVC("deny"),
+
+       /* schema */
+       BER_BVC(SLAPD_GROUP_CLASS),
+       BER_BVC(SLAPD_GROUP_ATTR),
+       BER_BVC(SLAPD_ROLE_CLASS),
+       BER_BVC(SLAPD_ROLE_ATTR),
+
+       BER_BVC(SLAPD_ACI_SET_ATTR),
+
+       BER_BVNULL
+};
+
+static AttributeDescription    *slap_ad_aci;
 
 static int
 OpenLDAPaciValidate(
@@ -197,14 +278,31 @@ aci_list_get_attr_rights(
        ACL_INIT(mask);
        for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
                if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
+                       Debug( LDAP_DEBUG_ACL,
+                               "        <= aci_list_get_attr_rights "
+                               "test %s for %s -> failed\n",
+                               bv.bv_val, attr->bv_val, 0 );
                        continue;
                }
 
+               Debug( LDAP_DEBUG_ACL,
+                       "        <= aci_list_get_attr_rights "
+                       "test %s for %s -> ok\n",
+                       bv.bv_val, attr->bv_val, 0 );
+
                if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
+                       Debug( LDAP_DEBUG_ACL,
+                               "        <= aci_list_get_attr_rights "
+                               "test no rights\n",
+                               0, 0, 0 );
                        continue;
                }
 
                mask |= aci_list_map_rights( &bv );
+               Debug( LDAP_DEBUG_ACL,
+                       "        <= aci_list_get_attr_rights "
+                       "rights %s to mask 0x%x\n",
+                       bv.bv_val, mask, 0 );
        }
 
        return mask;
@@ -212,22 +310,22 @@ aci_list_get_attr_rights(
 
 static int
 aci_list_get_rights(
-       struct berval           *list,
-       const struct berval     *attr,
-       struct berval           *val,
-       slap_access_t           *grant,
-       slap_access_t           *deny )
+       struct berval   *list,
+       struct berval   *attr,
+       struct berval   *val,
+       slap_access_t   *grant,
+       slap_access_t   *deny )
 {
-       struct berval   perm, actn;
+       struct berval   perm, actn, baseattr;
        slap_access_t   *mask;
        int             i, found;
 
-       if ( attr == NULL || BER_BVISEMPTY( attr )
-                       || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
-       {
-               attr = &aci_bv[ ACI_BV_BR_ENTRY ];
-       }
+       if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
+               attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
 
+       } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
+               attr = &baseattr;
+       }
        found = 0;
        ACL_INIT(*grant);
        ACL_INIT(*deny);
@@ -321,7 +419,7 @@ done:
        return rc;
 }
 
-int
+static int
 aci_mask(
        Operation               *op,
        Entry                   *e,
@@ -364,7 +462,7 @@ aci_mask(
           This routine now supports scope={ENTRY,CHILDREN}
           with the semantics:
             - ENTRY applies to "entry" and "subtree";
-            - CHILDREN aplies to "children" and "subtree"
+            - CHILDREN applies to "children" and "subtree"
         */
 
        /* check that the aci has all 5 components */
@@ -554,12 +652,12 @@ aci_mask(
                }
 
        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
-               if ( acl_match_set( &sdn, op, e, 0 ) ) {
+               if ( acl_match_set( &sdn, op, e, NULL ) ) {
                        return 1;
                }
 
        } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
-               if ( acl_match_set( &sdn, op, e, 1 ) ) {
+               if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
                        return 1;
                }
 
@@ -571,10 +669,10 @@ aci_mask(
        return 0;
 }
 
-int
+static int
 aci_init( void )
 {
-       /* OpenLDAP Experimental Syntax */
+       /* OpenLDAP eXperimental Syntax */
        static slap_syntax_defs_rec aci_syntax_def = {
                "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
                        SLAP_SYNTAX_HIDE,
@@ -605,10 +703,7 @@ aci_init( void )
                &slap_ad_aci
        };
 
-       LDAPAttributeType       *at;
-       AttributeType           *sat;
        int                     rc;
-       const char              *text;
 
        /* ACI syntax */
        rc = register_syntax( &aci_syntax_def );
@@ -623,47 +718,19 @@ aci_init( void )
        }
 
        /* ACI attribute */
-       at = ldap_str2attributetype( aci_at.desc,
-               &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
-       if ( !at ) {
-               Debug( LDAP_DEBUG_ANY,
-                       "%s AttributeType load failed: %s %s\n",
-                       aci_at.name, ldap_scherr2str( rc ), text );
-               return rc;
-       }
-
-       rc = at_add( at, 0, &sat, &text );
-       if ( rc != LDAP_SUCCESS ) {
-               ldap_attributetype_free( at );
-               fprintf( stderr, "iMUX_monitor_schema_init: "
-                       "AttributeType load failed: %s %s\n",
-                       scherr2str( rc ), text );
-               return rc;
-       }
-       ldap_memfree( at );
-
-       rc = slap_str2ad( aci_at.name,
-                       aci_at.ad, &text );
+       rc = register_at( aci_at.desc, aci_at.ad, 0 );
        if ( rc != LDAP_SUCCESS ) {
                Debug( LDAP_DEBUG_ANY,
-                       "unable to find AttributeDescription "
-                       "\"%s\": %d (%s)\n",
-                       aci_at.name, rc, text );
-               return 1;
+                       "aci_init: at_register failed\n", 0, 0, 0 );
+               return rc;
        }
 
        /* install flags */
-       sat->sat_flags |= aci_at.flags;
+       (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
 
        return rc;
 }
 
-#ifdef SLAP_DYNACL
-/*
- * FIXME: there is a silly dependence that makes it difficult
- * to move ACIs in a run-time loadable module under the "dynacl" 
- * umbrella, because sets share some helpers with ACIs.
- */
 static int
 dynacl_aci_parse(
        const char *fname,
@@ -744,6 +811,11 @@ dynacl_aci_mask(
        char                    accessmaskbuf1[ACCESSMASK_MAXLEN];
 #endif /* LDAP_DEBUG */
 
+       if ( BER_BVISEMPTY( &e->e_nname ) ) {
+               /* no ACIs in the root DSE */
+               return -1;
+       }
+
        /* start out with nothing granted, nothing denied */
        ACL_INIT(tgrant);
        ACL_INIT(tdeny);
@@ -767,7 +839,7 @@ dynacl_aci_mask(
                        }
                }
                
-               Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+               Debug( LDAP_DEBUG_ACL, "        <= aci_mask grant %s deny %s\n",
                          accessmask2str( tgrant, accessmaskbuf, 1 ), 
                          accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
        }
@@ -779,35 +851,41 @@ dynacl_aci_mask(
        if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
                struct berval   parent_ndn;
 
-#if 1
-               /* to solve the chicken'n'egg problem of accessing
-                * the OpenLDAPaci attribute, the direct access
-                * to the entry's attribute is unchecked; however,
-                * further accesses to OpenLDAPaci values in the 
-                * ancestors occur through backend_attribute(), i.e.
-                * with the identity of the operation, requiring
-                * further access checking.  For uniformity, this
-                * makes further requests occur as the rootdn, if
-                * any, i.e. searching for the OpenLDAPaci attribute
-                * is considered an internal search.  If this is not
-                * acceptable, then the same check needs be performed
-                * when accessing the entry's attribute. */
-               Operation       op2 = *op;
-
-               if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
-                       op2.o_dn = op->o_bd->be_rootdn;
-                       op2.o_ndn = op->o_bd->be_rootndn;
-               }
-#endif
-
                dnParent( &e->e_nname, &parent_ndn );
                while ( !BER_BVISEMPTY( &parent_ndn ) ){
                        int             i;
                        BerVarray       bvals = NULL;
                        int             ret, stop;
 
-                       Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
-                       ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+                       /* to solve the chicken'n'egg problem of accessing
+                        * the OpenLDAPaci attribute, the direct access
+                        * to the entry's attribute is unchecked; however,
+                        * further accesses to OpenLDAPaci values in the 
+                        * ancestors occur through backend_attribute(), i.e.
+                        * with the identity of the operation, requiring
+                        * further access checking.  For uniformity, this
+                        * makes further requests occur as the rootdn, if
+                        * any, i.e. searching for the OpenLDAPaci attribute
+                        * is considered an internal search.  If this is not
+                        * acceptable, then the same check needs be performed
+                        * when accessing the entry's attribute. */
+                       struct berval   save_o_dn, save_o_ndn;
+       
+                       if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+                               save_o_dn = op->o_dn;
+                               save_o_ndn = op->o_ndn;
+
+                               op->o_dn = op->o_bd->be_rootdn;
+                               op->o_ndn = op->o_bd->be_rootndn;
+                       }
+
+                       Debug( LDAP_DEBUG_ACL, "        checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
+                       ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+
+                       if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+                               op->o_dn = save_o_dn;
+                               op->o_ndn = save_o_ndn;
+                       }
 
                        switch ( ret ) {
                        case LDAP_SUCCESS :
@@ -895,7 +973,6 @@ dynacl_aci_init( void )
        return rc;
 }
 
-#endif /* SLAP_DYNACL */
 
 /* ACI syntax validation */
 
@@ -945,16 +1022,17 @@ bv_get_tail(
  * aci is accepted in following form:
  *    oid#scope#rights#type#subject
  * Where:
- *    oid       := numeric OID
- *    scope     := entry|children
+ *    oid       := numeric OID (currently ignored)
+ *    scope     := entry|children|subtree
  *    rights    := right[[$right]...]
  *    right     := (grant|deny);action
- *    action    := perms;attr[[;perms;attr]...]
+ *    action    := perms;attrs[[;perms;attrs]...]
  *    perms     := perm[[,perm]...]
  *    perm      := c|s|r|w|x
- *    attr      := attributeType|[all]
- *    type      :=  public|users|self|dnattr|group|role|set|set-ref|
- *                  access_id|subtree|onelevel|children
+ *    attrs     := attribute[[,attribute]..]|"[all]"
+ *    attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
+ *    type      := public|users|self|dnattr|group|role|set|set-ref|
+ *                 access_id|subtree|onelevel|children
  */
 static int 
 OpenLDAPaciValidatePerms(
@@ -973,6 +1051,7 @@ OpenLDAPaciValidatePerms(
                        break;
 
                default:
+                       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 );
                        return LDAP_INVALID_SYNTAX;
                }
 
@@ -986,6 +1065,7 @@ OpenLDAPaciValidatePerms(
                assert( i != perms->bv_len );
 
                if ( perms->bv_val[ i ] != ',' ) {
+                       Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val, 0, 0 );
                        return LDAP_INVALID_SYNTAX;
                }
 
@@ -1014,6 +1094,7 @@ OpenLDAPaciValidateRight(
        if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
                bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
        {
+               Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1027,16 +1108,36 @@ OpenLDAPaciValidateRight(
 
                } else {
                        /* attr */
-                       AttributeDescription    *ad = NULL;
-                       const char              *text = NULL;
+                       AttributeDescription    *ad;
+                       const char              *text;
+                       struct berval           attr, left, right;
+                       int                     j;
 
                        /* could be "[all]" or an attribute description */
                        if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
                                continue;
                        }
 
-                       if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
-                               return LDAP_INVALID_SYNTAX;
+
+                       for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 
+                       {
+                               ad = NULL;
+                               text = NULL;
+                               if ( acl_get_part( &attr, 0, '=', &left ) < 0
+                                       || acl_get_part( &attr, 1, '=', &right ) < 0 ) 
+                               {
+                                       if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 
+                                       {
+                                               Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
+                                               return LDAP_INVALID_SYNTAX;
+                                       }
+                               } else {
+                                       if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 
+                                       {
+                                               Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
+                                               return LDAP_INVALID_SYNTAX;
+                                       }
+                               }
                        }
                }
        }
@@ -1046,6 +1147,7 @@ OpenLDAPaciValidateRight(
                return LDAP_SUCCESS;
 
        } else {
+               Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1066,16 +1168,20 @@ OpenLDAPaciNormalizeRight(
 
        /* grant|deny */
        if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
+               Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
        idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
        if ( idx == -1 ) {
+               Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
        ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
 
        for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
+               struct berval   nattrs = BER_BVNULL;
+               int             freenattrs = 1;
                if ( i & 1 ) {
                        /* perms */
                        if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
@@ -1090,25 +1196,76 @@ OpenLDAPaciNormalizeRight(
 
                        /* could be "[all]" or an attribute description */
                        if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
-                               bv = aci_bv[ ACI_BV_BR_ALL ];
+                               nattrs = aci_bv[ ACI_BV_BR_ALL ];
+                               freenattrs = 0;
 
                        } else {
                                AttributeDescription    *ad = NULL;
+                               AttributeDescription    adstatic= { 0 };
                                const char              *text = NULL;
-                               int                     rc;
+                               struct berval           attr, left, right;
+                               int                     j;
+                               int                     len;
 
-                               rc = slap_bv2ad( &bv, &ad, &text );
-                               if ( rc != LDAP_SUCCESS ) {
-                                       return LDAP_INVALID_SYNTAX;
+                               for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) 
+                               {
+                                       ad = NULL;
+                                       text = NULL;
+                                       /* openldap 2.1 aci compabitibility [entry] -> entry */
+                                       if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
+                                               ad = &adstatic;
+                                               adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
+
+                                       /* openldap 2.1 aci compabitibility [children] -> children */
+                                       } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
+                                               ad = &adstatic;
+                                               adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
+
+                                       /* openldap 2.1 aci compabitibility [all] -> only [all] */
+                                       } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+                                               ber_memfree_x( nattrs.bv_val, ctx );
+                                               nattrs = aci_bv[ ACI_BV_BR_ALL ];
+                                               freenattrs = 0;
+                                               break;
+
+                                       } else if ( acl_get_part( &attr, 0, '=', &left ) < 0
+                                               || acl_get_part( &attr, 1, '=', &right ) < 0 ) 
+                                       {
+                                               if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) 
+                                               {
+                                                       ber_memfree_x( nattrs.bv_val, ctx );
+                                                       Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
+                                                       return LDAP_INVALID_SYNTAX;
+                                               }
+
+                                       } else {
+                                               if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) 
+                                               {
+                                                       ber_memfree_x( nattrs.bv_val, ctx );
+                                                       Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
+                                                       return LDAP_INVALID_SYNTAX;
+                                               }
+                                       }
+                                       
+                               
+                                       len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
+                                               + ad->ad_cname.bv_len;
+                                       nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx );
+                                       ptr = &nattrs.bv_val[ nattrs.bv_len ];
+                                       if ( !BER_BVISEMPTY( &nattrs ) ) {
+                                               *ptr++ = ',';
+                                       }
+                                       ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
+                                       ptr[ 0 ] = '\0';
+                                       nattrs.bv_len = len;
                                }
 
-                               bv = ad->ad_cname;
                        }
 
                        naction->bv_val = ber_memrealloc_x( naction->bv_val,
                                naction->bv_len + STRLENOF( ";" )
                                + perms.bv_len + STRLENOF( ";" )
-                               + bv.bv_len + 1,
+                               + nattrs.bv_len + 1,
                                ctx );
 
                        ptr = &naction->bv_val[ naction->bv_len ];
@@ -1117,10 +1274,13 @@ OpenLDAPaciNormalizeRight(
                        ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
                        ptr[ 0 ] = ';';
                        ptr++;
-                       ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
+                       ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
                        ptr[ 0 ] = '\0';
                        naction->bv_len += STRLENOF( ";" ) + perms.bv_len
-                               + STRLENOF( ";" ) + bv.bv_len;
+                               + STRLENOF( ";" ) + nattrs.bv_len;
+                       if ( freenattrs ) {
+                               ber_memfree_x( nattrs.bv_val, ctx );
+                       }
                }
        }
        
@@ -1129,6 +1289,7 @@ OpenLDAPaciNormalizeRight(
                return LDAP_SUCCESS;
 
        } else {
+               Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 }
@@ -1238,8 +1399,10 @@ OpenLDAPaciValidate(
                        type = BER_BVNULL,
                        subject = BER_BVNULL;
        int             idx;
-
+       int             rc;
+       
        if ( BER_BVISEMPTY( val ) ) {
+               Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n", 0, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1251,6 +1414,7 @@ OpenLDAPaciValidate(
                 * I'd replace it with X-ORDERED VALUES so that
                 * it's guaranteed values are maintained and used
                 * in the desired order */
+               Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1258,6 +1422,7 @@ OpenLDAPaciValidate(
        if ( acl_get_part( val, 1, '#', &scope ) < 0 || 
                bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
        {
+               Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1270,6 +1435,7 @@ OpenLDAPaciValidate(
 
        /* type */
        if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+               Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
        idx = bv_getcaseidx( &type, OpenLDAPacitypes );
@@ -1277,11 +1443,13 @@ OpenLDAPaciValidate(
                struct berval   isgr;
 
                if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+                       Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val, 0, 0 );
                        return LDAP_INVALID_SYNTAX;
                }
 
                idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
                if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+                       Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val, 0, 0 );
                        return LDAP_INVALID_SYNTAX;
                }
        }
@@ -1289,6 +1457,7 @@ OpenLDAPaciValidate(
        /* subject */
        bv_get_tail( val, &type, &subject );
        if ( subject.bv_val[ 0 ] != '#' ) {
+               Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1296,15 +1465,16 @@ OpenLDAPaciValidate(
                if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
                        AttributeDescription    *ad = NULL;
                        const char              *text = NULL;
-                       int                     rc;
 
                        rc = slap_bv2ad( &subject, &ad, &text );
                        if ( rc != LDAP_SUCCESS ) {
+                               Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
                                return LDAP_INVALID_SYNTAX;
                        }
 
                        if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
                                /* FIXME: allow nameAndOptionalUID? */
+                               Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
                                return LDAP_INVALID_SYNTAX;
                        }
                }
@@ -1319,16 +1489,14 @@ OpenLDAPaciValidate(
                struct berval   ocbv = BER_BVNULL,
                                atbv = BER_BVNULL;
 
-               ocbv.bv_val = strchr( type.bv_val, '/' );
-               if ( ocbv.bv_val != NULL
-                       && ( ocbv.bv_val - type.bv_val ) < type.bv_len )
-               {
+               ocbv.bv_val = ber_bvchr( &type, '/' );
+               if ( ocbv.bv_val != NULL ) {
                        ocbv.bv_val++;
+                       ocbv.bv_len = type.bv_len
+                                       - ( ocbv.bv_val - type.bv_val );
 
-                       atbv.bv_val = strchr( ocbv.bv_val, '/' );
-                       if ( atbv.bv_val != NULL
-                               && ( atbv.bv_val - ocbv.bv_val ) < ocbv.bv_len )
-                       {
+                       atbv.bv_val = ber_bvchr( &ocbv, '/' );
+                       if ( atbv.bv_val != NULL ) {
                                AttributeDescription    *ad = NULL;
                                const char              *text = NULL;
                                int                     rc;
@@ -1340,15 +1508,13 @@ OpenLDAPaciValidate(
 
                                rc = slap_bv2ad( &atbv, &ad, &text );
                                if ( rc != LDAP_SUCCESS ) {
+                                       Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
                                        return LDAP_INVALID_SYNTAX;
                                }
-                               
-                       } else {
-                               ocbv.bv_len = type.bv_len
-                                       - ( ocbv.bv_val - type.bv_val );
                        }
 
                        if ( oc_bvfind( &ocbv ) == NULL ) {
+                               Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val, 0, 0 );
                                return LDAP_INVALID_SYNTAX;
                        }
                }
@@ -1356,6 +1522,7 @@ OpenLDAPaciValidate(
 
        if ( BER_BVISEMPTY( &subject ) ) {
                /* empty DN invalid */
+               Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1363,7 +1530,11 @@ OpenLDAPaciValidate(
        subject.bv_len--;
 
        /* FIXME: pass DN syntax? */
-       return dnValidate( NULL, &subject );
+       rc = dnValidate( NULL, &subject );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val, 0, 0 );
+       }
+       return rc;
 }
 
 static int
@@ -1387,7 +1558,10 @@ OpenLDAPaciPrettyNormal(
                        freetype = 0;
        char            *ptr;
 
+       BER_BVZERO( out );
+
        if ( BER_BVISEMPTY( val ) ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n", 0, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
@@ -1395,21 +1569,25 @@ OpenLDAPaciPrettyNormal(
        if ( acl_get_part( val, 0, '#', &oid ) < 0 || 
                numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
        {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
 
        /* scope: normalize by replacing with OpenLDAPaciscopes */
        if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
        idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
        if ( idx == -1 ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
        scope = *OpenLDAPaciscopes[ idx ];
 
        /* rights */
        if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val, 0, 0 );
                return LDAP_INVALID_SYNTAX;
        }
        if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
@@ -1420,6 +1598,7 @@ OpenLDAPaciPrettyNormal(
 
        /* type */
        if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val, 0, 0 );
                rc = LDAP_INVALID_SYNTAX;
                goto cleanup;
        }
@@ -1428,12 +1607,14 @@ OpenLDAPaciPrettyNormal(
                struct berval   isgr;
 
                if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+                       Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val, 0, 0 );
                        rc = LDAP_INVALID_SYNTAX;
                        goto cleanup;
                }
 
                idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
                if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+                       Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val, 0, 0 );
                        rc = LDAP_INVALID_SYNTAX;
                        goto cleanup;
                }
@@ -1444,6 +1625,7 @@ OpenLDAPaciPrettyNormal(
        bv_get_tail( val, &type, &subject );
 
        if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
+               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val, 0, 0 );
                rc = LDAP_INVALID_SYNTAX;
                goto cleanup;
        }
@@ -1464,6 +1646,7 @@ OpenLDAPaciPrettyNormal(
                        freesubject = 1;
 
                } else {
+                       Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val, 0, 0 );
                        goto cleanup;
                }
 
@@ -1474,10 +1657,8 @@ OpenLDAPaciPrettyNormal(
                        struct berval   ocbv = BER_BVNULL,
                                        atbv = BER_BVNULL;
 
-                       ocbv.bv_val = strchr( type.bv_val, '/' );
-                       if ( ocbv.bv_val != NULL
-                               && ( ocbv.bv_val - type.bv_val ) < type.bv_len )
-                       {
+                       ocbv.bv_val = ber_bvchr( &type, '/' );
+                       if ( ocbv.bv_val != NULL ) {
                                ObjectClass             *oc = NULL;
                                AttributeDescription    *ad = NULL;
                                const char              *text = NULL;
@@ -1487,11 +1668,10 @@ OpenLDAPaciPrettyNormal(
                                bv.bv_len = ntype.bv_len;
 
                                ocbv.bv_val++;
+                               ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
 
-                               atbv.bv_val = strchr( ocbv.bv_val, '/' );
-                               if ( atbv.bv_val != NULL
-                                       && ( atbv.bv_val - ocbv.bv_val ) < ocbv.bv_len )
-                               {
+                               atbv.bv_val = ber_bvchr( &ocbv, '/' );
+                               if ( atbv.bv_val != NULL ) {
                                        atbv.bv_val++;
                                        atbv.bv_len = type.bv_len
                                                - ( atbv.bv_val - type.bv_val );
@@ -1499,19 +1679,17 @@ OpenLDAPaciPrettyNormal(
        
                                        rc = slap_bv2ad( &atbv, &ad, &text );
                                        if ( rc != LDAP_SUCCESS ) {
+                                               Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
                                                rc = LDAP_INVALID_SYNTAX;
                                                goto cleanup;
                                        }
 
                                        bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
-                                       
-                               } else {
-                                       ocbv.bv_len = type.bv_len
-                                               - ( ocbv.bv_val - type.bv_val );
                                }
 
                                oc = oc_bvfind( &ocbv );
                                if ( oc == NULL ) {
+                                        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val, 0, 0 );
                                        rc = LDAP_INVALID_SYNTAX;
                                        goto cleanup;
                                }
@@ -1547,12 +1725,14 @@ OpenLDAPaciPrettyNormal(
 
                rc = slap_bv2ad( &subject, &ad, &text );
                if ( rc != LDAP_SUCCESS ) {
+                        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
                        rc = LDAP_INVALID_SYNTAX;
                        goto cleanup;
                }
 
                if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
                        /* FIXME: allow nameAndOptionalUID? */
+                        Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
                        rc = LDAP_INVALID_SYNTAX;
                        goto cleanup;
                }
@@ -1564,7 +1744,7 @@ OpenLDAPaciPrettyNormal(
        out->bv_len = 
                oid.bv_len + STRLENOF( "#" )
                + scope.bv_len + STRLENOF( "#" )
-               + rights.bv_len + STRLENOF( "#" )
+               + nrights.bv_len + STRLENOF( "#" )
                + ntype.bv_len + STRLENOF( "#" )
                + nsubject.bv_len;
 
@@ -1632,7 +1812,7 @@ OpenLDAPaciNormalize(
 int
 init_module( int argc, char *argv[] )
 {
-       return slap_dynacl_register();
+       return dynacl_aci_init();
 }
 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */