/*	$NetBSD: config.c,v 1.1.1.7.6.1 2019/08/10 06:17:18 martin Exp $	*/
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software .
 *
 * Copyright 1999-2019 The OpenLDAP Foundation.
 * Portions Copyright 2001-2003 Pierangelo Masarati.
 * Portions Copyright 1999-2003 Howard Chu.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * .
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by the Howard Chu for inclusion
 * in OpenLDAP Software and subsequently enhanced by Pierangelo
 * Masarati.
 */
#include 
__RCSID("$NetBSD: config.c,v 1.1.1.7.6.1 2019/08/10 06:17:18 martin Exp $");
#include "portable.h"
#include 
#include 
#include 
#include 
#include "slap.h"
#include "config.h"
#include "lutil.h"
#include "ldif.h"
#include "../back-ldap/back-ldap.h"
#include "back-meta.h"
#ifdef LDAP_DEVEL
#define SLAP_AUTH_DN	1
#endif
static ConfigDriver meta_back_cf_gen;
static ConfigLDAPadd meta_ldadd;
static ConfigCfAdd meta_cfadd;
static int ldap_back_map_config(
	ConfigArgs *c,
	struct ldapmap	*oc_map,
	struct ldapmap	*at_map );
/* Three sets of enums:
 *	1) attrs that are only valid in the base config
 *	2) attrs that are valid in base or target
 *	3) attrs that are only valid in a target
 */
/* Base attrs */
enum {
	LDAP_BACK_CFG_CONN_TTL = 1,
	LDAP_BACK_CFG_DNCACHE_TTL,
	LDAP_BACK_CFG_IDLE_TIMEOUT,
	LDAP_BACK_CFG_ONERR,
	LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
	LDAP_BACK_CFG_SINGLECONN,
	LDAP_BACK_CFG_USETEMP,
	LDAP_BACK_CFG_CONNPOOLMAX,
	LDAP_BACK_CFG_LAST_BASE
};
/* Base or target */
enum {
	LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE,
	LDAP_BACK_CFG_CANCEL,
	LDAP_BACK_CFG_CHASE,
	LDAP_BACK_CFG_CLIENT_PR,
	LDAP_BACK_CFG_DEFAULT_T,
	LDAP_BACK_CFG_NETWORK_TIMEOUT,
	LDAP_BACK_CFG_NOREFS,
	LDAP_BACK_CFG_NOUNDEFFILTER,
	LDAP_BACK_CFG_NRETRIES,
	LDAP_BACK_CFG_QUARANTINE,
	LDAP_BACK_CFG_REBIND,
	LDAP_BACK_CFG_TIMEOUT,
	LDAP_BACK_CFG_VERSION,
	LDAP_BACK_CFG_ST_REQUEST,
	LDAP_BACK_CFG_T_F,
	LDAP_BACK_CFG_TLS,
	LDAP_BACK_CFG_LAST_BOTH
};
/* Target attrs */
enum {
	LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH,
	LDAP_BACK_CFG_ACL_AUTHCDN,
	LDAP_BACK_CFG_ACL_PASSWD,
	LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
	LDAP_BACK_CFG_IDASSERT_BIND,
	LDAP_BACK_CFG_REWRITE,
	LDAP_BACK_CFG_SUFFIXM,
	LDAP_BACK_CFG_MAP,
	LDAP_BACK_CFG_SUBTREE_EX,
	LDAP_BACK_CFG_SUBTREE_IN,
	LDAP_BACK_CFG_PSEUDOROOTDN,
	LDAP_BACK_CFG_PSEUDOROOTPW,
	LDAP_BACK_CFG_KEEPALIVE,
	LDAP_BACK_CFG_FILTER,
	LDAP_BACK_CFG_LAST
};
static ConfigTable metacfg[] = {
	{ "uri", "uri", 2, 0, 0,
		ARG_MAGIC|LDAP_BACK_CFG_URI,
		meta_back_cf_gen, "( OLcfgDbAt:0.14 "
			"NAME 'olcDbURI' "
			"DESC 'URI (list) for remote DSA' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "tls", "what", 2, 0, 0,
		ARG_MAGIC|LDAP_BACK_CFG_TLS,
		meta_back_cf_gen, "( OLcfgDbAt:3.1 "
			"NAME 'olcDbStartTLS' "
			"DESC 'StartTLS' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "acl-authcDN", "DN", 2, 2, 0,
		ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
		meta_back_cf_gen, "( OLcfgDbAt:3.2 "
			"NAME 'olcDbACLAuthcDn' "
			"DESC 'Remote ACL administrative identity' "
			"OBSOLETE "
			"SYNTAX OMsDN "
			"SINGLE-VALUE )",
		NULL, NULL },
	/* deprecated, will be removed; aliases "acl-authcDN" */
	{ "binddn", "DN", 2, 2, 0,
		ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
		meta_back_cf_gen, NULL, NULL, NULL },
	{ "acl-passwd", "cred", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
		meta_back_cf_gen, "( OLcfgDbAt:3.3 "
			"NAME 'olcDbACLPasswd' "
			"DESC 'Remote ACL administrative identity credentials' "
			"OBSOLETE "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	/* deprecated, will be removed; aliases "acl-passwd" */
	{ "bindpw", "cred", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
		meta_back_cf_gen, NULL, NULL, NULL },
	{ "idassert-bind", "args", 2, 0, 0,
		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
		meta_back_cf_gen, "( OLcfgDbAt:3.7 "
			"NAME 'olcDbIDAssertBind' "
			"DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "idassert-authzFrom", "authzRule", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
		meta_back_cf_gen, "( OLcfgDbAt:3.9 "
			"NAME 'olcDbIDAssertAuthzFrom' "
			"DESC 'Remote Identity Assertion authz rules' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString "
			"X-ORDERED 'VALUES' )",
		NULL, NULL },
	{ "rebind-as-user", "true|FALSE", 1, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
		meta_back_cf_gen, "( OLcfgDbAt:3.10 "
			"NAME 'olcDbRebindAsUser' "
			"DESC 'Rebind as user' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "chase-referrals", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
		meta_back_cf_gen, "( OLcfgDbAt:3.11 "
			"NAME 'olcDbChaseReferrals' "
			"DESC 'Chase referrals' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "t-f-support", "true|FALSE|discover", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_T_F,
		meta_back_cf_gen, "( OLcfgDbAt:3.12 "
			"NAME 'olcDbTFSupport' "
			"DESC 'Absolute filters support' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "timeout", "timeout(list)", 2, 0, 0,
		ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
		meta_back_cf_gen, "( OLcfgDbAt:3.14 "
			"NAME 'olcDbTimeout' "
			"DESC 'Per-operation timeouts' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "idle-timeout", "timeout", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
		meta_back_cf_gen, "( OLcfgDbAt:3.15 "
			"NAME 'olcDbIdleTimeout' "
			"DESC 'connection idle timeout' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "conn-ttl", "ttl", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
		meta_back_cf_gen, "( OLcfgDbAt:3.16 "
			"NAME 'olcDbConnTtl' "
			"DESC 'connection ttl' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "network-timeout", "timeout", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
		meta_back_cf_gen, "( OLcfgDbAt:3.17 "
			"NAME 'olcDbNetworkTimeout' "
			"DESC 'connection network timeout' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "protocol-version", "version", 2, 2, 0,
		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
		meta_back_cf_gen, "( OLcfgDbAt:3.18 "
			"NAME 'olcDbProtocolVersion' "
			"DESC 'protocol version' "
			"SYNTAX OMsInteger "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "single-conn", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
		meta_back_cf_gen, "( OLcfgDbAt:3.19 "
			"NAME 'olcDbSingleConn' "
			"DESC 'cache a single connection per identity' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "cancel", "ABANDON|ignore|exop", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
		meta_back_cf_gen, "( OLcfgDbAt:3.20 "
			"NAME 'olcDbCancel' "
			"DESC 'abandon/ignore/exop operations when appropriate' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "quarantine", "retrylist", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
		meta_back_cf_gen, "( OLcfgDbAt:3.21 "
			"NAME 'olcDbQuarantine' "
			"DESC 'Quarantine database if connection fails and retry according to rule' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "use-temporary-conn", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
		meta_back_cf_gen, "( OLcfgDbAt:3.22 "
			"NAME 'olcDbUseTemporaryConn' "
			"DESC 'Use temporary connections if the cached one is busy' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "conn-pool-max", "", 2, 2, 0,
		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
		meta_back_cf_gen, "( OLcfgDbAt:3.23 "
			"NAME 'olcDbConnectionPoolMax' "
			"DESC 'Max size of privileged connections pool' "
			"SYNTAX OMsInteger "
			"SINGLE-VALUE )",
		NULL, NULL },
#ifdef SLAP_CONTROL_X_SESSION_TRACKING
	{ "session-tracking-request", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
		meta_back_cf_gen, "( OLcfgDbAt:3.24 "
			"NAME 'olcDbSessionTrackingRequest' "
			"DESC 'Add session tracking control to proxied requests' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
	{ "norefs", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
		meta_back_cf_gen, "( OLcfgDbAt:3.25 "
			"NAME 'olcDbNoRefs' "
			"DESC 'Do not return search reference responses' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "noundeffilter", "true|FALSE", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
		meta_back_cf_gen, "( OLcfgDbAt:3.26 "
			"NAME 'olcDbNoUndefFilter' "
			"DESC 'Do not propagate undefined search filters' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ),
		ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
		meta_back_cf_gen, "( OLcfgDbAt:3.101 "
			"NAME 'olcDbRewrite' "
			"DESC 'DN rewriting rules' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString "
			"X-ORDERED 'VALUES' )",
		NULL, NULL },
	{ "suffixmassage", "virtual>  [*|] *|]", 1, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
		meta_back_cf_gen, "( OLcfgDbAt:3.105 "
			"NAME 'olcDbDefaultTarget' "
			"DESC 'Specify the default target' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "dncache-ttl", "ttl", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
		meta_back_cf_gen, "( OLcfgDbAt:3.106 "
			"NAME 'olcDbDnCacheTtl' "
			"DESC 'dncache ttl' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "bind-timeout", "microseconds", 2, 2, 0,
		ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
		meta_back_cf_gen, "( OLcfgDbAt:3.107 "
			"NAME 'olcDbBindTimeout' "
			"DESC 'bind timeout' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "onerr", "CONTINUE|report|stop", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_ONERR,
		meta_back_cf_gen, "( OLcfgDbAt:3.108 "
			"NAME 'olcDbOnErr' "
			"DESC 'error handling' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
		meta_back_cf_gen, "( OLcfgDbAt:3.109 "
			"NAME 'olcDbPseudoRootBindDefer' "
			"DESC 'error handling' "
			"SYNTAX OMsBoolean "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "root-bind-defer", "TRUE|false", 2, 2, 0,
		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
		meta_back_cf_gen, NULL, NULL, NULL },
	{ "pseudorootdn", "dn", 2, 2, 0,
		ARG_MAGIC|ARG_DN|LDAP_BACK_CFG_PSEUDOROOTDN,
		meta_back_cf_gen, NULL, NULL, NULL },
	{ "pseudorootpw", "password", 2, 2, 0,
		ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTDN,
		meta_back_cf_gen, NULL, NULL, NULL },
	{ "nretries", "NEVER|forever|", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
		meta_back_cf_gen, "( OLcfgDbAt:3.110 "
			"NAME 'olcDbNretries' "
			"DESC 'retry handling' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "client-pr", "accept-unsolicited|disable|", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
		meta_back_cf_gen, "( OLcfgDbAt:3.111 "
			"NAME 'olcDbClientPr' "
			"DESC 'PagedResults handling' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "", "", 0, 0, 0, ARG_IGNORED,
		NULL, "( OLcfgDbAt:3.100 NAME 'olcMetaSub' "
			"DESC 'Placeholder to name a Target entry' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
	{ "keepalive", "keepalive", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
		meta_back_cf_gen, "( OLcfgDbAt:3.29 "
			"NAME 'olcDbKeepalive' "
			"DESC 'TCP keepalive' "
			"SYNTAX OMsDirectoryString "
			"SINGLE-VALUE )",
		NULL, NULL },
	{ "filter", "pattern", 2, 2, 0,
		ARG_MAGIC|LDAP_BACK_CFG_FILTER,
		meta_back_cf_gen, "( OLcfgDbAt:3.112 "
			"NAME 'olcDbFilter' "
			"DESC 'Filter regex pattern to include in target' "
			"EQUALITY caseExactMatch "
			"SYNTAX OMsDirectoryString )",
		NULL, NULL },
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
		NULL, NULL, NULL, NULL }
};
#ifdef SLAP_CONTROL_X_SESSION_TRACKING
#define	ST_ATTR "$ olcDbSessionTrackingRequest "
#else
#define	ST_ATTR ""
#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
#define COMMON_ATTRS	\
			"$ olcDbBindTimeout " \
			"$ olcDbCancel " \
			"$ olcDbChaseReferrals " \
			"$ olcDbClientPr " \
			"$ olcDbDefaultTarget " \
			"$ olcDbNetworkTimeout " \
			"$ olcDbNoRefs " \
			"$ olcDbNoUndefFilter " \
			"$ olcDbNretries " \
			"$ olcDbProtocolVersion " \
			"$ olcDbQuarantine " \
			"$ olcDbRebindAsUser " \
			ST_ATTR \
			"$ olcDbStartTLS " \
			"$ olcDbTFSupport "
static ConfigOCs metaocs[] = {
	{ "( OLcfgDbOc:3.2 "
		"NAME 'olcMetaConfig' "
		"DESC 'Meta backend configuration' "
		"SUP olcDatabaseConfig "
		"MAY ( olcDbConnTtl "
			"$ olcDbDnCacheTtl "
			"$ olcDbIdleTimeout "
			"$ olcDbOnErr "
			"$ olcDbPseudoRootBindDefer "
			"$ olcDbSingleConn "
			"$ olcDbUseTemporaryConn "
			"$ olcDbConnectionPoolMax "
			/* defaults, may be overridden per-target */
			COMMON_ATTRS
		") )",
			Cft_Database, metacfg, NULL, meta_cfadd },
	{ "( OLcfgDbOc:3.3 "
		"NAME 'olcMetaTargetConfig' "
		"DESC 'Meta target configuration' "
		"SUP olcConfig STRUCTURAL "
		"MUST ( olcMetaSub $ olcDbURI ) "
		"MAY ( olcDbACLAuthcDn "
			"$ olcDbACLPasswd "
			"$ olcDbIDAssertAuthzFrom "
			"$ olcDbIDAssertBind "
			"$ olcDbMap "
			"$ olcDbRewrite "
			"$ olcDbSubtreeExclude "
			"$ olcDbSubtreeInclude "
			"$ olcDbTimeout "
			"$ olcDbKeepalive "
			"$ olcDbFilter "
			/* defaults may be inherited */
			COMMON_ATTRS
		") )",
			Cft_Misc, metacfg, meta_ldadd },
	{ NULL, 0, NULL }
};
static int
meta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
{
	if ( p->ce_type != Cft_Database || !p->ce_be ||
		p->ce_be->be_cf_ocs != metaocs )
		return LDAP_CONSTRAINT_VIOLATION;
	c->be = p->ce_be;
	return LDAP_SUCCESS;
}
static int
meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
{
	metainfo_t	*mi = ( metainfo_t * )c->be->be_private;
	struct berval bv;
	int i;
	bv.bv_val = c->cr_msg;
	for ( i=0; imi_ntargets; i++ ) {
		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
			"olcMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
		c->ca_private = mi->mi_targets[i];
		c->valx = i;
		config_build_entry( op, rs, p->e_private, c,
			&bv, &metaocs[1], NULL );
	}
	return LDAP_SUCCESS;
}
static int
meta_rwi_init( struct rewrite_info **rwm_rw )
{
	char			*rargv[ 3 ];
	*rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
	if ( *rwm_rw == NULL ) {
		return -1;
	}
	/*
	 * the filter rewrite as a string must be disabled
	 * by default; it can be re-enabled by adding rules;
	 * this creates an empty rewriteContext
	 */
	rargv[ 0 ] = "rewriteContext";
	rargv[ 1 ] = "searchFilter";
	rargv[ 2 ] = NULL;
	rewrite_parse( *rwm_rw, "", 1, 2, rargv );
	rargv[ 0 ] = "rewriteContext";
	rargv[ 1 ] = "default";
	rargv[ 2 ] = NULL;
	rewrite_parse( *rwm_rw, "", 1, 2, rargv );
	return 0;
}
static int
meta_back_new_target(
	metatarget_t	**mtp )
{
	metatarget_t		*mt;
	*mtp = NULL;
	mt = ch_calloc( sizeof( metatarget_t ), 1 );
	if ( meta_rwi_init( &mt->mt_rwmap.rwm_rw )) {
		ch_free( mt );
		return -1;
	}
	ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
	mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
	mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
	mt->mt_idassert_tls = SB_TLS_DEFAULT;
	/* by default, use proxyAuthz control on each operation */
	mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
	*mtp = mt;
	return 0;
}
/* Validation for suffixmassage_config */
static int
meta_suffixm_config(
	ConfigArgs *c,
	int argc,
	char **argv,
	metatarget_t *mt
)
{
	BackendDB 	*tmp_bd;
	struct berval	dn, nvnc, pvnc, nrnc, prnc;
	int j, rc;
	/*
	 * syntax:
	 *
	 * 	suffixmassage  
	 *
	 * the  field must be defined as a valid suffix
	 * (or suffixAlias?) for the current database;
	 * the  shouldn't have already been
	 * defined as a valid suffix or suffixAlias for the
	 * current server
	 */
	ber_str2bv( argv[ 1 ], 0, 0, &dn );
	if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
			"suffix \"%s\" is invalid",
			argv[1] );
		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
		return 1;
	}
	for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
		if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
			break;
		}
	}
	if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
			"suffix \"%s\" must be within the database naming context",
			argv[1] );
		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
		free( pvnc.bv_val );
		free( nvnc.bv_val );
		return 1;
	}
	ber_str2bv( argv[ 2 ], 0, 0, &dn );
	if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
			"massaged suffix \"%s\" is invalid",
			argv[2] );
		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
		free( pvnc.bv_val );
		free( nvnc.bv_val );
		return 1;
	}
	tmp_bd = select_backend( &nrnc, 0 );
	if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
		Debug( LDAP_DEBUG_ANY,
	"%s: warning:  \"%s\" resolves to this database, in "
	"\"suffixMassage  \"\n",
			c->log, prnc.bv_val, 0 );
	}
	/*
	 * The suffix massaging is emulated by means of the
	 * rewrite capabilities
	 */
	rc = suffix_massage_config( mt->mt_rwmap.rwm_rw,
			&pvnc, &nvnc, &prnc, &nrnc );
	free( pvnc.bv_val );
	free( nvnc.bv_val );
	free( prnc.bv_val );
	free( nrnc.bv_val );
	return rc;
}
static int
slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
{
	int		i;
	BerVarray	bva = NULL;
	char		ibuf[32], *ptr;
	struct berval	idx;
	assert( in != NULL );
	for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
		/* count'em */ ;
	if ( i == 0 ) {
		return 1;
	}
	idx.bv_val = ibuf;
	bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
	BER_BVZERO( &bva[ 0 ] );
	for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
		idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
		if ( idx.bv_len >= sizeof( ibuf ) ) {
			ber_bvarray_free( bva );
			return 1;
		}
		bva[i].bv_len = idx.bv_len + in[i].bv_len;
		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
		ptr = lutil_strcopy( bva[i].bv_val, ibuf );
		ptr = lutil_strcopy( ptr, in[i].bv_val );
		*ptr = '\0';
		BER_BVZERO( &bva[ i + 1 ] );
	}
	*out = bva;
	return 0;
}
int
meta_subtree_free( metasubtree_t *ms )
{
	switch ( ms->ms_type ) {
	case META_ST_SUBTREE:
	case META_ST_SUBORDINATE:
		ber_memfree( ms->ms_dn.bv_val );
		break;
	case META_ST_REGEX:
		regfree( &ms->ms_regex );
		ber_memfree( ms->ms_regex_pattern.bv_val );
		break;
	default:
		return -1;
	}
	ch_free( ms );
	return 0;
}
int
meta_subtree_destroy( metasubtree_t *ms )
{
	if ( ms->ms_next ) {
		meta_subtree_destroy( ms->ms_next );
	}
	return meta_subtree_free( ms );
}
static void
meta_filter_free( metafilter_t *mf )
{
	regfree( &mf->mf_regex );
	ber_memfree( mf->mf_regex_pattern.bv_val );
	ch_free( mf );
}
void
meta_filter_destroy( metafilter_t *mf )
{
	if ( mf->mf_next )
		meta_filter_destroy( mf->mf_next );
	meta_filter_free( mf );
}
static struct berval st_styles[] = {
	BER_BVC("subtree"),
	BER_BVC("children"),
	BER_BVC("regex")
};
static int
meta_subtree_unparse(
	ConfigArgs *c,
	metatarget_t *mt )
{
	metasubtree_t	*ms;
	struct berval bv, *style;
	if ( !mt->mt_subtree )
		return 1;
	/* can only be one of exclude or include */
	if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
		return 1;
	bv.bv_val = c->cr_msg;
	for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
		if (ms->ms_type == META_ST_SUBTREE)
			style = &st_styles[0];
		else if ( ms->ms_type == META_ST_SUBORDINATE )
			style = &st_styles[1];
		else if ( ms->ms_type == META_ST_REGEX )
			style = &st_styles[2];
		else {
			assert(0);
			continue;
		}
		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
			"dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
		value_add_one( &c->rvalue_vals, &bv );
	}
	return 0;
}
static int
meta_subtree_config(
	metatarget_t *mt,
	ConfigArgs *c )
{
	meta_st_t	type = META_ST_SUBTREE;
	char		*pattern;
	struct berval	ndn = BER_BVNULL;
	metasubtree_t	*ms = NULL;
	if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
		if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
			snprintf( c->cr_msg, sizeof(c->cr_msg),
				"\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
			return 1;
		}
		mt->mt_subtree_exclude = 1;
	} else {
		if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
			snprintf( c->cr_msg, sizeof(c->cr_msg),
				"\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
			return 1;
		}
	}
	pattern = c->argv[1];
	if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
		char *style;
		pattern = &pattern[STRLENOF( "dn")];
		if ( pattern[0] == '.' ) {
			style = &pattern[1];
			if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
				type = META_ST_SUBTREE;
				pattern = &style[STRLENOF( "subtree" )];
			} else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
				type = META_ST_SUBORDINATE;
				pattern = &style[STRLENOF( "children" )];
			} else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
				type = META_ST_SUBTREE;
				pattern = &style[STRLENOF( "sub" )];
			} else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
				type = META_ST_REGEX;
				pattern = &style[STRLENOF( "regex" )];
			} else {
				snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.