1/*	$NetBSD: config.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
2
3/* config.c - configuration parsing for back-asyncmeta */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2016-2021 The OpenLDAP Foundation.
8 * Portions Copyright 2016 Symas Corporation.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted only as authorized by the OpenLDAP
13 * Public License.
14 *
15 * A copy of this license is available in the file LICENSE in the
16 * top-level directory of the distribution or, alternatively, at
17 * <http://www.OpenLDAP.org/license.html>.
18 */
19
20/* ACKNOWLEDGEMENTS:
21 * This work was developed by Symas Corporation
22 * based on back-meta module for inclusion in OpenLDAP Software.
23 * This work was sponsored by Ericsson. */
24
25#include <sys/cdefs.h>
26__RCSID("$NetBSD: config.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
27
28#include "portable.h"
29
30#include <stdio.h>
31#include <ctype.h>
32
33#include <ac/string.h>
34#include <ac/socket.h>
35
36#include "slap.h"
37#include "slap-config.h"
38#include "lutil.h"
39#include "ldif.h"
40#include "../back-ldap/back-ldap.h"
41#include "back-asyncmeta.h"
42
43#ifdef LDAP_DEVEL
44#define SLAP_AUTH_DN	1
45#endif
46
47static ConfigDriver asyncmeta_back_cf_gen;
48static ConfigLDAPadd asyncmeta_ldadd;
49static ConfigCfAdd asyncmeta_cfadd;
50
51/* Three sets of enums:
52 *	1) attrs that are only valid in the base config
53 *	2) attrs that are valid in base or target
54 *	3) attrs that are only valid in a target
55 */
56
57/* Base attrs */
58enum {
59	LDAP_BACK_CFG_DNCACHE_TTL = 1,
60	LDAP_BACK_CFG_IDLE_TIMEOUT,
61	LDAP_BACK_CFG_ONERR,
62	LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
63	LDAP_BACK_CFG_CONNPOOLMAX,
64	LDAP_BACK_CFG_MAX_TIMEOUT_OPS,
65	LDAP_BACK_CFG_MAX_PENDING_OPS,
66	LDAP_BACK_CFG_MAX_TARGET_CONNS,
67	LDAP_BACK_CFG_LAST_BASE,
68};
69
70/* Base or target */
71enum {
72	LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE,
73	LDAP_BACK_CFG_CANCEL,
74	LDAP_BACK_CFG_CHASE,
75	LDAP_BACK_CFG_CLIENT_PR,
76	LDAP_BACK_CFG_DEFAULT_T,
77	LDAP_BACK_CFG_NETWORK_TIMEOUT,
78	LDAP_BACK_CFG_NOREFS,
79	LDAP_BACK_CFG_NOUNDEFFILTER,
80	LDAP_BACK_CFG_NRETRIES,
81	LDAP_BACK_CFG_QUARANTINE,
82	LDAP_BACK_CFG_REBIND,
83	LDAP_BACK_CFG_TIMEOUT,
84	LDAP_BACK_CFG_VERSION,
85	LDAP_BACK_CFG_ST_REQUEST,
86	LDAP_BACK_CFG_T_F,
87	LDAP_BACK_CFG_TLS,
88	LDAP_BACK_CFG_LAST_BOTH
89};
90
91/* Target attrs */
92enum {
93	LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH,
94	LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
95	LDAP_BACK_CFG_IDASSERT_BIND,
96	LDAP_BACK_CFG_SUFFIXM,
97	LDAP_BACK_CFG_SUBTREE_EX,
98	LDAP_BACK_CFG_SUBTREE_IN,
99	LDAP_BACK_CFG_KEEPALIVE,
100	LDAP_BACK_CFG_FILTER,
101	LDAP_BACK_CFG_TCP_USER_TIMEOUT,
102	LDAP_BACK_CFG_LAST
103};
104
105static ConfigTable a_metacfg[] = {
106	{ "uri", "uri", 2, 0, 0,
107		ARG_MAGIC|LDAP_BACK_CFG_URI,
108		asyncmeta_back_cf_gen, "( OLcfgDbAt:0.14 "
109			"NAME 'olcDbURI' "
110			"DESC 'URI (list) for remote DSA' "
111			"SYNTAX OMsDirectoryString "
112			"SINGLE-VALUE )",
113		NULL, NULL },
114	{ "tls", "what", 2, 0, 0,
115		ARG_MAGIC|LDAP_BACK_CFG_TLS,
116		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.1 "
117			"NAME 'olcDbStartTLS' "
118			"DESC 'StartTLS' "
119			"SYNTAX OMsDirectoryString "
120			"SINGLE-VALUE )",
121		NULL, NULL },
122	{ "idassert-bind", "args", 2, 0, 0,
123		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
124		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.7 "
125			"NAME 'olcDbIDAssertBind' "
126			"DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
127			"SYNTAX OMsDirectoryString "
128			"SINGLE-VALUE )",
129		NULL, NULL },
130	{ "idassert-authzFrom", "authzRule", 2, 2, 0,
131		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
132		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.9 "
133			"NAME 'olcDbIDAssertAuthzFrom' "
134			"DESC 'Remote Identity Assertion authz rules' "
135			"EQUALITY caseIgnoreMatch "
136			"SYNTAX OMsDirectoryString "
137			"X-ORDERED 'VALUES' )",
138		NULL, NULL },
139	{ "rebind-as-user", "true|FALSE", 1, 2, 0,
140		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
141		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.10 "
142			"NAME 'olcDbRebindAsUser' "
143			"DESC 'Rebind as user' "
144			"SYNTAX OMsBoolean "
145			"SINGLE-VALUE )",
146		NULL, NULL },
147	{ "chase-referrals", "true|FALSE", 2, 2, 0,
148		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
149		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.11 "
150			"NAME 'olcDbChaseReferrals' "
151			"DESC 'Chase referrals' "
152			"SYNTAX OMsBoolean "
153			"SINGLE-VALUE )",
154		NULL, NULL },
155	{ "t-f-support", "true|FALSE|discover", 2, 2, 0,
156		ARG_MAGIC|LDAP_BACK_CFG_T_F,
157		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.12 "
158			"NAME 'olcDbTFSupport' "
159			"DESC 'Absolute filters support' "
160			"SYNTAX OMsDirectoryString "
161			"SINGLE-VALUE )",
162		NULL, NULL },
163	{ "timeout", "timeout(list)", 2, 0, 0,
164		ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
165		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.14 "
166			"NAME 'olcDbTimeout' "
167			"DESC 'Per-operation timeouts' "
168			"SYNTAX OMsDirectoryString "
169			"SINGLE-VALUE )",
170		NULL, NULL },
171	{ "idle-timeout", "timeout", 2, 2, 0,
172		ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
173		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.15 "
174			"NAME 'olcDbIdleTimeout' "
175			"DESC 'connection idle timeout' "
176			"SYNTAX OMsDirectoryString "
177			"SINGLE-VALUE )",
178		NULL, NULL },
179	{ "network-timeout", "timeout", 2, 2, 0,
180		ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
181		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.17 "
182			"NAME 'olcDbNetworkTimeout' "
183			"DESC 'connection network timeout' "
184			"SYNTAX OMsDirectoryString "
185			"SINGLE-VALUE )",
186		NULL, NULL },
187	{ "protocol-version", "version", 2, 2, 0,
188		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
189		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.18 "
190			"NAME 'olcDbProtocolVersion' "
191			"DESC 'protocol version' "
192			"SYNTAX OMsInteger "
193			"SINGLE-VALUE )",
194		NULL, NULL },
195
196	{ "cancel", "ABANDON|ignore|exop", 2, 2, 0,
197		ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
198		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.20 "
199			"NAME 'olcDbCancel' "
200			"DESC 'abandon/ignore/exop operations when appropriate' "
201			"SYNTAX OMsDirectoryString "
202			"SINGLE-VALUE )",
203		NULL, NULL },
204	{ "quarantine", "retrylist", 2, 2, 0,
205		ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
206		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.21 "
207			"NAME 'olcDbQuarantine' "
208			"DESC 'Quarantine database if connection fails and retry according to rule' "
209			"SYNTAX OMsDirectoryString "
210			"SINGLE-VALUE )",
211		NULL, NULL },
212
213	{ "conn-pool-max", "<n>", 2, 2, 0,
214		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
215		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.23 "
216			"NAME 'olcDbConnectionPoolMax' "
217			"DESC 'Max size of privileged connections pool' "
218			"SYNTAX OMsInteger "
219			"SINGLE-VALUE )",
220		NULL, NULL },
221#ifdef SLAP_CONTROL_X_SESSION_TRACKING
222	{ "session-tracking-request", "true|FALSE", 2, 2, 0,
223		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
224		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.24 "
225			"NAME 'olcDbSessionTrackingRequest' "
226			"DESC 'Add session tracking control to proxied requests' "
227			"SYNTAX OMsBoolean "
228			"SINGLE-VALUE )",
229		NULL, NULL },
230#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
231	{ "norefs", "true|FALSE", 2, 2, 0,
232		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
233		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.25 "
234			"NAME 'olcDbNoRefs' "
235			"DESC 'Do not return search reference responses' "
236			"SYNTAX OMsBoolean "
237			"SINGLE-VALUE )",
238		NULL, NULL },
239	{ "noundeffilter", "true|FALSE", 2, 2, 0,
240		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
241		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.26 "
242			"NAME 'olcDbNoUndefFilter' "
243			"DESC 'Do not propagate undefined search filters' "
244			"SYNTAX OMsBoolean "
245			"SINGLE-VALUE )",
246		NULL, NULL },
247
248	{ "suffixmassage", "local> <remote", 2, 3, 0,
249		ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
250		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.117 "
251			"NAME 'olcDbSuffixMassage' "
252			"DESC 'DN suffix massage' "
253			"EQUALITY caseIgnoreMatch "
254			"SYNTAX OMsDirectoryString "
255			"SINGLE-VALUE )",
256		NULL, NULL },
257
258	{ "subtree-exclude", "pattern", 2, 2, 0,
259		ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX,
260		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.103 "
261			"NAME 'olcDbSubtreeExclude' "
262			"DESC 'DN of subtree to exclude from target' "
263			"EQUALITY caseIgnoreMatch "
264			"SYNTAX OMsDirectoryString )",
265		NULL, NULL },
266	{ "subtree-include", "pattern", 2, 2, 0,
267		ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN,
268		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.104 "
269			"NAME 'olcDbSubtreeInclude' "
270			"DESC 'DN of subtree to include in target' "
271			"EQUALITY caseIgnoreMatch "
272			"SYNTAX OMsDirectoryString )",
273		NULL, NULL },
274	{ "default-target", "[none|<target ID>]", 1, 2, 0,
275		ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
276		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.105 "
277			"NAME 'olcDbDefaultTarget' "
278			"DESC 'Specify the default target' "
279			"SYNTAX OMsDirectoryString "
280			"SINGLE-VALUE )",
281		NULL, NULL },
282	{ "dncache-ttl", "ttl", 2, 2, 0,
283		ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
284		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.106 "
285			"NAME 'olcDbDnCacheTtl' "
286			"DESC 'dncache ttl' "
287			"SYNTAX OMsDirectoryString "
288			"SINGLE-VALUE )",
289		NULL, NULL },
290	{ "bind-timeout", "microseconds", 2, 2, 0,
291		ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
292		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.107 "
293			"NAME 'olcDbBindTimeout' "
294			"DESC 'bind timeout' "
295			"SYNTAX OMsDirectoryString "
296			"SINGLE-VALUE )",
297		NULL, NULL },
298	{ "onerr", "CONTINUE|report|stop", 2, 2, 0,
299		ARG_MAGIC|LDAP_BACK_CFG_ONERR,
300		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.108 "
301			"NAME 'olcDbOnErr' "
302			"DESC 'error handling' "
303			"SYNTAX OMsDirectoryString "
304			"SINGLE-VALUE )",
305		NULL, NULL },
306	{ "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
307		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
308		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.109 "
309			"NAME 'olcDbPseudoRootBindDefer' "
310			"DESC 'error handling' "
311			"SYNTAX OMsBoolean "
312			"SINGLE-VALUE )",
313		NULL, NULL },
314	{ "root-bind-defer", "TRUE|false", 2, 2, 0,
315		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
316		asyncmeta_back_cf_gen, NULL, NULL, NULL },
317	{ "nretries", "NEVER|forever|<number>", 2, 2, 0,
318		ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
319		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.110 "
320			"NAME 'olcDbNretries' "
321			"DESC 'retry handling' "
322			"SYNTAX OMsDirectoryString "
323			"SINGLE-VALUE )",
324		NULL, NULL },
325	{ "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
326		ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
327		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.111 "
328			"NAME 'olcDbClientPr' "
329			"DESC 'PagedResults handling' "
330			"SYNTAX OMsDirectoryString "
331			"SINGLE-VALUE )",
332		NULL, NULL },
333
334	{ "", "", 0, 0, 0, ARG_IGNORED,
335		NULL, "( OLcfgDbAt:3.116 NAME 'olcAsyncMetaSub' "
336			"DESC 'Placeholder to name a Target entry' "
337			"EQUALITY caseIgnoreMatch "
338			"SYNTAX OMsDirectoryString "
339			"SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
340
341	{ "keepalive", "keepalive", 2, 2, 0,
342		ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
343		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.29 "
344			"NAME 'olcDbKeepalive' "
345			"DESC 'TCP keepalive' "
346			"SYNTAX OMsDirectoryString "
347			"SINGLE-VALUE )",
348		NULL, NULL },
349
350	{ "tcp-user-timeout", "milliseconds", 2, 2, 0,
351		ARG_MAGIC|ARG_UINT|LDAP_BACK_CFG_TCP_USER_TIMEOUT,
352		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.30 "
353			"NAME 'olcDbTcpUserTimeout' "
354			"DESC 'TCP User Timeout' "
355			"SYNTAX OMsInteger "
356			"SINGLE-VALUE )",
357		NULL, NULL },
358
359	{ "filter", "pattern", 2, 2, 0,
360		ARG_MAGIC|LDAP_BACK_CFG_FILTER,
361		asyncmeta_back_cf_gen, "( OLcfgDbAt:3.112 "
362			"NAME 'olcDbFilter' "
363			"DESC 'Filter regex pattern to include in target' "
364			"EQUALITY caseExactMatch "
365			"SYNTAX OMsDirectoryString )",
366		NULL, NULL },
367
368	{ "max-pending-ops", "<n>", 2, 2, 0,
369	  ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_PENDING_OPS,
370	  asyncmeta_back_cf_gen, "( OLcfgDbAt:3.113 "
371	  "NAME 'olcDbMaxPendingOps' "
372	  "DESC 'Maximum number of pending operations' "
373	  "SYNTAX OMsInteger "
374	  "SINGLE-VALUE )",
375	  NULL, NULL },
376
377	{ "max-target-conns", "<n>", 2, 2, 0,
378	  ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_TARGET_CONNS,
379	  asyncmeta_back_cf_gen, "( OLcfgDbAt:3.114 "
380	  "NAME 'olcDbMaxTargetConns' "
381	  "DESC 'Maximum number of open connections per target' "
382	  "SYNTAX OMsInteger "
383	  "SINGLE-VALUE )",
384	  NULL, NULL },
385
386	{ "max-timeout-ops", "<n>", 2, 2, 0,
387	  ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_TIMEOUT_OPS,
388	  asyncmeta_back_cf_gen, "( OLcfgDbAt:3.115 "
389	  "NAME 'olcDbMaxTimeoutOps' "
390	  "DESC 'Maximum number of consecutive timeout operations after which the connection is reset' "
391	  "SYNTAX OMsInteger "
392	  "SINGLE-VALUE )",
393	  NULL, NULL },
394
395	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
396		NULL, NULL, NULL, NULL }
397};
398
399#ifdef SLAP_CONTROL_X_SESSION_TRACKING
400#define	ST_ATTR "$ olcDbSessionTrackingRequest "
401#else
402#define	ST_ATTR ""
403#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
404
405#define COMMON_ATTRS	\
406			"$ olcDbBindTimeout " \
407			"$ olcDbCancel " \
408			"$ olcDbChaseReferrals " \
409			"$ olcDbClientPr " \
410			"$ olcDbDefaultTarget " \
411			"$ olcDbNetworkTimeout " \
412			"$ olcDbNoRefs " \
413			"$ olcDbNoUndefFilter " \
414			"$ olcDbNretries " \
415			"$ olcDbProtocolVersion " \
416			"$ olcDbQuarantine " \
417			"$ olcDbRebindAsUser " \
418			ST_ATTR \
419			"$ olcDbStartTLS " \
420			"$ olcDbTFSupport "
421
422static ConfigOCs a_metaocs[] = {
423	{ "( OLcfgDbOc:3.4 "
424		"NAME 'olcAsyncMetaConfig' "
425		"DESC 'Asyncmeta backend configuration' "
426		"SUP olcDatabaseConfig "
427		"MAY ( olcDbDnCacheTtl "
428			"$ olcDbIdleTimeout "
429			"$ olcDbOnErr "
430			"$ olcDbPseudoRootBindDefer "
431			"$ olcDbConnectionPoolMax "
432	                "$ olcDbMaxTimeoutOps"
433	                "$ olcDbMaxPendingOps "
434	                "$ olcDbMaxTargetConns"
435			/* defaults, may be overridden per-target */
436			COMMON_ATTRS
437		") )",
438			Cft_Database, a_metacfg, NULL, asyncmeta_cfadd },
439	{ "( OLcfgDbOc:3.5 "
440		"NAME 'olcAsyncMetaTargetConfig' "
441		"DESC 'Asyncmeta target configuration' "
442		"SUP olcConfig STRUCTURAL "
443		"MUST ( olcAsyncMetaSub $ olcDbURI ) "
444		"MAY ( olcDbIDAssertAuthzFrom "
445			"$ olcDbIDAssertBind "
446			"$ olcDbSuffixMassage "
447			"$ olcDbSubtreeExclude "
448			"$ olcDbSubtreeInclude "
449			"$ olcDbTimeout "
450			"$ olcDbKeepalive "
451			"$ olcDbFilter "
452			"$ olcDbTcpUserTimeout "
453
454			/* defaults may be inherited */
455			COMMON_ATTRS
456		") )",
457			Cft_Misc, a_metacfg, asyncmeta_ldadd },
458	{ NULL, 0, NULL }
459};
460
461static int
462asyncmeta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
463{
464	if ( p->ce_type != Cft_Database || !p->ce_be ||
465		p->ce_be->be_cf_ocs != a_metaocs )
466		return LDAP_CONSTRAINT_VIOLATION;
467
468	c->be = p->ce_be;
469	return LDAP_SUCCESS;
470}
471
472static int
473asyncmeta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
474{
475	a_metainfo_t	*mi = ( a_metainfo_t * )c->be->be_private;
476	struct berval bv;
477	int i;
478
479	bv.bv_val = c->cr_msg;
480	for ( i=0; i<mi->mi_ntargets; i++ ) {
481		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
482			"olcAsyncMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
483		c->ca_private = mi->mi_targets[i];
484		c->valx = i;
485		config_build_entry( op, rs, p->e_private, c,
486			&bv, &a_metaocs[1], NULL );
487	}
488
489	return LDAP_SUCCESS;
490}
491
492static int
493asyncmeta_back_new_target(
494	a_metatarget_t	**mtp )
495{
496	a_metatarget_t		*mt;
497
498	*mtp = NULL;
499
500	mt = ch_calloc( sizeof( a_metatarget_t ), 1 );
501
502	ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
503
504	mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
505	mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
506	mt->mt_idassert_tls = SB_TLS_DEFAULT;
507	/* by default, use proxyAuthz control on each operation */
508	mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
509
510	*mtp = mt;
511
512	return 0;
513}
514
515/* suffixmassage config */
516static int
517asyncmeta_suffixm_config(
518	ConfigArgs *c,
519	int argc,
520	char **argv,
521	a_metatarget_t *mt
522)
523{
524	BackendDB 	*tmp_bd;
525	struct berval	dn, nvnc, pvnc, nrnc, prnc;
526	int j;
527
528	/*
529	 * syntax:
530	 *
531	 * 	suffixmassage <local suffix> <remote suffix>
532	 *
533	 * the <local suffix> field must be defined as a valid suffix
534	 * (or suffixAlias?) for the current database;
535	 * the <remote suffix> shouldn't have already been
536	 * defined as a valid suffix or suffixAlias for the
537	 * current server
538	 */
539
540	ber_str2bv( argv[ 1 ], 0, 0, &dn );
541	if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
542		snprintf( c->cr_msg, sizeof( c->cr_msg ),
543			"suffix \"%s\" is invalid",
544			argv[1] );
545		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
546		return 1;
547	}
548
549	for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
550		if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
551			break;
552		}
553	}
554
555	if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
556		snprintf( c->cr_msg, sizeof( c->cr_msg ),
557			"suffix \"%s\" must be within the database naming context",
558			argv[1] );
559		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
560		free( pvnc.bv_val );
561		free( nvnc.bv_val );
562		return 1;
563	}
564
565	ber_str2bv( argv[ 2 ], 0, 0, &dn );
566	if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
567		snprintf( c->cr_msg, sizeof( c->cr_msg ),
568			"massaged suffix \"%s\" is invalid",
569			argv[2] );
570		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
571		free( pvnc.bv_val );
572		free( nvnc.bv_val );
573		return 1;
574	}
575
576	tmp_bd = select_backend( &nrnc, 0 );
577	if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
578		Debug( LDAP_DEBUG_ANY,
579	"%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
580	"\"suffixMassage <suffix> <massaged suffix>\"\n",
581			c->log, prnc.bv_val );
582	}
583
584	mt->mt_lsuffixm = pvnc;
585	mt->mt_rsuffixm = prnc;
586
587	free( nvnc.bv_val );
588	free( nrnc.bv_val );
589
590	return 0;
591}
592
593int
594asyncmeta_subtree_free( a_metasubtree_t *ms )
595{
596	switch ( ms->ms_type ) {
597	case META_ST_SUBTREE:
598	case META_ST_SUBORDINATE:
599		ber_memfree( ms->ms_dn.bv_val );
600		break;
601
602	case META_ST_REGEX:
603		regfree( &ms->ms_regex );
604		ber_memfree( ms->ms_regex_pattern.bv_val );
605		break;
606
607	default:
608		return -1;
609	}
610
611	ch_free( ms );
612	return 0;
613}
614
615int
616asyncmeta_subtree_destroy( a_metasubtree_t *ms )
617{
618	if ( ms->ms_next ) {
619		asyncmeta_subtree_destroy( ms->ms_next );
620	}
621
622	return asyncmeta_subtree_free( ms );
623}
624
625static void
626asyncmeta_filter_free( metafilter_t *mf )
627{
628	regfree( &mf->mf_regex );
629	ber_memfree( mf->mf_regex_pattern.bv_val );
630	ch_free( mf );
631}
632
633void
634asyncmeta_filter_destroy( metafilter_t *mf )
635{
636	if ( mf->mf_next )
637		asyncmeta_filter_destroy( mf->mf_next );
638	asyncmeta_filter_free( mf );
639}
640
641static struct berval st_styles[] = {
642	BER_BVC("subtree"),
643	BER_BVC("children"),
644	BER_BVC("regex")
645};
646
647static int
648asyncmeta_subtree_unparse(
649	ConfigArgs *c,
650	a_metatarget_t *mt )
651{
652	a_metasubtree_t	*ms;
653	struct berval bv, *style;
654
655	if ( !mt->mt_subtree )
656		return 1;
657
658	/* can only be one of exclude or include */
659	if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
660		return 1;
661
662	bv.bv_val = c->cr_msg;
663	for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
664		if (ms->ms_type == META_ST_SUBTREE)
665			style = &st_styles[0];
666		else if ( ms->ms_type == META_ST_SUBORDINATE )
667			style = &st_styles[1];
668		else if ( ms->ms_type == META_ST_REGEX )
669			style = &st_styles[2];
670		else {
671			assert(0);
672			continue;
673		}
674		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
675			"dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
676		value_add_one( &c->rvalue_vals, &bv );
677	}
678	return 0;
679}
680
681static int
682asyncmeta_subtree_config(
683	a_metatarget_t *mt,
684	ConfigArgs *c )
685{
686	meta_st_t	type = META_ST_SUBTREE;
687	char		*pattern;
688	struct berval	ndn = BER_BVNULL;
689	a_metasubtree_t	*ms = NULL;
690
691	if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
692		if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
693			snprintf( c->cr_msg, sizeof(c->cr_msg),
694				"\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
695			return 1;
696		}
697
698		mt->mt_subtree_exclude = 1;
699
700	} else {
701		if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
702			snprintf( c->cr_msg, sizeof(c->cr_msg),
703				"\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
704			return 1;
705		}
706	}
707
708	pattern = c->argv[1];
709	if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
710		char *style;
711
712		pattern = &pattern[STRLENOF( "dn")];
713
714		if ( pattern[0] == '.' ) {
715			style = &pattern[1];
716
717			if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
718				type = META_ST_SUBTREE;
719				pattern = &style[STRLENOF( "subtree" )];
720
721			} else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
722				type = META_ST_SUBORDINATE;
723				pattern = &style[STRLENOF( "children" )];
724
725			} else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
726				type = META_ST_SUBTREE;
727				pattern = &style[STRLENOF( "sub" )];
728
729			} else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
730				type = META_ST_REGEX;
731				pattern = &style[STRLENOF( "regex" )];
732
733			} else {
734				snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" );
735				return 1;
736			}
737		}
738
739		if ( pattern[0] != ':' ) {
740			snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" );
741			return 1;
742		}
743		pattern++;
744	}
745
746	switch ( type ) {
747	case META_ST_SUBTREE:
748	case META_ST_SUBORDINATE: {
749		struct berval dn;
750
751		ber_str2bv( pattern, 0, 0, &dn );
752		if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
753			!= LDAP_SUCCESS )
754		{
755			snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern );
756			return 1;
757		}
758
759		if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) {
760			snprintf( c->cr_msg, sizeof(c->cr_msg),
761				"DN=\"%s\" is not a subtree of target \"%s\"",
762				pattern, mt->mt_nsuffix.bv_val );
763			ber_memfree( ndn.bv_val );
764			return( 1 );
765		}
766		} break;
767
768	default:
769		/* silence warnings */
770		break;
771	}
772
773	ms = ch_calloc( sizeof( a_metasubtree_t ), 1 );
774	ms->ms_type = type;
775
776	switch ( ms->ms_type ) {
777	case META_ST_SUBTREE:
778	case META_ST_SUBORDINATE:
779		ms->ms_dn = ndn;
780		break;
781
782	case META_ST_REGEX: {
783		int rc;
784
785		rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE );
786		if ( rc != 0 ) {
787			char regerr[ SLAP_TEXT_BUFLEN ];
788
789			regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) );
790
791			snprintf( c->cr_msg, sizeof( c->cr_msg ),
792				"regular expression \"%s\" bad because of %s",
793				pattern, regerr );
794			ch_free( ms );
795			return 1;
796		}
797		ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern );
798		} break;
799	}
800
801	if ( mt->mt_subtree == NULL ) {
802		 mt->mt_subtree = ms;
803
804	} else {
805		a_metasubtree_t **msp;
806
807		for ( msp = &mt->mt_subtree; *msp; ) {
808			switch ( ms->ms_type ) {
809			case META_ST_SUBTREE:
810				switch ( (*msp)->ms_type ) {
811				case META_ST_SUBTREE:
812					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
813						a_metasubtree_t *tmp = *msp;
814						Debug( LDAP_DEBUG_CONFIG,
815							"%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
816							c->log, pattern, (*msp)->ms_dn.bv_val );
817						*msp = (*msp)->ms_next;
818						tmp->ms_next = NULL;
819						asyncmeta_subtree_destroy( tmp );
820						continue;
821
822					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
823						Debug( LDAP_DEBUG_CONFIG,
824							"%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
825							c->log, (*msp)->ms_dn.bv_val, pattern );
826						asyncmeta_subtree_destroy( ms );
827						ms = NULL;
828						return( 0 );
829					}
830					break;
831
832				case META_ST_SUBORDINATE:
833					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
834						a_metasubtree_t *tmp = *msp;
835						Debug( LDAP_DEBUG_CONFIG,
836							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
837							c->log, pattern, (*msp)->ms_dn.bv_val );
838						*msp = (*msp)->ms_next;
839						tmp->ms_next = NULL;
840						asyncmeta_subtree_destroy( tmp );
841						continue;
842
843					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
844						Debug( LDAP_DEBUG_CONFIG,
845							"%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
846							c->log, (*msp)->ms_dn.bv_val, pattern );
847						asyncmeta_subtree_destroy( ms );
848						ms = NULL;
849						return( 0 );
850					}
851					break;
852
853				case META_ST_REGEX:
854					if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
855						Debug( LDAP_DEBUG_CONFIG,
856							"%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
857							c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
858					}
859					break;
860				}
861				break;
862
863			case META_ST_SUBORDINATE:
864				switch ( (*msp)->ms_type ) {
865				case META_ST_SUBTREE:
866					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
867						a_metasubtree_t *tmp = *msp;
868						Debug( LDAP_DEBUG_CONFIG,
869							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
870							c->log, pattern, (*msp)->ms_dn.bv_val );
871						*msp = (*msp)->ms_next;
872						tmp->ms_next = NULL;
873						asyncmeta_subtree_destroy( tmp );
874						continue;
875
876					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
877						Debug( LDAP_DEBUG_CONFIG,
878							"%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
879							c->log, (*msp)->ms_dn.bv_val, pattern );
880						asyncmeta_subtree_destroy( ms );
881						ms = NULL;
882						return( 0 );
883					}
884					break;
885
886				case META_ST_SUBORDINATE:
887					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
888						a_metasubtree_t *tmp = *msp;
889						Debug( LDAP_DEBUG_CONFIG,
890							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n",
891							c->log, pattern, (*msp)->ms_dn.bv_val );
892						*msp = (*msp)->ms_next;
893						tmp->ms_next = NULL;
894						asyncmeta_subtree_destroy( tmp );
895						continue;
896
897					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
898						Debug( LDAP_DEBUG_CONFIG,
899							"%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n",
900							c->log, (*msp)->ms_dn.bv_val, pattern );
901						asyncmeta_subtree_destroy( ms );
902						ms = NULL;
903						return( 0 );
904					}
905					break;
906
907				case META_ST_REGEX:
908					if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
909						Debug( LDAP_DEBUG_CONFIG,
910							"%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
911							c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
912					}
913					break;
914				}
915				break;
916
917			case META_ST_REGEX:
918				switch ( (*msp)->ms_type ) {
919				case META_ST_SUBTREE:
920				case META_ST_SUBORDINATE:
921					if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
922						Debug( LDAP_DEBUG_CONFIG,
923							"%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
924							c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val );
925					}
926					break;
927
928				case META_ST_REGEX:
929					/* no check possible */
930					break;
931				}
932				break;
933			}
934
935			msp = &(*msp)->ms_next;
936		}
937
938		*msp = ms;
939	}
940
941	return 0;
942}
943
944static slap_verbmasks idassert_mode[] = {
945	{ BER_BVC("self"),		LDAP_BACK_IDASSERT_SELF },
946	{ BER_BVC("anonymous"),		LDAP_BACK_IDASSERT_ANONYMOUS },
947	{ BER_BVC("none"),		LDAP_BACK_IDASSERT_NOASSERT },
948	{ BER_BVC("legacy"),		LDAP_BACK_IDASSERT_LEGACY },
949	{ BER_BVNULL,			0 }
950};
951
952static slap_verbmasks tls_mode[] = {
953	{ BER_BVC( "propagate" ),	LDAP_BACK_F_TLS_PROPAGATE_MASK },
954	{ BER_BVC( "try-propagate" ),	LDAP_BACK_F_PROPAGATE_TLS },
955	{ BER_BVC( "start" ),		LDAP_BACK_F_TLS_USE_MASK },
956	{ BER_BVC( "try-start" ),	LDAP_BACK_F_USE_TLS },
957	{ BER_BVC( "ldaps" ),		LDAP_BACK_F_TLS_LDAPS },
958	{ BER_BVC( "none" ),		LDAP_BACK_F_NONE },
959	{ BER_BVNULL,			0 }
960};
961
962static slap_verbmasks t_f_mode[] = {
963	{ BER_BVC( "yes" ),		LDAP_BACK_F_T_F },
964	{ BER_BVC( "discover" ),	LDAP_BACK_F_T_F_DISCOVER },
965	{ BER_BVC( "no" ),		LDAP_BACK_F_NONE },
966	{ BER_BVNULL,			0 }
967};
968
969static slap_verbmasks cancel_mode[] = {
970	{ BER_BVC( "ignore" ),		LDAP_BACK_F_CANCEL_IGNORE },
971	{ BER_BVC( "exop" ),		LDAP_BACK_F_CANCEL_EXOP },
972	{ BER_BVC( "exop-discover" ),	LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
973	{ BER_BVC( "abandon" ),		LDAP_BACK_F_CANCEL_ABANDON },
974	{ BER_BVNULL,			0 }
975};
976
977static slap_verbmasks onerr_mode[] = {
978	{ BER_BVC( "stop" ),		META_BACK_F_ONERR_STOP },
979	{ BER_BVC( "report" ),	META_BACK_F_ONERR_REPORT },
980	{ BER_BVC( "continue" ),		LDAP_BACK_F_NONE },
981	{ BER_BVNULL,			0 }
982};
983
984/* see enum in slap.h */
985static slap_cf_aux_table timeout_table[] = {
986	{ BER_BVC("bind="),	SLAP_OP_BIND * sizeof( time_t ),	'u', 0, NULL },
987	/* unbind makes no sense */
988	{ BER_BVC("add="),	SLAP_OP_ADD * sizeof( time_t ),		'u', 0, NULL },
989	{ BER_BVC("delete="),	SLAP_OP_DELETE * sizeof( time_t ),	'u', 0, NULL },
990	{ BER_BVC("modrdn="),	SLAP_OP_MODRDN * sizeof( time_t ),	'u', 0, NULL },
991	{ BER_BVC("modify="),	SLAP_OP_MODIFY * sizeof( time_t ),	'u', 0, NULL },
992	{ BER_BVC("compare="),	SLAP_OP_COMPARE * sizeof( time_t ),	'u', 0, NULL },
993	{ BER_BVC("search="),	SLAP_OP_SEARCH * sizeof( time_t ),	'u', 0, NULL },
994	/* abandon makes little sense */
995#if 0	/* not implemented yet */
996	{ BER_BVC("extended="),	SLAP_OP_EXTENDED * sizeof( time_t ),	'u', 0, NULL },
997#endif
998	{ BER_BVNULL, 0, 0, 0, NULL }
999};
1000
1001static int
1002asyncmeta_cf_cleanup( ConfigArgs *c )
1003{
1004	a_metainfo_t	*mi = ( a_metainfo_t * )c->be->be_private;
1005	a_metatarget_t	*mt = c->ca_private;
1006
1007	return asyncmeta_target_finish( mi, mt, c->log, c->cr_msg, sizeof( c->cr_msg ));
1008}
1009
1010static int
1011asyncmeta_back_cf_gen( ConfigArgs *c )
1012{
1013	a_metainfo_t	*mi = ( a_metainfo_t * )c->be->be_private;
1014	a_metatarget_t	*mt = NULL;
1015	a_metacommon_t	*mc = NULL;
1016
1017	int i, rc = 0;
1018
1019	assert( mi != NULL );
1020
1021	if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) {
1022		if ( !mi )
1023			return 1;
1024
1025		if ( c->table == Cft_Database ) {
1026			mt = NULL;
1027			mc = &mi->mi_mc;
1028		} else {
1029			mt = c->ca_private;
1030			mc = &mt->mt_mc;
1031		}
1032	}
1033
1034	if ( c->op == SLAP_CONFIG_EMIT ) {
1035		struct berval bv = BER_BVNULL;
1036
1037		switch( c->type ) {
1038		/* Base attrs */
1039
1040		case LDAP_BACK_CFG_DNCACHE_TTL:
1041			if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) {
1042				return 1;
1043			} else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) {
1044				BER_BVSTR( &bv, "forever" );
1045			} else {
1046				char	buf[ SLAP_TEXT_BUFLEN ];
1047
1048				lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl );
1049				ber_str2bv( buf, 0, 0, &bv );
1050			}
1051			value_add_one( &c->rvalue_vals, &bv );
1052			break;
1053
1054		case LDAP_BACK_CFG_IDLE_TIMEOUT:
1055			if ( mi->mi_idle_timeout == 0 ) {
1056				return 1;
1057			} else {
1058				char	buf[ SLAP_TEXT_BUFLEN ];
1059
1060				lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout );
1061				ber_str2bv( buf, 0, 0, &bv );
1062				value_add_one( &c->rvalue_vals, &bv );
1063			}
1064			break;
1065
1066		case LDAP_BACK_CFG_ONERR:
1067			enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv );
1068			if ( BER_BVISNULL( &bv )) {
1069				rc = 1;
1070			} else {
1071				value_add_one( &c->rvalue_vals, &bv );
1072			}
1073			break;
1074
1075		case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
1076			c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi );
1077			break;
1078
1079		case LDAP_BACK_CFG_CONNPOOLMAX:
1080			c->value_int = mi->mi_conn_priv_max;
1081			break;
1082
1083		/* common attrs */
1084		case LDAP_BACK_CFG_BIND_TIMEOUT:
1085			if ( mc->mc_bind_timeout.tv_sec == 0 &&
1086				mc->mc_bind_timeout.tv_usec == 0 ) {
1087				return 1;
1088			} else {
1089				c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL +
1090					mc->mc_bind_timeout.tv_usec;
1091			}
1092			break;
1093
1094		case LDAP_BACK_CFG_CANCEL: {
1095			slap_mask_t	mask = LDAP_BACK_F_CANCEL_MASK2;
1096
1097			if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
1098				mask &= ~LDAP_BACK_F_CANCEL_EXOP;
1099			}
1100			enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv );
1101			if ( BER_BVISNULL( &bv ) ) {
1102				/* there's something wrong... */
1103				assert( 0 );
1104				rc = 1;
1105
1106			} else {
1107				value_add_one( &c->rvalue_vals, &bv );
1108			}
1109			} break;
1110
1111		case LDAP_BACK_CFG_CHASE:
1112			c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc);
1113			break;
1114
1115#ifdef SLAPD_META_CLIENT_PR
1116		case LDAP_BACK_CFG_CLIENT_PR:
1117			if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) {
1118				return 1;
1119			} else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) {
1120				BER_BVSTR( &bv, "accept-unsolicited" );
1121			} else {
1122				bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps );
1123				bv.bv_val = c->cr_msg;
1124			}
1125			value_add_one( &c->rvalue_vals, &bv );
1126			break;
1127#endif /* SLAPD_META_CLIENT_PR */
1128
1129		case LDAP_BACK_CFG_DEFAULT_T:
1130			if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
1131				return 1;
1132			bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget );
1133			bv.bv_val = c->cr_msg;
1134			value_add_one( &c->rvalue_vals, &bv );
1135			break;
1136
1137		case LDAP_BACK_CFG_NETWORK_TIMEOUT:
1138			if ( mc->mc_network_timeout == 0 ) {
1139				return 1;
1140			}
1141			bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld",
1142				mc->mc_network_timeout );
1143			bv.bv_val = c->cr_msg;
1144			value_add_one( &c->rvalue_vals, &bv );
1145			break;
1146
1147		case LDAP_BACK_CFG_NOREFS:
1148			c->value_int = META_BACK_CMN_NOREFS(mc);
1149			break;
1150
1151		case LDAP_BACK_CFG_NOUNDEFFILTER:
1152			c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc);
1153			break;
1154
1155		case LDAP_BACK_CFG_NRETRIES:
1156			if ( mc->mc_nretries == META_RETRY_FOREVER ) {
1157				BER_BVSTR( &bv, "forever" );
1158			} else if ( mc->mc_nretries == META_RETRY_NEVER ) {
1159				BER_BVSTR( &bv, "never" );
1160			} else {
1161				bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d",
1162					mc->mc_nretries );
1163				bv.bv_val = c->cr_msg;
1164			}
1165			value_add_one( &c->rvalue_vals, &bv );
1166			break;
1167
1168		case LDAP_BACK_CFG_QUARANTINE:
1169			if ( !META_BACK_CMN_QUARANTINE( mc )) {
1170				rc = 1;
1171				break;
1172			}
1173			rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv );
1174			if ( rc == 0 ) {
1175				ber_bvarray_add( &c->rvalue_vals, &bv );
1176			}
1177			break;
1178
1179		case LDAP_BACK_CFG_REBIND:
1180			c->value_int = META_BACK_CMN_SAVECRED(mc);
1181			break;
1182
1183		case LDAP_BACK_CFG_TIMEOUT:
1184			for ( i = 0; i < SLAP_OP_LAST; i++ ) {
1185				if ( mc->mc_timeout[ i ] != META_BACK_CFG_DEFAULT_OPS_TIMEOUT ) {
1186					break;
1187				}
1188			}
1189
1190			if ( i == SLAP_OP_LAST ) {
1191				return 1;
1192			}
1193
1194			BER_BVZERO( &bv );
1195			slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table );
1196
1197			if ( BER_BVISNULL( &bv ) ) {
1198				return 1;
1199			}
1200
1201			for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
1202				/* count spaces */ ;
1203
1204			if ( i ) {
1205				bv.bv_len -= i;
1206				AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
1207					bv.bv_len + 1 );
1208			}
1209
1210			ber_bvarray_add( &c->rvalue_vals, &bv );
1211			break;
1212
1213		case LDAP_BACK_CFG_VERSION:
1214			if ( mc->mc_version == 0 )
1215				return 1;
1216			c->value_int = mc->mc_version;
1217			break;
1218
1219#ifdef SLAP_CONTROL_X_SESSION_TRACKING
1220		case LDAP_BACK_CFG_ST_REQUEST:
1221			c->value_int = META_BACK_CMN_ST_REQUEST( mc );
1222			break;
1223#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1224
1225		case LDAP_BACK_CFG_T_F:
1226			enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv );
1227			if ( BER_BVISNULL( &bv ) ) {
1228				/* there's something wrong... */
1229				assert( 0 );
1230				rc = 1;
1231
1232			} else {
1233				value_add_one( &c->rvalue_vals, &bv );
1234			}
1235			break;
1236
1237		case LDAP_BACK_CFG_TLS: {
1238			struct berval bc = BER_BVNULL, bv2;
1239
1240			if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) {
1241				rc = 1;
1242				break;
1243			}
1244			enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv );
1245			assert( !BER_BVISNULL( &bv ) );
1246
1247			if ( mt ) {
1248				bindconf_tls_unparse( &mt->mt_tls, &bc );
1249			}
1250
1251			if ( !BER_BVISEMPTY( &bc )) {
1252				bv2.bv_len = bv.bv_len + bc.bv_len + 1;
1253				bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
1254				strcpy( bv2.bv_val, bv.bv_val );
1255				bv2.bv_val[bv.bv_len] = ' ';
1256				strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
1257				ber_memfree( bc.bv_val );
1258				ber_bvarray_add( &c->rvalue_vals, &bv2 );
1259			} else {
1260				value_add_one( &c->rvalue_vals, &bv );
1261			}
1262			} break;
1263
1264		/* target attrs */
1265		case LDAP_BACK_CFG_URI: {
1266			char *p2, *p1 = strchr( mt->mt_uri, ' ' );
1267			bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len;
1268			bv.bv_val = ch_malloc( bv.bv_len + 1 );
1269			p2 = bv.bv_val;
1270			*p2++ = '"';
1271			if ( p1 ) {
1272				p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri );
1273			} else {
1274				p2 = lutil_strcopy( p2, mt->mt_uri );
1275			}
1276			*p2++ = '/';
1277			p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val );
1278			*p2++ = '"';
1279			if ( p1 ) {
1280				strcpy( p2, p1 );
1281			}
1282			ber_bvarray_add( &c->rvalue_vals, &bv );
1283			} break;
1284
1285		case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
1286			BerVarray	*bvp;
1287			int		i;
1288			struct berval	bv = BER_BVNULL;
1289			char		buf[SLAP_TEXT_BUFLEN];
1290
1291			bvp = &mt->mt_idassert_authz;
1292			if ( *bvp == NULL ) {
1293				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
1294				{
1295					BER_BVSTR( &bv, "*" );
1296					value_add_one( &c->rvalue_vals, &bv );
1297
1298				} else {
1299					rc = 1;
1300				}
1301				break;
1302			}
1303
1304			for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
1305				char *ptr;
1306				int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
1307				bv.bv_len = ((*bvp)[ i ]).bv_len + len;
1308				bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
1309				ptr = bv.bv_val;
1310				ptr = lutil_strcopy( ptr, buf );
1311				ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
1312				value_add_one( &c->rvalue_vals, &bv );
1313			}
1314			if ( bv.bv_val ) {
1315				ber_memfree( bv.bv_val );
1316			}
1317			break;
1318		}
1319
1320		case LDAP_BACK_CFG_IDASSERT_BIND: {
1321			int		i;
1322			struct berval	bc = BER_BVNULL;
1323			char		*ptr;
1324
1325			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) {
1326				return 1;
1327			} else {
1328				ber_len_t	len;
1329
1330				switch ( mt->mt_idassert_mode ) {
1331				case LDAP_BACK_IDASSERT_OTHERID:
1332				case LDAP_BACK_IDASSERT_OTHERDN:
1333					break;
1334
1335				default: {
1336					struct berval	mode = BER_BVNULL;
1337
1338					enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode );
1339					if ( BER_BVISNULL( &mode ) ) {
1340						/* there's something wrong... */
1341						assert( 0 );
1342						rc = 1;
1343
1344					} else {
1345						bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
1346						bv.bv_val = ch_malloc( bv.bv_len + 1 );
1347
1348						ptr = lutil_strcopy( bv.bv_val, "mode=" );
1349						ptr = lutil_strcopy( ptr, mode.bv_val );
1350					}
1351					break;
1352				}
1353				}
1354
1355				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
1356					len = bv.bv_len + STRLENOF( "authz=native" );
1357
1358					if ( !BER_BVISEMPTY( &bv ) ) {
1359						len += STRLENOF( " " );
1360					}
1361
1362					bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1363
1364					ptr = &bv.bv_val[ bv.bv_len ];
1365
1366					if ( !BER_BVISEMPTY( &bv ) ) {
1367						ptr = lutil_strcopy( ptr, " " );
1368					}
1369
1370					(void)lutil_strcopy( ptr, "authz=native" );
1371				}
1372
1373				len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
1374				/* flags */
1375				if ( !BER_BVISEMPTY( &bv ) ) {
1376					len += STRLENOF( " " );
1377				}
1378
1379				bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1380
1381				ptr = &bv.bv_val[ bv.bv_len ];
1382
1383				if ( !BER_BVISEMPTY( &bv ) ) {
1384					ptr = lutil_strcopy( ptr, " " );
1385				}
1386
1387				ptr = lutil_strcopy( ptr, "flags=" );
1388
1389				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1390					ptr = lutil_strcopy( ptr, "prescriptive" );
1391				} else {
1392					ptr = lutil_strcopy( ptr, "non-prescriptive" );
1393				}
1394
1395				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
1396					ptr = lutil_strcopy( ptr, ",override" );
1397				}
1398
1399				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
1400					ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
1401
1402				} else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
1403					ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
1404				}
1405
1406				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
1407					ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
1408
1409				} else {
1410					ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
1411				}
1412
1413#ifdef SLAP_AUTH_DN
1414				switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
1415				case LDAP_BACK_AUTH_DN_AUTHZID:
1416					ptr = lutil_strcopy( ptr, ",dn-authzid" );
1417					break;
1418
1419				case LDAP_BACK_AUTH_DN_WHOAMI:
1420					ptr = lutil_strcopy( ptr, ",dn-whoami" );
1421					break;
1422
1423				default:
1424#if 0 /* implicit */
1425					ptr = lutil_strcopy( ptr, ",dn-none" );
1426#endif
1427					break;
1428				}
1429#endif
1430
1431				bv.bv_len = ( ptr - bv.bv_val );
1432				/* end-of-flags */
1433			}
1434
1435			bindconf_unparse( &mt->mt_idassert.si_bc, &bc );
1436
1437			if ( !BER_BVISNULL( &bv ) ) {
1438				ber_len_t	len = bv.bv_len + bc.bv_len;
1439
1440				bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1441
1442				assert( bc.bv_val[ 0 ] == ' ' );
1443
1444				ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
1445				free( bc.bv_val );
1446				bv.bv_len = ptr - bv.bv_val;
1447
1448			} else {
1449				for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
1450					/* count spaces */ ;
1451
1452				if ( i ) {
1453					bc.bv_len -= i;
1454					AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
1455				}
1456
1457				bv = bc;
1458			}
1459
1460			ber_bvarray_add( &c->rvalue_vals, &bv );
1461
1462			break;
1463		}
1464
1465		case LDAP_BACK_CFG_SUFFIXM:
1466			if ( mt->mt_lsuffixm.bv_val ) {
1467				struct berval bv;
1468				char *ptr;
1469				bv.bv_len = mt->mt_lsuffixm.bv_len + 2 + 1 + mt->mt_rsuffixm.bv_len + 2;
1470				bv.bv_val = ch_malloc( bv.bv_len + 1 );
1471				ptr = bv.bv_val;
1472				*ptr++ = '"';
1473				ptr = lutil_strcopy(ptr, mt->mt_lsuffixm.bv_val);
1474				ptr = lutil_strcopy(ptr, "\" \"");
1475				ptr = lutil_strcopy(ptr, mt->mt_rsuffixm.bv_val);
1476				*ptr++ = '"';
1477				*ptr = '\0';
1478				ber_bvarray_add( &c->rvalue_vals, &bv );
1479				rc = 0;
1480			} else
1481				rc = 1;
1482			break;
1483
1484		case LDAP_BACK_CFG_SUBTREE_EX:
1485		case LDAP_BACK_CFG_SUBTREE_IN:
1486			rc = asyncmeta_subtree_unparse( c, mt );
1487			break;
1488
1489		case LDAP_BACK_CFG_FILTER:
1490			if ( mt->mt_filter == NULL ) {
1491				rc = 1;
1492			} else {
1493				metafilter_t *mf;
1494				for ( mf = mt->mt_filter; mf; mf = mf->mf_next )
1495					value_add_one( &c->rvalue_vals, &mf->mf_regex_pattern );
1496			}
1497			break;
1498		case LDAP_BACK_CFG_MAX_PENDING_OPS:
1499			c->value_int = mi->mi_max_pending_ops;
1500			break;
1501
1502		case LDAP_BACK_CFG_MAX_TARGET_CONNS:
1503			c->value_int = mi->mi_max_target_conns;
1504			break;
1505		case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
1506			c->value_int = mi->mi_max_timeout_ops;
1507			break;
1508
1509		case LDAP_BACK_CFG_KEEPALIVE: {
1510				struct berval bv;
1511				char buf[AC_LINE_MAX];
1512				bv.bv_len = AC_LINE_MAX;
1513				bv.bv_val = &buf[0];
1514				slap_keepalive_parse(&bv, &mt->mt_tls.sb_keepalive, 0, 0, 1);
1515				value_add_one( &c->rvalue_vals, &bv );
1516				break;
1517			}
1518
1519		case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
1520			c->value_uint = mt->mt_tls.sb_tcp_user_timeout;
1521			break;
1522
1523		default:
1524			rc = 1;
1525		}
1526		return rc;
1527	} else if ( c->op == LDAP_MOD_DELETE ) {
1528		switch( c->type ) {
1529		/* Base attrs */
1530		case LDAP_BACK_CFG_DNCACHE_TTL:
1531			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
1532			break;
1533
1534		case LDAP_BACK_CFG_IDLE_TIMEOUT:
1535			mi->mi_idle_timeout = 0;
1536			break;
1537
1538		case LDAP_BACK_CFG_ONERR:
1539			mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
1540			break;
1541
1542		case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
1543			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
1544			break;
1545
1546		case LDAP_BACK_CFG_CONNPOOLMAX:
1547			mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
1548			break;
1549
1550		/* common attrs */
1551		case LDAP_BACK_CFG_BIND_TIMEOUT:
1552			mc->mc_bind_timeout.tv_sec = 0;
1553			mc->mc_bind_timeout.tv_usec = 0;
1554			break;
1555
1556		case LDAP_BACK_CFG_CANCEL:
1557			mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
1558			break;
1559
1560		case LDAP_BACK_CFG_CHASE:
1561			mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
1562			break;
1563
1564#ifdef SLAPD_META_CLIENT_PR
1565		case LDAP_BACK_CFG_CLIENT_PR:
1566			mc->mc_ps = META_CLIENT_PR_DISABLE;
1567			break;
1568#endif /* SLAPD_META_CLIENT_PR */
1569
1570		case LDAP_BACK_CFG_DEFAULT_T:
1571			mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
1572			break;
1573
1574		case LDAP_BACK_CFG_NETWORK_TIMEOUT:
1575			mc->mc_network_timeout = 0;
1576			break;
1577
1578		case LDAP_BACK_CFG_NOREFS:
1579			mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
1580			break;
1581
1582		case LDAP_BACK_CFG_NOUNDEFFILTER:
1583			mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
1584			break;
1585
1586		case LDAP_BACK_CFG_NRETRIES:
1587			mc->mc_nretries = META_RETRY_DEFAULT;
1588			break;
1589
1590		case LDAP_BACK_CFG_QUARANTINE:
1591			if ( META_BACK_CMN_QUARANTINE( mc )) {
1592				mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine );
1593				mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE;
1594				if ( mc == &mt->mt_mc ) {
1595					ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
1596					mt->mt_isquarantined = 0;
1597				}
1598			}
1599			break;
1600
1601		case LDAP_BACK_CFG_REBIND:
1602			mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
1603			break;
1604
1605		case LDAP_BACK_CFG_TIMEOUT:
1606			for ( i = 0; i < SLAP_OP_LAST; i++ ) {
1607				mc->mc_timeout[ i ] = 0;
1608			}
1609			break;
1610
1611		case LDAP_BACK_CFG_VERSION:
1612			mc->mc_version = 0;
1613			break;
1614
1615#ifdef SLAP_CONTROL_X_SESSION_TRACKING
1616		case LDAP_BACK_CFG_ST_REQUEST:
1617			mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
1618			break;
1619#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1620
1621		case LDAP_BACK_CFG_T_F:
1622			mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
1623			break;
1624
1625		case LDAP_BACK_CFG_TLS:
1626			mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
1627			if ( mt )
1628				bindconf_free( &mt->mt_tls );
1629			break;
1630
1631		/* target attrs */
1632		case LDAP_BACK_CFG_URI:
1633			if ( mt->mt_uri ) {
1634				ch_free( mt->mt_uri );
1635				mt->mt_uri = NULL;
1636			}
1637			/* FIXME: should have a way to close all cached
1638			 * connections associated with this target.
1639			 */
1640			break;
1641
1642		case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
1643			BerVarray *bvp;
1644
1645			bvp = &mt->mt_idassert_authz;
1646			if ( c->valx < 0 ) {
1647				if ( *bvp != NULL ) {
1648					ber_bvarray_free( *bvp );
1649					*bvp = NULL;
1650				}
1651
1652			} else {
1653				if ( *bvp == NULL ) {
1654					rc = 1;
1655					break;
1656				}
1657
1658				for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
1659					;
1660
1661				if ( i >= c->valx ) {
1662					rc = 1;
1663					break;
1664				}
1665				ber_memfree( ((*bvp)[ c->valx ]).bv_val );
1666				for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
1667					(*bvp)[ i ] = (*bvp)[ i + 1 ];
1668				}
1669				BER_BVZERO( &((*bvp)[ i ]) );
1670			}
1671			} break;
1672
1673		case LDAP_BACK_CFG_IDASSERT_BIND:
1674			bindconf_free( &mt->mt_idassert.si_bc );
1675			memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
1676			break;
1677
1678		case LDAP_BACK_CFG_SUFFIXM:
1679			if ( mt->mt_lsuffixm.bv_val ) {
1680				ch_free( mt->mt_lsuffixm.bv_val );
1681				ch_free( mt->mt_rsuffixm.bv_val );
1682				BER_BVZERO( &mt->mt_lsuffixm );
1683				BER_BVZERO( &mt->mt_rsuffixm );
1684			}
1685			break;
1686
1687		case LDAP_BACK_CFG_SUBTREE_EX:
1688		case LDAP_BACK_CFG_SUBTREE_IN:
1689			/* can only be one of exclude or include */
1690			if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
1691				rc = 1;
1692				break;
1693			}
1694			if ( c->valx < 0 ) {
1695				asyncmeta_subtree_destroy( mt->mt_subtree );
1696				mt->mt_subtree = NULL;
1697			} else {
1698				a_metasubtree_t *ms, **mprev;
1699				for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
1700					if ( i == c->valx ) {
1701						*mprev = ms->ms_next;
1702						asyncmeta_subtree_free( ms );
1703						break;
1704					}
1705					i++;
1706					mprev = &ms->ms_next;
1707				}
1708				if ( i != c->valx )
1709					rc = 1;
1710			}
1711			break;
1712
1713		case LDAP_BACK_CFG_FILTER:
1714			if ( c->valx < 0 ) {
1715				asyncmeta_filter_destroy( mt->mt_filter );
1716				mt->mt_filter = NULL;
1717			} else {
1718				metafilter_t *mf, **mprev;
1719				for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) {
1720					if ( i == c->valx ) {
1721						*mprev = mf->mf_next;
1722						asyncmeta_filter_free( mf );
1723						break;
1724					}
1725					i++;
1726					mprev = &mf->mf_next;
1727				}
1728				if ( i != c->valx )
1729					rc = 1;
1730			}
1731			break;
1732		case LDAP_BACK_CFG_MAX_PENDING_OPS:
1733			mi->mi_max_pending_ops = 0;
1734			break;
1735
1736		case LDAP_BACK_CFG_MAX_TARGET_CONNS:
1737			mi->mi_max_target_conns = 0;
1738			break;
1739
1740		case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
1741			mi->mi_max_timeout_ops = 0;
1742			break;
1743
1744		case LDAP_BACK_CFG_KEEPALIVE:
1745			mt->mt_tls.sb_keepalive.sk_idle = 0;
1746			mt->mt_tls.sb_keepalive.sk_probes = 0;
1747			mt->mt_tls.sb_keepalive.sk_interval = 0;
1748			break;
1749
1750		case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
1751			mt->mt_tls.sb_tcp_user_timeout = 0;
1752			break;
1753
1754		default:
1755			rc = 1;
1756			break;
1757		}
1758
1759		return rc;
1760	}
1761
1762	if ( c->op == SLAP_CONFIG_ADD ) {
1763		if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
1764			/* exclude CFG_URI from this check */
1765			if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
1766				if ( !mi->mi_ntargets ) {
1767					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1768						"need \"uri\" directive first" );
1769					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1770					return 1;
1771				}
1772			}
1773			if ( mi->mi_ntargets ) {
1774				mt = mi->mi_targets[ mi->mi_ntargets-1 ];
1775				mc = &mt->mt_mc;
1776			} else {
1777				mt = NULL;
1778				mc = &mi->mi_mc;
1779			}
1780		}
1781	} else {
1782		if ( c->table == Cft_Database ) {
1783			mt = NULL;
1784			mc = &mi->mi_mc;
1785		} else {
1786			mt = c->ca_private;
1787			if ( mt )
1788				mc = &mt->mt_mc;
1789			else
1790				mc = NULL;
1791		}
1792	}
1793
1794	switch( c->type ) {
1795	case LDAP_BACK_CFG_URI: {
1796		LDAPURLDesc 	*ludp;
1797		struct berval	dn;
1798		int		j;
1799
1800		char		**uris = NULL;
1801
1802		if ( c->be->be_nsuffix == NULL ) {
1803			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1804				"the suffix must be defined before any target" );
1805			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1806			return 1;
1807		}
1808
1809		i = mi->mi_ntargets++;
1810
1811		mi->mi_targets = ( a_metatarget_t ** )ch_realloc( mi->mi_targets,
1812			sizeof( a_metatarget_t * ) * mi->mi_ntargets );
1813		if ( mi->mi_targets == NULL ) {
1814			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1815				"out of memory while storing server name"
1816				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1817				c->argv[0] );
1818			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1819			return 1;
1820		}
1821
1822		if ( asyncmeta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
1823			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1824				"unable to init server"
1825				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1826				c->argv[0] );
1827			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1828			return 1;
1829		}
1830
1831		mt = mi->mi_targets[ i ];
1832
1833		mt->mt_rebind_f = mi->mi_rebind_f;
1834		mt->mt_urllist_f = mi->mi_urllist_f;
1835		mt->mt_urllist_p = mt;
1836
1837		if ( META_BACK_QUARANTINE( mi ) ) {
1838			ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
1839		}
1840		mt->mt_mc = mi->mi_mc;
1841
1842		for ( j = 1; j < c->argc; j++ ) {
1843			char	**tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
1844
1845			if ( tmpuris == NULL ) {
1846				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1847					"unable to parse URIs #%d"
1848					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1849					j-1, c->argv[0] );
1850				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1851				return 1;
1852			}
1853
1854			if ( j == 1 ) {
1855				uris = tmpuris;
1856
1857			} else {
1858				ldap_charray_merge( &uris, tmpuris );
1859				ldap_charray_free( tmpuris );
1860			}
1861		}
1862
1863		for ( j = 0; uris[ j ] != NULL; j++ ) {
1864			char *tmpuri = NULL;
1865
1866			/*
1867			 * uri MUST be legal!
1868			 */
1869			if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
1870					LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
1871				|| ludp->lud_next != NULL )
1872			{
1873				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1874					"unable to parse URI #%d"
1875					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1876					j-1, c->argv[0] );
1877				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1878				ldap_charray_free( uris );
1879				return 1;
1880			}
1881
1882			if ( j == 0 ) {
1883
1884				/*
1885				 * uri MUST have the <dn> part!
1886				 */
1887				if ( ludp->lud_dn == NULL ) {
1888					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1889						"missing <naming context> "
1890						" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1891						c->argv[0] );
1892					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1893					ldap_free_urllist( ludp );
1894					ldap_charray_free( uris );
1895					return 1;
1896				}
1897
1898				/*
1899				 * copies and stores uri and suffix
1900				 */
1901				ber_str2bv( ludp->lud_dn, 0, 0, &dn );
1902				rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
1903					&mt->mt_nsuffix, NULL );
1904				if ( rc != LDAP_SUCCESS ) {
1905					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1906						"target DN is invalid \"%s\"",
1907						c->argv[1] );
1908					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1909					ldap_free_urllist( ludp );
1910					ldap_charray_free( uris );
1911					return( 1 );
1912				}
1913
1914				ludp->lud_dn[ 0 ] = '\0';
1915
1916				switch ( ludp->lud_scope ) {
1917				case LDAP_SCOPE_DEFAULT:
1918					mt->mt_scope = LDAP_SCOPE_SUBTREE;
1919					break;
1920
1921				case LDAP_SCOPE_SUBTREE:
1922				case LDAP_SCOPE_SUBORDINATE:
1923					mt->mt_scope = ludp->lud_scope;
1924					break;
1925
1926				default:
1927					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1928						"invalid scope for target \"%s\"",
1929						c->argv[1] );
1930					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1931					ldap_free_urllist( ludp );
1932					ldap_charray_free( uris );
1933					return( 1 );
1934				}
1935
1936			} else {
1937				/* check all, to apply the scope check on the first one */
1938				if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
1939					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1940						"multiple URIs must have no DN part" );
1941					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1942					ldap_free_urllist( ludp );
1943					ldap_charray_free( uris );
1944					return( 1 );
1945
1946				}
1947			}
1948
1949			tmpuri = ldap_url_list2urls( ludp );
1950			ldap_free_urllist( ludp );
1951			if ( tmpuri == NULL ) {
1952				snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
1953				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1954				ldap_charray_free( uris );
1955				return( 1 );
1956			}
1957			ldap_memfree( uris[ j ] );
1958			uris[ j ] = tmpuri;
1959		}
1960
1961		mt->mt_uri = ldap_charray2str( uris, " " );
1962		ldap_charray_free( uris );
1963		if ( mt->mt_uri == NULL) {
1964			snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
1965			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1966			return( 1 );
1967		}
1968
1969		/*
1970		 * uri MUST be a branch of suffix!
1971		 */
1972		for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
1973			if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
1974				break;
1975			}
1976		}
1977
1978		if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
1979			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1980				"<naming context> of URI must be within the naming context of this database." );
1981			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1982			return 1;
1983		}
1984		c->ca_private = mt;
1985		config_push_cleanup( c, asyncmeta_cf_cleanup );
1986	} break;
1987	case LDAP_BACK_CFG_SUBTREE_EX:
1988	case LDAP_BACK_CFG_SUBTREE_IN:
1989	/* subtree-exclude */
1990		if ( asyncmeta_subtree_config( mt, c )) {
1991			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
1992			return 1;
1993		}
1994		break;
1995
1996	case LDAP_BACK_CFG_FILTER: {
1997		metafilter_t *mf, **m2;
1998		mf = ch_malloc( sizeof( metafilter_t ));
1999		rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED );
2000		if ( rc ) {
2001			char regerr[ SLAP_TEXT_BUFLEN ];
2002			regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) );
2003			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2004				"regular expression \"%s\" bad because of %s",
2005				c->argv[1], regerr );
2006			ch_free( mf );
2007			return 1;
2008		}
2009		ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern );
2010		for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next )
2011			;
2012		*m2 = mf;
2013	} break;
2014	case LDAP_BACK_CFG_MAX_PENDING_OPS:
2015		if (c->value_int < 0) {
2016			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2017				  "max-pending-ops invalid value %d",
2018				  c->value_int);
2019			return 1;
2020		}
2021		mi->mi_max_pending_ops = c->value_int;
2022		break;
2023	case LDAP_BACK_CFG_MAX_TARGET_CONNS:
2024	{
2025		if (c->value_int < 0) {
2026			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2027				  "max-target-conns invalid value %d",
2028				  c->value_int);
2029			return 1;
2030		}
2031		mi->mi_max_target_conns = c->value_int;
2032	}
2033		break;
2034	case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
2035		if (c->value_int < 0) {
2036			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2037				  "max-timeout-ops invalid value %d",
2038				  c->value_int);
2039			return 1;
2040		}
2041		mi->mi_max_timeout_ops = c->value_int;
2042		break;
2043
2044	case LDAP_BACK_CFG_DEFAULT_T:
2045	/* default target directive */
2046		i = mi->mi_ntargets - 1;
2047
2048		if ( c->argc == 1 ) {
2049			if ( i < 0 ) {
2050				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2051					"\"%s\" alone must be inside a \"uri\" directive",
2052					c->argv[0] );
2053				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2054				return 1;
2055			}
2056			mi->mi_defaulttarget = i;
2057
2058		} else {
2059			if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
2060				if ( i >= 0 ) {
2061					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2062						"\"%s none\" should go before uri definitions",
2063						c->argv[0] );
2064					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2065				}
2066				mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
2067
2068			} else {
2069
2070				if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
2071					|| mi->mi_defaulttarget < 0
2072					|| mi->mi_defaulttarget >= i - 1 )
2073				{
2074					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2075						"illegal target number %d",
2076						mi->mi_defaulttarget );
2077					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2078					return 1;
2079				}
2080			}
2081		}
2082		break;
2083
2084	case LDAP_BACK_CFG_DNCACHE_TTL:
2085	/* ttl of dn cache */
2086		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2087			mi->mi_cache.ttl = META_DNCACHE_FOREVER;
2088
2089		} else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
2090			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
2091
2092		} else {
2093			unsigned long	t;
2094
2095			if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
2096				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2097					"unable to parse dncache ttl \"%s\"",
2098					c->argv[ 1 ] );
2099				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2100				return 1;
2101			}
2102			mi->mi_cache.ttl = (time_t)t;
2103		}
2104		break;
2105
2106	case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
2107	/* network timeout when connecting to ldap servers */
2108		unsigned long t;
2109
2110		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2111			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2112				"unable to parse network timeout \"%s\"",
2113				c->argv[ 1 ] );
2114			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2115			return 1;
2116		}
2117		mc->mc_network_timeout = (time_t)t;
2118		} break;
2119
2120	case LDAP_BACK_CFG_IDLE_TIMEOUT: {
2121	/* idle timeout when connecting to ldap servers */
2122		unsigned long	t;
2123
2124		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2125			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2126				"unable to parse idle timeout \"%s\"",
2127				c->argv[ 1 ] );
2128			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2129			return 1;
2130
2131		}
2132		mi->mi_idle_timeout = (time_t)t;
2133		} break;
2134
2135	case LDAP_BACK_CFG_BIND_TIMEOUT:
2136	/* bind timeout when connecting to ldap servers */
2137		mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
2138		mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
2139		break;
2140
2141	case LDAP_BACK_CFG_REBIND:
2142	/* save bind creds for referral rebinds? */
2143		if ( c->argc == 1 || c->value_int ) {
2144			mc->mc_flags |= LDAP_BACK_F_SAVECRED;
2145		} else {
2146			mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
2147		}
2148		break;
2149
2150	case LDAP_BACK_CFG_CHASE:
2151		if ( c->argc == 1 || c->value_int ) {
2152			mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS;
2153		} else {
2154			mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
2155		}
2156		break;
2157
2158	case LDAP_BACK_CFG_TLS:
2159		i = verb_to_mask( c->argv[1], tls_mode );
2160		if ( BER_BVISNULL( &tls_mode[i].word ) ) {
2161			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2162				"%s unknown argument \"%s\"",
2163				c->argv[0], c->argv[1] );
2164			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2165			return 1;
2166		}
2167		mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
2168		mc->mc_flags |= tls_mode[i].mask;
2169
2170		if ( c->argc > 2 ) {
2171			if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
2172				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2173					"need \"uri\" directive first" );
2174				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2175				return 1;
2176			}
2177
2178			for ( i = 2; i < c->argc; i++ ) {
2179				if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
2180					return 1;
2181			}
2182			bindconf_tls_defaults( &mt->mt_tls );
2183		}
2184		break;
2185
2186	case LDAP_BACK_CFG_T_F:
2187		i = verb_to_mask( c->argv[1], t_f_mode );
2188		if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
2189			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2190				"%s unknown argument \"%s\"",
2191				c->argv[0], c->argv[1] );
2192			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2193			return 1;
2194		}
2195		mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
2196		mc->mc_flags |= t_f_mode[i].mask;
2197		break;
2198
2199	case LDAP_BACK_CFG_ONERR:
2200	/* onerr? */
2201		i = verb_to_mask( c->argv[1], onerr_mode );
2202		if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
2203			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2204				"%s unknown argument \"%s\"",
2205				c->argv[0], c->argv[1] );
2206			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2207			return 1;
2208		}
2209		mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
2210		mi->mi_flags |= onerr_mode[i].mask;
2211		break;
2212
2213	case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
2214	/* bind-defer? */
2215		if ( c->argc == 1 || c->value_int ) {
2216			mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
2217		} else {
2218			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
2219		}
2220		break;
2221
2222	case LDAP_BACK_CFG_CONNPOOLMAX:
2223	/* privileged connections pool max size ? */
2224		if ( mi->mi_ntargets > 0 ) {
2225			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2226				"\"%s\" must appear before target definitions",
2227				c->argv[0] );
2228			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2229			return( 1 );
2230		}
2231
2232		if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
2233			|| c->value_int > LDAP_BACK_CONN_PRIV_MAX )
2234		{
2235			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2236				"invalid max size " "of privileged "
2237				"connections pool \"%s\" "
2238				"in \"conn-pool-max <n> "
2239				"(must be between %d and %d)\"",
2240				c->argv[ 1 ],
2241				LDAP_BACK_CONN_PRIV_MIN,
2242				LDAP_BACK_CONN_PRIV_MAX );
2243			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2244			return 1;
2245		}
2246		mi->mi_conn_priv_max = c->value_int;
2247		break;
2248
2249	case LDAP_BACK_CFG_CANCEL:
2250		i = verb_to_mask( c->argv[1], cancel_mode );
2251		if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
2252			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2253				"%s unknown argument \"%s\"",
2254				c->argv[0], c->argv[1] );
2255			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2256			return 1;
2257		}
2258		mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
2259		mc->mc_flags |= cancel_mode[i].mask;
2260		break;
2261
2262	case LDAP_BACK_CFG_TIMEOUT:
2263		for ( i = 1; i < c->argc; i++ ) {
2264			if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
2265				int		j;
2266				unsigned	u;
2267
2268				if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
2269					snprintf( c->cr_msg, sizeof( c->cr_msg),
2270						"unable to parse timeout \"%s\"",
2271						c->argv[ i ] );
2272					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2273					return 1;
2274				}
2275
2276				for ( j = 0; j < SLAP_OP_LAST; j++ ) {
2277					mc->mc_timeout[ j ] = u;
2278				}
2279
2280				continue;
2281			}
2282
2283			if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) {
2284				snprintf( c->cr_msg, sizeof( c->cr_msg),
2285					"unable to parse timeout \"%s\"",
2286					c->argv[ i ] );
2287				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2288				return 1;
2289			}
2290		}
2291		break;
2292
2293	case LDAP_BACK_CFG_IDASSERT_BIND:
2294	/* idassert-bind */
2295		rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
2296		break;
2297
2298	case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
2299	/* idassert-authzFrom */
2300		rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert );
2301		break;
2302
2303	case LDAP_BACK_CFG_QUARANTINE:
2304	/* quarantine */
2305		if ( META_BACK_CMN_QUARANTINE( mc ) )
2306		{
2307			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2308				"quarantine already defined" );
2309			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2310			return 1;
2311		}
2312
2313		if ( mt ) {
2314			mc->mc_quarantine.ri_interval = NULL;
2315			mc->mc_quarantine.ri_num = NULL;
2316			if ( !META_BACK_QUARANTINE( mi ) ) {
2317				ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
2318			}
2319		}
2320
2321		if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) {
2322			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2323			return 1;
2324		}
2325
2326		mc->mc_flags |= LDAP_BACK_F_QUARANTINE;
2327		break;
2328
2329#ifdef SLAP_CONTROL_X_SESSION_TRACKING
2330	case LDAP_BACK_CFG_ST_REQUEST:
2331	/* session tracking request */
2332		if ( c->value_int ) {
2333			mc->mc_flags |= LDAP_BACK_F_ST_REQUEST;
2334		} else {
2335			mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
2336		}
2337		break;
2338#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
2339
2340	case LDAP_BACK_CFG_SUFFIXM:
2341		rc = asyncmeta_suffixm_config( c, c->argc, c->argv, mt );
2342		break;
2343
2344	case LDAP_BACK_CFG_NRETRIES: {
2345		int		nretries = META_RETRY_UNDEFINED;
2346
2347		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2348			nretries = META_RETRY_FOREVER;
2349
2350		} else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
2351			nretries = META_RETRY_NEVER;
2352
2353		} else {
2354			if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
2355				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2356					"unable to parse nretries {never|forever|<retries>}: \"%s\"",
2357					c->argv[ 1 ] );
2358				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2359				return 1;
2360			}
2361		}
2362
2363		mc->mc_nretries = nretries;
2364		} break;
2365
2366	case LDAP_BACK_CFG_VERSION:
2367		if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
2368			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2369				"unsupported protocol version \"%s\"",
2370				c->argv[ 1 ] );
2371			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2372			return 1;
2373		}
2374		mc->mc_version = c->value_int;
2375		break;
2376
2377	case LDAP_BACK_CFG_NOREFS:
2378	/* do not return search references */
2379		if ( c->value_int ) {
2380			mc->mc_flags |= LDAP_BACK_F_NOREFS;
2381		} else {
2382			mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
2383		}
2384		break;
2385
2386	case LDAP_BACK_CFG_NOUNDEFFILTER:
2387	/* do not propagate undefined search filters */
2388		if ( c->value_int ) {
2389			mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER;
2390		} else {
2391			mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
2392		}
2393		break;
2394
2395#ifdef SLAPD_META_CLIENT_PR
2396	case LDAP_BACK_CFG_CLIENT_PR:
2397		if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
2398			mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
2399
2400		} else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
2401			mc->mc_ps = META_CLIENT_PR_DISABLE;
2402
2403		} else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) {
2404			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2405				"unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
2406				c->argv[ 1 ] );
2407			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
2408			return( 1 );
2409		}
2410		break;
2411#endif /* SLAPD_META_CLIENT_PR */
2412
2413	case LDAP_BACK_CFG_KEEPALIVE:
2414		slap_keepalive_parse( ber_bvstrdup(c->argv[1]),
2415				 &mt->mt_tls.sb_keepalive, 0, 0, 0);
2416		break;
2417
2418	case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
2419		mt->mt_tls.sb_tcp_user_timeout = c->value_uint;
2420		break;
2421
2422	/* anything else */
2423	default:
2424		return SLAP_CONF_UNKNOWN;
2425	}
2426
2427	return rc;
2428}
2429
2430int
2431asyncmeta_back_init_cf( BackendInfo *bi )
2432{
2433	int			rc;
2434
2435	/* Make sure we don't exceed the bits reserved for userland */
2436	config_check_userland( LDAP_BACK_CFG_LAST );
2437
2438	bi->bi_cf_ocs = a_metaocs;
2439
2440	rc = config_register_schema( a_metacfg, a_metaocs );
2441	if ( rc ) {
2442		return rc;
2443	}
2444
2445	return 0;
2446}
2447