xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-meta/config.c (revision 87d689fb734c654d2486f87f7be32f1b53ecdbec)
1 /*	$NetBSD: config.c,v 1.1.1.6 2017/02/09 01:47:04 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1999-2016 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.1.1.6 2017/02/09 01:47:04 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 			if ( mt->mt_rwmap.rwm_bva_rewrite ) {
1836 				ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
1837 				mt->mt_rwmap.rwm_bva_rewrite = NULL;
1838 			}
1839 			if ( mt->mt_rwmap.rwm_rw )
1840 				rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
1841 			break;
1842 
1843 		case LDAP_BACK_CFG_MAP:
1844 			if ( mt->mt_rwmap.rwm_bva_map ) {
1845 				ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
1846 				mt->mt_rwmap.rwm_bva_map = NULL;
1847 			}
1848 			meta_back_map_free( &mt->mt_rwmap.rwm_oc );
1849 			meta_back_map_free( &mt->mt_rwmap.rwm_at );
1850 			mt->mt_rwmap.rwm_oc.drop_missing = 0;
1851 			mt->mt_rwmap.rwm_at.drop_missing = 0;
1852 			break;
1853 
1854 		case LDAP_BACK_CFG_SUBTREE_EX:
1855 		case LDAP_BACK_CFG_SUBTREE_IN:
1856 			/* can only be one of exclude or include */
1857 			if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
1858 				rc = 1;
1859 				break;
1860 			}
1861 			if ( c->valx < 0 ) {
1862 				meta_subtree_destroy( mt->mt_subtree );
1863 				mt->mt_subtree = NULL;
1864 			} else {
1865 				metasubtree_t *ms, **mprev;
1866 				for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
1867 					if ( i == c->valx ) {
1868 						*mprev = ms->ms_next;
1869 						meta_subtree_free( ms );
1870 						break;
1871 					}
1872 					i++;
1873 					mprev = &ms->ms_next;
1874 				}
1875 				if ( i != c->valx )
1876 					rc = 1;
1877 			}
1878 			break;
1879 
1880 		case LDAP_BACK_CFG_FILTER:
1881 			if ( c->valx < 0 ) {
1882 				meta_filter_destroy( mt->mt_filter );
1883 				mt->mt_filter = NULL;
1884 			} else {
1885 				metafilter_t *mf, **mprev;
1886 				for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) {
1887 					if ( i == c->valx ) {
1888 						*mprev = mf->mf_next;
1889 						meta_filter_free( mf );
1890 						break;
1891 					}
1892 					i++;
1893 					mprev = &mf->mf_next;
1894 				}
1895 				if ( i != c->valx )
1896 					rc = 1;
1897 			}
1898 			break;
1899 
1900 		case LDAP_BACK_CFG_KEEPALIVE:
1901 			mt->mt_tls.sb_keepalive.sk_idle = 0;
1902 			mt->mt_tls.sb_keepalive.sk_probes = 0;
1903 			mt->mt_tls.sb_keepalive.sk_interval = 0;
1904 			break;
1905 
1906 		default:
1907 			rc = 1;
1908 			break;
1909 		}
1910 
1911 		return rc;
1912 	}
1913 
1914 	if ( c->op == SLAP_CONFIG_ADD ) {
1915 		if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
1916 			/* exclude CFG_URI from this check */
1917 			if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
1918 				if ( !mi->mi_ntargets ) {
1919 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1920 						"need \"uri\" directive first" );
1921 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1922 					return 1;
1923 				}
1924 			}
1925 			if ( mi->mi_ntargets ) {
1926 				mt = mi->mi_targets[ mi->mi_ntargets-1 ];
1927 				mc = &mt->mt_mc;
1928 			} else {
1929 				mt = NULL;
1930 				mc = &mi->mi_mc;
1931 			}
1932 		}
1933 	} else {
1934 		if ( c->table == Cft_Database ) {
1935 			mt = NULL;
1936 			mc = &mi->mi_mc;
1937 		} else {
1938 			mt = c->ca_private;
1939 			if ( mt )
1940 				mc = &mt->mt_mc;
1941 			else
1942 				mc = NULL;
1943 		}
1944 	}
1945 
1946 	switch( c->type ) {
1947 	case LDAP_BACK_CFG_URI: {
1948 		LDAPURLDesc 	*ludp;
1949 		struct berval	dn;
1950 		int		j;
1951 
1952 		char		**uris = NULL;
1953 
1954 		if ( c->be->be_nsuffix == NULL ) {
1955 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1956 				"the suffix must be defined before any target" );
1957 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1958 			return 1;
1959 		}
1960 
1961 		i = mi->mi_ntargets++;
1962 
1963 		mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
1964 			sizeof( metatarget_t * ) * mi->mi_ntargets );
1965 		if ( mi->mi_targets == NULL ) {
1966 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1967 				"out of memory while storing server name"
1968 				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1969 				c->argv[0] );
1970 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1971 			return 1;
1972 		}
1973 
1974 		if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
1975 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1976 				"unable to init server"
1977 				" in \"%s <protocol>://<server>[:port]/<naming context>\"",
1978 				c->argv[0] );
1979 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1980 			return 1;
1981 		}
1982 
1983 		mt = mi->mi_targets[ i ];
1984 
1985 		mt->mt_rebind_f = mi->mi_rebind_f;
1986 		mt->mt_urllist_f = mi->mi_urllist_f;
1987 		mt->mt_urllist_p = mt;
1988 
1989 		if ( META_BACK_QUARANTINE( mi ) ) {
1990 			ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
1991 		}
1992 		mt->mt_mc = mi->mi_mc;
1993 
1994 		for ( j = 1; j < c->argc; j++ ) {
1995 			char	**tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
1996 
1997 			if ( tmpuris == NULL ) {
1998 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1999 					"unable to parse URIs #%d"
2000 					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2001 					j-1, c->argv[0] );
2002 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2003 				return 1;
2004 			}
2005 
2006 			if ( j == 1 ) {
2007 				uris = tmpuris;
2008 
2009 			} else {
2010 				ldap_charray_merge( &uris, tmpuris );
2011 				ldap_charray_free( tmpuris );
2012 			}
2013 		}
2014 
2015 		for ( j = 0; uris[ j ] != NULL; j++ ) {
2016 			char *tmpuri = NULL;
2017 
2018 			/*
2019 			 * uri MUST be legal!
2020 			 */
2021 			if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
2022 					LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
2023 				|| ludp->lud_next != NULL )
2024 			{
2025 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2026 					"unable to parse URI #%d"
2027 					" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2028 					j-1, c->argv[0] );
2029 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2030 				ldap_charray_free( uris );
2031 				return 1;
2032 			}
2033 
2034 			if ( j == 0 ) {
2035 
2036 				/*
2037 				 * uri MUST have the <dn> part!
2038 				 */
2039 				if ( ludp->lud_dn == NULL ) {
2040 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2041 						"missing <naming context> "
2042 						" in \"%s <protocol>://<server>[:port]/<naming context>\"",
2043 						c->argv[0] );
2044 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2045 					ldap_free_urllist( ludp );
2046 					ldap_charray_free( uris );
2047 					return 1;
2048 				}
2049 
2050 				/*
2051 				 * copies and stores uri and suffix
2052 				 */
2053 				ber_str2bv( ludp->lud_dn, 0, 0, &dn );
2054 				rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
2055 					&mt->mt_nsuffix, NULL );
2056 				if ( rc != LDAP_SUCCESS ) {
2057 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2058 						"target DN is invalid \"%s\"",
2059 						c->argv[1] );
2060 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2061 					ldap_free_urllist( ludp );
2062 					ldap_charray_free( uris );
2063 					return( 1 );
2064 				}
2065 
2066 				ludp->lud_dn[ 0 ] = '\0';
2067 
2068 				switch ( ludp->lud_scope ) {
2069 				case LDAP_SCOPE_DEFAULT:
2070 					mt->mt_scope = LDAP_SCOPE_SUBTREE;
2071 					break;
2072 
2073 				case LDAP_SCOPE_SUBTREE:
2074 				case LDAP_SCOPE_SUBORDINATE:
2075 					mt->mt_scope = ludp->lud_scope;
2076 					break;
2077 
2078 				default:
2079 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2080 						"invalid scope for target \"%s\"",
2081 						c->argv[1] );
2082 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2083 					ldap_free_urllist( ludp );
2084 					ldap_charray_free( uris );
2085 					return( 1 );
2086 				}
2087 
2088 			} else {
2089 				/* check all, to apply the scope check on the first one */
2090 				if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
2091 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2092 						"multiple URIs must have no DN part" );
2093 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2094 					ldap_free_urllist( ludp );
2095 					ldap_charray_free( uris );
2096 					return( 1 );
2097 
2098 				}
2099 			}
2100 
2101 			tmpuri = ldap_url_list2urls( ludp );
2102 			ldap_free_urllist( ludp );
2103 			if ( tmpuri == NULL ) {
2104 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
2105 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2106 				ldap_charray_free( uris );
2107 				return( 1 );
2108 			}
2109 			ldap_memfree( uris[ j ] );
2110 			uris[ j ] = tmpuri;
2111 		}
2112 
2113 		mt->mt_uri = ldap_charray2str( uris, " " );
2114 		ldap_charray_free( uris );
2115 		if ( mt->mt_uri == NULL) {
2116 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
2117 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2118 			return( 1 );
2119 		}
2120 
2121 		/*
2122 		 * uri MUST be a branch of suffix!
2123 		 */
2124 		for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
2125 			if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
2126 				break;
2127 			}
2128 		}
2129 
2130 		if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
2131 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2132 				"<naming context> of URI must be within the naming context of this database." );
2133 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2134 			return 1;
2135 		}
2136 		c->ca_private = mt;
2137 		c->cleanup = meta_cf_cleanup;
2138 	} break;
2139 	case LDAP_BACK_CFG_SUBTREE_EX:
2140 	case LDAP_BACK_CFG_SUBTREE_IN:
2141 	/* subtree-exclude */
2142 		if ( meta_subtree_config( mt, c )) {
2143 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2144 			return 1;
2145 		}
2146 		break;
2147 
2148 	case LDAP_BACK_CFG_FILTER: {
2149 		metafilter_t *mf, **m2;
2150 		mf = ch_malloc( sizeof( metafilter_t ));
2151 		rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED );
2152 		if ( rc ) {
2153 			char regerr[ SLAP_TEXT_BUFLEN ];
2154 			regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) );
2155 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2156 				"regular expression \"%s\" bad because of %s",
2157 				c->argv[1], regerr );
2158 			ch_free( mf );
2159 			return 1;
2160 		}
2161 		ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern );
2162 		for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next )
2163 			;
2164 		*m2 = mf;
2165 	} break;
2166 
2167 	case LDAP_BACK_CFG_DEFAULT_T:
2168 	/* default target directive */
2169 		i = mi->mi_ntargets - 1;
2170 
2171 		if ( c->argc == 1 ) {
2172  			if ( i < 0 ) {
2173 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2174 					"\"%s\" alone must be inside a \"uri\" directive",
2175 					c->argv[0] );
2176 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2177 				return 1;
2178 			}
2179 			mi->mi_defaulttarget = i;
2180 
2181 		} else {
2182 			if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
2183 				if ( i >= 0 ) {
2184 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2185 						"\"%s none\" should go before uri definitions",
2186 						c->argv[0] );
2187 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2188 				}
2189 				mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
2190 
2191 			} else {
2192 
2193 				if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
2194 					|| mi->mi_defaulttarget < 0
2195 					|| mi->mi_defaulttarget >= i - 1 )
2196 				{
2197 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
2198 						"illegal target number %d",
2199 						mi->mi_defaulttarget );
2200 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2201 					return 1;
2202 				}
2203 			}
2204 		}
2205 		break;
2206 
2207 	case LDAP_BACK_CFG_DNCACHE_TTL:
2208 	/* ttl of dn cache */
2209 		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2210 			mi->mi_cache.ttl = META_DNCACHE_FOREVER;
2211 
2212 		} else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
2213 			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
2214 
2215 		} else {
2216 			unsigned long	t;
2217 
2218 			if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
2219 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2220 					"unable to parse dncache ttl \"%s\"",
2221 					c->argv[ 1 ] );
2222 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2223 				return 1;
2224 			}
2225 			mi->mi_cache.ttl = (time_t)t;
2226 		}
2227 		break;
2228 
2229 	case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
2230 	/* network timeout when connecting to ldap servers */
2231 		unsigned long t;
2232 
2233 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2234 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2235 				"unable to parse network timeout \"%s\"",
2236 				c->argv[ 1 ] );
2237 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2238 			return 1;
2239 		}
2240 		mc->mc_network_timeout = (time_t)t;
2241 		} break;
2242 
2243 	case LDAP_BACK_CFG_IDLE_TIMEOUT: {
2244 	/* idle timeout when connecting to ldap servers */
2245 		unsigned long	t;
2246 
2247 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2248 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2249 				"unable to parse idle timeout \"%s\"",
2250 				c->argv[ 1 ] );
2251 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2252 			return 1;
2253 
2254 		}
2255 		mi->mi_idle_timeout = (time_t)t;
2256 		} break;
2257 
2258 	case LDAP_BACK_CFG_CONN_TTL: {
2259 	/* conn ttl */
2260 		unsigned long	t;
2261 
2262 		if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
2263 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2264 				"unable to parse conn ttl \"%s\"",
2265 				c->argv[ 1 ] );
2266 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2267 			return 1;
2268 
2269 		}
2270 		mi->mi_conn_ttl = (time_t)t;
2271 		} break;
2272 
2273 	case LDAP_BACK_CFG_BIND_TIMEOUT:
2274 	/* bind timeout when connecting to ldap servers */
2275 		mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
2276 		mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
2277 		break;
2278 
2279 	case LDAP_BACK_CFG_ACL_AUTHCDN:
2280 	/* name to use for meta_back_group */
2281 		if ( strcasecmp( c->argv[ 0 ], "binddn" ) == 0 ) {
2282 			Debug( LDAP_DEBUG_ANY, "%s: "
2283 				"\"binddn\" statement is deprecated; "
2284 				"use \"acl-authcDN\" instead\n",
2285 				c->log, 0, 0 );
2286 			/* FIXME: some day we'll need to throw an error */
2287 		}
2288 
2289 		ber_memfree_x( c->value_dn.bv_val, NULL );
2290 		mt->mt_binddn = c->value_ndn;
2291 		BER_BVZERO( &c->value_dn );
2292 		BER_BVZERO( &c->value_ndn );
2293 		break;
2294 
2295 	case LDAP_BACK_CFG_ACL_PASSWD:
2296 	/* password to use for meta_back_group */
2297 		if ( strcasecmp( c->argv[ 0 ], "bindpw" ) == 0 ) {
2298 			Debug( LDAP_DEBUG_ANY, "%s "
2299 				"\"bindpw\" statement is deprecated; "
2300 				"use \"acl-passwd\" instead\n",
2301 				c->log, 0, 0 );
2302 			/* FIXME: some day we'll need to throw an error */
2303 		}
2304 
2305 		ber_str2bv( c->argv[ 1 ], 0L, 1, &mt->mt_bindpw );
2306 		break;
2307 
2308 	case LDAP_BACK_CFG_REBIND:
2309 	/* save bind creds for referral rebinds? */
2310 		if ( c->argc == 1 || c->value_int ) {
2311 			mc->mc_flags |= LDAP_BACK_F_SAVECRED;
2312 		} else {
2313 			mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
2314 		}
2315 		break;
2316 
2317 	case LDAP_BACK_CFG_CHASE:
2318 		if ( c->argc == 1 || c->value_int ) {
2319 			mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS;
2320 		} else {
2321 			mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
2322 		}
2323 		break;
2324 
2325 	case LDAP_BACK_CFG_TLS:
2326 		i = verb_to_mask( c->argv[1], tls_mode );
2327 		if ( BER_BVISNULL( &tls_mode[i].word ) ) {
2328 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2329 				"%s unknown argument \"%s\"",
2330 				c->argv[0], c->argv[1] );
2331 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2332 			return 1;
2333 		}
2334 		mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
2335 		mc->mc_flags |= tls_mode[i].mask;
2336 
2337 		if ( c->argc > 2 ) {
2338 			if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
2339 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2340 					"need \"uri\" directive first" );
2341 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2342 				return 1;
2343 			}
2344 
2345 			for ( i = 2; i < c->argc; i++ ) {
2346 				if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
2347 					return 1;
2348 			}
2349 			bindconf_tls_defaults( &mt->mt_tls );
2350 		}
2351 		break;
2352 
2353 	case LDAP_BACK_CFG_T_F:
2354 		i = verb_to_mask( c->argv[1], t_f_mode );
2355 		if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
2356 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2357 				"%s unknown argument \"%s\"",
2358 				c->argv[0], c->argv[1] );
2359 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2360 			return 1;
2361 		}
2362 		mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
2363 		mc->mc_flags |= t_f_mode[i].mask;
2364 		break;
2365 
2366 	case LDAP_BACK_CFG_ONERR:
2367 	/* onerr? */
2368 		i = verb_to_mask( c->argv[1], onerr_mode );
2369 		if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
2370 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2371 				"%s unknown argument \"%s\"",
2372 				c->argv[0], c->argv[1] );
2373 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2374 			return 1;
2375 		}
2376 		mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
2377 		mi->mi_flags |= onerr_mode[i].mask;
2378 		break;
2379 
2380 	case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
2381 	/* bind-defer? */
2382 		if ( c->argc == 1 || c->value_int ) {
2383 			mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
2384 		} else {
2385 			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
2386 		}
2387 		break;
2388 
2389 	case LDAP_BACK_CFG_SINGLECONN:
2390 	/* single-conn? */
2391 		if ( mi->mi_ntargets > 0 ) {
2392 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2393 				"\"%s\" must appear before target definitions",
2394 				c->argv[0] );
2395 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2396 			return( 1 );
2397 		}
2398 		if ( c->value_int ) {
2399 			mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
2400 		} else {
2401 			mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
2402 		}
2403 		break;
2404 
2405 	case LDAP_BACK_CFG_USETEMP:
2406 	/* use-temporaries? */
2407 		if ( mi->mi_ntargets > 0 ) {
2408 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2409 				"\"%s\" must appear before target definitions",
2410 				c->argv[0] );
2411 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2412 			return( 1 );
2413 		}
2414 		if ( c->value_int ) {
2415 			mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
2416 		} else {
2417 			mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
2418 		}
2419 		break;
2420 
2421 	case LDAP_BACK_CFG_CONNPOOLMAX:
2422 	/* privileged connections pool max size ? */
2423 		if ( mi->mi_ntargets > 0 ) {
2424 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2425 				"\"%s\" must appear before target definitions",
2426 				c->argv[0] );
2427 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2428 			return( 1 );
2429 		}
2430 
2431 		if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
2432 			|| c->value_int > LDAP_BACK_CONN_PRIV_MAX )
2433 		{
2434 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2435 				"invalid max size " "of privileged "
2436 				"connections pool \"%s\" "
2437 				"in \"conn-pool-max <n> "
2438 				"(must be between %d and %d)\"",
2439 				c->argv[ 1 ],
2440 				LDAP_BACK_CONN_PRIV_MIN,
2441 				LDAP_BACK_CONN_PRIV_MAX );
2442 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2443 			return 1;
2444 		}
2445 		mi->mi_conn_priv_max = c->value_int;
2446 		break;
2447 
2448 	case LDAP_BACK_CFG_CANCEL:
2449 		i = verb_to_mask( c->argv[1], cancel_mode );
2450 		if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
2451 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2452 				"%s unknown argument \"%s\"",
2453 				c->argv[0], c->argv[1] );
2454 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2455 			return 1;
2456 		}
2457 		mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
2458 		mc->mc_flags |= cancel_mode[i].mask;
2459 		break;
2460 
2461 	case LDAP_BACK_CFG_TIMEOUT:
2462 		for ( i = 1; i < c->argc; i++ ) {
2463 			if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
2464 				int		j;
2465 				unsigned	u;
2466 
2467 				if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
2468 					snprintf( c->cr_msg, sizeof( c->cr_msg),
2469 						"unable to parse timeout \"%s\"",
2470 						c->argv[ i ] );
2471 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2472 					return 1;
2473 				}
2474 
2475 				for ( j = 0; j < SLAP_OP_LAST; j++ ) {
2476 					mc->mc_timeout[ j ] = u;
2477 				}
2478 
2479 				continue;
2480 			}
2481 
2482 			if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) {
2483 				snprintf( c->cr_msg, sizeof( c->cr_msg),
2484 					"unable to parse timeout \"%s\"",
2485 					c->argv[ i ] );
2486 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2487 				return 1;
2488 			}
2489 		}
2490 		break;
2491 
2492 	case LDAP_BACK_CFG_PSEUDOROOTDN:
2493 	/* name to use as pseudo-root dn */
2494 		/*
2495 		 * exact replacement:
2496 		 *
2497 
2498 idassert-bind	bindmethod=simple
2499 		binddn=<pseudorootdn>
2500 		credentials=<pseudorootpw>
2501 		mode=none
2502 		flags=non-prescriptive
2503 idassert-authzFrom	"dn:<rootdn>"
2504 
2505 		 * so that only when authc'd as <rootdn> the proxying occurs
2506 		 * rebinding as the <pseudorootdn> without proxyAuthz.
2507 		 */
2508 
2509 		Debug( LDAP_DEBUG_ANY,
2510 			"%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
2511 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
2512 			c->log, 0, 0 );
2513 
2514 		{
2515 			char	binddn[ SLAP_TEXT_BUFLEN ];
2516 			char	*cargv[] = {
2517 				"idassert-bind",
2518 				"bindmethod=simple",
2519 				NULL,
2520 				"mode=none",
2521 				"flags=non-prescriptive",
2522 				NULL
2523 			};
2524 			char **oargv;
2525 			int oargc;
2526 			int	cargc = 5;
2527 			int	rc;
2528 
2529 
2530 			if ( BER_BVISNULL( &c->be->be_rootndn ) ) {
2531 				Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
2532 					c->log, 0, 0 );
2533 				return 1;
2534 			}
2535 
2536 			if ( sizeof( binddn ) <= (unsigned) snprintf( binddn,
2537 					sizeof( binddn ), "binddn=%s", c->argv[ 1 ] ))
2538 			{
2539 				Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootdn\" too long.\n",
2540 					c->log, 0, 0 );
2541 				return 1;
2542 			}
2543 			cargv[ 2 ] = binddn;
2544 
2545 			oargv = c->argv;
2546 			oargc = c->argc;
2547 			c->argv = cargv;
2548 			c->argc = cargc;
2549 			rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
2550 			c->argv = oargv;
2551 			c->argc = oargc;
2552 			if ( rc == 0 ) {
2553 				struct berval	bv;
2554 
2555 				if ( mt->mt_idassert_authz != NULL ) {
2556 					Debug( LDAP_DEBUG_ANY, "%s: \"idassert-authzFrom\" already defined (discarded).\n",
2557 						c->log, 0, 0 );
2558 					ber_bvarray_free( mt->mt_idassert_authz );
2559 					mt->mt_idassert_authz = NULL;
2560 				}
2561 
2562 				assert( !BER_BVISNULL( &mt->mt_idassert_authcDN ) );
2563 
2564 				bv.bv_len = STRLENOF( "dn:" ) + c->be->be_rootndn.bv_len;
2565 				bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2566 				AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
2567 				AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], c->be->be_rootndn.bv_val, c->be->be_rootndn.bv_len + 1 );
2568 
2569 				ber_bvarray_add( &mt->mt_idassert_authz, &bv );
2570 			}
2571 
2572 			return rc;
2573 		}
2574 		break;
2575 
2576 	case LDAP_BACK_CFG_PSEUDOROOTPW:
2577 	/* password to use as pseudo-root */
2578 		Debug( LDAP_DEBUG_ANY,
2579 			"%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
2580 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
2581 			c->log, 0, 0 );
2582 
2583 		if ( BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
2584 			Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
2585 				c->log, 0, 0 );
2586 			return 1;
2587 		}
2588 
2589 		if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
2590 			memset( mt->mt_idassert_passwd.bv_val, 0,
2591 				mt->mt_idassert_passwd.bv_len );
2592 			ber_memfree( mt->mt_idassert_passwd.bv_val );
2593 		}
2594 		ber_str2bv( c->argv[ 1 ], 0, 1, &mt->mt_idassert_passwd );
2595 		break;
2596 
2597 	case LDAP_BACK_CFG_IDASSERT_BIND:
2598 	/* idassert-bind */
2599 		rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
2600 		break;
2601 
2602 	case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
2603 	/* idassert-authzFrom */
2604 		rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert );
2605 		break;
2606 
2607 	case LDAP_BACK_CFG_QUARANTINE:
2608 	/* quarantine */
2609 		if ( META_BACK_CMN_QUARANTINE( mc ) )
2610 		{
2611 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2612 				"quarantine already defined" );
2613 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2614 			return 1;
2615 		}
2616 
2617 		if ( mt ) {
2618 			mc->mc_quarantine.ri_interval = NULL;
2619 			mc->mc_quarantine.ri_num = NULL;
2620 			if ( !META_BACK_QUARANTINE( mi ) ) {
2621 				ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
2622 			}
2623 		}
2624 
2625 		if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) {
2626 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2627 			return 1;
2628 		}
2629 
2630 		mc->mc_flags |= LDAP_BACK_F_QUARANTINE;
2631 		break;
2632 
2633 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
2634 	case LDAP_BACK_CFG_ST_REQUEST:
2635 	/* session tracking request */
2636 		if ( c->value_int ) {
2637 			mc->mc_flags |= LDAP_BACK_F_ST_REQUEST;
2638 		} else {
2639 			mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
2640 		}
2641 		break;
2642 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
2643 
2644 	case LDAP_BACK_CFG_SUFFIXM:	/* FALLTHRU */
2645 	case LDAP_BACK_CFG_REWRITE: {
2646 	/* rewrite stuff ... */
2647 		ConfigArgs ca = { 0 };
2648 		char *line, **argv;
2649 		struct rewrite_info *rwi;
2650 		int cnt = 0, argc, ix = c->valx;
2651 
2652 		if ( mt->mt_rwmap.rwm_bva_rewrite ) {
2653 			for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ )
2654 				/* count */ ;
2655 		}
2656 
2657 		if ( ix >= cnt || ix < 0 ) {
2658 			ix = cnt;
2659 		} else {
2660 			rwi = mt->mt_rwmap.rwm_rw;
2661 
2662 			mt->mt_rwmap.rwm_rw = NULL;
2663 			rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
2664 
2665 			/* re-parse all rewrite rules, up to the one
2666 			 * that needs to be added */
2667 			ca.be = c->be;
2668 			ca.fname = c->fname;
2669 			ca.lineno = c->lineno;
2670 			for ( i = 0; i < ix; i++ ) {
2671 				ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
2672 				ca.argc = 0;
2673 				config_fp_parse_line( &ca );
2674 
2675 				if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
2676 					rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
2677 				} else {
2678 					rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2679 						c->fname, c->lineno, ca.argc, ca.argv );
2680 				}
2681 				assert( rc == 0 );
2682 				ch_free( ca.argv );
2683 				ch_free( ca.tline );
2684 			}
2685 		}
2686 		argc = c->argc;
2687 		argv = c->argv;
2688 		if ( c->op != SLAP_CONFIG_ADD ) {
2689 			argc--;
2690 			argv++;
2691 		}
2692 		/* add the new rule */
2693 		if ( !strcasecmp( argv[0], "suffixmassage" )) {
2694 			rc = meta_suffixm_config( c, argc, argv, mt );
2695 		} else {
2696 			rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2697 						c->fname, c->lineno, argc, argv );
2698 		}
2699 		if ( rc ) {
2700 			if ( ix < cnt ) {
2701 				rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
2702 				mt->mt_rwmap.rwm_rw = rwi;
2703 			}
2704 			return 1;
2705 		}
2706 		if ( ix < cnt ) {
2707 			for ( ; i < cnt; i++ ) {
2708 				ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
2709 				ca.argc = 0;
2710 				config_fp_parse_line( &ca );
2711 
2712 				if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
2713 					rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
2714 				} else {
2715 					rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
2716 						c->fname, c->lineno, ca.argc, argv );
2717 				}
2718 				assert( rc == 0 );
2719 				ch_free( ca.argv );
2720 				ch_free( ca.tline );
2721 			}
2722 		}
2723 
2724 		/* save the rule info */
2725 		line = ldap_charray2str( argv, "\" \"" );
2726 		if ( line != NULL ) {
2727 			struct berval bv;
2728 			int len = strlen( argv[ 0 ] );
2729 
2730 			ber_str2bv( line, 0, 0, &bv );
2731 			AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
2732 				bv.bv_len - ( len + 1 ));
2733 			bv.bv_val[ bv.bv_len - 1] = '"';
2734 			ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv );
2735 			/* move it to the right slot */
2736 			if ( ix < cnt ) {
2737 				for ( i=cnt; i>ix; i-- )
2738 					mt->mt_rwmap.rwm_bva_rewrite[i+1] = mt->mt_rwmap.rwm_bva_rewrite[i];
2739 				mt->mt_rwmap.rwm_bva_rewrite[i] = bv;
2740 
2741 				/* destroy old rules */
2742 				rewrite_info_delete( &rwi );
2743 			}
2744 		}
2745 		} break;
2746 
2747 	case LDAP_BACK_CFG_MAP: {
2748 	/* objectclass/attribute mapping */
2749 		ConfigArgs ca = { 0 };
2750 		char *argv[5];
2751 		struct ldapmap rwm_oc;
2752 		struct ldapmap rwm_at;
2753 		int cnt = 0, ix = c->valx;
2754 
2755 		if ( mt->mt_rwmap.rwm_bva_map ) {
2756 			for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ )
2757 				/* count */ ;
2758 		}
2759 
2760 		if ( ix >= cnt || ix < 0 ) {
2761 			ix = cnt;
2762 		} else {
2763 			rwm_oc = mt->mt_rwmap.rwm_oc;
2764 			rwm_at = mt->mt_rwmap.rwm_at;
2765 
2766 			memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) );
2767 			memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) );
2768 
2769 			/* re-parse all mappings, up to the one
2770 			 * that needs to be added */
2771 			argv[0] = c->argv[0];
2772 			ca.fname = c->fname;
2773 			ca.lineno = c->lineno;
2774 			for ( i = 0; i < ix; i++ ) {
2775 				ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
2776 				ca.argc = 0;
2777 				config_fp_parse_line( &ca );
2778 
2779 				argv[1] = ca.argv[0];
2780 				argv[2] = ca.argv[1];
2781 				argv[3] = ca.argv[2];
2782 				argv[4] = ca.argv[3];
2783 				ch_free( ca.argv );
2784 				ca.argv = argv;
2785 				ca.argc++;
2786 				rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
2787 					&mt->mt_rwmap.rwm_at );
2788 
2789 				ch_free( ca.tline );
2790 				ca.tline = NULL;
2791 				ca.argv = NULL;
2792 
2793 				/* in case of failure, restore
2794 				 * the existing mapping */
2795 				if ( rc ) {
2796 					goto map_fail;
2797 				}
2798 			}
2799 		}
2800 		/* add the new mapping */
2801 		rc = ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc,
2802 					&mt->mt_rwmap.rwm_at );
2803 		if ( rc ) {
2804 			goto map_fail;
2805 		}
2806 
2807 		if ( ix < cnt ) {
2808 			for ( ; i<cnt ; cnt++ ) {
2809 				ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
2810 				ca.argc = 0;
2811 				config_fp_parse_line( &ca );
2812 
2813 				argv[1] = ca.argv[0];
2814 				argv[2] = ca.argv[1];
2815 				argv[3] = ca.argv[2];
2816 				argv[4] = ca.argv[3];
2817 
2818 				ch_free( ca.argv );
2819 				ca.argv = argv;
2820 				ca.argc++;
2821 				rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
2822 					&mt->mt_rwmap.rwm_at );
2823 
2824 				ch_free( ca.tline );
2825 				ca.tline = NULL;
2826 				ca.argv = NULL;
2827 
2828 				/* in case of failure, restore
2829 				 * the existing mapping */
2830 				if ( rc ) {
2831 					goto map_fail;
2832 				}
2833 			}
2834 		}
2835 
2836 		/* save the map info */
2837 		argv[0] = ldap_charray2str( &c->argv[ 1 ], " " );
2838 		if ( argv[0] != NULL ) {
2839 			struct berval bv;
2840 			ber_str2bv( argv[0], 0, 0, &bv );
2841 			ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv );
2842 			/* move it to the right slot */
2843 			if ( ix < cnt ) {
2844 				for ( i=cnt; i>ix; i-- )
2845 					mt->mt_rwmap.rwm_bva_map[i+1] = mt->mt_rwmap.rwm_bva_map[i];
2846 				mt->mt_rwmap.rwm_bva_map[i] = bv;
2847 
2848 				/* destroy old mapping */
2849 				meta_back_map_free( &rwm_oc );
2850 				meta_back_map_free( &rwm_at );
2851 			}
2852 		}
2853 		break;
2854 
2855 map_fail:;
2856 		if ( ix < cnt ) {
2857 			meta_back_map_free( &mt->mt_rwmap.rwm_oc );
2858 			meta_back_map_free( &mt->mt_rwmap.rwm_at );
2859 			mt->mt_rwmap.rwm_oc = rwm_oc;
2860 			mt->mt_rwmap.rwm_at = rwm_at;
2861 		}
2862 		} break;
2863 
2864 	case LDAP_BACK_CFG_NRETRIES: {
2865 		int		nretries = META_RETRY_UNDEFINED;
2866 
2867 		if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
2868 			nretries = META_RETRY_FOREVER;
2869 
2870 		} else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
2871 			nretries = META_RETRY_NEVER;
2872 
2873 		} else {
2874 			if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
2875 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
2876 					"unable to parse nretries {never|forever|<retries>}: \"%s\"",
2877 					c->argv[ 1 ] );
2878 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2879 				return 1;
2880 			}
2881 		}
2882 
2883 		mc->mc_nretries = nretries;
2884 		} break;
2885 
2886 	case LDAP_BACK_CFG_VERSION:
2887 		if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
2888 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2889 				"unsupported protocol version \"%s\"",
2890 				c->argv[ 1 ] );
2891 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2892 			return 1;
2893 		}
2894 		mc->mc_version = c->value_int;
2895 		break;
2896 
2897 	case LDAP_BACK_CFG_NOREFS:
2898 	/* do not return search references */
2899 		if ( c->value_int ) {
2900 			mc->mc_flags |= LDAP_BACK_F_NOREFS;
2901 		} else {
2902 			mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
2903 		}
2904 		break;
2905 
2906 	case LDAP_BACK_CFG_NOUNDEFFILTER:
2907 	/* do not propagate undefined search filters */
2908 		if ( c->value_int ) {
2909 			mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER;
2910 		} else {
2911 			mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
2912 		}
2913 		break;
2914 
2915 #ifdef SLAPD_META_CLIENT_PR
2916 	case LDAP_BACK_CFG_CLIENT_PR:
2917 		if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
2918 			mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
2919 
2920 		} else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
2921 			mc->mc_ps = META_CLIENT_PR_DISABLE;
2922 
2923 		} else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) {
2924 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2925 				"unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
2926 				c->argv[ 1 ] );
2927 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2928 			return( 1 );
2929 		}
2930 		break;
2931 #endif /* SLAPD_META_CLIENT_PR */
2932 
2933 	case LDAP_BACK_CFG_KEEPALIVE:
2934 		slap_keepalive_parse( ber_bvstrdup(c->argv[1]),
2935 				 &mt->mt_tls.sb_keepalive, 0, 0, 0);
2936 		break;
2937 
2938 	/* anything else */
2939 	default:
2940 		return SLAP_CONF_UNKNOWN;
2941 	}
2942 
2943 	return rc;
2944 }
2945 
2946 int
2947 meta_back_init_cf( BackendInfo *bi )
2948 {
2949 	int			rc;
2950 	AttributeDescription	*ad = NULL;
2951 	const char		*text;
2952 
2953 	/* Make sure we don't exceed the bits reserved for userland */
2954 	config_check_userland( LDAP_BACK_CFG_LAST );
2955 
2956 	bi->bi_cf_ocs = metaocs;
2957 
2958 	rc = config_register_schema( metacfg, metaocs );
2959 	if ( rc ) {
2960 		return rc;
2961 	}
2962 
2963 	/* setup olcDbAclPasswd and olcDbIDAssertPasswd
2964 	 * to be base64-encoded when written in LDIF form;
2965 	 * basically, we don't care if it fails */
2966 	rc = slap_str2ad( "olcDbACLPasswd", &ad, &text );
2967 	if ( rc ) {
2968 		Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
2969 			"warning, unable to get \"olcDbACLPasswd\" "
2970 			"attribute description: %d: %s\n",
2971 			rc, text, 0 );
2972 	} else {
2973 		(void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
2974 			ad->ad_type->sat_oid );
2975 	}
2976 
2977 	ad = NULL;
2978 	rc = slap_str2ad( "olcDbIDAssertPasswd", &ad, &text );
2979 	if ( rc ) {
2980 		Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
2981 			"warning, unable to get \"olcDbIDAssertPasswd\" "
2982 			"attribute description: %d: %s\n",
2983 			rc, text, 0 );
2984 	} else {
2985 		(void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
2986 			ad->ad_type->sat_oid );
2987 	}
2988 
2989 	return 0;
2990 }
2991 
2992 static int
2993 ldap_back_map_config(
2994 		ConfigArgs *c,
2995 		struct ldapmap	*oc_map,
2996 		struct ldapmap	*at_map )
2997 {
2998 	struct ldapmap		*map;
2999 	struct ldapmapping	*mapping;
3000 	char			*src, *dst;
3001 	int			is_oc = 0;
3002 
3003 	if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) {
3004 		map = oc_map;
3005 		is_oc = 1;
3006 
3007 	} else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) {
3008 		map = at_map;
3009 
3010 	} else {
3011 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3012 			"%s unknown argument \"%s\"",
3013 			c->argv[0], c->argv[1] );
3014 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3015 		return 1;
3016 	}
3017 
3018 	if ( !is_oc && map->map == NULL ) {
3019 		/* only init if required */
3020 		ldap_back_map_init( map, &mapping );
3021 	}
3022 
3023 	if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) {
3024 		if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) {
3025 			map->drop_missing = ( c->argc < 4 );
3026 			goto success_return;
3027 		}
3028 		src = dst = c->argv[ 3 ];
3029 
3030 	} else if ( c->argc < 4 ) {
3031 		src = "";
3032 		dst = c->argv[ 2 ];
3033 
3034 	} else {
3035 		src = c->argv[ 2 ];
3036 		dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] );
3037 	}
3038 
3039 	if ( ( map == at_map )
3040 		&& ( strcasecmp( src, "objectclass" ) == 0
3041 			|| strcasecmp( dst, "objectclass" ) == 0 ) )
3042 	{
3043 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3044 			"objectclass attribute cannot be mapped" );
3045 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3046 		return 1;
3047 	}
3048 
3049 	mapping = (struct ldapmapping *)ch_calloc( 2,
3050 		sizeof(struct ldapmapping) );
3051 	if ( mapping == NULL ) {
3052 		snprintf( c->cr_msg, sizeof(c->cr_msg),
3053 			"out of memory" );
3054 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3055 		return 1;
3056 	}
3057 	ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
3058 	ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
3059 	mapping[ 1 ].src = mapping[ 0 ].dst;
3060 	mapping[ 1 ].dst = mapping[ 0 ].src;
3061 
3062 	/*
3063 	 * schema check
3064 	 */
3065 	if ( is_oc ) {
3066 		if ( src[ 0 ] != '\0' ) {
3067 			if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
3068 				Debug( LDAP_DEBUG_ANY,
3069 	"warning, source objectClass '%s' should be defined in schema\n",
3070 					c->log, src, 0 );
3071 
3072 				/*
3073 				 * FIXME: this should become an err
3074 				 */
3075 				goto error_return;
3076 			}
3077 		}
3078 
3079 		if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
3080 			Debug( LDAP_DEBUG_ANY,
3081 	"warning, destination objectClass '%s' is not defined in schema\n",
3082 				c->log, dst, 0 );
3083 		}
3084 	} else {
3085 		int			rc;
3086 		const char		*text = NULL;
3087 		AttributeDescription	*ad = NULL;
3088 
3089 		if ( src[ 0 ] != '\0' ) {
3090 			rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
3091 			if ( rc != LDAP_SUCCESS ) {
3092 				Debug( LDAP_DEBUG_ANY,
3093 	"warning, source attributeType '%s' should be defined in schema\n",
3094 					c->log, src, 0 );
3095 
3096 				/*
3097 				 * FIXME: this should become an err
3098 				 */
3099 				/*
3100 				 * we create a fake "proxied" ad
3101 				 * and add it here.
3102 				 */
3103 
3104 				rc = slap_bv2undef_ad( &mapping[ 0 ].src,
3105 						&ad, &text, SLAP_AD_PROXIED );
3106 				if ( rc != LDAP_SUCCESS ) {
3107 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
3108 						"source attributeType \"%s\": %d (%s)",
3109 						src, rc, text ? text : "" );
3110 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3111 					goto error_return;
3112 				}
3113 			}
3114 
3115 			ad = NULL;
3116 		}
3117 
3118 		rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
3119 		if ( rc != LDAP_SUCCESS ) {
3120 			Debug( LDAP_DEBUG_ANY,
3121 	"warning, destination attributeType '%s' is not defined in schema\n",
3122 				c->log, dst, 0 );
3123 
3124 			/*
3125 			 * we create a fake "proxied" ad
3126 			 * and add it here.
3127 			 */
3128 
3129 			rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
3130 					&ad, &text, SLAP_AD_PROXIED );
3131 			if ( rc != LDAP_SUCCESS ) {
3132 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
3133 					"destination attributeType \"%s\": %d (%s)\n",
3134 					dst, rc, text ? text : "" );
3135 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3136 				return 1;
3137 			}
3138 		}
3139 	}
3140 
3141 	if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
3142 			|| avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
3143 	{
3144 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
3145 			"duplicate mapping found." );
3146 		Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
3147 		goto error_return;
3148 	}
3149 
3150 	if ( src[ 0 ] != '\0' ) {
3151 		avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
3152 					mapping_cmp, mapping_dup );
3153 	}
3154 	avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
3155 				mapping_cmp, mapping_dup );
3156 
3157 success_return:;
3158 	return 0;
3159 
3160 error_return:;
3161 	if ( mapping ) {
3162 		ch_free( mapping[ 0 ].src.bv_val );
3163 		ch_free( mapping[ 0 ].dst.bv_val );
3164 		ch_free( mapping );
3165 	}
3166 
3167 	return 1;
3168 }
3169 
3170 
3171 #ifdef ENABLE_REWRITE
3172 static char *
3173 suffix_massage_regexize( const char *s )
3174 {
3175 	char *res, *ptr;
3176 	const char *p, *r;
3177 	int i;
3178 
3179 	if ( s[ 0 ] == '\0' ) {
3180 		return ch_strdup( "^(.+)$" );
3181 	}
3182 
3183 	for ( i = 0, p = s;
3184 			( r = strchr( p, ',' ) ) != NULL;
3185 			p = r + 1, i++ )
3186 		;
3187 
3188 	res = ch_calloc( sizeof( char ),
3189 			strlen( s )
3190 			+ STRLENOF( "((.+),)?" )
3191 			+ STRLENOF( "[ ]?" ) * i
3192 			+ STRLENOF( "$" ) + 1 );
3193 
3194 	ptr = lutil_strcopy( res, "((.+),)?" );
3195 	for ( i = 0, p = s;
3196 			( r = strchr( p, ',' ) ) != NULL;
3197 			p = r + 1 , i++ ) {
3198 		ptr = lutil_strncopy( ptr, p, r - p + 1 );
3199 		ptr = lutil_strcopy( ptr, "[ ]?" );
3200 
3201 		if ( r[ 1 ] == ' ' ) {
3202 			r++;
3203 		}
3204 	}
3205 	ptr = lutil_strcopy( ptr, p );
3206 	ptr[ 0 ] = '$';
3207 	ptr++;
3208 	ptr[ 0 ] = '\0';
3209 
3210 	return res;
3211 }
3212 
3213 static char *
3214 suffix_massage_patternize( const char *s, const char *p )
3215 {
3216 	ber_len_t	len;
3217 	char		*res, *ptr;
3218 
3219 	len = strlen( p );
3220 
3221 	if ( s[ 0 ] == '\0' ) {
3222 		len++;
3223 	}
3224 
3225 	res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
3226 	if ( res == NULL ) {
3227 		return NULL;
3228 	}
3229 
3230 	ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
3231 	if ( s[ 0 ] == '\0' ) {
3232 		ptr[ 0 ] = ',';
3233 		ptr++;
3234 	}
3235 	lutil_strcopy( ptr, p );
3236 
3237 	return res;
3238 }
3239 
3240 int
3241 suffix_massage_config(
3242 		struct rewrite_info *info,
3243 		struct berval *pvnc,
3244 		struct berval *nvnc,
3245 		struct berval *prnc,
3246 		struct berval *nrnc
3247 )
3248 {
3249 	char *rargv[ 5 ];
3250 	int line = 0;
3251 
3252 	rargv[ 0 ] = "rewriteEngine";
3253 	rargv[ 1 ] = "on";
3254 	rargv[ 2 ] = NULL;
3255 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3256 
3257 	rargv[ 0 ] = "rewriteContext";
3258 	rargv[ 1 ] = "default";
3259 	rargv[ 2 ] = NULL;
3260 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3261 
3262 	rargv[ 0 ] = "rewriteRule";
3263 	rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
3264 	rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
3265 	rargv[ 3 ] = ":";
3266 	rargv[ 4 ] = NULL;
3267 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3268 	ch_free( rargv[ 1 ] );
3269 	ch_free( rargv[ 2 ] );
3270 
3271 	if ( BER_BVISEMPTY( pvnc ) ) {
3272 		rargv[ 0 ] = "rewriteRule";
3273 		rargv[ 1 ] = "^$";
3274 		rargv[ 2 ] = prnc->bv_val;
3275 		rargv[ 3 ] = ":";
3276 		rargv[ 4 ] = NULL;
3277 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3278 	}
3279 
3280 	rargv[ 0 ] = "rewriteContext";
3281 	rargv[ 1 ] = "searchEntryDN";
3282 	rargv[ 2 ] = NULL;
3283 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3284 
3285 	rargv[ 0 ] = "rewriteRule";
3286 	rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
3287 	rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
3288 	rargv[ 3 ] = ":";
3289 	rargv[ 4 ] = NULL;
3290 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3291 	ch_free( rargv[ 1 ] );
3292 	ch_free( rargv[ 2 ] );
3293 
3294 	if ( BER_BVISEMPTY( prnc ) ) {
3295 		rargv[ 0 ] = "rewriteRule";
3296 		rargv[ 1 ] = "^$";
3297 		rargv[ 2 ] = pvnc->bv_val;
3298 		rargv[ 3 ] = ":";
3299 		rargv[ 4 ] = NULL;
3300 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3301 	}
3302 
3303 	/* backward compatibility */
3304 	rargv[ 0 ] = "rewriteContext";
3305 	rargv[ 1 ] = "searchResult";
3306 	rargv[ 2 ] = "alias";
3307 	rargv[ 3 ] = "searchEntryDN";
3308 	rargv[ 4 ] = NULL;
3309 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3310 
3311 	rargv[ 0 ] = "rewriteContext";
3312 	rargv[ 1 ] = "matchedDN";
3313 	rargv[ 2 ] = "alias";
3314 	rargv[ 3 ] = "searchEntryDN";
3315 	rargv[ 4 ] = NULL;
3316 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3317 
3318 	rargv[ 0 ] = "rewriteContext";
3319 	rargv[ 1 ] = "searchAttrDN";
3320 	rargv[ 2 ] = "alias";
3321 	rargv[ 3 ] = "searchEntryDN";
3322 	rargv[ 4 ] = NULL;
3323 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
3324 
3325 	/* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
3326 	 * see servers/slapd/overlays/rwm.h for details */
3327         rargv[ 0 ] = "rewriteContext";
3328 	rargv[ 1 ] = "referralAttrDN";
3329 	rargv[ 2 ] = NULL;
3330 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3331 
3332 	rargv[ 0 ] = "rewriteContext";
3333 	rargv[ 1 ] = "referralDN";
3334 	rargv[ 2 ] = NULL;
3335 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
3336 
3337 	return 0;
3338 }
3339 #endif /* ENABLE_REWRITE */
3340 
3341