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