]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/bconfig.c
ITS#6967 normalize schema RDN
[openldap] / servers / slapd / bconfig.c
index a20852e0119ba9b40959e39861bf31fe8d8144f7..3be11658901039b9f28adf8837dc25d6085894fc 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2005-2010 The OpenLDAP Foundation.
+ * Copyright 2005-2011 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -135,6 +135,7 @@ static ConfigDriver config_referral;
 static ConfigDriver config_loglevel;
 static ConfigDriver config_updatedn;
 static ConfigDriver config_updateref;
+static ConfigDriver config_extra_attrs;
 static ConfigDriver config_include;
 static ConfigDriver config_obsolete;
 #ifdef HAVE_TLS
@@ -178,6 +179,8 @@ enum {
        CFG_LASTMOD,
        CFG_AZPOLICY,
        CFG_AZREGEXP,
+       CFG_AZDUC,
+       CFG_AZDUC_IGNORE,
        CFG_SASLSECP,
        CFG_SSTR_IF_MAX,
        CFG_SSTR_IF_MIN,
@@ -191,6 +194,7 @@ enum {
        CFG_SYNTAX,
        CFG_ACL_ADD,
        CFG_SYNC_SUBENTRY,
+       CFG_LTHREADS,
 
        CFG_LAST
 };
@@ -240,8 +244,12 @@ static OidRec OidMacros[] = {
  * OLcfg{Bk|Db}{Oc|At}:3               -> back-ldap
  * OLcfg{Bk|Db}{Oc|At}:4               -> back-monitor
  * OLcfg{Bk|Db}{Oc|At}:5               -> back-relay
- * OLcfg{Bk|Db}{Oc|At}:6               -> back-sql
+ * OLcfg{Bk|Db}{Oc|At}:6               -> back-sql(/back-ndb)
  * OLcfg{Bk|Db}{Oc|At}:7               -> back-sock
+ * OLcfg{Bk|Db}{Oc|At}:8               -> back-null
+ * OLcfg{Bk|Db}{Oc|At}:9               -> back-passwd
+ * OLcfg{Bk|Db}{Oc|At}:10              -> back-shell
+ * OLcfg{Bk|Db}{Oc|At}:11              -> back-perl
  */
 
 /*
@@ -311,6 +319,7 @@ static ConfigTable config_back_cf_table[] = {
                &config_generic, "( OLcfgGlAt:4 NAME 'olcAttributeTypes' "
                        "DESC 'OpenLDAP attributeTypes' "
                        "EQUALITY caseIgnoreMatch "
+                       "SUBSTR caseIgnoreSubstringsMatch "
                        "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
                                NULL, NULL },
        { "authid-rewrite", NULL, 2, 0, STRLENOF( "authid-rewrite" ),
@@ -360,8 +369,13 @@ static ConfigTable config_back_cf_table[] = {
                &config_generic, "( OLcfgGlAt:16 NAME 'olcDitContentRules' "
                        "DESC 'OpenLDAP DIT content rules' "
                        "EQUALITY caseIgnoreMatch "
+                       "SUBSTR caseIgnoreSubstringsMatch "
                        "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
                        NULL, NULL },
+       { "extra_attrs", "attrlist", 2, 2, 0, ARG_DB|ARG_MAGIC,
+               &config_extra_attrs, "( OLcfgDbAt:0.20 NAME 'olcExtraAttrs' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
        { "gentlehup", "on|off", 2, 2, 0,
 #ifdef SIGHUP
                ARG_ON_OFF, &global_gentlehup,
@@ -402,12 +416,21 @@ static ConfigTable config_back_cf_table[] = {
                &config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' "
                        "DESC 'OpenLDAP ldapSyntax' "
                        "EQUALITY caseIgnoreMatch "
+                       "SUBSTR caseIgnoreSubstringsMatch "
                        "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
                                NULL, NULL },
        { "limits", "limits", 2, 0, 0, ARG_DB|ARG_MAGIC|CFG_LIMITS,
                &config_generic, "( OLcfgDbAt:0.5 NAME 'olcLimits' "
                        "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+       { "listener-threads", "count", 2, 0, 0,
+#ifdef NO_THREADS
+               ARG_IGNORED, NULL,
+#else
+               ARG_UINT|ARG_MAGIC|CFG_LTHREADS, &config_generic,
+#endif
+               "( OLcfgGlAt:93 NAME 'olcListenerThreads' "
+                       "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
        { "localSSF", "ssf", 2, 2, 0, ARG_INT,
                &local_ssf, "( OLcfgGlAt:26 NAME 'olcLocalSSF' "
                        "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
@@ -449,11 +472,13 @@ static ConfigTable config_back_cf_table[] = {
                &config_generic, "( OLcfgGlAt:32 NAME 'olcObjectClasses' "
                "DESC 'OpenLDAP object classes' "
                "EQUALITY caseIgnoreMatch "
+               "SUBSTR caseIgnoreSubstringsMatch "
                "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
                        NULL, NULL },
        { "objectidentifier", "name> <oid",     3, 3, 0, ARG_MAGIC|CFG_OID,
                &config_generic, "( OLcfgGlAt:33 NAME 'olcObjectIdentifier' "
                        "EQUALITY caseIgnoreMatch "
+                       "SUBSTR caseIgnoreSubstringsMatch "
                        "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
        { "overlay", "overlay", 2, 2, 0, ARG_MAGIC,
                &config_overlay, "( OLcfgGlAt:34 NAME 'olcOverlay' "
@@ -544,6 +569,24 @@ static ConfigTable config_back_cf_table[] = {
 #endif
                "( OLcfgGlAt:89 NAME 'olcSaslAuxprops' "
                        "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "sasl-auxprops-dontusecopy", NULL, 2, 0, 0,
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+               ARG_MAGIC|CFG_AZDUC, &config_generic,
+#else
+               ARG_IGNORED, NULL,
+#endif
+               "( OLcfgGlAt:91 NAME 'olcSaslAuxpropsDontUseCopy' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { "sasl-auxprops-dontusecopy-ignore", "true|FALSE", 2, 0, 0,
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+               ARG_ON_OFF|CFG_AZDUC_IGNORE, &slap_dontUseCopy_ignore,
+#else
+               ARG_IGNORED, NULL,
+#endif
+               "( OLcfgGlAt:92 NAME 'olcSaslAuxpropsDontUseCopyIgnore' "
+                       "EQUALITY booleanMatch "
+                       "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
        { "sasl-host", "host", 2, 2, 0,
 #ifdef HAVE_CYRUS_SASL
                ARG_STRING|ARG_UNIQUE, &sasl_host,
@@ -787,7 +830,8 @@ static ConfigOCs cf_ocs[] = {
                 "olcPluginLogFile $ olcReadOnly $ olcReferral $ "
                 "olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
                 "olcRootDSE $ "
-                "olcSaslAuxprops $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
+                "olcSaslAuxprops $ olcSaslAuxpropsDontUseCopy $ olcSaslAuxpropsDontUseCopyIgnore $ "
+                "olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
                 "olcSecurity $ olcServerID $ olcSizeLimit $ "
                 "olcSockbufMaxIncoming $ olcSockbufMaxIncomingAuth $ "
                 "olcTCPBuffer $ "
@@ -822,7 +866,7 @@ static ConfigOCs cf_ocs[] = {
                 "olcReplogFile $ olcRequires $ olcRestrict $ olcRootDN $ olcRootPW $ "
                 "olcSchemaDN $ olcSecurity $ olcSizeLimit $ olcSyncUseSubentry $ olcSyncrepl $ "
                 "olcTimeLimit $ olcUpdateDN $ olcUpdateRef $ olcMirrorMode $ "
-                "olcMonitoring ) )",
+                "olcMonitoring $ olcExtraAttrs ) )",
                        Cft_Database, NULL, cfAddDatabase },
        { "( OLcfgGlOc:5 "
                "NAME 'olcOverlayConfig' "
@@ -894,6 +938,9 @@ config_generic(ConfigArgs *c) {
                case CFG_TTHREADS:
                        c->value_int = slap_tool_thread_max;
                        break;
+               case CFG_LTHREADS:
+                       c->value_uint = slapd_daemon_threads;
+                       break;
                case CFG_SALT:
                        if ( passwd_salt )
                                c->value_string = ch_strdup( passwd_salt );
@@ -934,6 +981,47 @@ config_generic(ConfigArgs *c) {
                        if ( !c->rvalue_vals ) rc = 1;
                        break;
 #ifdef HAVE_CYRUS_SASL
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+               case CFG_AZDUC: {
+                       static int duc_done = 0;
+
+                       /* take the opportunity to initialize with known values */
+                       if ( !duc_done ) {
+                               struct berval duc[] = { BER_BVC("cmusaslsecretOTP"), BER_BVNULL };
+                               int i;
+                               
+                               for ( i = 0; !BER_BVISNULL( &duc[ i ] ); i++ ) {
+                                       const char *text = NULL;
+                                       AttributeDescription *ad = NULL;
+
+                                       if ( slap_bv2ad( &duc[ i ], &ad, &text ) == LDAP_SUCCESS ) {
+                                               int gotit = 0;
+                                               if ( slap_dontUseCopy_propnames ) {
+                                                       int j;
+
+                                                       for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ] ); j++ ) {
+                                                               if ( bvmatch( &slap_dontUseCopy_propnames[ j ], &ad->ad_cname ) ) {
+                                                                       gotit = 1;
+                                                               }
+                                                       }
+                                               }
+
+                                               if ( !gotit ) {
+                                                       value_add_one( &slap_dontUseCopy_propnames, &ad->ad_cname );
+                                               }
+                                       }
+                               }
+
+                               duc_done = 1;
+                       }
+
+                       if ( slap_dontUseCopy_propnames != NULL ) {
+                               ber_bvarray_dup_x( &c->rvalue_vals, slap_dontUseCopy_propnames, NULL );
+                       } else {
+                               rc = 1;
+                       }
+                       } break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
                case CFG_SASLSECP: {
                        struct berval bv = BER_BVNULL;
                        slap_sasl_secprops_unparse( &bv );
@@ -1195,6 +1283,7 @@ config_generic(ConfigArgs *c) {
                case CFG_CONCUR:
                case CFG_THREADS:
                case CFG_TTHREADS:
+               case CFG_LTHREADS:
                case CFG_RO:
                case CFG_AZPOLICY:
                case CFG_DEPTH:
@@ -1216,6 +1305,35 @@ config_generic(ConfigArgs *c) {
                        snprintf(c->log, sizeof( c->log ), "change requires slapd restart");
                        break;
 
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+               case CFG_AZDUC:
+                       if ( c->valx < 0 ) {
+                               if ( slap_dontUseCopy_propnames != NULL ) {
+                                       ber_bvarray_free( slap_dontUseCopy_propnames );
+                                       slap_dontUseCopy_propnames = NULL;
+                               }
+       
+                       } else {
+                               int i;
+
+                               if ( slap_dontUseCopy_propnames == NULL ) {
+                                       rc = 1;
+                                       break;
+                               }
+
+                               for ( i = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ i ] ) && i < c->valx; i++ );
+                               if ( i < c->valx ) {
+                                       rc = 1;
+                                       break;
+                               }
+                               ber_memfree( slap_dontUseCopy_propnames[ i ].bv_val );
+                               for ( ; !BER_BVISNULL( &slap_dontUseCopy_propnames[ i + 1 ] ); i++ ) {
+                                       slap_dontUseCopy_propnames[ i ] = slap_dontUseCopy_propnames[ i + 1 ];
+                               }
+                               BER_BVZERO( &slap_dontUseCopy_propnames[ i ] );
+                       }
+                       break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
                case CFG_SALT:
                        ch_free( passwd_salt );
                        passwd_salt = NULL;
@@ -1517,6 +1635,19 @@ config_generic(ConfigArgs *c) {
                        slap_tool_thread_max = c->value_int;    /* save for reference */
                        break;
 
+               case CFG_LTHREADS:
+                       { int mask = 0;
+                       /* use a power of two */
+                       while (c->value_uint > 1) {
+                               c->value_uint >>= 1;
+                               mask <<= 1;
+                               mask |= 1;
+                       }
+                       slapd_daemon_mask = mask;
+                       slapd_daemon_threads = mask+1;
+                       }
+                       break;
+
                case CFG_SALT:
                        if ( passwd_salt ) ch_free( passwd_salt );
                        passwd_salt = c->value_string;
@@ -1551,6 +1682,47 @@ config_generic(ConfigArgs *c) {
                        break;
                                
 #ifdef HAVE_CYRUS_SASL
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+               case CFG_AZDUC: {
+                       int arg, cnt;
+
+                       for ( arg = 1; arg < c->argc; arg++ ) {
+                               int duplicate = 0, err;
+                               AttributeDescription *ad = NULL;
+                               const char *text = NULL;
+
+                               err = slap_str2ad( c->argv[ arg ], &ad, &text );
+                               if ( err != LDAP_SUCCESS ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s>: attr #%d (\"%s\") unknown (err=%d \"%s\"; ignored)",
+                                               c->argv[0], arg, c->argv[ arg ], err, text );
+                                       Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+                                               c->log, c->cr_msg, 0 );
+
+                               } else {
+                                       if ( slap_dontUseCopy_propnames != NULL ) {
+                                               for ( cnt = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ cnt ] ); cnt++ ) {
+                                                       if ( bvmatch( &slap_dontUseCopy_propnames[ cnt ], &ad->ad_cname ) ) {
+                                                               duplicate = 1;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+
+                                       if ( duplicate ) {
+                                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s>: attr #%d (\"%s\") already defined (ignored)",
+                                                       c->argv[0], arg, ad->ad_cname.bv_val);
+                                               Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+                                                       c->log, c->cr_msg, 0 );
+                                               continue;
+                                       }
+
+                                       value_add_one( &slap_dontUseCopy_propnames, &ad->ad_cname );
+                               }
+                       }
+                       
+                       } break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
                case CFG_SASLSECP:
                        {
                        char *txt = slap_sasl_secprops( c->argv[1] );
@@ -1594,7 +1766,8 @@ config_generic(ConfigArgs *c) {
                                        int i;
                                        for (i=0, oc = cfn->c_oc_head; i<c->valx; i++) {
                                                prev = oc;
-                                               oc_next( &oc );
+                                               if ( !oc_next( &oc ))
+                                                       break;
                                        }
                                } else
                                /* If adding the first, and head exists, find its prev */
@@ -1626,7 +1799,8 @@ config_generic(ConfigArgs *c) {
                                        int i;
                                        for (i=0, at = cfn->c_at_head; i<c->valx; i++) {
                                                prev = at;
-                                               at_next( &at );
+                                               if ( !at_next( &at ))
+                                                       break;
                                        }
                                } else
                                /* If adding the first, and head exists, find its prev */
@@ -1658,7 +1832,8 @@ config_generic(ConfigArgs *c) {
                                        int i;
                                        for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++ ) {
                                                prev = syn;
-                                               syn_next( &syn );
+                                               if ( !syn_next( &syn ))
+                                                       break;
                                        }
                                } else
                                /* If adding the first, and head exists, find its prev */
@@ -3139,6 +3314,58 @@ config_requires(ConfigArgs *c) {
        return(0);
 }
 
+static int
+config_extra_attrs(ConfigArgs *c)
+{
+       assert( c->be != NULL );
+
+       if ( c->op == SLAP_CONFIG_EMIT ) {
+               int i;
+
+               if ( c->be->be_extra_anlist == NULL ) {
+                       return 1;
+               }
+
+               for ( i = 0; !BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ); i++ ) {
+                       value_add_one( &c->rvalue_vals, &c->be->be_extra_anlist[i].an_name );
+               }
+
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               if ( c->be->be_extra_anlist == NULL ) {
+                       return 1;
+               }
+
+               if ( c->valx < 0 ) {
+                       anlist_free( c->be->be_extra_anlist, 1, NULL );
+                       c->be->be_extra_anlist = NULL;
+
+               } else {
+                       int i;
+
+                       for ( i = 0; i < c->valx && !BER_BVISNULL( &c->be->be_extra_anlist[i + 1].an_name ); i++ )
+                               ;
+
+                       if ( BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ) ) {
+                               return 1;
+                       }
+
+                       ch_free( c->be->be_extra_anlist[i].an_name.bv_val );
+
+                       for ( ; !BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ); i++ ) {
+                               c->be->be_extra_anlist[i] = c->be->be_extra_anlist[i + 1];
+                       }
+               }
+
+       } else {
+               c->be->be_extra_anlist = str2anlist( c->be->be_extra_anlist, c->argv[1], " ,\t" );
+               if ( c->be->be_extra_anlist == NULL ) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 static slap_verbmasks  *loglevel_ops;
 
 static int
@@ -3977,10 +4204,12 @@ config_setup_ldif( BackendDB *be, const char *dir, int readit ) {
 
                op->o_tag = LDAP_REQ_ADD;
                if ( rc == LDAP_SUCCESS && sc.frontend ) {
+                       rs_reinit( &rs, REP_RESULT );
                        op->ora_e = sc.frontend;
                        rc = op->o_bd->be_add( op, &rs );
                }
                if ( rc == LDAP_SUCCESS && sc.config ) {
+                       rs_reinit( &rs, REP_RESULT );
                        op->ora_e = sc.config;
                        rc = op->o_bd->be_add( op, &rs );
                }
@@ -4528,12 +4757,19 @@ check_name_index( CfEntryInfo *parent, ConfigType ce_type, Entry *e,
        return rc;
 }
 
+/* Insert all superior classes of the given class */
 static int
 count_oc( ObjectClass *oc, ConfigOCs ***copp, int *nocs )
 {
        ConfigOCs       co, *cop;
        ObjectClass     **sups;
 
+       for ( sups = oc->soc_sups; sups && *sups; sups++ ) {
+               if ( count_oc( *sups, copp, nocs ) ) {
+                       return -1;
+               }
+       }
+
        co.co_name = &oc->soc_cname;
        cop = avl_find( CfOcTree, &co, CfOc_cmp );
        if ( cop ) {
@@ -4557,27 +4793,23 @@ count_oc( ObjectClass *oc, ConfigOCs ***copp, int *nocs )
                }
        }
 
-       for ( sups = oc->soc_sups; sups && *sups; sups++ ) {
-               if ( count_oc( *sups, copp, nocs ) ) {
-                       return -1;
-               }
-       }
-
        return 0;
 }
 
+/* Find all superior classes of the given objectclasses,
+ * return list in order of most-subordinate first.
+ *
+ * Special / auxiliary / Cft_Misc classes always take precedence.
+ */
 static ConfigOCs **
 count_ocs( Attribute *oc_at, int *nocs )
 {
-       int             i;
+       int             i, j, misc = -1;
        ConfigOCs       **colst = NULL;
 
        *nocs = 0;
 
-       for ( i = 0; !BER_BVISNULL( &oc_at->a_nvals[i] ); i++ )
-               /* count attrs */ ;
-
-       for ( ; i--; ) {
+       for ( i = oc_at->a_numvals; i--; ) {
                ObjectClass     *oc = oc_bvfind( &oc_at->a_nvals[i] );
 
                assert( oc != NULL );
@@ -4587,6 +4819,25 @@ count_ocs( Attribute *oc_at, int *nocs )
                }
        }
 
+       /* invert order */
+       i = 0;
+       j = *nocs - 1;
+       while ( i < j ) {
+               ConfigOCs *tmp = colst[i];
+               colst[i] = colst[j];
+               colst[j] = tmp;
+               if (tmp->co_type == Cft_Misc)
+                       misc = j;
+               i++; j--;
+       }
+       /* Move misc class to front of list */
+       if (misc > 0) {
+               ConfigOCs *tmp = colst[misc];
+               for (i=misc; i>0; i--)
+                       colst[i] = colst[i-1];
+               colst[0] = tmp;
+       }
+
        return colst;
 }
 
@@ -5263,6 +5514,7 @@ out2:;
 out:;
        {       int repl = op->o_dont_replicate;
                if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+                       rs->sr_text = NULL; /* Set after config_add_internal */
                        rs->sr_err = LDAP_SUCCESS;
                        op->o_dont_replicate = 1;
                }
@@ -5324,6 +5576,11 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
        oc_at = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
        if ( !oc_at ) return LDAP_OBJECT_CLASS_VIOLATION;
 
+       for (ml = op->orm_modlist; ml; ml=ml->sml_next) {
+               if (ml->sml_desc == slap_schema.si_ad_objectClass)
+                       return rc;
+       }
+
        colst = count_ocs( oc_at, &nocs );
 
        /* make sure add/del flags are clear; should always be true */
@@ -5346,7 +5603,9 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
                ct = config_find_table( colst, nocs, ml->sml_desc, ca );
                switch (ml->sml_op) {
                case LDAP_MOD_DELETE:
-               case LDAP_MOD_REPLACE: {
+               case LDAP_MOD_REPLACE:
+               case SLAP_MOD_SOFTDEL:
+               {
                        BerVarray vals = NULL, nvals = NULL;
                        int *idx = NULL;
                        if ( ct && ( ct->arg_type & ARG_NO_DELETE )) {
@@ -5385,11 +5644,24 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
                                ml->sml_values = vals;
                                ml->sml_nvalues = nvals;
                        }
+                       if ( rc == LDAP_NO_SUCH_ATTRIBUTE && ml->sml_op == SLAP_MOD_SOFTDEL )
+                       {
+                               rc = LDAP_SUCCESS;
+                       }
+                       /* FIXME: check rc before fallthru? */
                        if ( !vals )
                                break;
-                       }
+               }
                        /* FALLTHRU: LDAP_MOD_REPLACE && vals */
 
+               case SLAP_MOD_ADD_IF_NOT_PRESENT:
+                       if ( ml->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
+                               && attr_find( e->e_attrs, ml->sml_desc ) )
+                       {
+                               rc = LDAP_SUCCESS;
+                               break;
+                       }
+
                case LDAP_MOD_ADD:
                case SLAP_MOD_SOFTADD: {
                        int mop = ml->sml_op;
@@ -5594,6 +5866,7 @@ out:
 out_noop:
        if ( rc == LDAP_SUCCESS ) {
                attrs_free( save_attrs );
+               rs->sr_text = NULL;
        } else {
                attrs_free( e->e_attrs );
                e->e_attrs = save_attrs;
@@ -5640,7 +5913,10 @@ config_back_modify( Operation *op, SlapReply *rs )
        rdn = ce->ce_entry->e_nname;
        ptr = strchr( rdn.bv_val, '=' );
        rdn.bv_len = ptr - rdn.bv_val;
-       slap_bv2ad( &rdn, &rad, &rs->sr_text );
+       rs->sr_err = slap_bv2ad( &rdn, &rad, &rs->sr_text );
+       if ( rs->sr_err != LDAP_SUCCESS ) {
+               goto out;
+       }
 
        /* Some basic validation... */
        for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
@@ -5921,24 +6197,65 @@ config_back_delete( Operation *op, SlapReply *rs )
                        rs->sr_matched = last->ce_entry->e_name.bv_val;
                rs->sr_err = LDAP_NO_SUCH_OBJECT;
        } else if ( ce->ce_kids ) {
-               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
        } else if ( op->o_abandon ) {
                rs->sr_err = SLAPD_ABANDON;
-       } else if ( ce->ce_type == Cft_Overlay || ce->ce_type == Cft_Database ){
+       } else if ( ce->ce_type == Cft_Overlay ||
+                       ce->ce_type == Cft_Database ||
+                       ce->ce_type == Cft_Misc ){
                char *iptr;
                int count, ixold;
 
                ldap_pvt_thread_pool_pause( &connection_pool );
 
                if ( ce->ce_type == Cft_Overlay ){
-                       overlay_remove( ce->ce_be, (slap_overinst *)ce->ce_bi );
-               } else { /* Cft_Database*/
+                       overlay_remove( ce->ce_be, (slap_overinst *)ce->ce_bi, op );
+               } else if ( ce->ce_type == Cft_Misc ) {
+                       /*
+                        * only Cft_Misc objects that have a co_lddel handler set in
+                        * the ConfigOCs struct can be deleted. This code also
+                        * assumes that the entry can be only have one objectclass
+                        * with co_type == Cft_Misc
+                        */
+                       ConfigOCs co, *coptr;
+                       Attribute *oc_at;
+                       int i;
+
+                       oc_at = attr_find( ce->ce_entry->e_attrs,
+                                       slap_schema.si_ad_objectClass );
+                       if ( !oc_at ) {
+                               rs->sr_err = LDAP_OTHER;
+                               rs->sr_text = "objectclass not found";
+                               ldap_pvt_thread_pool_resume( &connection_pool );
+                               goto out;
+                       }
+                       for ( i=0; !BER_BVISNULL(&oc_at->a_nvals[i]); i++ ) {
+                               co.co_name = &oc_at->a_nvals[i];
+                               coptr = avl_find( CfOcTree, &co, CfOc_cmp );
+                               if ( coptr == NULL || coptr->co_type != Cft_Misc ) {
+                                       continue;
+                               }
+                               if ( ! coptr->co_lddel || coptr->co_lddel( ce, op ) ){
+                                       rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                                       if ( ! coptr->co_lddel ) {
+                                               rs->sr_text = "No delete handler found";
+                                       } else {
+                                               rs->sr_err = LDAP_OTHER;
+                                               /* FIXME: We should return a helpful error message
+                                                * here */
+                                       }
+                                       ldap_pvt_thread_pool_resume( &connection_pool );
+                                       goto out;
+                               }
+                               break;
+                       }
+               } else if (ce->ce_type == Cft_Database ) {
                        if ( ce->ce_be == frontendDB || ce->ce_be == op->o_bd ){
                                rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
                                rs->sr_text = "Cannot delete config or frontend database";
                                ldap_pvt_thread_pool_resume( &connection_pool );
                                goto out;
-                       } 
+                       }
                        if ( ce->ce_be->bd_info->bi_db_close ) {
                                ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
                        }
@@ -6002,10 +6319,10 @@ config_back_delete( Operation *op, SlapReply *rs )
        } else {
                rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
        }
+out:
 #else
        rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
 #endif /* SLAP_CONFIG_DELETE */
-out:
        send_ldap_result( op, rs );
        return rs->sr_err;
 }
@@ -6155,6 +6472,8 @@ config_build_attrs( Entry *e, AttributeType **at, AttributeDescription *ad,
        return 0;
 }
 
+/* currently (2010) does not access rs except possibly writing rs->sr_err */
+
 Entry *
 config_build_entry( Operation *op, SlapReply *rs, CfEntryInfo *parent,
        ConfigArgs *c, struct berval *rdn, ConfigOCs *main, ConfigOCs *extra )
@@ -6252,9 +6571,12 @@ fail:
                op->ora_modlist = NULL;
                slap_add_opattrs( op, NULL, NULL, 0, 0 );
                if ( !op->o_noop ) {
-                       op->o_bd->be_add( op, rs );
-                       if ( ( rs->sr_err != LDAP_SUCCESS ) 
-                                       && (rs->sr_err != LDAP_ALREADY_EXISTS) ) {
+                       SlapReply rs2 = {REP_RESULT};
+                       op->o_bd->be_add( op, &rs2 );
+                       rs->sr_err = rs2.sr_err;
+                       rs_assert_done( &rs2 );
+                       if ( ( rs2.sr_err != LDAP_SUCCESS ) 
+                                       && (rs2.sr_err != LDAP_ALREADY_EXISTS) ) {
                                goto fail;
                        }
                }
@@ -6303,11 +6625,12 @@ config_build_schema_inc( ConfigArgs *c, CfEntryInfo *ceparent,
                        bv.bv_len );
                c->value_dn.bv_len += bv.bv_len;
                c->value_dn.bv_val[c->value_dn.bv_len] ='\0';
-               rdn = c->value_dn;
+               rdnNormalize( 0, NULL, NULL, &c->value_dn, &rdn, NULL );
 
                c->ca_private = cf;
                e = config_build_entry( op, rs, ceparent, c, &rdn,
                        &CFOC_SCHEMA, NULL );
+               ch_free( rdn.bv_val );
                if ( !e ) {
                        return -1;
                } else if ( e && cf->c_kids ) {
@@ -6470,8 +6793,8 @@ config_back_db_open( BackendDB *be, ConfigReply *cr )
        /* If we have no explicitly configured ACLs, don't just use
         * the global ACLs. Explicitly deny access to everything.
         */
-       if ( !be->be_acl ) {
-               parse_acl(be, "config_back_db_open", 0, 6, (char **)defacl, 0 );
+       if ( !be->bd_self->be_acl ) {
+               parse_acl(be->bd_self, "config_back_db_open", 0, 6, (char **)defacl, 0 );
        }
 
        thrctx = ldap_pvt_thread_pool_context();
@@ -6535,9 +6858,11 @@ config_back_db_open( BackendDB *be, ConfigReply *cr )
 
        /* Create schema nodes for included schema... */
        if ( cfb->cb_config->c_kids ) {
+               int rc;
                c.depth = 0;
                c.ca_private = cfb->cb_config->c_kids;
-               if (config_build_schema_inc( &c, ce, op, &rs )) {
+               rc = config_build_schema_inc( &c, ce, op, &rs );
+               if ( rc ) {
                        return -1;
                }
        }
@@ -6612,8 +6937,10 @@ config_back_db_open( BackendDB *be, ConfigReply *cr )
                        return -1;
                }
                ce = e->e_private;
-               if ( be->be_cf_ocs && be->be_cf_ocs->co_cfadd )
+               if ( be->be_cf_ocs && be->be_cf_ocs->co_cfadd ) {
+                       rs_reinit( &rs, REP_RESULT );
                        be->be_cf_ocs->co_cfadd( op, &rs, e, &c );
+               }
                /* Iterate through overlays */
                if ( oi ) {
                        slap_overinst *on;
@@ -6652,8 +6979,10 @@ config_back_db_open( BackendDB *be, ConfigReply *cr )
                                if ( !oe ) {
                                        return -1;
                                }
-                               if ( c.bi->bi_cf_ocs && c.bi->bi_cf_ocs->co_cfadd )
+                               if ( c.bi->bi_cf_ocs && c.bi->bi_cf_ocs->co_cfadd ) {
+                                       rs_reinit( &rs, REP_RESULT );
                                        c.bi->bi_cf_ocs->co_cfadd( op, &rs, oe, &c );
+                               }
                        }
                }
        }
@@ -6869,6 +7198,7 @@ config_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
        Operation *op = NULL;
        void *thrctx;
        int isFrontend = 0;
+       int isFrontendChild = 0;
 
        /* Create entry for frontend database if it does not exist already */
        if ( !entry_put_got_frontend ) {
@@ -6922,8 +7252,34 @@ config_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
                        }
                }
        }
+
+       /* Child entries of the frontend database, e.g. slapo-chain's back-ldap
+        * instances, may appear before the config database entry in the ldif, skip
+        * auto-creation of olcDatabase={0}config in such a case */
+       if ( !entry_put_got_config &&
+                       !strncmp( e->e_nname.bv_val, "olcDatabase", STRLENOF( "olcDatabase" ))) {
+               struct berval pdn;
+               dnParent( &e->e_nname, &pdn );
+               while ( pdn.bv_len ) {
+                       if ( !strncmp( pdn.bv_val, "olcDatabase",
+                                       STRLENOF( "olcDatabase" ))) {
+                               if ( !strncmp( pdn.bv_val +
+                                               STRLENOF( "olcDatabase" ), "={-1}frontend",
+                                               STRLENOF( "={-1}frontend" )) ||
+                                               !strncmp( pdn.bv_val +
+                                               STRLENOF( "olcDatabase" ), "=frontend",
+                                               STRLENOF( "=frontend" ))) {
+
+                                       isFrontendChild = 1;
+                                       break;
+                               }
+                       }
+                       dnParent( &pdn, &pdn );
+               }
+       }
+
        /* Create entry for config database if it does not exist already */
-       if ( !entry_put_got_config && !isFrontend ) {
+       if ( !entry_put_got_config && !isFrontend && !isFrontendChild ) {
                if ( !strncmp( e->e_nname.bv_val, "olcDatabase",
                                STRLENOF( "olcDatabase" ))) {
                        if ( strncmp( e->e_nname.bv_val +