xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-meta/config.c (revision 4d342c046e3288fb5a1edcd33cfec48c41c80664)
1 /*	$NetBSD: config.c,v 1.2 2020/08/11 13:15:40 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1999-2020 The OpenLDAP Foundation.
7  * Portions Copyright 2001-2003 Pierangelo Masarati.
8  * Portions Copyright 1999-2003 Howard Chu.
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 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by the Howard Chu for inclusion
21  * in OpenLDAP Software and subsequently enhanced by Pierangelo
22  * Masarati.
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: config.c,v 1.2 2020/08/11 13:15:40 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 "config.h"
38 #include "lutil.h"
39 #include "ldif.h"
40 #include "../back-ldap/back-ldap.h"
41 #include "back-meta.h"
42 
43 #ifdef LDAP_DEVEL
44 #define SLAP_AUTH_DN	1
45 #endif
46 
47 static ConfigDriver meta_back_cf_gen;
48 static ConfigLDAPadd meta_ldadd;
49 static ConfigCfAdd meta_cfadd;
50 
51 static int ldap_back_map_config(
52 	ConfigArgs *c,
53 	struct ldapmap	*oc_map,
54 	struct ldapmap	*at_map );
55 
56 /* Three sets of enums:
57  *	1) attrs that are only valid in the base config
58  *	2) attrs that are valid in base or target
59  *	3) attrs that are only valid in a target
60  */
61 
62 /* Base attrs */
63 enum {
64 	LDAP_BACK_CFG_CONN_TTL = 1,
65 	LDAP_BACK_CFG_DNCACHE_TTL,
66 	LDAP_BACK_CFG_IDLE_TIMEOUT,
67 	LDAP_BACK_CFG_ONERR,
68 	LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
69 	LDAP_BACK_CFG_SINGLECONN,
70 	LDAP_BACK_CFG_USETEMP,
71 	LDAP_BACK_CFG_CONNPOOLMAX,
72 	LDAP_BACK_CFG_LAST_BASE
73 };
74 
75 /* Base or target */
76 enum {
77 	LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE,
78 	LDAP_BACK_CFG_CANCEL,
79 	LDAP_BACK_CFG_CHASE,
80 	LDAP_BACK_CFG_CLIENT_PR,
81 	LDAP_BACK_CFG_DEFAULT_T,
82 	LDAP_BACK_CFG_NETWORK_TIMEOUT,
83 	LDAP_BACK_CFG_NOREFS,
84 	LDAP_BACK_CFG_NOUNDEFFILTER,
85 	LDAP_BACK_CFG_NRETRIES,
86 	LDAP_BACK_CFG_QUARANTINE,
87 	LDAP_BACK_CFG_REBIND,
88 	LDAP_BACK_CFG_TIMEOUT,
89 	LDAP_BACK_CFG_VERSION,
90 	LDAP_BACK_CFG_ST_REQUEST,
91 	LDAP_BACK_CFG_T_F,
92 	LDAP_BACK_CFG_TLS,
93 	LDAP_BACK_CFG_LAST_BOTH
94 };
95 
96 /* Target attrs */
97 enum {
98 	LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH,
99 	LDAP_BACK_CFG_ACL_AUTHCDN,
100 	LDAP_BACK_CFG_ACL_PASSWD,
101 	LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
102 	LDAP_BACK_CFG_IDASSERT_BIND,
103 	LDAP_BACK_CFG_REWRITE,
104 	LDAP_BACK_CFG_SUFFIXM,
105 	LDAP_BACK_CFG_MAP,
106 	LDAP_BACK_CFG_SUBTREE_EX,
107 	LDAP_BACK_CFG_SUBTREE_IN,
108 	LDAP_BACK_CFG_PSEUDOROOTDN,
109 	LDAP_BACK_CFG_PSEUDOROOTPW,
110 	LDAP_BACK_CFG_KEEPALIVE,
111 	LDAP_BACK_CFG_FILTER,
112 
113 	LDAP_BACK_CFG_LAST
114 };
115 
116 static ConfigTable metacfg[] = {
117 	{ "uri", "uri", 2, 0, 0,
118 		ARG_MAGIC|LDAP_BACK_CFG_URI,
119 		meta_back_cf_gen, "( OLcfgDbAt:0.14 "
120 			"NAME 'olcDbURI' "
121 			"DESC 'URI (list) for remote DSA' "
122 			"SYNTAX OMsDirectoryString "
123 			"SINGLE-VALUE )",
124 		NULL, NULL },
125 	{ "tls", "what", 2, 0, 0,
126 		ARG_MAGIC|LDAP_BACK_CFG_TLS,
127 		meta_back_cf_gen, "( OLcfgDbAt:3.1 "
128 			"NAME 'olcDbStartTLS' "
129 			"DESC 'StartTLS' "
130 			"SYNTAX OMsDirectoryString "
131 			"SINGLE-VALUE )",
132 		NULL, NULL },
133 	{ "acl-authcDN", "DN", 2, 2, 0,
134 		ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
135 		meta_back_cf_gen, "( OLcfgDbAt:3.2 "
136 			"NAME 'olcDbACLAuthcDn' "
137 			"DESC 'Remote ACL administrative identity' "
138 			"OBSOLETE "
139 			"SYNTAX OMsDN "
140 			"SINGLE-VALUE )",
141 		NULL, NULL },
142 	/* deprecated, will be removed; aliases "acl-authcDN" */
143 	{ "binddn", "DN", 2, 2, 0,
144 		ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
145 		meta_back_cf_gen, NULL, NULL, NULL },
146 	{ "acl-passwd", "cred", 2, 2, 0,
147 		ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
148 		meta_back_cf_gen, "( OLcfgDbAt:3.3 "
149 			"NAME 'olcDbACLPasswd' "
150 			"DESC 'Remote ACL administrative identity credentials' "
151 			"OBSOLETE "
152 			"SYNTAX OMsDirectoryString "
153 			"SINGLE-VALUE )",
154 		NULL, NULL },
155 	/* deprecated, will be removed; aliases "acl-passwd" */
156 	{ "bindpw", "cred", 2, 2, 0,
157 		ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
158 		meta_back_cf_gen, NULL, NULL, NULL },
159 	{ "idassert-bind", "args", 2, 0, 0,
160 		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
161 		meta_back_cf_gen, "( OLcfgDbAt:3.7 "
162 			"NAME 'olcDbIDAssertBind' "
163 			"DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
164 			"SYNTAX OMsDirectoryString "
165 			"SINGLE-VALUE )",
166 		NULL, NULL },
167 	{ "idassert-authzFrom", "authzRule", 2, 2, 0,
168 		ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
169 		meta_back_cf_gen, "( OLcfgDbAt:3.9 "
170 			"NAME 'olcDbIDAssertAuthzFrom' "
171 			"DESC 'Remote Identity Assertion authz rules' "
172 			"EQUALITY caseIgnoreMatch "
173 			"SYNTAX OMsDirectoryString "
174 			"X-ORDERED 'VALUES' )",
175 		NULL, NULL },
176 	{ "rebind-as-user", "true|FALSE", 1, 2, 0,
177 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
178 		meta_back_cf_gen, "( OLcfgDbAt:3.10 "
179 			"NAME 'olcDbRebindAsUser' "
180 			"DESC 'Rebind as user' "
181 			"SYNTAX OMsBoolean "
182 			"SINGLE-VALUE )",
183 		NULL, NULL },
184 	{ "chase-referrals", "true|FALSE", 2, 2, 0,
185 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
186 		meta_back_cf_gen, "( OLcfgDbAt:3.11 "
187 			"NAME 'olcDbChaseReferrals' "
188 			"DESC 'Chase referrals' "
189 			"SYNTAX OMsBoolean "
190 			"SINGLE-VALUE )",
191 		NULL, NULL },
192 	{ "t-f-support", "true|FALSE|discover", 2, 2, 0,
193 		ARG_MAGIC|LDAP_BACK_CFG_T_F,
194 		meta_back_cf_gen, "( OLcfgDbAt:3.12 "
195 			"NAME 'olcDbTFSupport' "
196 			"DESC 'Absolute filters support' "
197 			"SYNTAX OMsDirectoryString "
198 			"SINGLE-VALUE )",
199 		NULL, NULL },
200 	{ "timeout", "timeout(list)", 2, 0, 0,
201 		ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
202 		meta_back_cf_gen, "( OLcfgDbAt:3.14 "
203 			"NAME 'olcDbTimeout' "
204 			"DESC 'Per-operation timeouts' "
205 			"SYNTAX OMsDirectoryString "
206 			"SINGLE-VALUE )",
207 		NULL, NULL },
208 	{ "idle-timeout", "timeout", 2, 2, 0,
209 		ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
210 		meta_back_cf_gen, "( OLcfgDbAt:3.15 "
211 			"NAME 'olcDbIdleTimeout' "
212 			"DESC 'connection idle timeout' "
213 			"SYNTAX OMsDirectoryString "
214 			"SINGLE-VALUE )",
215 		NULL, NULL },
216 	{ "conn-ttl", "ttl", 2, 2, 0,
217 		ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
218 		meta_back_cf_gen, "( OLcfgDbAt:3.16 "
219 			"NAME 'olcDbConnTtl' "
220 			"DESC 'connection ttl' "
221 			"SYNTAX OMsDirectoryString "
222 			"SINGLE-VALUE )",
223 		NULL, NULL },
224 	{ "network-timeout", "timeout", 2, 2, 0,
225 		ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
226 		meta_back_cf_gen, "( OLcfgDbAt:3.17 "
227 			"NAME 'olcDbNetworkTimeout' "
228 			"DESC 'connection network timeout' "
229 			"SYNTAX OMsDirectoryString "
230 			"SINGLE-VALUE )",
231 		NULL, NULL },
232 	{ "protocol-version", "version", 2, 2, 0,
233 		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
234 		meta_back_cf_gen, "( OLcfgDbAt:3.18 "
235 			"NAME 'olcDbProtocolVersion' "
236 			"DESC 'protocol version' "
237 			"SYNTAX OMsInteger "
238 			"SINGLE-VALUE )",
239 		NULL, NULL },
240 	{ "single-conn", "true|FALSE", 2, 2, 0,
241 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
242 		meta_back_cf_gen, "( OLcfgDbAt:3.19 "
243 			"NAME 'olcDbSingleConn' "
244 			"DESC 'cache a single connection per identity' "
245 			"SYNTAX OMsBoolean "
246 			"SINGLE-VALUE )",
247 		NULL, NULL },
248 	{ "cancel", "ABANDON|ignore|exop", 2, 2, 0,
249 		ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
250 		meta_back_cf_gen, "( OLcfgDbAt:3.20 "
251 			"NAME 'olcDbCancel' "
252 			"DESC 'abandon/ignore/exop operations when appropriate' "
253 			"SYNTAX OMsDirectoryString "
254 			"SINGLE-VALUE )",
255 		NULL, NULL },
256 	{ "quarantine", "retrylist", 2, 2, 0,
257 		ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
258 		meta_back_cf_gen, "( OLcfgDbAt:3.21 "
259 			"NAME 'olcDbQuarantine' "
260 			"DESC 'Quarantine database if connection fails and retry according to rule' "
261 			"SYNTAX OMsDirectoryString "
262 			"SINGLE-VALUE )",
263 		NULL, NULL },
264 	{ "use-temporary-conn", "true|FALSE", 2, 2, 0,
265 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
266 		meta_back_cf_gen, "( OLcfgDbAt:3.22 "
267 			"NAME 'olcDbUseTemporaryConn' "
268 			"DESC 'Use temporary connections if the cached one is busy' "
269 			"SYNTAX OMsBoolean "
270 			"SINGLE-VALUE )",
271 		NULL, NULL },
272 	{ "conn-pool-max", "<n>", 2, 2, 0,
273 		ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
274 		meta_back_cf_gen, "( OLcfgDbAt:3.23 "
275 			"NAME 'olcDbConnectionPoolMax' "
276 			"DESC 'Max size of privileged connections pool' "
277 			"SYNTAX OMsInteger "
278 			"SINGLE-VALUE )",
279 		NULL, NULL },
280 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
281 	{ "session-tracking-request", "true|FALSE", 2, 2, 0,
282 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
283 		meta_back_cf_gen, "( OLcfgDbAt:3.24 "
284 			"NAME 'olcDbSessionTrackingRequest' "
285 			"DESC 'Add session tracking control to proxied requests' "
286 			"SYNTAX OMsBoolean "
287 			"SINGLE-VALUE )",
288 		NULL, NULL },
289 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
290 	{ "norefs", "true|FALSE", 2, 2, 0,
291 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
292 		meta_back_cf_gen, "( OLcfgDbAt:3.25 "
293 			"NAME 'olcDbNoRefs' "
294 			"DESC 'Do not return search reference responses' "
295 			"SYNTAX OMsBoolean "
296 			"SINGLE-VALUE )",
297 		NULL, NULL },
298 	{ "noundeffilter", "true|FALSE", 2, 2, 0,
299 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
300 		meta_back_cf_gen, "( OLcfgDbAt:3.26 "
301 			"NAME 'olcDbNoUndefFilter' "
302 			"DESC 'Do not propagate undefined search filters' "
303 			"SYNTAX OMsBoolean "
304 			"SINGLE-VALUE )",
305 		NULL, NULL },
306 
307 	{ "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ),
308 		ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
309 		meta_back_cf_gen, "( OLcfgDbAt:3.101 "
310 			"NAME 'olcDbRewrite' "
311 			"DESC 'DN rewriting rules' "
312 			"EQUALITY caseIgnoreMatch "
313 			"SYNTAX OMsDirectoryString "
314 			"X-ORDERED 'VALUES' )",
315 		NULL, NULL },
316 	{ "suffixmassage", "virtual> <real", 2, 3, 0,
317 		ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
318 		meta_back_cf_gen, NULL, NULL, NULL },
319 
320 	{ "map", "attribute|objectClass> [*|<local>] *|<remote", 3, 4, 0,
321 		ARG_MAGIC|LDAP_BACK_CFG_MAP,
322 		meta_back_cf_gen, "( OLcfgDbAt:3.102 "
323 			"NAME 'olcDbMap' "
324 			"DESC 'Map attribute and objectclass names' "
325 			"EQUALITY caseIgnoreMatch "
326 			"SYNTAX OMsDirectoryString "
327 			"X-ORDERED 'VALUES' )",
328 		NULL, NULL },
329 
330 	{ "subtree-exclude", "pattern", 2, 2, 0,
331 		ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX,
332 		meta_back_cf_gen, "( OLcfgDbAt:3.103 "
333 			"NAME 'olcDbSubtreeExclude' "
334 			"DESC 'DN of subtree to exclude from target' "
335 			"EQUALITY caseIgnoreMatch "
336 			"SYNTAX OMsDirectoryString )",
337 		NULL, NULL },
338 	{ "subtree-include", "pattern", 2, 2, 0,
339 		ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN,
340 		meta_back_cf_gen, "( OLcfgDbAt:3.104 "
341 			"NAME 'olcDbSubtreeInclude' "
342 			"DESC 'DN of subtree to include in target' "
343 			"EQUALITY caseIgnoreMatch "
344 			"SYNTAX OMsDirectoryString )",
345 		NULL, NULL },
346 	{ "default-target", "[none|<target ID>]", 1, 2, 0,
347 		ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
348 		meta_back_cf_gen, "( OLcfgDbAt:3.105 "
349 			"NAME 'olcDbDefaultTarget' "
350 			"DESC 'Specify the default target' "
351 			"SYNTAX OMsDirectoryString "
352 			"SINGLE-VALUE )",
353 		NULL, NULL },
354 	{ "dncache-ttl", "ttl", 2, 2, 0,
355 		ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
356 		meta_back_cf_gen, "( OLcfgDbAt:3.106 "
357 			"NAME 'olcDbDnCacheTtl' "
358 			"DESC 'dncache ttl' "
359 			"SYNTAX OMsDirectoryString "
360 			"SINGLE-VALUE )",
361 		NULL, NULL },
362 	{ "bind-timeout", "microseconds", 2, 2, 0,
363 		ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
364 		meta_back_cf_gen, "( OLcfgDbAt:3.107 "
365 			"NAME 'olcDbBindTimeout' "
366 			"DESC 'bind timeout' "
367 			"SYNTAX OMsDirectoryString "
368 			"SINGLE-VALUE )",
369 		NULL, NULL },
370 	{ "onerr", "CONTINUE|report|stop", 2, 2, 0,
371 		ARG_MAGIC|LDAP_BACK_CFG_ONERR,
372 		meta_back_cf_gen, "( OLcfgDbAt:3.108 "
373 			"NAME 'olcDbOnErr' "
374 			"DESC 'error handling' "
375 			"SYNTAX OMsDirectoryString "
376 			"SINGLE-VALUE )",
377 		NULL, NULL },
378 	{ "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
379 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
380 		meta_back_cf_gen, "( OLcfgDbAt:3.109 "
381 			"NAME 'olcDbPseudoRootBindDefer' "
382 			"DESC 'error handling' "
383 			"SYNTAX OMsBoolean "
384 			"SINGLE-VALUE )",
385 		NULL, NULL },
386 	{ "root-bind-defer", "TRUE|false", 2, 2, 0,
387 		ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
388 		meta_back_cf_gen, NULL, NULL, NULL },
389 	{ "pseudorootdn", "dn", 2, 2, 0,
390 		ARG_MAGIC|ARG_DN|LDAP_BACK_CFG_PSEUDOROOTDN,
391 		meta_back_cf_gen, NULL, NULL, NULL },
392 	{ "pseudorootpw", "password", 2, 2, 0,
393 		ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTDN,
394 		meta_back_cf_gen, NULL, NULL, NULL },
395 	{ "nretries", "NEVER|forever|<number>", 2, 2, 0,
396 		ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
397 		meta_back_cf_gen, "( OLcfgDbAt:3.110 "
398 			"NAME 'olcDbNretries' "
399 			"DESC 'retry handling' "
400 			"SYNTAX OMsDirectoryString "
401 			"SINGLE-VALUE )",
402 		NULL, NULL },
403 	{ "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
404 		ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
405 		meta_back_cf_gen, "( OLcfgDbAt:3.111 "
406 			"NAME 'olcDbClientPr' "
407 			"DESC 'PagedResults handling' "
408 			"SYNTAX OMsDirectoryString "
409 			"SINGLE-VALUE )",
410 		NULL, NULL },
411 
412 	{ "", "", 0, 0, 0, ARG_IGNORED,
413 		NULL, "( OLcfgDbAt:3.100 NAME 'olcMetaSub' "
414 			"DESC 'Placeholder to name a Target entry' "
415 			"EQUALITY caseIgnoreMatch "
416 			"SYNTAX OMsDirectoryString "
417 			"SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
418 
419 	{ "keepalive", "keepalive", 2, 2, 0,
420 		ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
421 		meta_back_cf_gen, "( OLcfgDbAt:3.29 "
422 			"NAME 'olcDbKeepalive' "
423 			"DESC 'TCP keepalive' "
424 			"SYNTAX OMsDirectoryString "
425 			"SINGLE-VALUE )",
426 		NULL, NULL },
427 
428 	{ "filter", "pattern", 2, 2, 0,
429 		ARG_MAGIC|LDAP_BACK_CFG_FILTER,
430 		meta_back_cf_gen, "( OLcfgDbAt:3.112 "
431 			"NAME 'olcDbFilter' "
432 			"DESC 'Filter regex pattern to include in target' "
433 			"EQUALITY caseExactMatch "
434 			"SYNTAX OMsDirectoryString )",
435 		NULL, NULL },
436 
437 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
438 		NULL, NULL, NULL, NULL }
439 };
440 
441 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
442 #define	ST_ATTR "$ olcDbSessionTrackingRequest "
443 #else
444 #define	ST_ATTR ""
445 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
446 
447 #define COMMON_ATTRS	\
448 			"$ olcDbBindTimeout " \
449 			"$ olcDbCancel " \
450 			"$ olcDbChaseReferrals " \
451 			"$ olcDbClientPr " \
452 			"$ olcDbDefaultTarget " \
453 			"$ olcDbNetworkTimeout " \
454 			"$ olcDbNoRefs " \
455 			"$ olcDbNoUndefFilter " \
456 			"$ olcDbNretries " \
457 			"$ olcDbProtocolVersion " \
458 			"$ olcDbQuarantine " \
459 			"$ olcDbRebindAsUser " \
460 			ST_ATTR \
461 			"$ olcDbStartTLS " \
462 			"$ olcDbTFSupport "
463 
464 static ConfigOCs metaocs[] = {
465 	{ "( OLcfgDbOc:3.2 "
466 		"NAME 'olcMetaConfig' "
467 		"DESC 'Meta backend configuration' "
468 		"SUP olcDatabaseConfig "
469 		"MAY ( olcDbConnTtl "
470 			"$ olcDbDnCacheTtl "
471 			"$ olcDbIdleTimeout "
472 			"$ olcDbOnErr "
473 			"$ olcDbPseudoRootBindDefer "
474 			"$ olcDbSingleConn "
475 			"$ olcDbUseTemporaryConn "
476 			"$ olcDbConnectionPoolMax "
477 
478 			/* defaults, may be overridden per-target */
479 			COMMON_ATTRS
480 		") )",
481 			Cft_Database, metacfg, NULL, meta_cfadd },
482 	{ "( OLcfgDbOc:3.3 "
483 		"NAME 'olcMetaTargetConfig' "
484 		"DESC 'Meta target configuration' "
485 		"SUP olcConfig STRUCTURAL "
486 		"MUST ( olcMetaSub $ olcDbURI ) "
487 		"MAY ( olcDbACLAuthcDn "
488 			"$ olcDbACLPasswd "
489 			"$ olcDbIDAssertAuthzFrom "
490 			"$ olcDbIDAssertBind "
491 			"$ olcDbMap "
492 			"$ olcDbRewrite "
493 			"$ olcDbSubtreeExclude "
494 			"$ olcDbSubtreeInclude "
495 			"$ olcDbTimeout "
496 			"$ olcDbKeepalive "
497 			"$ olcDbFilter "
498 
499 			/* defaults may be inherited */
500 			COMMON_ATTRS
501 		") )",
502 			Cft_Misc, metacfg, meta_ldadd },
503 	{ NULL, 0, NULL }
504 };
505 
506 static int
507 meta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
508 {
509 	if ( p->ce_type != Cft_Database || !p->ce_be ||
510 		p->ce_be->be_cf_ocs != metaocs )
511 		return LDAP_CONSTRAINT_VIOLATION;
512 
513 	c->be = p->ce_be;
514 	return LDAP_SUCCESS;
515 }
516 
517 static int
518 meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
519 {
520 	metainfo_t	*mi = ( metainfo_t * )c->be->be_private;
521 	struct berval bv;
522 	int i;
523 
524 	bv.bv_val = c->cr_msg;
525 	for ( i=0; i<mi->mi_ntargets; i++ ) {
526 		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
527 			"olcMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
528 		c->ca_private = mi->mi_targets[i];
529 		c->valx = i;
530 		config_build_entry( op, rs, p->e_private, c,
531 			&bv, &metaocs[1], NULL );
532 	}
533 
534 	return LDAP_SUCCESS;
535 }
536 
537 static int
538 meta_rwi_init( struct rewrite_info **rwm_rw )
539 {
540 	char			*rargv[ 3 ];
541 
542 	*rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
543 	if ( *rwm_rw == NULL ) {
544 		return -1;
545 	}
546 	/*
547 	 * the filter rewrite as a string must be disabled
548 	 * by default; it can be re-enabled by adding rules;
549 	 * this creates an empty rewriteContext
550 	 */
551 	rargv[ 0 ] = "rewriteContext";
552 	rargv[ 1 ] = "searchFilter";
553 	rargv[ 2 ] = NULL;
554 	rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
555 
556 	rargv[ 0 ] = "rewriteContext";
557 	rargv[ 1 ] = "default";
558 	rargv[ 2 ] = NULL;
559 	rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
560 
561 	return 0;
562 }
563 
564 static int
565 meta_back_new_target(
566 	metatarget_t	**mtp )
567 {
568 	metatarget_t		*mt;
569 
570 	*mtp = NULL;
571 
572 	mt = ch_calloc( sizeof( metatarget_t ), 1 );
573 
574 	if ( meta_rwi_init( &mt->mt_rwmap.rwm_rw )) {
575 		ch_free( mt );
576 		return -1;
577 	}
578 
579 	ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
580 
581 	mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
582 	mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
583 	mt->mt_idassert_tls = SB_TLS_DEFAULT;
584 
585 	/* by default, use proxyAuthz control on each operation */
586 	mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
587 
588 	*mtp = mt;
589 
590 	return 0;
591 }
592 
593 /* Validation for suffixmassage_config */
594 static int
595 meta_suffixm_config(
596 	ConfigArgs *c,
597 	int argc,
598 	char **argv,
599 	metatarget_t *mt
600 )
601 {
602 	BackendDB 	*tmp_bd;
603 	struct berval	dn, nvnc, pvnc, nrnc, prnc;
604 	int j, rc;
605 
606 	/*
607 	 * syntax:
608 	 *
609 	 * 	suffixmassage <suffix> <massaged suffix>
610 	 *
611 	 * the <suffix> field must be defined as a valid suffix
612 	 * (or suffixAlias?) for the current database;
613 	 * the <massaged suffix> shouldn't have already been
614 	 * defined as a valid suffix or suffixAlias for the
615 	 * current server
616 	 */
617 
618 	ber_str2bv( argv[ 1 ], 0, 0, &dn );
619 	if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
620 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
621 			"suffix \"%s\" is invalid",
622 			argv[1] );
623 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
624 		return 1;
625 	}
626 
627 	for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
628 		if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
629 			break;
630 		}
631 	}
632 
633 	if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
634 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
635 			"suffix \"%s\" must be within the database naming context",
636 			argv[1] );
637 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
638 		free( pvnc.bv_val );
639 		free( nvnc.bv_val );
640 		return 1;
641 	}
642 
643 	ber_str2bv( argv[ 2 ], 0, 0, &dn );
644 	if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
645 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
646 			"massaged suffix \"%s\" is invalid",
647 			argv[2] );
648 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
649 		free( pvnc.bv_val );
650 		free( nvnc.bv_val );
651 		return 1;
652 	}
653 
654 	tmp_bd = select_backend( &nrnc, 0 );
655 	if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
656 		Debug( LDAP_DEBUG_ANY,
657 	"%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
658 	"\"suffixMassage <suffix> <massaged suffix>\"\n",
659 			c->log, prnc.bv_val, 0 );
660 	}
661 
662 	/*
663 	 * The suffix massaging is emulated by means of the
664 	 * rewrite capabilities
665 	 */
666 	rc = suffix_massage_config( mt->mt_rwmap.rwm_rw,
667 			&pvnc, &nvnc, &prnc, &nrnc );
668 
669 	free( pvnc.bv_val );
670 	free( nvnc.bv_val );
671 	free( prnc.bv_val );
672 	free( nrnc.bv_val );
673 
674 	return rc;
675 }
676 
677 static int
678 slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
679 {
680 	int		i;
681 	BerVarray	bva = NULL;
682 	char		ibuf[32], *ptr;
683 	struct berval	idx;
684 
685 	assert( in != NULL );
686 
687 	for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
688 		/* count'em */ ;
689 
690 	if ( i == 0 ) {
691 		return 1;
692 	}
693 
694 	idx.bv_val = ibuf;
695 
696 	bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
697 	BER_BVZERO( &bva[ 0 ] );
698 
699 	for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
700 		idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
701 		if ( idx.bv_len >= sizeof( ibuf ) ) {
702 			ber_bvarray_free( bva );
703 			return 1;
704 		}
705 
706 		bva[i].bv_len = idx.bv_len + in[i].bv_len;
707 		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
708 		ptr = lutil_strcopy( bva[i].bv_val, ibuf );
709 		ptr = lutil_strcopy( ptr, in[i].bv_val );
710 		*ptr = '\0';
711 		BER_BVZERO( &bva[ i + 1 ] );
712 	}
713 
714 	*out = bva;
715 	return 0;
716 }
717 
718 int
719 meta_subtree_free( metasubtree_t *ms )
720 {
721 	switch ( ms->ms_type ) {
722 	case META_ST_SUBTREE:
723 	case META_ST_SUBORDINATE:
724 		ber_memfree( ms->ms_dn.bv_val );
725 		break;
726 
727 	case META_ST_REGEX:
728 		regfree( &ms->ms_regex );
729 		ber_memfree( ms->ms_regex_pattern.bv_val );
730 		break;
731 
732 	default:
733 		return -1;
734 	}
735 
736 	ch_free( ms );
737 	return 0;
738 }
739 
740 int
741 meta_subtree_destroy( metasubtree_t *ms )
742 {
743 	if ( ms->ms_next ) {
744 		meta_subtree_destroy( ms->ms_next );
745 	}
746 
747 	return meta_subtree_free( ms );
748 }
749 
750 static void
751 meta_filter_free( metafilter_t *mf )
752 {
753 	regfree( &mf->mf_regex );
754 	ber_memfree( mf->mf_regex_pattern.bv_val );
755 	ch_free( mf );
756 }
757 
758 void
759 meta_filter_destroy( metafilter_t *mf )
760 {
761 	if ( mf->mf_next )
762 		meta_filter_destroy( mf->mf_next );
763 	meta_filter_free( mf );
764 }
765 
766 static struct berval st_styles[] = {
767 	BER_BVC("subtree"),
768 	BER_BVC("children"),
769 	BER_BVC("regex")
770 };
771 
772 static int
773 meta_subtree_unparse(
774 	ConfigArgs *c,
775 	metatarget_t *mt )
776 {
777 	metasubtree_t	*ms;
778 	struct berval bv, *style;
779 
780 	if ( !mt->mt_subtree )
781 		return 1;
782 
783 	/* can only be one of exclude or include */
784 	if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
785 		return 1;
786 
787 	bv.bv_val = c->cr_msg;
788 	for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
789 		if (ms->ms_type == META_ST_SUBTREE)
790 			style = &st_styles[0];
791 		else if ( ms->ms_type == META_ST_SUBORDINATE )
792 			style = &st_styles[1];
793 		else if ( ms->ms_type == META_ST_REGEX )
794 			style = &st_styles[2];
795 		else {
796 			assert(0);
797 			continue;
798 		}
799 		bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
800 			"dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
801 		value_add_one( &c->rvalue_vals, &bv );
802 	}
803 	return 0;
804 }
805 
806 static int
807 meta_subtree_config(
808 	metatarget_t *mt,
809 	ConfigArgs *c )
810 {
811 	meta_st_t	type = META_ST_SUBTREE;
812 	char		*pattern;
813 	struct berval	ndn = BER_BVNULL;
814 	metasubtree_t	*ms = NULL;
815 
816 	if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
817 		if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
818 			snprintf( c->cr_msg, sizeof(c->cr_msg),
819 				"\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
820 			return 1;
821 		}
822 
823 		mt->mt_subtree_exclude = 1;
824 
825 	} else {
826 		if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
827 			snprintf( c->cr_msg, sizeof(c->cr_msg),
828 				"\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
829 			return 1;
830 		}
831 	}
832 
833 	pattern = c->argv[1];
834 	if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
835 		char *style;
836 
837 		pattern = &pattern[STRLENOF( "dn")];
838 
839 		if ( pattern[0] == '.' ) {
840 			style = &pattern[1];
841 
842 			if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
843 				type = META_ST_SUBTREE;
844 				pattern = &style[STRLENOF( "subtree" )];
845 
846 			} else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
847 				type = META_ST_SUBORDINATE;
848 				pattern = &style[STRLENOF( "children" )];
849 
850 			} else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
851 				type = META_ST_SUBTREE;
852 				pattern = &style[STRLENOF( "sub" )];
853 
854 			} else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
855 				type = META_ST_REGEX;
856 				pattern = &style[STRLENOF( "regex" )];
857 
858 			} else {
859 				snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" );
860 				return 1;
861 			}
862 		}
863 
864 		if ( pattern[0] != ':' ) {
865 			snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" );
866 			return 1;
867 		}
868 		pattern++;
869 	}
870 
871 	switch ( type ) {
872 	case META_ST_SUBTREE:
873 	case META_ST_SUBORDINATE: {
874 		struct berval dn;
875 
876 		ber_str2bv( pattern, 0, 0, &dn );
877 		if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
878 			!= LDAP_SUCCESS )
879 		{
880 			snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern );
881 			return 1;
882 		}
883 
884 		if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) {
885 			snprintf( c->cr_msg, sizeof(c->cr_msg),
886 				"DN=\"%s\" is not a subtree of target \"%s\"",
887 				pattern, mt->mt_nsuffix.bv_val );
888 			ber_memfree( ndn.bv_val );
889 			return( 1 );
890 		}
891 		} break;
892 
893 	default:
894 		/* silence warnings */
895 		break;
896 	}
897 
898 	ms = ch_calloc( sizeof( metasubtree_t ), 1 );
899 	ms->ms_type = type;
900 
901 	switch ( ms->ms_type ) {
902 	case META_ST_SUBTREE:
903 	case META_ST_SUBORDINATE:
904 		ms->ms_dn = ndn;
905 		break;
906 
907 	case META_ST_REGEX: {
908 		int rc;
909 
910 		rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE );
911 		if ( rc != 0 ) {
912 			char regerr[ SLAP_TEXT_BUFLEN ];
913 
914 			regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) );
915 
916 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
917 				"regular expression \"%s\" bad because of %s",
918 				pattern, regerr );
919 			ch_free( ms );
920 			return 1;
921 		}
922 		ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern );
923 		} break;
924 	}
925 
926 	if ( mt->mt_subtree == NULL ) {
927 		 mt->mt_subtree = ms;
928 
929 	} else {
930 		metasubtree_t **msp;
931 
932 		for ( msp = &mt->mt_subtree; *msp; ) {
933 			switch ( ms->ms_type ) {
934 			case META_ST_SUBTREE:
935 				switch ( (*msp)->ms_type ) {
936 				case META_ST_SUBTREE:
937 					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
938 						metasubtree_t *tmp = *msp;
939 						Debug( LDAP_DEBUG_CONFIG,
940 							"%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
941 							c->log, pattern, (*msp)->ms_dn.bv_val );
942 						*msp = (*msp)->ms_next;
943 						tmp->ms_next = NULL;
944 						meta_subtree_destroy( tmp );
945 						continue;
946 
947 					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
948 						Debug( LDAP_DEBUG_CONFIG,
949 							"%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
950 							c->log, (*msp)->ms_dn.bv_val, pattern );
951 						meta_subtree_destroy( ms );
952 						ms = NULL;
953 						return( 0 );
954 					}
955 					break;
956 
957 				case META_ST_SUBORDINATE:
958 					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
959 						metasubtree_t *tmp = *msp;
960 						Debug( LDAP_DEBUG_CONFIG,
961 							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
962 							c->log, pattern, (*msp)->ms_dn.bv_val );
963 						*msp = (*msp)->ms_next;
964 						tmp->ms_next = NULL;
965 						meta_subtree_destroy( tmp );
966 						continue;
967 
968 					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
969 						Debug( LDAP_DEBUG_CONFIG,
970 							"%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
971 							c->log, (*msp)->ms_dn.bv_val, pattern );
972 						meta_subtree_destroy( ms );
973 						ms = NULL;
974 						return( 0 );
975 					}
976 					break;
977 
978 				case META_ST_REGEX:
979 					if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
980 						Debug( LDAP_DEBUG_CONFIG,
981 							"%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
982 							c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
983 					}
984 					break;
985 				}
986 				break;
987 
988 			case META_ST_SUBORDINATE:
989 				switch ( (*msp)->ms_type ) {
990 				case META_ST_SUBTREE:
991 					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
992 						metasubtree_t *tmp = *msp;
993 						Debug( LDAP_DEBUG_CONFIG,
994 							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
995 							c->log, pattern, (*msp)->ms_dn.bv_val );
996 						*msp = (*msp)->ms_next;
997 						tmp->ms_next = NULL;
998 						meta_subtree_destroy( tmp );
999 						continue;
1000 
1001 					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
1002 						Debug( LDAP_DEBUG_CONFIG,
1003 							"%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
1004 							c->log, (*msp)->ms_dn.bv_val, pattern );
1005 						meta_subtree_destroy( ms );
1006 						ms = NULL;
1007 						return( 0 );
1008 					}
1009 					break;
1010 
1011 				case META_ST_SUBORDINATE:
1012 					if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
1013 						metasubtree_t *tmp = *msp;
1014 						Debug( LDAP_DEBUG_CONFIG,
1015 							"%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n",
1016 							c->log, pattern, (*msp)->ms_dn.bv_val );
1017 						*msp = (*msp)->ms_next;
1018 						tmp->ms_next = NULL;
1019 						meta_subtree_destroy( tmp );
1020 						continue;
1021 
1022 					} else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
1023 						Debug( LDAP_DEBUG_CONFIG,
1024 							"%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n",
1025 							c->log, (*msp)->ms_dn.bv_val, pattern );
1026 						meta_subtree_destroy( ms );
1027 						ms = NULL;
1028 						return( 0 );
1029 					}
1030 					break;
1031 
1032 				case META_ST_REGEX:
1033 					if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
1034 						Debug( LDAP_DEBUG_CONFIG,
1035 							"%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
1036 							c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
1037 					}
1038 					break;
1039 				}
1040 				break;
1041 
1042 			case META_ST_REGEX:
1043 				switch ( (*msp)->ms_type ) {
1044 				case META_ST_SUBTREE:
1045 				case META_ST_SUBORDINATE:
1046 					if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
1047 						Debug( LDAP_DEBUG_CONFIG,
1048 							"%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
1049 							c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val );
1050 					}
1051 					break;
1052 
1053 				case META_ST_REGEX:
1054 					/* no check possible */
1055 					break;
1056 				}
1057 				break;
1058 			}
1059 
1060 			msp = &(*msp)->ms_next;
1061 		}
1062 
1063 		*msp = ms;
1064 	}
1065 
1066 	return 0;
1067 }
1068 
1069 static slap_verbmasks idassert_mode[] = {
1070 	{ BER_BVC("self"),		LDAP_BACK_IDASSERT_SELF },
1071 	{ BER_BVC("anonymous"),		LDAP_BACK_IDASSERT_ANONYMOUS },
1072 	{ BER_BVC("none"),		LDAP_BACK_IDASSERT_NOASSERT },
1073 	{ BER_BVC("legacy"),		LDAP_BACK_IDASSERT_LEGACY },
1074 	{ BER_BVNULL,			0 }
1075 };
1076 
1077 static slap_verbmasks tls_mode[] = {
1078 	{ BER_BVC( "propagate" ),	LDAP_BACK_F_TLS_PROPAGATE_MASK },
1079 	{ BER_BVC( "try-propagate" ),	LDAP_BACK_F_PROPAGATE_TLS },
1080 	{ BER_BVC( "start" ),		LDAP_BACK_F_TLS_USE_MASK },
1081 	{ BER_BVC( "try-start" ),	LDAP_BACK_F_USE_TLS },
1082 	{ BER_BVC( "ldaps" ),		LDAP_BACK_F_TLS_LDAPS },
1083 	{ BER_BVC( "none" ),		LDAP_BACK_F_NONE },
1084 	{ BER_BVNULL,			0 }
1085 };
1086 
1087 static slap_verbmasks t_f_mode[] = {
1088 	{ BER_BVC( "yes" ),		LDAP_BACK_F_T_F },
1089 	{ BER_BVC( "discover" ),	LDAP_BACK_F_T_F_DISCOVER },
1090 	{ BER_BVC( "no" ),		LDAP_BACK_F_NONE },
1091 	{ BER_BVNULL,			0 }
1092 };
1093 
1094 static slap_verbmasks cancel_mode[] = {
1095 	{ BER_BVC( "ignore" ),		LDAP_BACK_F_CANCEL_IGNORE },
1096 	{ BER_BVC( "exop" ),		LDAP_BACK_F_CANCEL_EXOP },
1097 	{ BER_BVC( "exop-discover" ),	LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
1098 	{ BER_BVC( "abandon" ),		LDAP_BACK_F_CANCEL_ABANDON },
1099 	{ BER_BVNULL,			0 }
1100 };
1101 
1102 static slap_verbmasks onerr_mode[] = {
1103 	{ BER_BVC( "stop" ),		META_BACK_F_ONERR_STOP },
1104 	{ BER_BVC( "report" ),	META_BACK_F_ONERR_REPORT },
1105 	{ BER_BVC( "continue" ),		LDAP_BACK_F_NONE },
1106 	{ BER_BVNULL,			0 }
1107 };
1108 
1109 /* see enum in slap.h */
1110 static slap_cf_aux_table timeout_table[] = {
1111 	{ BER_BVC("bind="),	SLAP_OP_BIND * sizeof( time_t ),	'u', 0, NULL },
1112 	/* unbind makes no sense */
1113 	{ BER_BVC("add="),	SLAP_OP_ADD * sizeof( time_t ),		'u', 0, NULL },
1114 	{ BER_BVC("delete="),	SLAP_OP_DELETE * sizeof( time_t ),	'u', 0, NULL },
1115 	{ BER_BVC("modrdn="),	SLAP_OP_MODRDN * sizeof( time_t ),	'u', 0, NULL },
1116 	{ BER_BVC("modify="),	SLAP_OP_MODIFY * sizeof( time_t ),	'u', 0, NULL },
1117 	{ BER_BVC("compare="),	SLAP_OP_COMPARE * sizeof( time_t ),	'u', 0, NULL },
1118 	{ BER_BVC("search="),	SLAP_OP_SEARCH * sizeof( time_t ),	'u', 0, NULL },
1119 	/* abandon makes little sense */
1120 #if 0	/* not implemented yet */
1121 	{ BER_BVC("extended="),	SLAP_OP_EXTENDED * sizeof( time_t ),	'u', 0, NULL },
1122 #endif
1123 	{ BER_BVNULL, 0, 0, 0, NULL }
1124 };
1125 
1126 static int
1127 meta_cf_cleanup( ConfigArgs *c )
1128 {
1129 	metainfo_t	*mi = ( metainfo_t * )c->be->be_private;
1130 	metatarget_t	*mt = c->ca_private;
1131 
1132 	return meta_target_finish( mi, mt, c->log, c->cr_msg, sizeof( c->cr_msg ));
1133 }
1134 
1135 static int
1136 meta_back_cf_gen( ConfigArgs *c )
1137 {
1138 	metainfo_t	*mi = ( metainfo_t * )c->be->be_private;
1139 	metatarget_t	*mt;
1140 	metacommon_t	*mc;
1141 
1142 	int i, rc = 0;
1143 
1144 	assert( mi != NULL );
1145 
1146 	if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) {
1147 		if ( !mi )
1148 			return 1;
1149 
1150 		if ( c->table == Cft_Database ) {
1151 			mt = NULL;
1152 			mc = &mi->mi_mc;
1153 		} else {
1154 			mt = c->ca_private;
1155 			mc = &mt->mt_mc;
1156 		}
1157 	}
1158 
1159 	if ( c->op == SLAP_CONFIG_EMIT ) {
1160 		struct berval bv = BER_BVNULL;
1161 
1162 		switch( c->type ) {
1163 		/* Base attrs */
1164 		case LDAP_BACK_CFG_CONN_TTL:
1165 			if ( mi->mi_conn_ttl == 0 ) {
1166 				return 1;
1167 			} else {
1168 				char	buf[ SLAP_TEXT_BUFLEN ];
1169 
1170 				lutil_unparse_time( buf, sizeof( buf ), mi->mi_conn_ttl );
1171 				ber_str2bv( buf, 0, 0, &bv );
1172 				value_add_one( &c->rvalue_vals, &bv );
1173 			}
1174 			break;
1175 
1176 		case LDAP_BACK_CFG_DNCACHE_TTL:
1177 			if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) {
1178 				return 1;
1179 			} else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) {
1180 				BER_BVSTR( &bv, "forever" );
1181 			} else {
1182 				char	buf[ SLAP_TEXT_BUFLEN ];
1183 
1184 				lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl );
1185 				ber_str2bv( buf, 0, 0, &bv );
1186 			}
1187 			value_add_one( &c->rvalue_vals, &bv );
1188 			break;
1189 
1190 		case LDAP_BACK_CFG_IDLE_TIMEOUT:
1191 			if ( mi->mi_idle_timeout == 0 ) {
1192 				return 1;
1193 			} else {
1194 				char	buf[ SLAP_TEXT_BUFLEN ];
1195 
1196 				lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout );
1197 				ber_str2bv( buf, 0, 0, &bv );
1198 				value_add_one( &c->rvalue_vals, &bv );
1199 			}
1200 			break;
1201 
1202 		case LDAP_BACK_CFG_ONERR:
1203 			enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv );
1204 			if ( BER_BVISNULL( &bv )) {
1205 				rc = 1;
1206 			} else {
1207 				value_add_one( &c->rvalue_vals, &bv );
1208 			}
1209 			break;
1210 
1211 		case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
1212 			c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi );
1213 			break;
1214 
1215 		case LDAP_BACK_CFG_SINGLECONN:
1216 			c->value_int = LDAP_BACK_SINGLECONN( mi );
1217 			break;
1218 
1219 		case LDAP_BACK_CFG_USETEMP:
1220 			c->value_int = LDAP_BACK_USE_TEMPORARIES( mi );
1221 			break;
1222 
1223 		case LDAP_BACK_CFG_CONNPOOLMAX:
1224 			c->value_int = mi->mi_conn_priv_max;
1225 			break;
1226 
1227 		/* common attrs */
1228 		case LDAP_BACK_CFG_BIND_TIMEOUT:
1229 			if ( mc->mc_bind_timeout.tv_sec == 0 &&
1230 				mc->mc_bind_timeout.tv_usec == 0 ) {
1231 				return 1;
1232 			} else {
1233 				c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL +
1234 					mc->mc_bind_timeout.tv_usec;
1235 			}
1236 			break;
1237 
1238 		case LDAP_BACK_CFG_CANCEL: {
1239 			slap_mask_t	mask = LDAP_BACK_F_CANCEL_MASK2;
1240 
1241 			if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
1242 				mask &= ~LDAP_BACK_F_CANCEL_EXOP;
1243 			}
1244 			enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv );
1245 			if ( BER_BVISNULL( &bv ) ) {
1246 				/* there's something wrong... */
1247 				assert( 0 );
1248 				rc = 1;
1249 
1250 			} else {
1251 				value_add_one( &c->rvalue_vals, &bv );
1252 			}
1253 			} break;
1254 
1255 		case LDAP_BACK_CFG_CHASE:
1256 			c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc);
1257 			break;
1258 
1259 #ifdef SLAPD_META_CLIENT_PR
1260 		case LDAP_BACK_CFG_CLIENT_PR:
1261 			if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) {
1262 				return 1;
1263 			} else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) {
1264 				BER_BVSTR( &bv, "accept-unsolicited" );
1265 			} else {
1266 				bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps );
1267 				bv.bv_val = c->cr_msg;
1268 			}
1269 			value_add_one( &c->rvalue_vals, &bv );
1270 			break;
1271 #endif /* SLAPD_META_CLIENT_PR */
1272 
1273 		case LDAP_BACK_CFG_DEFAULT_T:
1274 			if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
1275 				return 1;
1276 			bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget );
1277 			bv.bv_val = c->cr_msg;
1278 			value_add_one( &c->rvalue_vals, &bv );
1279 			break;
1280 
1281 		case LDAP_BACK_CFG_NETWORK_TIMEOUT:
1282 			if ( mc->mc_network_timeout == 0 ) {
1283 				return 1;
1284 			}
1285 			bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld",
1286 				mc->mc_network_timeout );
1287 			bv.bv_val = c->cr_msg;
1288 			value_add_one( &c->rvalue_vals, &bv );
1289 			break;
1290 
1291 		case LDAP_BACK_CFG_NOREFS:
1292 			c->value_int = META_BACK_CMN_NOREFS(mc);
1293 			break;
1294 
1295 		case LDAP_BACK_CFG_NOUNDEFFILTER:
1296 			c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc);
1297 			break;
1298 
1299 		case LDAP_BACK_CFG_NRETRIES:
1300 			if ( mc->mc_nretries == META_RETRY_FOREVER ) {
1301 				BER_BVSTR( &bv, "forever" );
1302 			} else if ( mc->mc_nretries == META_RETRY_NEVER ) {
1303 				BER_BVSTR( &bv, "never" );
1304 			} else {
1305 				bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d",
1306 					mc->mc_nretries );
1307 				bv.bv_val = c->cr_msg;
1308 			}
1309 			value_add_one( &c->rvalue_vals, &bv );
1310 			break;
1311 
1312 		case LDAP_BACK_CFG_QUARANTINE:
1313 			if ( !META_BACK_CMN_QUARANTINE( mc )) {
1314 				rc = 1;
1315 				break;
1316 			}
1317 			rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv );
1318 			if ( rc == 0 ) {
1319 				ber_bvarray_add( &c->rvalue_vals, &bv );
1320 			}
1321 			break;
1322 
1323 		case LDAP_BACK_CFG_REBIND:
1324 			c->value_int = META_BACK_CMN_SAVECRED(mc);
1325 			break;
1326 
1327 		case LDAP_BACK_CFG_TIMEOUT:
1328 			for ( i = 0; i < SLAP_OP_LAST; i++ ) {
1329 				if ( mc->mc_timeout[ i ] != 0 ) {
1330 					break;
1331 				}
1332 			}
1333 
1334 			if ( i == SLAP_OP_LAST ) {
1335 				return 1;
1336 			}
1337 
1338 			BER_BVZERO( &bv );
1339 			slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table );
1340 
1341 			if ( BER_BVISNULL( &bv ) ) {
1342 				return 1;
1343 			}
1344 
1345 			for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
1346 				/* count spaces */ ;
1347 
1348 			if ( i ) {
1349 				bv.bv_len -= i;
1350 				AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
1351 					bv.bv_len + 1 );
1352 			}
1353 
1354 			ber_bvarray_add( &c->rvalue_vals, &bv );
1355 			break;
1356 
1357 		case LDAP_BACK_CFG_VERSION:
1358 			if ( mc->mc_version == 0 )
1359 				return 1;
1360 			c->value_int = mc->mc_version;
1361 			break;
1362 
1363 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1364 		case LDAP_BACK_CFG_ST_REQUEST:
1365 			c->value_int = META_BACK_CMN_ST_REQUEST( mc );
1366 			break;
1367 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1368 
1369 		case LDAP_BACK_CFG_T_F:
1370 			enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv );
1371 			if ( BER_BVISNULL( &bv ) ) {
1372 				/* there's something wrong... */
1373 				assert( 0 );
1374 				rc = 1;
1375 
1376 			} else {
1377 				value_add_one( &c->rvalue_vals, &bv );
1378 			}
1379 			break;
1380 
1381 		case LDAP_BACK_CFG_TLS: {
1382 			struct berval bc = BER_BVNULL, bv2;
1383 
1384 			if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) {
1385 				rc = 1;
1386 				break;
1387 			}
1388 			enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv );
1389 			assert( !BER_BVISNULL( &bv ) );
1390 
1391 			if ( mt ) {
1392 				bindconf_tls_unparse( &mt->mt_tls, &bc );
1393 			}
1394 
1395 			if ( !BER_BVISEMPTY( &bc )) {
1396 				bv2.bv_len = bv.bv_len + bc.bv_len + 1;
1397 				bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
1398 				strcpy( bv2.bv_val, bv.bv_val );
1399 				bv2.bv_val[bv.bv_len] = ' ';
1400 				strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
1401 				ber_memfree( bc.bv_val );
1402 				ber_bvarray_add( &c->rvalue_vals, &bv2 );
1403 			} else {
1404 				value_add_one( &c->rvalue_vals, &bv );
1405 			}
1406 			} break;
1407 
1408 		/* target attrs */
1409 		case LDAP_BACK_CFG_URI: {
1410 			char *p2, *p1 = strchr( mt->mt_uri, ' ' );
1411 			bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len;
1412 			bv.bv_val = ch_malloc( bv.bv_len + 1 );
1413 			p2 = bv.bv_val;
1414 			*p2++ = '"';
1415 			if ( p1 ) {
1416 				p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri );
1417 			} else {
1418 				p2 = lutil_strcopy( p2, mt->mt_uri );
1419 			}
1420 			*p2++ = '/';
1421 			p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val );
1422 			*p2++ = '"';
1423 			if ( p1 ) {
1424 				strcpy( p2, p1 );
1425 			}
1426 			ber_bvarray_add( &c->rvalue_vals, &bv );
1427 			} break;
1428 
1429 		case LDAP_BACK_CFG_ACL_AUTHCDN:
1430 		case LDAP_BACK_CFG_ACL_PASSWD:
1431 			/* FIXME no point here, there is no code implementing
1432 			 * their features. Was this supposed to implement
1433 			 * acl-bind like back-ldap?
1434 			 */
1435 			rc = 1;
1436 			break;
1437 
1438 		case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
1439 			BerVarray	*bvp;
1440 			int		i;
1441 			struct berval	bv = BER_BVNULL;
1442 			char		buf[SLAP_TEXT_BUFLEN];
1443 
1444 			bvp = &mt->mt_idassert_authz;
1445 			if ( *bvp == NULL ) {
1446 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
1447 				{
1448 					BER_BVSTR( &bv, "*" );
1449 					value_add_one( &c->rvalue_vals, &bv );
1450 
1451 				} else {
1452 					rc = 1;
1453 				}
1454 				break;
1455 			}
1456 
1457 			for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
1458 				char *ptr;
1459 				int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
1460 				bv.bv_len = ((*bvp)[ i ]).bv_len + len;
1461 				bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
1462 				ptr = bv.bv_val;
1463 				ptr = lutil_strcopy( ptr, buf );
1464 				ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
1465 				value_add_one( &c->rvalue_vals, &bv );
1466 			}
1467 			if ( bv.bv_val ) {
1468 				ber_memfree( bv.bv_val );
1469 			}
1470 			break;
1471 		}
1472 
1473 		case LDAP_BACK_CFG_IDASSERT_BIND: {
1474 			int		i;
1475 			struct berval	bc = BER_BVNULL;
1476 			char		*ptr;
1477 
1478 			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) {
1479 				return 1;
1480 			} else {
1481 				ber_len_t	len;
1482 
1483 				switch ( mt->mt_idassert_mode ) {
1484 				case LDAP_BACK_IDASSERT_OTHERID:
1485 				case LDAP_BACK_IDASSERT_OTHERDN:
1486 					break;
1487 
1488 				default: {
1489 					struct berval	mode = BER_BVNULL;
1490 
1491 					enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode );
1492 					if ( BER_BVISNULL( &mode ) ) {
1493 						/* there's something wrong... */
1494 						assert( 0 );
1495 						rc = 1;
1496 
1497 					} else {
1498 						bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
1499 						bv.bv_val = ch_malloc( bv.bv_len + 1 );
1500 
1501 						ptr = lutil_strcopy( bv.bv_val, "mode=" );
1502 						ptr = lutil_strcopy( ptr, mode.bv_val );
1503 					}
1504 					break;
1505 				}
1506 				}
1507 
1508 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
1509 					len = bv.bv_len + STRLENOF( "authz=native" );
1510 
1511 					if ( !BER_BVISEMPTY( &bv ) ) {
1512 						len += STRLENOF( " " );
1513 					}
1514 
1515 					bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1516 
1517 					ptr = &bv.bv_val[ bv.bv_len ];
1518 
1519 					if ( !BER_BVISEMPTY( &bv ) ) {
1520 						ptr = lutil_strcopy( ptr, " " );
1521 					}
1522 
1523 					(void)lutil_strcopy( ptr, "authz=native" );
1524 				}
1525 
1526 				len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
1527 				/* flags */
1528 				if ( !BER_BVISEMPTY( &bv ) ) {
1529 					len += STRLENOF( " " );
1530 				}
1531 
1532 				bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1533 
1534 				ptr = &bv.bv_val[ bv.bv_len ];
1535 
1536 				if ( !BER_BVISEMPTY( &bv ) ) {
1537 					ptr = lutil_strcopy( ptr, " " );
1538 				}
1539 
1540 				ptr = lutil_strcopy( ptr, "flags=" );
1541 
1542 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1543 					ptr = lutil_strcopy( ptr, "prescriptive" );
1544 				} else {
1545 					ptr = lutil_strcopy( ptr, "non-prescriptive" );
1546 				}
1547 
1548 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
1549 					ptr = lutil_strcopy( ptr, ",override" );
1550 				}
1551 
1552 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
1553 					ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
1554 
1555 				} else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
1556 					ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
1557 				}
1558 
1559 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
1560 					ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
1561 
1562 				} else {
1563 					ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
1564 				}
1565 
1566 #ifdef SLAP_AUTH_DN
1567 				switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
1568 				case LDAP_BACK_AUTH_DN_AUTHZID:
1569 					ptr = lutil_strcopy( ptr, ",dn-authzid" );
1570 					break;
1571 
1572 				case LDAP_BACK_AUTH_DN_WHOAMI:
1573 					ptr = lutil_strcopy( ptr, ",dn-whoami" );
1574 					break;
1575 
1576 				default:
1577 #if 0 /* implicit */
1578 					ptr = lutil_strcopy( ptr, ",dn-none" );
1579 #endif
1580 					break;
1581 				}
1582 #endif
1583 
1584 				bv.bv_len = ( ptr - bv.bv_val );
1585 				/* end-of-flags */
1586 			}
1587 
1588 			bindconf_unparse( &mt->mt_idassert.si_bc, &bc );
1589 
1590 			if ( !BER_BVISNULL( &bv ) ) {
1591 				ber_len_t	len = bv.bv_len + bc.bv_len;
1592 
1593 				bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
1594 
1595 				assert( bc.bv_val[ 0 ] == ' ' );
1596 
1597 				ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
1598 				free( bc.bv_val );
1599 				bv.bv_len = ptr - bv.bv_val;
1600 
1601 			} else {
1602 				for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
1603 					/* count spaces */ ;
1604 
1605 				if ( i ) {
1606 					bc.bv_len -= i;
1607 					AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
1608 				}
1609 
1610 				bv = bc;
1611 			}
1612 
1613 			ber_bvarray_add( &c->rvalue_vals, &bv );
1614 
1615 			break;
1616 		}
1617 
1618 		case LDAP_BACK_CFG_SUFFIXM:	/* unused */
1619 		case LDAP_BACK_CFG_REWRITE:
1620 			if ( mt->mt_rwmap.rwm_bva_rewrite == NULL ) {
1621 				rc = 1;
1622 			} else {
1623 				rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_rewrite, &c->rvalue_vals );
1624 			}
1625 			break;
1626 
1627 		case LDAP_BACK_CFG_MAP:
1628 			if ( mt->mt_rwmap.rwm_bva_map == NULL ) {
1629 				rc = 1;
1630 			} else {
1631 				rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_map, &c->rvalue_vals );
1632 			}
1633 			break;
1634 
1635 		case LDAP_BACK_CFG_SUBTREE_EX:
1636 		case LDAP_BACK_CFG_SUBTREE_IN:
1637 			rc = meta_subtree_unparse( c, mt );
1638 			break;
1639 
1640 		case LDAP_BACK_CFG_FILTER:
1641 			if ( mt->mt_filter == NULL ) {
1642 				rc = 1;
1643 			} else {
1644 				metafilter_t *mf;
1645 				for ( mf = mt->mt_filter; mf; mf = mf->mf_next )
1646 					value_add_one( &c->rvalue_vals, &mf->mf_regex_pattern );
1647 			}
1648 			break;
1649 
1650 		/* replaced by idassert */
1651 		case LDAP_BACK_CFG_PSEUDOROOTDN:
1652 		case LDAP_BACK_CFG_PSEUDOROOTPW:
1653 			rc = 1;
1654 			break;
1655 
1656 		case LDAP_BACK_CFG_KEEPALIVE: {
1657 				struct berval bv;
1658 				char buf[AC_LINE_MAX];
1659 				bv.bv_len = AC_LINE_MAX;
1660 				bv.bv_val = &buf[0];
1661 				slap_keepalive_parse(&bv, &mt->mt_tls.sb_keepalive, 0, 0, 1);
1662 				value_add_one( &c->rvalue_vals, &bv );
1663 				break;
1664 			}
1665 
1666 		default:
1667 			rc = 1;
1668 		}
1669 		return rc;
1670 	} else if ( c->op == LDAP_MOD_DELETE ) {
1671 		switch( c->type ) {
1672 		/* Base attrs */
1673 		case LDAP_BACK_CFG_CONN_TTL:
1674 			mi->mi_conn_ttl = 0;
1675 			break;
1676 
1677 		case LDAP_BACK_CFG_DNCACHE_TTL:
1678 			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
1679 			break;
1680 
1681 		case LDAP_BACK_CFG_IDLE_TIMEOUT:
1682 			mi->mi_idle_timeout = 0;
1683 			break;
1684 
1685 		case LDAP_BACK_CFG_ONERR:
1686 			mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
1687 			break;
1688 
1689 		case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
1690 			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
1691 			break;
1692 
1693 		case LDAP_BACK_CFG_SINGLECONN:
1694 			mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
1695 			break;
1696 
1697 		case LDAP_BACK_CFG_USETEMP:
1698 			mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
1699 			break;
1700 
1701 		case LDAP_BACK_CFG_CONNPOOLMAX:
1702 			mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
1703 			break;
1704 
1705 		/* common attrs */
1706 		case LDAP_BACK_CFG_BIND_TIMEOUT:
1707 			mc->mc_bind_timeout.tv_sec = 0;
1708 			mc->mc_bind_timeout.tv_usec = 0;
1709 			break;
1710 
1711 		case LDAP_BACK_CFG_CANCEL:
1712 			mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
1713 			break;
1714 
1715 		case LDAP_BACK_CFG_CHASE:
1716 			mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
1717 			break;
1718 
1719 #ifdef SLAPD_META_CLIENT_PR
1720 		case LDAP_BACK_CFG_CLIENT_PR:
1721 			mc->mc_ps = META_CLIENT_PR_DISABLE;
1722 			break;
1723 #endif /* SLAPD_META_CLIENT_PR */
1724 
1725 		case LDAP_BACK_CFG_DEFAULT_T:
1726 			mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
1727 			break;
1728 
1729 		case LDAP_BACK_CFG_NETWORK_TIMEOUT:
1730 			mc->mc_network_timeout = 0;
1731 			break;
1732 
1733 		case LDAP_BACK_CFG_NOREFS:
1734 			mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
1735 			break;
1736 
1737 		case LDAP_BACK_CFG_NOUNDEFFILTER:
1738 			mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
1739 			break;
1740 
1741 		case LDAP_BACK_CFG_NRETRIES:
1742 			mc->mc_nretries = META_RETRY_DEFAULT;
1743 			break;
1744 
1745 		case LDAP_BACK_CFG_QUARANTINE:
1746 			if ( META_BACK_CMN_QUARANTINE( mc )) {
1747 				mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine );
1748 				mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE;
1749 				if ( mc == &mt->mt_mc ) {
1750 					ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
1751 					mt->mt_isquarantined = 0;
1752 				}
1753 			}
1754 			break;
1755 
1756 		case LDAP_BACK_CFG_REBIND:
1757 			mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
1758 			break;
1759 
1760 		case LDAP_BACK_CFG_TIMEOUT:
1761 			for ( i = 0; i < SLAP_OP_LAST; i++ ) {
1762 				mc->mc_timeout[ i ] = 0;
1763 			}
1764 			break;
1765 
1766 		case LDAP_BACK_CFG_VERSION:
1767 			mc->mc_version = 0;
1768 			break;
1769 
1770 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1771 		case LDAP_BACK_CFG_ST_REQUEST:
1772 			mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
1773 			break;
1774 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1775 
1776 		case LDAP_BACK_CFG_T_F:
1777 			mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
1778 			break;
1779 
1780 		case LDAP_BACK_CFG_TLS:
1781 			mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
1782 			if ( mt )
1783 				bindconf_free( &mt->mt_tls );
1784 			break;
1785 
1786 		/* target attrs */
1787 		case LDAP_BACK_CFG_URI:
1788 			if ( mt->mt_uri ) {
1789 				ch_free( mt->mt_uri );
1790 				mt->mt_uri = NULL;
1791 			}
1792 			/* FIXME: should have a way to close all cached
1793 			 * connections associated with this target.
1794 			 */
1795 			break;
1796 
1797 		case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
1798 			BerVarray *bvp;
1799 
1800 			bvp = &mt->mt_idassert_authz;
1801 			if ( c->valx < 0 ) {
1802 				if ( *bvp != NULL ) {
1803 					ber_bvarray_free( *bvp );
1804 					*bvp = NULL;
1805 				}
1806 
1807 			} else {
1808 				if ( *bvp == NULL ) {
1809 					rc = 1;
1810 					break;
1811 				}
1812 
1813 				for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
1814 					;
1815 
1816 				if ( i >= c->valx ) {
1817 					rc = 1;
1818 					break;
1819 				}
1820 				ber_memfree( ((*bvp)[ c->valx ]).bv_val );
1821 				for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
1822 					(*bvp)[ i ] = (*bvp)[ i + 1 ];
1823 				}
1824 				BER_BVZERO( &((*bvp)[ i ]) );
1825 			}
1826 			} break;
1827 
1828 		case LDAP_BACK_CFG_IDASSERT_BIND:
1829 			bindconf_free( &mt->mt_idassert.si_bc );
1830 			memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
1831 			break;
1832 
1833 		case LDAP_BACK_CFG_SUFFIXM:	/* unused */
1834 		case LDAP_BACK_CFG_REWRITE:
1835 		{
1836 			if ( c->valx >= 0 ) {
1837 				int i;
1838 
1839 				for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ );
1840 
1841 				if ( c->valx >= i ) {
1842 					rc = 1;
1843 					break;
1844 				}
1845 
1846 				ber_memfree( mt->mt_rwmap.rwm_bva_rewrite[ c->valx ].bv_val );
1847 				for ( i = c->valx; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ] ); i++ )
1848 				{
1849 					mt->mt_rwmap.rwm_bva_rewrite[ i ] = mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ];
1850 				}
1851 				BER_BVZERO( &mt->mt_rwmap.rwm_bva_rewrite[ i ] );
1852 
1853 				rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
1854 				assert( mt->mt_rwmap.rwm_rw == NULL );
1855 
1856 				rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
1857 
1858 				for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ )
1859 				{
1860 					ConfigArgs ca = { 0 };
1861 
1862 					ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
1863 					init_config_argv( &ca );
1864 					config_parse_ldif( &ca );
1865 
1866 					if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
1867 						rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
1868 					} else {
1869 						rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
1870 								    c->fname, c->lineno, ca.argc, ca.argv );
1871 					}
1872 
1873 
1874 					ch_free( ca.tline );
1875 					ch_free( ca.argv );
1876 
1877 					assert( rc == 0 );
1878 				}
1879 
1880 			} else if ( mt->mt_rwmap.rwm_rw != NULL ) {
1881 				if ( mt->mt_rwmap.rwm_bva_rewrite ) {
1882 					ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
1883 					mt->mt_rwmap.rwm_bva_rewrite = NULL;
1884 				}
1885 				if ( mt->mt_rwmap.rwm_rw )
1886 					rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
1887 
1888 				meta_rwi_init( &mt->mt_rwmap.rwm_rw );
1889 			}
1890 		}
1891 		break;
1892 
1893 		case LDAP_BACK_CFG_MAP:
1894 			if ( mt->mt_rwmap.rwm_bva_map ) {
1895 				ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
1896 				mt->mt_rwmap.rwm_bva_map = NULL;
1897 			}
1898 			meta_back_map_free( &mt->mt_rwmap.rwm_oc );
1899 			meta_back_map_free( &mt->mt_rwmap.rwm_at );
1900 			mt->mt_rwmap.rwm_oc.drop_missing = 0;
1901 			mt->mt_rwmap.rwm_at.drop_missing = 0;
1902 			break;
1903 
1904 		case LDAP_BACK_CFG_SUBTREE_EX:
1905 		case LDAP_BACK_CFG_SUBTREE_IN:
1906 			/* can only be one of exclude or include */
1907 			if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
1908 				rc = 1;
1909 				break;
1910 			}
1911 			if ( c->valx < 0 ) {
1912 				meta_subtree_destroy( mt->mt_subtree );
1913 				mt->mt_subtree = NULL;
1914 			} else {
1915 				metasubtree_t *ms, **mprev;
1916 				for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
1917 					if ( i == c->valx ) {
1918 						*mprev = ms->ms_next;
1919 						meta_subtree_free( ms );
1920 						break;
1921 					}
1922 					i++;
1923 					mprev = &ms->ms_next;
1924 				}
1925 				if ( i != c->valx )
1926 					rc = 1;
1927 			}
1928 			break;
1929 
1930 		case LDAP_BACK_CFG_FILTER:
1931 			if ( c->valx < 0 ) {
1932 				meta_filter_destroy( mt->mt_filter );
1933 				mt->mt_filter = NULL;
1934 			} else {
1935 				metafilter_t *mf, **mprev;
1936 				for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) {
1937 					if ( i == c->valx ) {
1938 						*mprev = mf->mf_next;
1939 						meta_filter_free( mf );
1940 						break;
1941 					}
1942 					i++;
1943 					mprev = &mf->mf_next;
1944 				}
1945 				if ( i != c->valx )
1946 					rc = 1;
1947 			}
1948 			break;
1949 
1950 		case LDAP_BACK_CFG_KEEPALIVE:
1951 			mt->mt_tls.sb_keepalive.sk_idle = 0;
1952 			mt->mt_tls.sb_keepalive.sk_probes = 0;
1953 			mt->mt_tls.sb_keepalive.sk_interval = 0;
1954 			break;
1955 
1956 		default:
1957 			rc = 1;
1958 			break;
1959 		}
1960 
1961 		return rc;
1962 	}
1963 
1964 	if ( c->op == SLAP_CONFIG_ADD ) {
1965 		if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
1966 			/* exclude CFG_URI from this check */
1967 			if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
1968 				if ( !mi->mi_ntargets ) {
1969 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1970 						"need \"uri\" directive first" );
1971 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1972 					return 1;
1973 				}
1974 			}
1975 			if ( mi->mi_ntargets ) {
1976 				mt = mi->mi_targets[ mi->mi_ntargets-1 ];
1977 				mc = &mt->mt_mc;
1978 			} else {
1979 				mt = NULL;
1980 				mc = &mi->mi_mc;
1981 			}
1982 		}
1983 	} else {
1984 		if ( c->table == Cft_Database ) {
1985 			mt = NULL;
1986 			mc = &mi->mi_mc;
1987 		} else {
1988 			mt = c->ca_private;
1989 			if ( mt )
1990 				mc = &mt->mt_mc;
1991 			else
1992 				mc = NULL;
1993 		}
1994 	}
1995 
1996 	switch( c->type ) {
1997 	case LDAP_BACK_CFG_URI: {
1998 		LDAPURLDesc 	*ludp;
1999 		struct berval	dn;
2000 		int		j;
2001 
2002 		char		**uris = NULL;
2003 
2004 		if ( c->be->be_nsuffix == NULL ) {
2005 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2006 				"the suffix must be defined before any target" );
2007 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2008 			return 1;
2009 		}
2010 
2011 		i = mi->mi_ntargets++;
2012 
2013 		mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
2014 			sizeof( metatarget_t * ) * mi->mi_ntargets );
2015 		if ( mi->mi_targets == NULL ) {
2016 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2017 				"out of memory while storing server name"
2018 				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2019 				c->argv[0] );
2020 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2021 			return 1;
2022 		}
2023 
2024 		if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
2025 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2026 				"unable to init server"
2027 				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2028 				c->argv[0] );
2029 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2030 			return 1;
2031 		}
2032 
2033 		mt = mi->mi_targets[ i ];
2034 
2035 		mt->mt_rebind_f = mi->mi_rebind_f;
2036 		mt->mt_urllist_f = mi->mi_urllist_f;
2037 		mt->mt_urllist_p = mt;
2038 
2039 		if ( META_BACK_QUARANTINE( mi ) ) {
2040 			ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
2041 		}
2042 		mt->mt_mc = mi->mi_mc;
2043 
2044 		for ( j = 1; j < c->argc; j++ ) {
2045 			char	**tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
2046 
2047 			if ( tmpuris == NULL ) {
2048 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2049 					"unable to parse URIs #%d"
2050 					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2051 					j-1, c->argv[0] );
2052 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2053 				return 1;
2054 			}
2055 
2056 			if ( j == 1 ) {
2057 				uris = tmpuris;
2058 
2059 			} else {
2060 				ldap_charray_merge( &uris, tmpuris );
2061 				ldap_charray_free( tmpuris );
2062 			}
2063 		}
2064 
2065 		for ( j = 0; uris[ j ] != NULL; j++ ) {
2066 			char *tmpuri = NULL;
2067 
2068 			/*
2069 			 * uri MUST be legal!
2070 			 */
2071 			if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
2072 					LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
2073 				|| ludp->lud_next != NULL )
2074 			{
2075 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2076 					"unable to parse URI #%d"
2077 					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2078 					j-1, c->argv[0] );
2079 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2080 				ldap_charray_free( uris );
2081 				return 1;
2082 			}
2083 
2084 			if ( j == 0 ) {
2085 
2086 				/*
2087 				 * uri MUST have the <dn> part!
2088 				 */
2089 				if ( ludp->lud_dn == NULL ) {
2090 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2091 						"missing <naming context> "
2092 						" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2093 						c->argv[0] );
2094 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2095 					ldap_free_urllist( ludp );
2096 					ldap_charray_free( uris );
2097 					return 1;
2098 				}
2099 
2100 				/*
2101 				 * copies and stores uri and suffix
2102 				 */
2103 				ber_str2bv( ludp->lud_dn, 0, 0, &dn );
2104 				rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
2105 					&mt->mt_nsuffix, NULL );
2106 				if ( rc != LDAP_SUCCESS ) {
2107 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2108 						"target DN is invalid \"%s\"",
2109 						c->argv[1] );
2110 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2111 					ldap_free_urllist( ludp );
2112 					ldap_charray_free( uris );
2113 					return( 1 );
2114 				}
2115 
2116 				ludp->lud_dn[ 0 ] = '\0';
2117 
2118 				switch ( ludp->lud_scope ) {
2119 				case LDAP_SCOPE_DEFAULT:
2120 					mt->mt_scope = LDAP_SCOPE_SUBTREE;
2121 					break;
2122 
2123 				case LDAP_SCOPE_SUBTREE:
2124 				case LDAP_SCOPE_SUBORDINATE:
2125 					mt->mt_scope = ludp->lud_scope;
2126 					break;
2127 
2128 				default:
2129 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2130 						"invalid scope for target \"%s\"",
2131 						c->argv[1] );
2132 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2133 					ldap_free_urllist( ludp );
2134 					ldap_charray_free( uris );
2135 					return( 1 );
2136 				}
2137 
2138 			} else {
2139 				/* check all, to apply the scope check on the first one */
2140 				if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
2141 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2142 						"multiple URIs must have no DN part" );
2143 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2144 					ldap_free_urllist( ludp );
2145 					ldap_charray_free( uris );
2146 					return( 1 );
2147 
2148 				}
2149 			}
2150 
2151 			tmpuri = ldap_url_list2urls( ludp );
2152 			ldap_free_urllist( ludp );
2153 			if ( tmpuri == NULL ) {
2154 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
2155 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2156 				ldap_charray_free( uris );
2157 				return( 1 );
2158 			}
2159 			ldap_memfree( uris[ j ] );
2160 			uris[ j ] = tmpuri;
2161 		}
2162 
2163 		mt->mt_uri = ldap_charray2str( uris, " " );
2164 		ldap_charray_free( uris );
2165 		if ( mt->mt_uri == NULL) {
2166 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
2167 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2168 			return( 1 );
2169 		}
2170 
2171 		/*
2172 		 * uri MUST be a branch of suffix!
2173 		 */
2174 		for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
2175 			if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
2176 				break;
2177 			}
2178 		}
2179 
2180 		if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
2181 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2182 				"<naming context> of URI must be within the naming context of this database." );
2183 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2184 			return 1;
2185 		}
2186 		c->ca_private = mt;
2187 		c->cleanup = meta_cf_cleanup;
2188 	} break;
2189 	case LDAP_BACK_CFG_SUBTREE_EX:
2190 	case LDAP_BACK_CFG_SUBTREE_IN:
2191 	/* subtree-exclude */
2192 		if ( meta_subtree_config( mt, c )) {
2193 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2194 			return 1;
2195 		}
2196 		break;
2197 
2198 	case LDAP_BACK_CFG_FILTER: {
2199 		metafilter_t *mf, **m2;
2200 		mf = ch_calloc( 1, sizeof( metafilter_t ));
2201 		rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED );
2202 		if ( rc ) {
2203 			char regerr[ SLAP_TEXT_BUFLEN ];
2204 			regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) );
2205 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2206 				"regular expression \"%s\" bad because of %s",
2207 				c->argv[1], regerr );
2208 			ch_free( mf );
2209 			return 1;
2210 		}
2211 		ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern );
2212 		for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next )
2213 			;
2214 		*m2 = mf;
2215 	} break;
2216 
2217 	case LDAP_BACK_CFG_DEFAULT_T:
2218 	/* default target directive */
2219 		i = mi->mi_ntargets - 1;
2220 
2221 		if ( c->argc == 1 ) {
2222  			if ( i < 0 ) {
2223 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2224 					"\"%s\" alone must be inside a \"uri\" directive",
2225 					c->argv[0] );
2226 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2227 				return 1;
2228 			}
2229 			mi->mi_defaulttarget = i;
2230 
2231 		} else {
2232 			if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
2233 				if ( i >= 0 ) {
2234 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2235 						"\"%s none\" should go before uri definitions",
2236 						c->argv[0] );
2237 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2238 				}
2239 				mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
2240 
2241 			} else {
2242 
2243 				if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
2244 					|| mi->mi_defaulttarget < 0
2245 					|| mi->mi_defaulttarget >= i - 1 )
2246 				{
2247 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2248 						"illegal target number %d",
2249 						mi->mi_defaulttarget );
2250 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2251 					return 1;
2252 				}
2253 			}
2254 		}
2255 		break;
2256 
2257 	case LDAP_BACK_CFG_DNCACHE_TTL:
2258 	/* ttl of dn cache */
2259 		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2260 			mi->mi_cache.ttl = META_DNCACHE_FOREVER;
2261 
2262 		} else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
2263 			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
2264 
2265 		} else {
2266 			unsigned long	t;
2267 
2268 			if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
2269 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2270 					"unable to parse dncache ttl \"%s\"",
2271 					c->argv[ 1 ] );
2272 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2273 				return 1;
2274 			}
2275 			mi->mi_cache.ttl = (time_t)t;
2276 		}
2277 		break;
2278 
2279 	case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
2280 	/* network timeout when connecting to ldap servers */
2281 		unsigned long t;
2282 
2283 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2284 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2285 				"unable to parse network timeout \"%s\"",
2286 				c->argv[ 1 ] );
2287 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2288 			return 1;
2289 		}
2290 		mc->mc_network_timeout = (time_t)t;
2291 		} break;
2292 
2293 	case LDAP_BACK_CFG_IDLE_TIMEOUT: {
2294 	/* idle timeout when connecting to ldap servers */
2295 		unsigned long	t;
2296 
2297 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2298 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2299 				"unable to parse idle timeout \"%s\"",
2300 				c->argv[ 1 ] );
2301 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2302 			return 1;
2303 
2304 		}
2305 		mi->mi_idle_timeout = (time_t)t;
2306 		} break;
2307 
2308 	case LDAP_BACK_CFG_CONN_TTL: {
2309 	/* conn ttl */
2310 		unsigned long	t;
2311 
2312 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2313 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2314 				"unable to parse conn ttl \"%s\"",
2315 				c->argv[ 1 ] );
2316 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2317 			return 1;
2318 
2319 		}
2320 		mi->mi_conn_ttl = (time_t)t;
2321 		} break;
2322 
2323 	case LDAP_BACK_CFG_BIND_TIMEOUT:
2324 	/* bind timeout when connecting to ldap servers */
2325 		mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
2326 		mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
2327 		break;
2328 
2329 	case LDAP_BACK_CFG_ACL_AUTHCDN:
2330 	/* name to use for meta_back_group */
2331 		if ( strcasecmp( c->argv[ 0 ], "binddn" ) == 0 ) {
2332 			Debug( LDAP_DEBUG_ANY, "%s: "
2333 				"\"binddn\" statement is deprecated; "
2334 				"use \"acl-authcDN\" instead\n",
2335 				c->log, 0, 0 );
2336 			/* FIXME: some day we'll need to throw an error */
2337 		}
2338 
2339 		ber_memfree_x( c->value_dn.bv_val, NULL );
2340 		mt->mt_binddn = c->value_ndn;
2341 		BER_BVZERO( &c->value_dn );
2342 		BER_BVZERO( &c->value_ndn );
2343 		break;
2344 
2345 	case LDAP_BACK_CFG_ACL_PASSWD:
2346 	/* password to use for meta_back_group */
2347 		if ( strcasecmp( c->argv[ 0 ], "bindpw" ) == 0 ) {
2348 			Debug( LDAP_DEBUG_ANY, "%s "
2349 				"\"bindpw\" statement is deprecated; "
2350 				"use \"acl-passwd\" instead\n",
2351 				c->log, 0, 0 );
2352 			/* FIXME: some day we'll need to throw an error */
2353 		}
2354 
2355 		ber_str2bv( c->argv[ 1 ], 0L, 1, &mt->mt_bindpw );
2356 		break;
2357 
2358 	case LDAP_BACK_CFG_REBIND:
2359 	/* save bind creds for referral rebinds? */
2360 		if ( c->argc == 1 || c->value_int ) {
2361 			mc->mc_flags |= LDAP_BACK_F_SAVECRED;
2362 		} else {
2363 			mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
2364 		}
2365 		break;
2366 
2367 	case LDAP_BACK_CFG_CHASE:
2368 		if ( c->argc == 1 || c->value_int ) {
2369 			mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS;
2370 		} else {
2371 			mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
2372 		}
2373 		break;
2374 
2375 	case LDAP_BACK_CFG_TLS:
2376 		i = verb_to_mask( c->argv[1], tls_mode );
2377 		if ( BER_BVISNULL( &tls_mode[i].word ) ) {
2378 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2379 				"%s unknown argument \"%s\"",
2380 				c->argv[0], c->argv[1] );
2381 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2382 			return 1;
2383 		}
2384 		mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
2385 		mc->mc_flags |= tls_mode[i].mask;
2386 
2387 		if ( c->argc > 2 ) {
2388 			if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
2389 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2390 					"need \"uri\" directive first" );
2391 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2392 				return 1;
2393 			}
2394 
2395 			for ( i = 2; i < c->argc; i++ ) {
2396 				if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
2397 					return 1;
2398 			}
2399 			bindconf_tls_defaults( &mt->mt_tls );
2400 		}
2401 		break;
2402 
2403 	case LDAP_BACK_CFG_T_F:
2404 		i = verb_to_mask( c->argv[1], t_f_mode );
2405 		if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
2406 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2407 				"%s unknown argument \"%s\"",
2408 				c->argv[0], c->argv[1] );
2409 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2410 			return 1;
2411 		}
2412 		mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
2413 		mc->mc_flags |= t_f_mode[i].mask;
2414 		break;
2415 
2416 	case LDAP_BACK_CFG_ONERR:
2417 	/* onerr? */
2418 		i = verb_to_mask( c->argv[1], onerr_mode );
2419 		if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
2420 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2421 				"%s unknown argument \"%s\"",
2422 				c->argv[0], c->argv[1] );
2423 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2424 			return 1;
2425 		}
2426 		mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
2427 		mi->mi_flags |= onerr_mode[i].mask;
2428 		break;
2429 
2430 	case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
2431 	/* bind-defer? */
2432 		if ( c->argc == 1 || c->value_int ) {
2433 			mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
2434 		} else {
2435 			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
2436 		}
2437 		break;
2438 
2439 	case LDAP_BACK_CFG_SINGLECONN:
2440 	/* single-conn? */
2441 		if ( mi->mi_ntargets > 0 ) {
2442 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2443 				"\"%s\" must appear before target definitions",
2444 				c->argv[0] );
2445 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2446 			return( 1 );
2447 		}
2448 		if ( c->value_int ) {
2449 			mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
2450 		} else {
2451 			mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
2452 		}
2453 		break;
2454 
2455 	case LDAP_BACK_CFG_USETEMP:
2456 	/* use-temporaries? */
2457 		if ( mi->mi_ntargets > 0 ) {
2458 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2459 				"\"%s\" must appear before target definitions",
2460 				c->argv[0] );
2461 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2462 			return( 1 );
2463 		}
2464 		if ( c->value_int ) {
2465 			mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
2466 		} else {
2467 			mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
2468 		}
2469 		break;
2470 
2471 	case LDAP_BACK_CFG_CONNPOOLMAX:
2472 	/* privileged connections pool max size ? */
2473 		if ( mi->mi_ntargets > 0 ) {
2474 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2475 				"\"%s\" must appear before target definitions",
2476 				c->argv[0] );
2477 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2478 			return( 1 );
2479 		}
2480 
2481 		if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
2482 			|| c->value_int > LDAP_BACK_CONN_PRIV_MAX )
2483 		{
2484 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2485 				"invalid max size " "of privileged "
2486 				"connections pool \"%s\" "
2487 				"in \"conn-pool-max <n> "
2488 				"(must be between %d and %d)\"",
2489 				c->argv[ 1 ],
2490 				LDAP_BACK_CONN_PRIV_MIN,
2491 				LDAP_BACK_CONN_PRIV_MAX );
2492 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2493 			return 1;
2494 		}
2495 		mi->mi_conn_priv_max = c->value_int;
2496 		break;
2497 
2498 	case LDAP_BACK_CFG_CANCEL:
2499 		i = verb_to_mask( c->argv[1], cancel_mode );
2500 		if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
2501 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2502 				"%s unknown argument \"%s\"",
2503 				c->argv[0], c->argv[1] );
2504 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2505 			return 1;
2506 		}
2507 		mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
2508 		mc->mc_flags |= cancel_mode[i].mask;
2509 		break;
2510 
2511 	case LDAP_BACK_CFG_TIMEOUT:
2512 		for ( i = 1; i < c->argc; i++ ) {
2513 			if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
2514 				int		j;
2515 				unsigned	u;
2516 
2517 				if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
2518 					snprintf( c->cr_msg, sizeof( c->cr_msg),
2519 						"unable to parse timeout \"%s\"",
2520 						c->argv[ i ] );
2521 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2522 					return 1;
2523 				}
2524 
2525 				for ( j = 0; j < SLAP_OP_LAST; j++ ) {
2526 					mc->mc_timeout[ j ] = u;
2527 				}
2528 
2529 				continue;
2530 			}
2531 
2532 			if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) {
2533 				snprintf( c->cr_msg, sizeof( c->cr_msg),
2534 					"unable to parse timeout \"%s\"",
2535 					c->argv[ i ] );
2536 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2537 				return 1;
2538 			}
2539 		}
2540 		break;
2541 
2542 	case LDAP_BACK_CFG_PSEUDOROOTDN:
2543 	/* name to use as pseudo-root dn */
2544 		/*
2545 		 * exact replacement:
2546 		 *
2547 
2548 idassert-bind	bindmethod=simple
2549 		binddn=<pseudorootdn>
2550 		credentials=<pseudorootpw>
2551 		mode=none
2552 		flags=non-prescriptive
2553 idassert-authzFrom	"dn:<rootdn>"
2554 
2555 		 * so that only when authc'd as <rootdn> the proxying occurs
2556 		 * rebinding as the <pseudorootdn> without proxyAuthz.
2557 		 */
2558 
2559 		Debug( LDAP_DEBUG_ANY,
2560 			"%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
2561 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
2562 			c->log, 0, 0 );
2563 
2564 		{
2565 			char	binddn[ SLAP_TEXT_BUFLEN ];
2566 			char	*cargv[] = {
2567 				"idassert-bind",
2568 				"bindmethod=simple",
2569 				NULL,
2570 				"mode=none",
2571 				"flags=non-prescriptive",
2572 				NULL
2573 			};
2574 			char **oargv;
2575 			int oargc;
2576 			int	cargc = 5;
2577 			int	rc;
2578 
2579 
2580 			if ( BER_BVISNULL( &c->be->be_rootndn ) ) {
2581 				Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
2582 					c->log, 0, 0 );
2583 				return 1;
2584 			}
2585 
2586 			if ( sizeof( binddn ) <= (unsigned) snprintf( binddn,
2587 					sizeof( binddn ), "binddn=%s", c->argv[ 1 ] ))
2588 			{
2589 				Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootdn\" too long.\n",
2590 					c->log, 0, 0 );
2591 				return 1;
2592 			}
2593 			cargv[ 2 ] = binddn;
2594 
2595 			oargv = c->argv;
2596 			oargc = c->argc;
2597 			c->argv = cargv;
2598 			c->argc = cargc;
2599 			rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
2600 			c->argv = oargv;
2601 			c->argc = oargc;
2602 			if ( rc == 0 ) {
2603 				struct berval	bv;
2604 
2605 				if ( mt->mt_idassert_authz != NULL ) {
2606 					Debug( LDAP_DEBUG_ANY, "%s: \"idassert-authzFrom\" already defined (discarded).\n",
2607 						c->log, 0, 0 );
2608 					ber_bvarray_free( mt->mt_idassert_authz );
2609 					mt->mt_idassert_authz = NULL;
2610 				}
2611 
2612 				assert( !BER_BVISNULL( &mt->mt_idassert_authcDN ) );
2613 
2614 				bv.bv_len = STRLENOF( "dn:" ) + c->be->be_rootndn.bv_len;
2615 				bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2616 				AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
2617 				AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], c->be->be_rootndn.bv_val, c->be->be_rootndn.bv_len + 1 );
2618 
2619 				ber_bvarray_add( &mt->mt_idassert_authz, &bv );
2620 			}
2621 
2622 			return rc;
2623 		}
2624 		break;
2625 
2626 	case LDAP_BACK_CFG_PSEUDOROOTPW:
2627 	/* password to use as pseudo-root */
2628 		Debug( LDAP_DEBUG_ANY,
2629 			"%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
2630 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
2631 			c->log, 0, 0 );
2632 
2633 		if ( BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
2634 			Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
2635 				c->log, 0, 0 );
2636 			return 1;
2637 		}
2638 
2639 		if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
2640 			memset( mt->mt_idassert_passwd.bv_val, 0,
2641 				mt->mt_idassert_passwd.bv_len );
2642 			ber_memfree( mt->mt_idassert_passwd.bv_val );
2643 		}
2644 		ber_str2bv( c->argv[ 1 ], 0, 1, &mt->mt_idassert_passwd );
2645 		break;
2646 
2647 	case LDAP_BACK_CFG_IDASSERT_BIND:
2648 	/* idassert-bind */
2649 		rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
2650 		break;
2651 
2652 	case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
2653 	/* idassert-authzFrom */
2654 		rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert );
2655 		break;
2656 
2657 	case LDAP_BACK_CFG_QUARANTINE:
2658 	/* quarantine */
2659 		if ( META_BACK_CMN_QUARANTINE( mc ) )
2660 		{
2661 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2662 				"quarantine already defined" );
2663 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2664 			return 1;
2665 		}
2666 
2667 		if ( mt ) {
2668 			mc->mc_quarantine.ri_interval = NULL;
2669 			mc->mc_quarantine.ri_num = NULL;
2670 			if ( !META_BACK_QUARANTINE( mi ) ) {
2671 				ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
2672 			}
2673 		}
2674 
2675 		if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) {
2676 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2677 			return 1;
2678 		}
2679 
2680 		mc->mc_flags |= LDAP_BACK_F_QUARANTINE;
2681 		break;
2682 
2683 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
2684 	case LDAP_BACK_CFG_ST_REQUEST:
2685 	/* session tracking request */
2686 		if ( c->value_int ) {
2687 			mc->mc_flags |= LDAP_BACK_F_ST_REQUEST;
2688 		} else {
2689 			mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
2690 		}
2691 		break;
2692 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
2693 
2694 	case LDAP_BACK_CFG_SUFFIXM:	/* FALLTHRU */
2695 	case LDAP_BACK_CFG_REWRITE: {
2696 	/* rewrite stuff ... */
2697 		ConfigArgs ca = { 0 };
2698 		char *line, **argv;
2699 		struct rewrite_info *rwi;
2700 		int cnt = 0, argc, ix = c->valx;
2701 
2702 		if ( mt->mt_rwmap.rwm_bva_rewrite ) {
2703 			for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ )
2704 				/* count */ ;
2705 		}
2706 
2707 		if ( ix >= cnt || ix < 0 ) {
2708 			ix = cnt;
2709 		} else {
2710 			rwi = mt->mt_rwmap.rwm_rw;
2711 
2712 			mt->mt_rwmap.rwm_rw = NULL;
2713 			rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
2714 
2715 			/* re-parse all rewrite rules, up to the one
2716 			 * that needs to be added */
2717 			ca.be = c->be;
2718 			ca.fname = c->fname;
2719 			ca.lineno = c->lineno;
2720 			for ( i = 0; i < ix; i++ ) {
2721 				ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
2722 				ca.argc = 0;
2723 				config_fp_parse_line( &ca );
2724 
2725 				if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
2726 					rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
2727 				} else {
2728 					rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2729 						c->fname, c->lineno, ca.argc, ca.argv );
2730 				}
2731 				assert( rc == 0 );
2732 				ch_free( ca.argv );
2733 				ch_free( ca.tline );
2734 			}
2735 		}
2736 		argc = c->argc;
2737 		argv = c->argv;
2738 		if ( c->op != SLAP_CONFIG_ADD ) {
2739 			argc--;
2740 			argv++;
2741 		}
2742 		/* add the new rule */
2743 		if ( !strcasecmp( argv[0], "suffixmassage" )) {
2744 			rc = meta_suffixm_config( c, argc, argv, mt );
2745 		} else {
2746 			rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2747 						c->fname, c->lineno, argc, argv );
2748 		}
2749 		if ( rc ) {
2750 			if ( ix < cnt ) {
2751 				rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
2752 				mt->mt_rwmap.rwm_rw = rwi;
2753 			}
2754 			return 1;
2755 		}
2756 		if ( ix < cnt ) {
2757 			for ( ; i < cnt; i++ ) {
2758 				ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
2759 				ca.argc = 0;
2760 				config_fp_parse_line( &ca );
2761 
2762 				if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
2763 					rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
2764 				} else {
2765 					rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2766 						c->fname, c->lineno, ca.argc, argv );
2767 				}
2768 				assert( rc == 0 );
2769 				ch_free( ca.argv );
2770 				ch_free( ca.tline );
2771 			}
2772 		}
2773 
2774 		/* save the rule info */
2775 		line = ldap_charray2str( argv, "\" \"" );
2776 		if ( line != NULL ) {
2777 			struct berval bv;
2778 			int len = strlen( argv[ 0 ] );
2779 
2780 			ber_str2bv( line, 0, 0, &bv );
2781 			AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
2782 				bv.bv_len - ( len + 1 ));
2783 			bv.bv_val[ bv.bv_len - 1] = '"';
2784 			ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv );
2785 			/* move it to the right slot */
2786 			if ( ix < cnt ) {
2787 				for ( i=cnt; i>ix; i-- )
2788 					mt->mt_rwmap.rwm_bva_rewrite[i+1] = mt->mt_rwmap.rwm_bva_rewrite[i];
2789 				mt->mt_rwmap.rwm_bva_rewrite[i] = bv;
2790 
2791 				/* destroy old rules */
2792 				rewrite_info_delete( &rwi );
2793 			}
2794 		}
2795 		} break;
2796 
2797 	case LDAP_BACK_CFG_MAP: {
2798 	/* objectclass/attribute mapping */
2799 		ConfigArgs ca = { 0 };
2800 		char *argv[5];
2801 		struct ldapmap rwm_oc;
2802 		struct ldapmap rwm_at;
2803 		int cnt = 0, ix = c->valx;
2804 
2805 		if ( mt->mt_rwmap.rwm_bva_map ) {
2806 			for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ )
2807 				/* count */ ;
2808 		}
2809 
2810 		if ( ix >= cnt || ix < 0 ) {
2811 			ix = cnt;
2812 		} else {
2813 			rwm_oc = mt->mt_rwmap.rwm_oc;
2814 			rwm_at = mt->mt_rwmap.rwm_at;
2815 
2816 			memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) );
2817 			memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) );
2818 
2819 			/* re-parse all mappings, up to the one
2820 			 * that needs to be added */
2821 			argv[0] = c->argv[0];
2822 			ca.fname = c->fname;
2823 			ca.lineno = c->lineno;
2824 			for ( i = 0; i < ix; i++ ) {
2825 				ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
2826 				ca.argc = 0;
2827 				config_fp_parse_line( &ca );
2828 
2829 				argv[1] = ca.argv[0];
2830 				argv[2] = ca.argv[1];
2831 				argv[3] = ca.argv[2];
2832 				argv[4] = ca.argv[3];
2833 				ch_free( ca.argv );
2834 				ca.argv = argv;
2835 				ca.argc++;
2836 				rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
2837 					&mt->mt_rwmap.rwm_at );
2838 
2839 				ch_free( ca.tline );
2840 				ca.tline = NULL;
2841 				ca.argv = NULL;
2842 
2843 				/* in case of failure, restore
2844 				 * the existing mapping */
2845 				if ( rc ) {
2846 					goto map_fail;
2847 				}
2848 			}
2849 		}
2850 		/* add the new mapping */
2851 		rc = ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc,
2852 					&mt->mt_rwmap.rwm_at );
2853 		if ( rc ) {
2854 			goto map_fail;
2855 		}
2856 
2857 		if ( ix < cnt ) {
2858 			for ( ; i<cnt ; cnt++ ) {
2859 				ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
2860 				ca.argc = 0;
2861 				config_fp_parse_line( &ca );
2862 
2863 				argv[1] = ca.argv[0];
2864 				argv[2] = ca.argv[1];
2865 				argv[3] = ca.argv[2];
2866 				argv[4] = ca.argv[3];
2867 
2868 				ch_free( ca.argv );
2869 				ca.argv = argv;
2870 				ca.argc++;
2871 				rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
2872 					&mt->mt_rwmap.rwm_at );
2873 
2874 				ch_free( ca.tline );
2875 				ca.tline = NULL;
2876 				ca.argv = NULL;
2877 
2878 				/* in case of failure, restore
2879 				 * the existing mapping */
2880 				if ( rc ) {
2881 					goto map_fail;
2882 				}
2883 			}
2884 		}
2885 
2886 		/* save the map info */
2887 		argv[0] = ldap_charray2str( &c->argv[ 1 ], " " );
2888 		if ( argv[0] != NULL ) {
2889 			struct berval bv;
2890 			ber_str2bv( argv[0], 0, 0, &bv );
2891 			ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv );
2892 			/* move it to the right slot */
2893 			if ( ix < cnt ) {
2894 				for ( i=cnt; i>ix; i-- )
2895 					mt->mt_rwmap.rwm_bva_map[i+1] = mt->mt_rwmap.rwm_bva_map[i];
2896 				mt->mt_rwmap.rwm_bva_map[i] = bv;
2897 
2898 				/* destroy old mapping */
2899 				meta_back_map_free( &rwm_oc );
2900 				meta_back_map_free( &rwm_at );
2901 			}
2902 		}
2903 		break;
2904 
2905 map_fail:;
2906 		if ( ix < cnt ) {
2907 			meta_back_map_free( &mt->mt_rwmap.rwm_oc );
2908 			meta_back_map_free( &mt->mt_rwmap.rwm_at );
2909 			mt->mt_rwmap.rwm_oc = rwm_oc;
2910 			mt->mt_rwmap.rwm_at = rwm_at;
2911 		}
2912 		} break;
2913 
2914 	case LDAP_BACK_CFG_NRETRIES: {
2915 		int		nretries = META_RETRY_UNDEFINED;
2916 
2917 		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2918 			nretries = META_RETRY_FOREVER;
2919 
2920 		} else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
2921 			nretries = META_RETRY_NEVER;
2922 
2923 		} else {
2924 			if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
2925 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2926 					"unable to parse nretries {never|forever|<retries>}: \"%s\"",
2927 					c->argv[ 1 ] );
2928 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2929 				return 1;
2930 			}
2931 		}
2932 
2933 		mc->mc_nretries = nretries;
2934 		} break;
2935 
2936 	case LDAP_BACK_CFG_VERSION:
2937 		if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
2938 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2939 				"unsupported protocol version \"%s\"",
2940 				c->argv[ 1 ] );
2941 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2942 			return 1;
2943 		}
2944 		mc->mc_version = c->value_int;
2945 		break;
2946 
2947 	case LDAP_BACK_CFG_NOREFS:
2948 	/* do not return search references */
2949 		if ( c->value_int ) {
2950 			mc->mc_flags |= LDAP_BACK_F_NOREFS;
2951 		} else {
2952 			mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
2953 		}
2954 		break;
2955 
2956 	case LDAP_BACK_CFG_NOUNDEFFILTER:
2957 	/* do not propagate undefined search filters */
2958 		if ( c->value_int ) {
2959 			mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER;
2960 		} else {
2961 			mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
2962 		}
2963 		break;
2964 
2965 #ifdef SLAPD_META_CLIENT_PR
2966 	case LDAP_BACK_CFG_CLIENT_PR:
2967 		if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
2968 			mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
2969 
2970 		} else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
2971 			mc->mc_ps = META_CLIENT_PR_DISABLE;
2972 
2973 		} else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) {
2974 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2975 				"unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
2976 				c->argv[ 1 ] );
2977 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2978 			return( 1 );
2979 		}
2980 		break;
2981 #endif /* SLAPD_META_CLIENT_PR */
2982 
2983 	case LDAP_BACK_CFG_KEEPALIVE:
2984 		slap_keepalive_parse( ber_bvstrdup(c->argv[1]),
2985 				 &mt->mt_tls.sb_keepalive, 0, 0, 0);
2986 		break;
2987 
2988 	/* anything else */
2989 	default:
2990 		return SLAP_CONF_UNKNOWN;
2991 	}
2992 
2993 	return rc;
2994 }
2995 
2996 int
2997 meta_back_init_cf( BackendInfo *bi )
2998 {
2999 	int			rc;
3000 	AttributeDescription	*ad = NULL;
3001 	const char		*text;
3002 
3003 	/* Make sure we don't exceed the bits reserved for userland */
3004 	config_check_userland( LDAP_BACK_CFG_LAST );
3005 
3006 	bi->bi_cf_ocs = metaocs;
3007 
3008 	rc = config_register_schema( metacfg, metaocs );
3009 	if ( rc ) {
3010 		return rc;
3011 	}
3012 
3013 	/* setup olcDbAclPasswd and olcDbIDAssertPasswd
3014 	 * to be base64-encoded when written in LDIF form;
3015 	 * basically, we don't care if it fails */
3016 	rc = slap_str2ad( "olcDbACLPasswd", &ad, &text );
3017 	if ( rc ) {
3018 		Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
3019 			"warning, unable to get \"olcDbACLPasswd\" "
3020 			"attribute description: %d: %s\n",
3021 			rc, text, 0 );
3022 	} else {
3023 		(void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
3024 			ad->ad_type->sat_oid );
3025 	}
3026 
3027 	ad = NULL;
3028 	rc = slap_str2ad( "olcDbIDAssertPasswd", &ad, &text );
3029 	if ( rc ) {
3030 		Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
3031 			"warning, unable to get \"olcDbIDAssertPasswd\" "
3032 			"attribute description: %d: %s\n",
3033 			rc, text, 0 );
3034 	} else {
3035 		(void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
3036 			ad->ad_type->sat_oid );
3037 	}
3038 
3039 	return 0;
3040 }
3041 
3042 static int
3043 ldap_back_map_config(
3044 		ConfigArgs *c,
3045 		struct ldapmap	*oc_map,
3046 		struct ldapmap	*at_map )
3047 {
3048 	struct ldapmap		*map;
3049 	struct ldapmapping	*mapping;
3050 	char			*src, *dst;
3051 	int			is_oc = 0;
3052 
3053 	if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) {
3054 		map = oc_map;
3055 		is_oc = 1;
3056 
3057 	} else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) {
3058 		map = at_map;
3059 
3060 	} else {
3061 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3062 			"%s unknown argument \"%s\"",
3063 			c->argv[0], c->argv[1] );
3064 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3065 		return 1;
3066 	}
3067 
3068 	if ( !is_oc && map->map == NULL ) {
3069 		/* only init if required */
3070 		ldap_back_map_init( map, &mapping );
3071 	}
3072 
3073 	if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) {
3074 		if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) {
3075 			map->drop_missing = ( c->argc < 4 );
3076 			goto success_return;
3077 		}
3078 		src = dst = c->argv[ 3 ];
3079 
3080 	} else if ( c->argc < 4 ) {
3081 		src = "";
3082 		dst = c->argv[ 2 ];
3083 
3084 	} else {
3085 		src = c->argv[ 2 ];
3086 		dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] );
3087 	}
3088 
3089 	if ( ( map == at_map )
3090 		&& ( strcasecmp( src, "objectclass" ) == 0
3091 			|| strcasecmp( dst, "objectclass" ) == 0 ) )
3092 	{
3093 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3094 			"objectclass attribute cannot be mapped" );
3095 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3096 		return 1;
3097 	}
3098 
3099 	mapping = (struct ldapmapping *)ch_calloc( 2,
3100 		sizeof(struct ldapmapping) );
3101 	if ( mapping == NULL ) {
3102 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3103 			"out of memory" );
3104 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3105 		return 1;
3106 	}
3107 	ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
3108 	ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
3109 	mapping[ 1 ].src = mapping[ 0 ].dst;
3110 	mapping[ 1 ].dst = mapping[ 0 ].src;
3111 
3112 	/*
3113 	 * schema check
3114 	 */
3115 	if ( is_oc ) {
3116 		if ( src[ 0 ] != '\0' ) {
3117 			if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
3118 				Debug( LDAP_DEBUG_ANY,
3119 	"warning, source objectClass '%s' should be defined in schema\n",
3120 					c->log, src, 0 );
3121 
3122 				/*
3123 				 * FIXME: this should become an err
3124 				 */
3125 				goto error_return;
3126 			}
3127 		}
3128 
3129 		if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
3130 			Debug( LDAP_DEBUG_ANY,
3131 	"warning, destination objectClass '%s' is not defined in schema\n",
3132 				c->log, dst, 0 );
3133 		}
3134 	} else {
3135 		int			rc;
3136 		const char		*text = NULL;
3137 		AttributeDescription	*ad = NULL;
3138 
3139 		if ( src[ 0 ] != '\0' ) {
3140 			rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
3141 			if ( rc != LDAP_SUCCESS ) {
3142 				Debug( LDAP_DEBUG_ANY,
3143 	"warning, source attributeType '%s' should be defined in schema\n",
3144 					c->log, src, 0 );
3145 
3146 				/*
3147 				 * FIXME: this should become an err
3148 				 */
3149 				/*
3150 				 * we create a fake "proxied" ad
3151 				 * and add it here.
3152 				 */
3153 
3154 				rc = slap_bv2undef_ad( &mapping[ 0 ].src,
3155 						&ad, &text, SLAP_AD_PROXIED );
3156 				if ( rc != LDAP_SUCCESS ) {
3157 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
3158 						"source attributeType \"%s\": %d (%s)",
3159 						src, rc, text ? text : "" );
3160 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3161 					goto error_return;
3162 				}
3163 			}
3164 
3165 			ad = NULL;
3166 		}
3167 
3168 		rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
3169 		if ( rc != LDAP_SUCCESS ) {
3170 			Debug( LDAP_DEBUG_ANY,
3171 	"warning, destination attributeType '%s' is not defined in schema\n",
3172 				c->log, dst, 0 );
3173 
3174 			/*
3175 			 * we create a fake "proxied" ad
3176 			 * and add it here.
3177 			 */
3178 
3179 			rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
3180 					&ad, &text, SLAP_AD_PROXIED );
3181 			if ( rc != LDAP_SUCCESS ) {
3182 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
3183 					"destination attributeType \"%s\": %d (%s)\n",
3184 					dst, rc, text ? text : "" );
3185 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3186 				return 1;
3187 			}
3188 		}
3189 	}
3190 
3191 	if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
3192 			|| avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
3193 	{
3194 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
3195 			"duplicate mapping found." );
3196 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3197 		goto error_return;
3198 	}
3199 
3200 	if ( src[ 0 ] != '\0' ) {
3201 		avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
3202 					mapping_cmp, mapping_dup );
3203 	}
3204 	avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
3205 				mapping_cmp, mapping_dup );
3206 
3207 success_return:;
3208 	return 0;
3209 
3210 error_return:;
3211 	if ( mapping ) {
3212 		ch_free( mapping[ 0 ].src.bv_val );
3213 		ch_free( mapping[ 0 ].dst.bv_val );
3214 		ch_free( mapping );
3215 	}
3216 
3217 	return 1;
3218 }
3219 
3220 
3221 #ifdef ENABLE_REWRITE
3222 static char *
3223 suffix_massage_regexize( const char *s )
3224 {
3225 	char *res, *ptr;
3226 	const char *p, *r;
3227 	int i;
3228 
3229 	if ( s[ 0 ] == '\0' ) {
3230 		return ch_strdup( "^(.+)$" );
3231 	}
3232 
3233 	for ( i = 0, p = s;
3234 			( r = strchr( p, ',' ) ) != NULL;
3235 			p = r + 1, i++ )
3236 		;
3237 
3238 	res = ch_calloc( sizeof( char ),
3239 			strlen( s )
3240 			+ STRLENOF( "((.+),)?" )
3241 			+ STRLENOF( "[ ]?" ) * i
3242 			+ STRLENOF( "$" ) + 1 );
3243 
3244 	ptr = lutil_strcopy( res, "((.+),)?" );
3245 	for ( i = 0, p = s;
3246 			( r = strchr( p, ',' ) ) != NULL;
3247 			p = r + 1 , i++ ) {
3248 		ptr = lutil_strncopy( ptr, p, r - p + 1 );
3249 		ptr = lutil_strcopy( ptr, "[ ]?" );
3250 
3251 		if ( r[ 1 ] == ' ' ) {
3252 			r++;
3253 		}
3254 	}
3255 	ptr = lutil_strcopy( ptr, p );
3256 	ptr[ 0 ] = '$';
3257 	ptr++;
3258 	ptr[ 0 ] = '\0';
3259 
3260 	return res;
3261 }
3262 
3263 static char *
3264 suffix_massage_patternize( const char *s, const char *p )
3265 {
3266 	ber_len_t	len;
3267 	char		*res, *ptr;
3268 
3269 	len = strlen( p );
3270 
3271 	if ( s[ 0 ] == '\0' ) {
3272 		len++;
3273 	}
3274 
3275 	res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
3276 	if ( res == NULL ) {
3277 		return NULL;
3278 	}
3279 
3280 	ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
3281 	if ( s[ 0 ] == '\0' ) {
3282 		ptr[ 0 ] = ',';
3283 		ptr++;
3284 	}
3285 	lutil_strcopy( ptr, p );
3286 
3287 	return res;
3288 }
3289 
3290 int
3291 suffix_massage_config(
3292 		struct rewrite_info *info,
3293 		struct berval *pvnc,
3294 		struct berval *nvnc,
3295 		struct berval *prnc,
3296 		struct berval *nrnc
3297 )
3298 {
3299 	char *rargv[ 5 ];
3300 	int line = 0;
3301 
3302 	rargv[ 0 ] = "rewriteEngine";
3303 	rargv[ 1 ] = "on";
3304 	rargv[ 2 ] = NULL;
3305 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3306 
3307 	rargv[ 0 ] = "rewriteContext";
3308 	rargv[ 1 ] = "default";
3309 	rargv[ 2 ] = NULL;
3310 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3311 
3312 	rargv[ 0 ] = "rewriteRule";
3313 	rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
3314 	rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
3315 	rargv[ 3 ] = ":";
3316 	rargv[ 4 ] = NULL;
3317 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3318 	ch_free( rargv[ 1 ] );
3319 	ch_free( rargv[ 2 ] );
3320 
3321 	if ( BER_BVISEMPTY( pvnc ) ) {
3322 		rargv[ 0 ] = "rewriteRule";
3323 		rargv[ 1 ] = "^$";
3324 		rargv[ 2 ] = prnc->bv_val;
3325 		rargv[ 3 ] = ":";
3326 		rargv[ 4 ] = NULL;
3327 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3328 	}
3329 
3330 	rargv[ 0 ] = "rewriteContext";
3331 	rargv[ 1 ] = "searchEntryDN";
3332 	rargv[ 2 ] = NULL;
3333 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3334 
3335 	rargv[ 0 ] = "rewriteRule";
3336 	rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
3337 	rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
3338 	rargv[ 3 ] = ":";
3339 	rargv[ 4 ] = NULL;
3340 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3341 	ch_free( rargv[ 1 ] );
3342 	ch_free( rargv[ 2 ] );
3343 
3344 	if ( BER_BVISEMPTY( prnc ) ) {
3345 		rargv[ 0 ] = "rewriteRule";
3346 		rargv[ 1 ] = "^$";
3347 		rargv[ 2 ] = pvnc->bv_val;
3348 		rargv[ 3 ] = ":";
3349 		rargv[ 4 ] = NULL;
3350 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3351 	}
3352 
3353 	/* backward compatibility */
3354 	rargv[ 0 ] = "rewriteContext";
3355 	rargv[ 1 ] = "searchResult";
3356 	rargv[ 2 ] = "alias";
3357 	rargv[ 3 ] = "searchEntryDN";
3358 	rargv[ 4 ] = NULL;
3359 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3360 
3361 	rargv[ 0 ] = "rewriteContext";
3362 	rargv[ 1 ] = "matchedDN";
3363 	rargv[ 2 ] = "alias";
3364 	rargv[ 3 ] = "searchEntryDN";
3365 	rargv[ 4 ] = NULL;
3366 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3367 
3368 	rargv[ 0 ] = "rewriteContext";
3369 	rargv[ 1 ] = "searchAttrDN";
3370 	rargv[ 2 ] = "alias";
3371 	rargv[ 3 ] = "searchEntryDN";
3372 	rargv[ 4 ] = NULL;
3373 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3374 
3375 	/* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
3376 	 * see servers/slapd/overlays/rwm.h for details */
3377         rargv[ 0 ] = "rewriteContext";
3378 	rargv[ 1 ] = "referralAttrDN";
3379 	rargv[ 2 ] = NULL;
3380 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3381 
3382 	rargv[ 0 ] = "rewriteContext";
3383 	rargv[ 1 ] = "referralDN";
3384 	rargv[ 2 ] = NULL;
3385 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3386 
3387 	return 0;
3388 }
3389 #endif /* ENABLE_REWRITE */
3390 
3391