xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-meta/config.c (revision c8da0e5fefd3800856b306200a18b2315c7fbb9f)
1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-meta/config.c,v 1.74.2.13 2008/07/10 00:28:39 quanah Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2008 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 
27 #include <ac/string.h>
28 #include <ac/socket.h>
29 
30 #include "slap.h"
31 #include "lutil.h"
32 #include "../back-ldap/back-ldap.h"
33 #undef ldap_debug       /* silence a warning in ldap-int.h */
34 #include "../../../libraries/libldap/ldap-int.h"
35 #include "back-meta.h"
36 
37 static int
38 meta_back_new_target(
39 	metatarget_t	**mtp )
40 {
41 	char			*rargv[ 3 ];
42 	metatarget_t		*mt;
43 
44 	*mtp = NULL;
45 
46 	mt = ch_calloc( sizeof( metatarget_t ), 1 );
47 
48 	mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
49 	if ( mt->mt_rwmap.rwm_rw == NULL ) {
50 		ch_free( mt );
51 		return -1;
52 	}
53 
54 	/*
55 	 * the filter rewrite as a string must be disabled
56 	 * by default; it can be re-enabled by adding rules;
57 	 * this creates an empty rewriteContext
58 	 */
59 	rargv[ 0 ] = "rewriteContext";
60 	rargv[ 1 ] = "searchFilter";
61 	rargv[ 2 ] = NULL;
62 	rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
63 
64 	rargv[ 0 ] = "rewriteContext";
65 	rargv[ 1 ] = "default";
66 	rargv[ 2 ] = NULL;
67 	rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
68 
69 	ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
70 
71 	mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
72 	mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
73 	mt->mt_idassert_tls = SB_TLS_DEFAULT;
74 
75 	/* by default, use proxyAuthz control on each operation */
76 	mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
77 
78 	*mtp = mt;
79 
80 	return 0;
81 }
82 
83 static int
84 check_true_false( char *str )
85 {
86 	if ( strcasecmp( str, "true" ) == 0 || strcasecmp( str, "yes" ) == 0 ) {
87 		return 1;
88 	}
89 
90 	if ( strcasecmp( str, "false" ) == 0 || strcasecmp( str, "no" ) == 0 ) {
91 		return 0;
92 	}
93 
94 	return -1;
95 }
96 
97 
98 int
99 meta_back_db_config(
100 		BackendDB	*be,
101 		const char	*fname,
102 		int		lineno,
103 		int		argc,
104 		char		**argv
105 )
106 {
107 	metainfo_t	*mi = ( metainfo_t * )be->be_private;
108 
109 	assert( mi != NULL );
110 
111 	/* URI of server to query */
112 	if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
113 		int 		i = mi->mi_ntargets;
114 		LDAPURLDesc 	*ludp;
115 		struct berval	dn;
116 		int		rc;
117 		int		c;
118 
119 		metatarget_t	*mt;
120 
121 		char		**uris = NULL;
122 
123 		if ( argc == 1 ) {
124 			Debug( LDAP_DEBUG_ANY,
125 	"%s: line %d: missing URI "
126 	"in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
127 				fname, lineno, 0 );
128 			return 1;
129 		}
130 
131 		if ( be->be_nsuffix == NULL ) {
132 			Debug( LDAP_DEBUG_ANY,
133 	"%s: line %d: the suffix must be defined before any target.\n",
134 				fname, lineno, 0 );
135 			return 1;
136 		}
137 
138 		++mi->mi_ntargets;
139 
140 		mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
141 			sizeof( metatarget_t * ) * mi->mi_ntargets );
142 		if ( mi->mi_targets == NULL ) {
143 			Debug( LDAP_DEBUG_ANY,
144 	"%s: line %d: out of memory while storing server name"
145 	" in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
146 				fname, lineno, 0 );
147 			return 1;
148 		}
149 
150 		if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
151 			Debug( LDAP_DEBUG_ANY,
152 	"%s: line %d: unable to init server"
153 	" in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
154 				fname, lineno, 0 );
155 			return 1;
156 		}
157 
158 		mt = mi->mi_targets[ i ];
159 
160 		mt->mt_rebind_f = mi->mi_rebind_f;
161 		mt->mt_urllist_f = mi->mi_urllist_f;
162 		mt->mt_urllist_p = mt;
163 
164 		mt->mt_nretries = mi->mi_nretries;
165 		mt->mt_quarantine = mi->mi_quarantine;
166 		if ( META_BACK_QUARANTINE( mi ) ) {
167 			ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
168 		}
169 		mt->mt_flags = mi->mi_flags;
170 		mt->mt_version = mi->mi_version;
171 		mt->mt_network_timeout = mi->mi_network_timeout;
172 		mt->mt_bind_timeout = mi->mi_bind_timeout;
173 		for ( c = 0; c < SLAP_OP_LAST; c++ ) {
174 			mt->mt_timeout[ c ] = mi->mi_timeout[ c ];
175 		}
176 
177 		for ( c = 1; c < argc; c++ ) {
178 			char	**tmpuris = ldap_str2charray( argv[ c ], "\t" );
179 
180 			if ( tmpuris == NULL ) {
181 				Debug( LDAP_DEBUG_ANY,
182 	"%s: line %d: unable to parse URIs #%d"
183 	" in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
184 				fname, lineno, c - 1 );
185 				return 1;
186 			}
187 
188 			if ( c == 0 ) {
189 				uris = tmpuris;
190 
191 			} else {
192 				ldap_charray_merge( &uris, tmpuris );
193 				ldap_charray_free( tmpuris );
194 			}
195 		}
196 
197 		for ( c = 0; uris[ c ] != NULL; c++ ) {
198 			char *tmpuri = NULL;
199 
200 			/*
201 			 * uri MUST be legal!
202 			 */
203 			if ( ldap_url_parselist_ext( &ludp, uris[ c ], "\t",
204 					LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
205 				|| ludp->lud_next != NULL )
206 			{
207 				Debug( LDAP_DEBUG_ANY,
208 		"%s: line %d: unable to parse URI #%d"
209 		" in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
210 					fname, lineno, c );
211 				ldap_charray_free( uris );
212 				return 1;
213 			}
214 
215 			if ( c == 0 ) {
216 
217 				/*
218 				 * uri MUST have the <dn> part!
219 				 */
220 				if ( ludp->lud_dn == NULL ) {
221 					Debug( LDAP_DEBUG_ANY,
222 			"%s: line %d: missing <naming context> "
223 			" in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
224 						fname, lineno, 0 );
225 					ldap_free_urllist( ludp );
226 					ldap_charray_free( uris );
227 					return 1;
228 				}
229 
230 				/*
231 				 * copies and stores uri and suffix
232 				 */
233 				ber_str2bv( ludp->lud_dn, 0, 0, &dn );
234 				rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
235 					&mt->mt_nsuffix, NULL );
236 				if ( rc != LDAP_SUCCESS ) {
237 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
238 						"target \"%s\" DN is invalid\n",
239 						fname, lineno, argv[ 1 ] );
240 					ldap_free_urllist( ludp );
241 					ldap_charray_free( uris );
242 					return( 1 );
243 				}
244 
245 				ludp->lud_dn[ 0 ] = '\0';
246 
247 				switch ( ludp->lud_scope ) {
248 				case LDAP_SCOPE_DEFAULT:
249 					mt->mt_scope = LDAP_SCOPE_SUBTREE;
250 					break;
251 
252 				case LDAP_SCOPE_SUBTREE:
253 				case LDAP_SCOPE_SUBORDINATE:
254 					mt->mt_scope = ludp->lud_scope;
255 					break;
256 
257 				default:
258 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
259 						"invalid scope for target \"%s\"\n",
260 						fname, lineno, argv[ 1 ] );
261 					ldap_free_urllist( ludp );
262 					ldap_charray_free( uris );
263 					return( 1 );
264 				}
265 
266 			} else {
267 				/* check all, to apply the scope check on the first one */
268 				if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
269 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
270 						"multiple URIs must have "
271 						"no DN part\n",
272 						fname, lineno, 0 );
273 					ldap_free_urllist( ludp );
274 					ldap_charray_free( uris );
275 					return( 1 );
276 
277 				}
278 			}
279 
280 			tmpuri = ldap_url_list2urls( ludp );
281 			ldap_free_urllist( ludp );
282 			if ( tmpuri == NULL ) {
283 				Debug( LDAP_DEBUG_ANY, "%s: line %d: no memory?\n",
284 					fname, lineno, 0 );
285 				ldap_charray_free( uris );
286 				return( 1 );
287 			}
288 			ldap_memfree( uris[ c ] );
289 			uris[ c ] = tmpuri;
290 		}
291 
292 		mt->mt_uri = ldap_charray2str( uris, " " );
293 		ldap_charray_free( uris );
294 		if ( mt->mt_uri == NULL) {
295 			Debug( LDAP_DEBUG_ANY, "%s: line %d: no memory?\n",
296 				fname, lineno, 0 );
297 			return( 1 );
298 		}
299 
300 		/*
301 		 * uri MUST be a branch of suffix!
302 		 */
303 		for ( c = 0; !BER_BVISNULL( &be->be_nsuffix[ c ] ); c++ ) {
304 			if ( dnIsSuffix( &mt->mt_nsuffix, &be->be_nsuffix[ c ] ) ) {
305 				break;
306 			}
307 		}
308 
309 		if ( BER_BVISNULL( &be->be_nsuffix[ c ] ) ) {
310 			Debug( LDAP_DEBUG_ANY,
311 	"%s: line %d: <naming context> of URI must be within the naming context of this database.\n",
312 				fname, lineno, 0 );
313 			return 1;
314 		}
315 
316 	/* subtree-exclude */
317 	} else if ( strcasecmp( argv[ 0 ], "subtree-exclude" ) == 0 ) {
318 		int 		i = mi->mi_ntargets - 1;
319 		struct berval	dn, ndn;
320 
321 		if ( i < 0 ) {
322 			Debug( LDAP_DEBUG_ANY,
323 	"%s: line %d: need \"uri\" directive first\n",
324 				fname, lineno, 0 );
325 			return 1;
326 		}
327 
328 		switch ( argc ) {
329 		case 1:
330 			Debug( LDAP_DEBUG_ANY,
331 	"%s: line %d: missing DN in \"subtree-exclude <DN>\" line\n",
332 			    fname, lineno, 0 );
333 			return 1;
334 
335 		case 2:
336 			break;
337 
338 		default:
339 			Debug( LDAP_DEBUG_ANY,
340 	"%s: line %d: too many args in \"subtree-exclude <DN>\" line\n",
341 			    fname, lineno, 0 );
342 			return 1;
343 		}
344 
345 		ber_str2bv( argv[ 1 ], 0, 0, &dn );
346 		if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
347 			!= LDAP_SUCCESS )
348 		{
349 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
350 					"subtree-exclude DN=\"%s\" is invalid\n",
351 					fname, lineno, argv[ 1 ] );
352 			return( 1 );
353 		}
354 
355 		if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_nsuffix ) ) {
356 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
357 					"subtree-exclude DN=\"%s\" "
358 					"must be subtree of target\n",
359 					fname, lineno, argv[ 1 ] );
360 			ber_memfree( ndn.bv_val );
361 			return( 1 );
362 		}
363 
364 		if ( mi->mi_targets[ i ]->mt_subtree_exclude != NULL ) {
365 			int		j;
366 
367 			for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ); j++ )
368 			{
369 				if ( dnIsSuffix( &mi->mi_targets[ i ]->mt_subtree_exclude[ j ], &ndn ) ) {
370 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
371 							"subtree-exclude DN=\"%s\" "
372 							"is suffix of another subtree-exclude\n",
373 							fname, lineno, argv[ 1 ] );
374 					/* reject, because it might be superior
375 					 * to more than one subtree-exclude */
376 					ber_memfree( ndn.bv_val );
377 					return( 1 );
378 
379 				} else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ]->mt_subtree_exclude[ j ] ) ) {
380 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
381 							"another subtree-exclude is suffix of "
382 							"subtree-exclude DN=\"%s\"\n",
383 							fname, lineno, argv[ 1 ] );
384 					ber_memfree( ndn.bv_val );
385 					return( 0 );
386 				}
387 			}
388 		}
389 
390 		ber_bvarray_add( &mi->mi_targets[ i ]->mt_subtree_exclude, &ndn );
391 
392 	/* default target directive */
393 	} else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
394 		int 		i = mi->mi_ntargets - 1;
395 
396 		if ( argc == 1 ) {
397  			if ( i < 0 ) {
398 				Debug( LDAP_DEBUG_ANY,
399 	"%s: line %d: \"default-target\" alone need be"
400        	" inside a \"uri\" directive\n",
401 					fname, lineno, 0 );
402 				return 1;
403 			}
404 			mi->mi_defaulttarget = i;
405 
406 		} else {
407 			if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) {
408 				if ( i >= 0 ) {
409 					Debug( LDAP_DEBUG_ANY,
410 	"%s: line %d: \"default-target none\""
411        	" should go before uri definitions\n",
412 						fname, lineno, 0 );
413 				}
414 				mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
415 
416 			} else {
417 
418 				if ( lutil_atoi( &mi->mi_defaulttarget, argv[ 1 ] ) != 0
419 					|| mi->mi_defaulttarget < 0
420 					|| mi->mi_defaulttarget >= i - 1 )
421 				{
422 					Debug( LDAP_DEBUG_ANY,
423 	"%s: line %d: illegal target number %d\n",
424 						fname, lineno, mi->mi_defaulttarget );
425 					return 1;
426 				}
427 			}
428 		}
429 
430 	/* ttl of dn cache */
431 	} else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) {
432 		if ( argc != 2 ) {
433 			Debug( LDAP_DEBUG_ANY,
434 	"%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n",
435 				fname, lineno, 0 );
436 			return 1;
437 		}
438 
439 		if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
440 			mi->mi_cache.ttl = META_DNCACHE_FOREVER;
441 
442 		} else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) {
443 			mi->mi_cache.ttl = META_DNCACHE_DISABLED;
444 
445 		} else {
446 			unsigned long	t;
447 
448 			if ( lutil_parse_time( argv[ 1 ], &t ) != 0 ) {
449 				Debug( LDAP_DEBUG_ANY,
450 	"%s: line %d: unable to parse ttl \"%s\" in \"dncache-ttl <ttl>\" line\n",
451 					fname, lineno, argv[ 1 ] );
452 				return 1;
453 			}
454 			mi->mi_cache.ttl = (time_t)t;
455 		}
456 
457 	/* network timeout when connecting to ldap servers */
458 	} else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) {
459 		unsigned long	t;
460 		time_t		*tp = mi->mi_ntargets ?
461 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_network_timeout
462 				: &mi->mi_network_timeout;
463 
464 		if ( argc != 2 ) {
465 			Debug( LDAP_DEBUG_ANY,
466 	"%s: line %d: missing network timeout in \"network-timeout <seconds>\" line\n",
467 				fname, lineno, 0 );
468 			return 1;
469 		}
470 
471 		if ( lutil_parse_time( argv[ 1 ], &t ) ) {
472 			Debug( LDAP_DEBUG_ANY,
473 	"%s: line %d: unable to parse timeout \"%s\" in \"network-timeout <seconds>\" line\n",
474 				fname, lineno, argv[ 1 ] );
475 			return 1;
476 
477 		}
478 
479 		*tp = (time_t)t;
480 
481 	/* idle timeout when connecting to ldap servers */
482 	} else if ( strcasecmp( argv[ 0 ], "idle-timeout" ) == 0 ) {
483 		unsigned long	t;
484 
485 		switch ( argc ) {
486 		case 1:
487 			Debug( LDAP_DEBUG_ANY,
488 	"%s: line %d: missing timeout value in \"idle-timeout <seconds>\" line\n",
489 				fname, lineno, 0 );
490 			return 1;
491 		case 2:
492 			break;
493 		default:
494 			Debug( LDAP_DEBUG_ANY,
495 	"%s: line %d: extra cruft after timeout value in \"idle-timeout <seconds>\" line\n",
496 				fname, lineno, 0 );
497 			return 1;
498 		}
499 
500 		if ( lutil_parse_time( argv[ 1 ], &t ) ) {
501 			Debug( LDAP_DEBUG_ANY,
502 	"%s: line %d: unable to parse timeout \"%s\" in \"idle-timeout <seconds>\" line\n",
503 				fname, lineno, argv[ 1 ] );
504 			return 1;
505 
506 		}
507 
508 		mi->mi_idle_timeout = (time_t)t;
509 
510 	/* conn ttl */
511 	} else if ( strcasecmp( argv[ 0 ], "conn-ttl" ) == 0 ) {
512 		unsigned long	t;
513 
514 		switch ( argc ) {
515 		case 1:
516 			Debug( LDAP_DEBUG_ANY,
517 	"%s: line %d: missing ttl value in \"conn-ttl <seconds>\" line\n",
518 				fname, lineno, 0 );
519 			return 1;
520 		case 2:
521 			break;
522 		default:
523 			Debug( LDAP_DEBUG_ANY,
524 	"%s: line %d: extra cruft after ttl value in \"conn-ttl <seconds>\" line\n",
525 				fname, lineno, 0 );
526 			return 1;
527 		}
528 
529 		if ( lutil_parse_time( argv[ 1 ], &t ) ) {
530 			Debug( LDAP_DEBUG_ANY,
531 	"%s: line %d: unable to parse ttl \"%s\" in \"conn-ttl <seconds>\" line\n",
532 				fname, lineno, argv[ 1 ] );
533 			return 1;
534 
535 		}
536 
537 		mi->mi_conn_ttl = (time_t)t;
538 
539 	/* bind timeout when connecting to ldap servers */
540 	} else if ( strcasecmp( argv[ 0 ], "bind-timeout" ) == 0 ) {
541 		unsigned long	t;
542 		struct timeval	*tp = mi->mi_ntargets ?
543 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_bind_timeout
544 				: &mi->mi_bind_timeout;
545 
546 		switch ( argc ) {
547 		case 1:
548 			Debug( LDAP_DEBUG_ANY,
549 	"%s: line %d: missing timeout value in \"bind-timeout <microseconds>\" line\n",
550 				fname, lineno, 0 );
551 			return 1;
552 		case 2:
553 			break;
554 		default:
555 			Debug( LDAP_DEBUG_ANY,
556 	"%s: line %d: extra cruft after timeout value in \"bind-timeout <microseconds>\" line\n",
557 				fname, lineno, 0 );
558 			return 1;
559 		}
560 
561 		if ( lutil_atoul( &t, argv[ 1 ] ) != 0 ) {
562 			Debug( LDAP_DEBUG_ANY,
563 	"%s: line %d: unable to parse timeout \"%s\" in \"bind-timeout <microseconds>\" line\n",
564 				fname, lineno, argv[ 1 ] );
565 			return 1;
566 
567 		}
568 
569 		tp->tv_sec = t/1000000;
570 		tp->tv_usec = t%1000000;
571 
572 	/* name to use for meta_back_group */
573 	} else if ( strcasecmp( argv[ 0 ], "acl-authcDN" ) == 0
574 			|| strcasecmp( argv[ 0 ], "binddn" ) == 0 )
575 	{
576 		int 		i = mi->mi_ntargets - 1;
577 		struct berval	dn;
578 
579 		if ( i < 0 ) {
580 			Debug( LDAP_DEBUG_ANY,
581 	"%s: line %d: need \"uri\" directive first\n",
582 				fname, lineno, 0 );
583 			return 1;
584 		}
585 
586 		if ( argc != 2 ) {
587 			Debug( LDAP_DEBUG_ANY,
588 	"%s: line %d: missing name in \"binddn <name>\" line\n",
589 				fname, lineno, 0 );
590 			return 1;
591 		}
592 
593 		if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) {
594 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
595 				"\"binddn\" statement is deprecated; "
596 				"use \"acl-authcDN\" instead\n",
597 				fname, lineno, 0 );
598 			/* FIXME: some day we'll need to throw an error */
599 		}
600 
601 		ber_str2bv( argv[ 1 ], 0, 0, &dn );
602 		if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ]->mt_binddn,
603 			NULL ) != LDAP_SUCCESS )
604 		{
605 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
606 					"bind DN '%s' is invalid\n",
607 					fname, lineno, argv[ 1 ] );
608 			return( 1 );
609 		}
610 
611 	/* password to use for meta_back_group */
612 	} else if ( strcasecmp( argv[ 0 ], "acl-passwd" ) == 0
613 			|| strcasecmp( argv[ 0 ], "bindpw" ) == 0 )
614 	{
615 		int 		i = mi->mi_ntargets - 1;
616 
617 		if ( i < 0 ) {
618 			Debug( LDAP_DEBUG_ANY,
619 	"%s: line %d: need \"uri\" directive first\n",
620 				fname, lineno, 0 );
621 			return 1;
622 		}
623 
624 		if ( argc != 2 ) {
625 			Debug( LDAP_DEBUG_ANY,
626 	"%s: line %d: missing password in \"bindpw <password>\" line\n",
627 			    fname, lineno, 0 );
628 			return 1;
629 		}
630 
631 		if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) {
632 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
633 				"\"bindpw\" statement is deprecated; "
634 				"use \"acl-passwd\" instead\n",
635 				fname, lineno, 0 );
636 			/* FIXME: some day we'll need to throw an error */
637 		}
638 
639 		ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ]->mt_bindpw );
640 
641 	/* save bind creds for referral rebinds? */
642 	} else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) {
643 		if ( argc > 2 ) {
644 			Debug( LDAP_DEBUG_ANY,
645 	"%s: line %d: \"rebind-as-user {NO|yes}\" takes 1 argument.\n",
646 			    fname, lineno, 0 );
647 			return( 1 );
648 		}
649 
650 		if ( argc == 1 ) {
651 			Debug( LDAP_DEBUG_ANY,
652 	"%s: line %d: deprecated use of \"rebind-as-user {FALSE|true}\" with no arguments.\n",
653 			    fname, lineno, 0 );
654 			mi->mi_flags |= LDAP_BACK_F_SAVECRED;
655 
656 		} else {
657 			switch ( check_true_false( argv[ 1 ] ) ) {
658 			case 0:
659 				mi->mi_flags &= ~LDAP_BACK_F_SAVECRED;
660 				break;
661 
662 			case 1:
663 				mi->mi_flags |= LDAP_BACK_F_SAVECRED;
664 				break;
665 
666 			default:
667 				Debug( LDAP_DEBUG_ANY,
668 	"%s: line %d: \"rebind-as-user {FALSE|true}\" unknown argument \"%s\".\n",
669 				    fname, lineno, argv[ 1 ] );
670 				return 1;
671 			}
672 		}
673 
674 	} else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) {
675 		unsigned	*flagsp = mi->mi_ntargets ?
676 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
677 				: &mi->mi_flags;
678 
679 		if ( argc != 2 ) {
680 			Debug( LDAP_DEBUG_ANY,
681 	"%s: line %d: \"chase-referrals {TRUE|false}\" needs 1 argument.\n",
682 				fname, lineno, 0 );
683 			return( 1 );
684 		}
685 
686 		/* this is the default; we add it because the default might change... */
687 		switch ( check_true_false( argv[ 1 ] ) ) {
688 		case 1:
689 			*flagsp |= LDAP_BACK_F_CHASE_REFERRALS;
690 			break;
691 
692 		case 0:
693 			*flagsp &= ~LDAP_BACK_F_CHASE_REFERRALS;
694 			break;
695 
696 		default:
697 			Debug( LDAP_DEBUG_ANY,
698 		"%s: line %d: \"chase-referrals {TRUE|false}\": unknown argument \"%s\".\n",
699 				fname, lineno, argv[ 1 ] );
700 			return( 1 );
701 		}
702 
703 	} else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) {
704 		unsigned	*flagsp = mi->mi_ntargets ?
705 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
706 				: &mi->mi_flags;
707 
708 		if ( argc != 2 ) {
709 			Debug( LDAP_DEBUG_ANY,
710 		"%s: line %d: \"tls <what>\" needs 1 argument.\n",
711 				fname, lineno, 0 );
712 			return( 1 );
713 		}
714 
715 		/* start */
716 		if ( strcasecmp( argv[ 1 ], "start" ) == 0 ) {
717 			*flagsp |= ( LDAP_BACK_F_USE_TLS | LDAP_BACK_F_TLS_CRITICAL );
718 
719 		/* try start tls */
720 		} else if ( strcasecmp( argv[ 1 ], "try-start" ) == 0 ) {
721 			*flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
722 			*flagsp |= LDAP_BACK_F_USE_TLS;
723 
724 		/* propagate start tls */
725 		} else if ( strcasecmp( argv[ 1 ], "propagate" ) == 0 ) {
726 			*flagsp |= ( LDAP_BACK_F_PROPAGATE_TLS | LDAP_BACK_F_TLS_CRITICAL );
727 
728 		/* try start tls */
729 		} else if ( strcasecmp( argv[ 1 ], "try-propagate" ) == 0 ) {
730 			*flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
731 			*flagsp |= LDAP_BACK_F_PROPAGATE_TLS;
732 
733 		} else {
734 			Debug( LDAP_DEBUG_ANY,
735 		"%s: line %d: \"tls <what>\": unknown argument \"%s\".\n",
736 				fname, lineno, argv[ 1 ] );
737 			return( 1 );
738 		}
739 
740 	} else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) {
741 		unsigned	*flagsp = mi->mi_ntargets ?
742 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
743 				: &mi->mi_flags;
744 
745 		if ( argc != 2 ) {
746 			Debug( LDAP_DEBUG_ANY,
747 		"%s: line %d: \"t-f-support {FALSE|true|discover}\" needs 1 argument.\n",
748 				fname, lineno, 0 );
749 			return( 1 );
750 		}
751 
752 		switch ( check_true_false( argv[ 1 ] ) ) {
753 		case 0:
754 			*flagsp &= ~LDAP_BACK_F_T_F_MASK2;
755 			break;
756 
757 		case 1:
758 			*flagsp |= LDAP_BACK_F_T_F;
759 			break;
760 
761 		default:
762 			if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
763 				*flagsp |= LDAP_BACK_F_T_F_DISCOVER;
764 
765 			} else {
766 				Debug( LDAP_DEBUG_ANY,
767 	"%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
768 					fname, lineno, argv[ 1 ] );
769 				return 1;
770 			}
771 			break;
772 		}
773 
774 	/* onerr? */
775 	} else if ( strcasecmp( argv[ 0 ], "onerr" ) == 0 ) {
776 		if ( argc != 2 ) {
777 			Debug( LDAP_DEBUG_ANY,
778 	"%s: line %d: \"onerr {CONTINUE|report|stop}\" takes 1 argument\n",
779 				fname, lineno, 0 );
780 			return( 1 );
781 		}
782 
783 		if ( strcasecmp( argv[ 1 ], "continue" ) == 0 ) {
784 			mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
785 
786 		} else if ( strcasecmp( argv[ 1 ], "stop" ) == 0 ) {
787 			mi->mi_flags |= META_BACK_F_ONERR_STOP;
788 
789 		} else if ( strcasecmp( argv[ 1 ], "report" ) == 0 ) {
790 			mi->mi_flags |= META_BACK_F_ONERR_REPORT;
791 
792 		} else {
793 			Debug( LDAP_DEBUG_ANY,
794 	"%s: line %d: \"onerr {CONTINUE|report|stop}\": invalid arg \"%s\".\n",
795 				fname, lineno, argv[ 1 ] );
796 			return 1;
797 		}
798 
799 	/* bind-defer? */
800 	} else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0
801 		|| strcasecmp( argv[ 0 ], "root-bind-defer" ) == 0 )
802 	{
803 		if ( argc != 2 ) {
804 			Debug( LDAP_DEBUG_ANY,
805 	"%s: line %d: \"[pseudo]root-bind-defer {TRUE|false}\" takes 1 argument\n",
806 				fname, lineno, 0 );
807 			return( 1 );
808 		}
809 
810 		switch ( check_true_false( argv[ 1 ] ) ) {
811 		case 0:
812 			mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
813 			break;
814 
815 		case 1:
816 			mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
817 			break;
818 
819 		default:
820 			Debug( LDAP_DEBUG_ANY,
821 	"%s: line %d: \"[pseudo]root-bind-defer {TRUE|false}\": invalid arg \"%s\".\n",
822 				fname, lineno, argv[ 1 ] );
823 			return 1;
824 		}
825 
826 	/* single-conn? */
827 	} else if ( strcasecmp( argv[ 0 ], "single-conn" ) == 0 ) {
828 		if ( argc != 2 ) {
829 			Debug( LDAP_DEBUG_ANY,
830 	"%s: line %d: \"single-conn {FALSE|true}\" takes 1 argument\n",
831 				fname, lineno, 0 );
832 			return( 1 );
833 		}
834 
835 		if ( mi->mi_ntargets > 0 ) {
836 			Debug( LDAP_DEBUG_ANY,
837 	"%s: line %d: \"single-conn\" must appear before target definitions\n",
838 				fname, lineno, 0 );
839 			return( 1 );
840 		}
841 
842 		switch ( check_true_false( argv[ 1 ] ) ) {
843 		case 0:
844 			mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
845 			break;
846 
847 		case 1:
848 			mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
849 			break;
850 
851 		default:
852 			Debug( LDAP_DEBUG_ANY,
853 	"%s: line %d: \"single-conn {FALSE|true}\": invalid arg \"%s\".\n",
854 				fname, lineno, argv[ 1 ] );
855 			return 1;
856 		}
857 
858 	/* use-temporaries? */
859 	} else if ( strcasecmp( argv[ 0 ], "use-temporary-conn" ) == 0 ) {
860 		if ( argc != 2 ) {
861 			Debug( LDAP_DEBUG_ANY,
862 	"%s: line %d: \"use-temporary-conn {FALSE|true}\" takes 1 argument\n",
863 				fname, lineno, 0 );
864 			return( 1 );
865 		}
866 
867 		if ( mi->mi_ntargets > 0 ) {
868 			Debug( LDAP_DEBUG_ANY,
869 	"%s: line %d: \"use-temporary-conn\" must appear before target definitions\n",
870 				fname, lineno, 0 );
871 			return( 1 );
872 		}
873 
874 		switch ( check_true_false( argv[ 1 ] ) ) {
875 		case 0:
876 			mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
877 			break;
878 
879 		case 1:
880 			mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
881 			break;
882 
883 		default:
884 			Debug( LDAP_DEBUG_ANY,
885 	"%s: line %d: \"use-temporary-conn {FALSE|true}\": invalid arg \"%s\".\n",
886 				fname, lineno, argv[ 1 ] );
887 			return 1;
888 		}
889 
890 	/* privileged connections pool max size ? */
891 	} else if ( strcasecmp( argv[ 0 ], "conn-pool-max" ) == 0 ) {
892 		if ( argc != 2 ) {
893 			Debug( LDAP_DEBUG_ANY,
894 	"%s: line %d: \"conn-pool-max <n>\" takes 1 argument\n",
895 				fname, lineno, 0 );
896 			return( 1 );
897 		}
898 
899 		if ( mi->mi_ntargets > 0 ) {
900 			Debug( LDAP_DEBUG_ANY,
901 	"%s: line %d: \"conn-pool-max\" must appear before target definitions\n",
902 				fname, lineno, 0 );
903 			return( 1 );
904 		}
905 
906 		if ( lutil_atoi( &mi->mi_conn_priv_max, argv[1] )
907 			|| mi->mi_conn_priv_max < LDAP_BACK_CONN_PRIV_MIN
908 			|| mi->mi_conn_priv_max > LDAP_BACK_CONN_PRIV_MAX )
909 		{
910 			Debug( LDAP_DEBUG_ANY,
911 	"%s: line %d: \"conn-pool-max <n>\": invalid arg \"%s\".\n",
912 				fname, lineno, argv[ 1 ] );
913 			return 1;
914 		}
915 
916 	} else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
917 		unsigned 	flag = 0;
918 		unsigned	*flagsp = mi->mi_ntargets ?
919 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
920 				: &mi->mi_flags;
921 
922 		if ( argc != 2 ) {
923 			Debug( LDAP_DEBUG_ANY,
924 	"%s: line %d: \"cancel {abandon|ignore|exop}\" takes 1 argument\n",
925 				fname, lineno, 0 );
926 			return( 1 );
927 		}
928 
929 		if ( strcasecmp( argv[ 1 ], "abandon" ) == 0 ) {
930 			flag = LDAP_BACK_F_CANCEL_ABANDON;
931 
932 		} else if ( strcasecmp( argv[ 1 ], "ignore" ) == 0 ) {
933 			flag = LDAP_BACK_F_CANCEL_IGNORE;
934 
935 		} else if ( strcasecmp( argv[ 1 ], "exop" ) == 0 ) {
936 			flag = LDAP_BACK_F_CANCEL_EXOP;
937 
938 		} else if ( strcasecmp( argv[ 1 ], "exop-discover" ) == 0 ) {
939 			flag = LDAP_BACK_F_CANCEL_EXOP_DISCOVER;
940 
941 		} else {
942 			Debug( LDAP_DEBUG_ANY,
943 	"%s: line %d: \"cancel {abandon|ignore|exop[-discover]}\": unknown mode \"%s\" \n",
944 				fname, lineno, argv[ 1 ] );
945 			return( 1 );
946 		}
947 
948 		*flagsp &= ~LDAP_BACK_F_CANCEL_MASK2;
949 		*flagsp |= flag;
950 
951 	} else if ( strcasecmp( argv[ 0 ], "timeout" ) == 0 ) {
952 		char	*sep;
953 		time_t	*tv = mi->mi_ntargets ?
954 				mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_timeout
955 				: mi->mi_timeout;
956 		int	c;
957 
958 		if ( argc < 2 ) {
959 			Debug( LDAP_DEBUG_ANY,
960 	"%s: line %d: \"timeout [{add|bind|delete|modify|modrdn}=]<val> [...]\" takes at least 1 argument\n",
961 				fname, lineno, 0 );
962 			return( 1 );
963 		}
964 
965 		for ( c = 1; c < argc; c++ ) {
966 			time_t		*t = NULL;
967 			unsigned long	val;
968 
969 			sep = strchr( argv[ c ], '=' );
970 			if ( sep != NULL ) {
971 				size_t	len = sep - argv[ c ];
972 
973 				if ( strncasecmp( argv[ c ], "bind", len ) == 0 ) {
974 					t = &tv[ SLAP_OP_BIND ];
975 				/* unbind makes little sense */
976 				} else if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
977 					t = &tv[ SLAP_OP_ADD ];
978 				} else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
979 					t = &tv[ SLAP_OP_DELETE ];
980 				} else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
981 					t = &tv[ SLAP_OP_MODRDN ];
982 				} else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
983 					t = &tv[ SLAP_OP_MODIFY ];
984 				} else if ( strncasecmp( argv[ c ], "compare", len ) == 0 ) {
985 					t = &tv[ SLAP_OP_COMPARE ];
986 				} else if ( strncasecmp( argv[ c ], "search", len ) == 0 ) {
987 					t = &tv[ SLAP_OP_SEARCH ];
988 				/* abandon makes little sense */
989 #if 0				/* not implemented yet */
990 				} else if ( strncasecmp( argv[ c ], "extended", len ) == 0 ) {
991 					t = &tv[ SLAP_OP_EXTENDED ];
992 #endif
993 				} else {
994 					char	buf[ SLAP_TEXT_BUFLEN ];
995 					snprintf( buf, sizeof( buf ),
996 						"unknown/unhandled operation \"%s\" for timeout #%d",
997 						argv[ c ], c - 1 );
998 					Debug( LDAP_DEBUG_ANY,
999 						"%s: line %d: %s.\n",
1000 						fname, lineno, buf );
1001 					return 1;
1002 				}
1003 				sep++;
1004 
1005 			} else {
1006 				sep = argv[ c ];
1007 			}
1008 
1009 			if ( lutil_parse_time( sep, &val ) != 0 ) {
1010 				Debug( LDAP_DEBUG_ANY,
1011 		"%s: line %d: unable to parse value \"%s\" for timeout.\n",
1012 					fname, lineno, sep );
1013 				return 1;
1014 			}
1015 
1016 			if ( t ) {
1017 				*t = (time_t)val;
1018 
1019 			} else {
1020 				int	i;
1021 
1022 				for ( i = 0; i < SLAP_OP_LAST; i++ ) {
1023 					tv[ i ] = (time_t)val;
1024 				}
1025 			}
1026 		}
1027 
1028 	/* name to use as pseudo-root dn */
1029 	} else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) {
1030 		int 		i = mi->mi_ntargets - 1;
1031 
1032 		if ( i < 0 ) {
1033 			Debug( LDAP_DEBUG_ANY,
1034 	"%s: line %d: need \"uri\" directive first\n",
1035 				fname, lineno, 0 );
1036 			return 1;
1037 		}
1038 
1039 		if ( argc != 2 ) {
1040 			Debug( LDAP_DEBUG_ANY,
1041 	"%s: line %d: missing name in \"pseudorootdn <name>\" line\n",
1042 				fname, lineno, 0 );
1043 			return 1;
1044 		}
1045 
1046 		/*
1047 		 * exact replacement:
1048 		 *
1049 
1050 idassert-bind	bindmethod=simple
1051 		binddn=<pseudorootdn>
1052 		credentials=<pseudorootpw>
1053 		mode=none
1054 		flags=non-prescriptive
1055 idassert-authzFrom	"dn:<rootdn>"
1056 
1057 		 * so that only when authc'd as <rootdn> the proxying occurs
1058 		 * rebinding as the <pseudorootdn> without proxyAuthz.
1059 		 */
1060 
1061 		Debug( LDAP_DEBUG_ANY,
1062 			"%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
1063 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
1064 			fname, lineno, 0 );
1065 
1066 		{
1067 			char	binddn[ SLAP_TEXT_BUFLEN ];
1068 			char	*cargv[] = {
1069 				"idassert-bind",
1070 				"bindmethod=simple",
1071 				NULL,
1072 				"mode=none",
1073 				"flags=non-prescriptive",
1074 				NULL
1075 			};
1076 			int	cargc = 5;
1077 			int	rc;
1078 
1079 			if ( BER_BVISNULL( &be->be_rootndn ) ) {
1080 				Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
1081 					fname, lineno, 0 );
1082 				return 1;
1083 			}
1084 
1085 			if ( snprintf( binddn, sizeof( binddn ), "binddn=%s", argv[ 1 ] ) >= sizeof( binddn ) ) {
1086 				Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootdn\" too long.\n",
1087 					fname, lineno, 0 );
1088 				return 1;
1089 			}
1090 			cargv[ 2 ] = binddn;
1091 
1092 			rc = mi->mi_ldap_extra->idassert_parse_cf( fname, lineno, cargc, cargv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1093 			if ( rc == 0 ) {
1094 				struct berval	bv;
1095 
1096 				if ( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz != NULL ) {
1097 					Debug( LDAP_DEBUG_ANY, "%s: line %d: \"idassert-authzFrom\" already defined (discarded).\n",
1098 						fname, lineno, 0 );
1099 					ber_bvarray_free( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz );
1100 					mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz = NULL;
1101 				}
1102 
1103 				assert( !BER_BVISNULL( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authcDN ) );
1104 
1105 				bv.bv_len = STRLENOF( "dn:" ) + be->be_rootndn.bv_len;
1106 				bv.bv_val = ber_memalloc( bv.bv_len + 1 );
1107 				AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
1108 				AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], be->be_rootndn.bv_val, be->be_rootndn.bv_len + 1 );
1109 
1110 				ber_bvarray_add( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz, &bv );
1111 			}
1112 
1113 			return rc;
1114 		}
1115 
1116 	/* password to use as pseudo-root */
1117 	} else if ( strcasecmp( argv[ 0 ], "pseudorootpw" ) == 0 ) {
1118 		int 		i = mi->mi_ntargets - 1;
1119 
1120 		if ( i < 0 ) {
1121 			Debug( LDAP_DEBUG_ANY,
1122 	"%s: line %d: need \"uri\" directive first\n",
1123 				fname, lineno, 0 );
1124 			return 1;
1125 		}
1126 
1127 		if ( argc != 2 ) {
1128 			Debug( LDAP_DEBUG_ANY,
1129 	"%s: line %d: missing password in \"pseudorootpw <password>\" line\n",
1130 			    fname, lineno, 0 );
1131 			return 1;
1132 		}
1133 
1134 		Debug( LDAP_DEBUG_ANY,
1135 			"%s: line %d: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
1136 			"use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
1137 			fname, lineno, 0 );
1138 
1139 		if ( BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_authcDN ) ) {
1140 			Debug( LDAP_DEBUG_ANY, "%s: line %d: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
1141 				fname, lineno, 0 );
1142 			return 1;
1143 		}
1144 
1145 		if ( !BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_passwd ) ) {
1146 			memset( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val, 0,
1147 				mi->mi_targets[ i ]->mt_idassert_passwd.bv_len );
1148 			ber_memfree( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val );
1149 		}
1150 		ber_str2bv( argv[ 1 ], 0, 1, &mi->mi_targets[ i ]->mt_idassert_passwd );
1151 
1152 	/* idassert-bind */
1153 	} else if ( strcasecmp( argv[ 0 ], "idassert-bind" ) == 0 ) {
1154 		if ( mi->mi_ntargets == 0 ) {
1155 			Debug( LDAP_DEBUG_ANY,
1156 				"%s: line %d: \"idassert-bind\" "
1157 				"must appear inside a target specification.\n",
1158 				fname, lineno, 0 );
1159 			return 1;
1160 		}
1161 
1162 		return mi->mi_ldap_extra->idassert_parse_cf( fname, lineno, argc, argv, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1163 
1164 	/* idassert-authzFrom */
1165 	} else if ( strcasecmp( argv[ 0 ], "idassert-authzFrom" ) == 0 ) {
1166 		if ( mi->mi_ntargets == 0 ) {
1167 			Debug( LDAP_DEBUG_ANY,
1168 				"%s: line %d: \"idassert-bind\" "
1169 				"must appear inside a target specification.\n",
1170 				fname, lineno, 0 );
1171 			return 1;
1172 		}
1173 
1174 		switch ( argc ) {
1175 		case 2:
1176 			break;
1177 
1178 		case 1:
1179 			Debug( LDAP_DEBUG_ANY,
1180 				"%s: line %d: missing <id> in \"idassert-authzFrom <id>\".\n",
1181 				fname, lineno, 0 );
1182 			return 1;
1183 
1184 		default:
1185 			Debug( LDAP_DEBUG_ANY,
1186 				"%s: line %d: extra cruft after <id> in \"idassert-authzFrom <id>\".\n",
1187 				fname, lineno, 0 );
1188 			return 1;
1189 		}
1190 
1191 		return mi->mi_ldap_extra->idassert_authzfrom_parse_cf( fname, lineno, argv[ 1 ], &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1192 
1193 	/* quarantine */
1194 	} else if ( strcasecmp( argv[ 0 ], "quarantine" ) == 0 ) {
1195 		char			buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
1196 		slap_retry_info_t	*ri = mi->mi_ntargets ?
1197 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine
1198 				: &mi->mi_quarantine;
1199 
1200 		if ( ( mi->mi_ntargets == 0 && META_BACK_QUARANTINE( mi ) )
1201 			|| ( mi->mi_ntargets > 0 && META_BACK_TGT_QUARANTINE( mi->mi_targets[ mi->mi_ntargets - 1 ] ) ) )
1202 		{
1203 			Debug( LDAP_DEBUG_ANY,
1204 				"%s: line %d: quarantine already defined.\n",
1205 				fname, lineno, 0 );
1206 			return 1;
1207 		}
1208 
1209 		switch ( argc ) {
1210 		case 2:
1211 			break;
1212 
1213 		case 1:
1214 			Debug( LDAP_DEBUG_ANY,
1215 				"%s: line %d: missing arg in \"quarantine <pattern list>\".\n",
1216 				fname, lineno, 0 );
1217 			return 1;
1218 
1219 		default:
1220 			Debug( LDAP_DEBUG_ANY,
1221 				"%s: line %d: extra cruft after \"quarantine <pattern list>\".\n",
1222 				fname, lineno, 0 );
1223 			return 1;
1224 		}
1225 
1226 		if ( ri != &mi->mi_quarantine ) {
1227 			ri->ri_interval = NULL;
1228 			ri->ri_num = NULL;
1229 		}
1230 
1231 		if ( mi->mi_ntargets > 0 && !META_BACK_QUARANTINE( mi ) ) {
1232 			ldap_pvt_thread_mutex_init( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine_mutex );
1233 		}
1234 
1235 		if ( mi->mi_ldap_extra->retry_info_parse( argv[ 1 ], ri, buf, sizeof( buf ) ) ) {
1236 			Debug( LDAP_DEBUG_ANY,
1237 				"%s line %d: %s.\n",
1238 				fname, lineno, buf );
1239 			return 1;
1240 		}
1241 
1242 		if ( mi->mi_ntargets == 0 ) {
1243 			mi->mi_flags |= LDAP_BACK_F_QUARANTINE;
1244 
1245 		} else {
1246 			mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags |= LDAP_BACK_F_QUARANTINE;
1247 		}
1248 
1249 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1250 	/* session tracking request */
1251 	} else if ( strcasecmp( argv[ 0 ], "session-tracking-request" ) == 0 ) {
1252 		unsigned	*flagsp = mi->mi_ntargets ?
1253 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1254 				: &mi->mi_flags;
1255 
1256 		if ( argc != 2 ) {
1257 			Debug( LDAP_DEBUG_ANY,
1258 	"%s: line %d: \"session-tracking-request {TRUE|false}\" needs 1 argument.\n",
1259 				fname, lineno, 0 );
1260 			return( 1 );
1261 		}
1262 
1263 		/* this is the default; we add it because the default might change... */
1264 		switch ( check_true_false( argv[ 1 ] ) ) {
1265 		case 1:
1266 			*flagsp |= LDAP_BACK_F_ST_REQUEST;
1267 			break;
1268 
1269 		case 0:
1270 			*flagsp &= ~LDAP_BACK_F_ST_REQUEST;
1271 			break;
1272 
1273 		default:
1274 			Debug( LDAP_DEBUG_ANY,
1275 		"%s: line %d: \"session-tracking-request {TRUE|false}\": unknown argument \"%s\".\n",
1276 				fname, lineno, argv[ 1 ] );
1277 			return( 1 );
1278 		}
1279 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1280 
1281 	/* dn massaging */
1282 	} else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
1283 		BackendDB 	*tmp_bd;
1284 		int 		i = mi->mi_ntargets - 1, c, rc;
1285 		struct berval	dn, nvnc, pvnc, nrnc, prnc;
1286 
1287 		if ( i < 0 ) {
1288 			Debug( LDAP_DEBUG_ANY,
1289 	"%s: line %d: need \"uri\" directive first\n",
1290 				fname, lineno, 0 );
1291 			return 1;
1292 		}
1293 
1294 		/*
1295 		 * syntax:
1296 		 *
1297 		 * 	suffixmassage <suffix> <massaged suffix>
1298 		 *
1299 		 * the <suffix> field must be defined as a valid suffix
1300 		 * (or suffixAlias?) for the current database;
1301 		 * the <massaged suffix> shouldn't have already been
1302 		 * defined as a valid suffix or suffixAlias for the
1303 		 * current server
1304 		 */
1305 		if ( argc != 3 ) {
1306  			Debug( LDAP_DEBUG_ANY,
1307 	"%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
1308 				fname, lineno, 0 );
1309 			return 1;
1310 		}
1311 
1312 		ber_str2bv( argv[ 1 ], 0, 0, &dn );
1313 		if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
1314 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1315 					"suffix \"%s\" is invalid\n",
1316 					fname, lineno, argv[ 1 ] );
1317 			return 1;
1318 		}
1319 
1320 		for ( c = 0; !BER_BVISNULL( &be->be_nsuffix[ c ] ); c++ ) {
1321 			if ( dnIsSuffix( &nvnc, &be->be_nsuffix[ 0 ] ) ) {
1322 				break;
1323 			}
1324 		}
1325 
1326 		if ( BER_BVISNULL( &be->be_nsuffix[ c ] ) ) {
1327 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1328 	"<suffix> \"%s\" must be within the database naming context, in "
1329 	"\"suffixMassage <suffix> <massaged suffix>\"\n",
1330 				fname, lineno, pvnc.bv_val );
1331 			free( pvnc.bv_val );
1332 			free( nvnc.bv_val );
1333 			return 1;
1334 		}
1335 
1336 		ber_str2bv( argv[ 2 ], 0, 0, &dn );
1337 		if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
1338 			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1339 				"massaged suffix \"%s\" is invalid\n",
1340 				fname, lineno, argv[ 2 ] );
1341 			free( pvnc.bv_val );
1342 			free( nvnc.bv_val );
1343 			return 1;
1344 		}
1345 
1346 		tmp_bd = select_backend( &nrnc, 0 );
1347 		if ( tmp_bd != NULL && tmp_bd->be_private == be->be_private ) {
1348 			Debug( LDAP_DEBUG_ANY,
1349 	"%s: line %d: warning: <massaged suffix> \"%s\" resolves to this database, in "
1350 	"\"suffixMassage <suffix> <massaged suffix>\"\n",
1351 				fname, lineno, prnc.bv_val );
1352 		}
1353 
1354 		/*
1355 		 * The suffix massaging is emulated by means of the
1356 		 * rewrite capabilities
1357 		 */
1358 	 	rc = suffix_massage_config( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
1359 				&pvnc, &nvnc, &prnc, &nrnc );
1360 
1361 		free( pvnc.bv_val );
1362 		free( nvnc.bv_val );
1363 		free( prnc.bv_val );
1364 		free( nrnc.bv_val );
1365 
1366 		return rc;
1367 
1368 	/* rewrite stuff ... */
1369  	} else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
1370 		int 		i = mi->mi_ntargets - 1;
1371 
1372 		if ( i < 0 ) {
1373 			Debug( LDAP_DEBUG_ANY, "%s: line %d: \"rewrite\" "
1374 				"statement outside target definition.\n",
1375 				fname, lineno, 0 );
1376 			return 1;
1377 		}
1378 
1379  		return rewrite_parse( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
1380 				fname, lineno, argc, argv );
1381 
1382 	/* objectclass/attribute mapping */
1383 	} else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) {
1384 		int 		i = mi->mi_ntargets - 1;
1385 
1386 		if ( i < 0 ) {
1387 			Debug( LDAP_DEBUG_ANY,
1388 	"%s: line %d: need \"uri\" directive first\n",
1389 				fname, lineno, 0 );
1390 			return 1;
1391 		}
1392 
1393 		return ldap_back_map_config( &mi->mi_targets[ i ]->mt_rwmap.rwm_oc,
1394 				&mi->mi_targets[ i ]->mt_rwmap.rwm_at,
1395 				fname, lineno, argc, argv );
1396 
1397 	} else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
1398 		int 		i = mi->mi_ntargets - 1;
1399 		int		nretries = META_RETRY_UNDEFINED;
1400 
1401 		if ( argc != 2 ) {
1402 			Debug( LDAP_DEBUG_ANY,
1403 	"%s: line %d: need value in \"nretries <value>\"\n",
1404 				fname, lineno, 0 );
1405 			return 1;
1406 		}
1407 
1408 		if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
1409 			nretries = META_RETRY_FOREVER;
1410 
1411 		} else if ( strcasecmp( argv[ 1 ], "never" ) == 0 ) {
1412 			nretries = META_RETRY_NEVER;
1413 
1414 		} else {
1415 			if ( lutil_atoi( &nretries, argv[ 1 ] ) != 0 ) {
1416 				Debug( LDAP_DEBUG_ANY,
1417 	"%s: line %d: unable to parse value \"%s\" in \"nretries <value>\"\n",
1418 					fname, lineno, argv[ 1 ] );
1419 				return 1;
1420 			}
1421 		}
1422 
1423 		if ( i < 0 ) {
1424 			mi->mi_nretries = nretries;
1425 
1426 		} else {
1427 			mi->mi_targets[ i ]->mt_nretries = nretries;
1428 		}
1429 
1430 	} else if ( strcasecmp( argv[ 0 ], "protocol-version" ) == 0 ) {
1431 		int	*version = mi->mi_ntargets ?
1432 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_version
1433 				: &mi->mi_version;
1434 
1435 		if ( argc != 2 ) {
1436 			Debug( LDAP_DEBUG_ANY,
1437 	"%s: line %d: need value in \"protocol-version <version>\"\n",
1438 				fname, lineno, 0 );
1439 			return 1;
1440 		}
1441 
1442 		if ( lutil_atoi( version, argv[ 1 ] ) != 0 ) {
1443 			Debug( LDAP_DEBUG_ANY,
1444 	"%s: line %d: unable to parse version \"%s\" in \"protocol-version <version>\"\n",
1445 				fname, lineno, argv[ 1 ] );
1446 			return 1;
1447 		}
1448 
1449 		if ( *version != 0 && ( *version < LDAP_VERSION_MIN || *version > LDAP_VERSION_MAX ) ) {
1450 			Debug( LDAP_DEBUG_ANY,
1451 	"%s: line %d: unsupported version \"%s\" in \"protocol-version <version>\"\n",
1452 				fname, lineno, argv[ 1 ] );
1453 			return 1;
1454 		}
1455 
1456 	/* do not return search references */
1457 	} else if ( strcasecmp( argv[ 0 ], "norefs" ) == 0 ) {
1458 		unsigned	*flagsp = mi->mi_ntargets ?
1459 				&mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1460 				: &mi->mi_flags;
1461 
1462 		if ( argc != 2 ) {
1463 			Debug( LDAP_DEBUG_ANY,
1464 	"%s: line %d: \"norefs {TRUE|false}\" needs 1 argument.\n",
1465 				fname, lineno, 0 );
1466 			return( 1 );
1467 		}
1468 
1469 		/* this is the default; we add it because the default might change... */
1470 		switch ( check_true_false( argv[ 1 ] ) ) {
1471 		case 1:
1472 			*flagsp |= LDAP_BACK_F_NOREFS;
1473 			break;
1474 
1475 		case 0:
1476 			*flagsp &= ~LDAP_BACK_F_NOREFS;
1477 			break;
1478 
1479 		default:
1480 			Debug( LDAP_DEBUG_ANY,
1481 		"%s: line %d: \"norefs {TRUE|false}\": unknown argument \"%s\".\n",
1482 				fname, lineno, argv[ 1 ] );
1483 			return( 1 );
1484 		}
1485 
1486 	/* anything else */
1487 	} else {
1488 		return SLAP_CONF_UNKNOWN;
1489 	}
1490 	return 0;
1491 }
1492 
1493 int
1494 ldap_back_map_config(
1495 		struct ldapmap	*oc_map,
1496 		struct ldapmap	*at_map,
1497 		const char	*fname,
1498 		int		lineno,
1499 		int		argc,
1500 		char		**argv )
1501 {
1502 	struct ldapmap		*map;
1503 	struct ldapmapping	*mapping;
1504 	char			*src, *dst;
1505 	int			is_oc = 0;
1506 
1507 	if ( argc < 3 || argc > 4 ) {
1508 		Debug( LDAP_DEBUG_ANY,
1509 	"%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
1510 			fname, lineno, 0 );
1511 		return 1;
1512 	}
1513 
1514 	if ( strcasecmp( argv[ 1 ], "objectclass" ) == 0 ) {
1515 		map = oc_map;
1516 		is_oc = 1;
1517 
1518 	} else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) {
1519 		map = at_map;
1520 
1521 	} else {
1522 		Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is "
1523 			"\"map {objectclass | attribute} [<local> | *] "
1524 			"{<foreign> | *}\"\n",
1525 			fname, lineno, 0 );
1526 		return 1;
1527 	}
1528 
1529 	if ( strcmp( argv[ 2 ], "*" ) == 0 ) {
1530 		if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) {
1531 			map->drop_missing = ( argc < 4 );
1532 			goto success_return;
1533 		}
1534 		src = dst = argv[ 3 ];
1535 
1536 	} else if ( argc < 4 ) {
1537 		src = "";
1538 		dst = argv[ 2 ];
1539 
1540 	} else {
1541 		src = argv[ 2 ];
1542 		dst = ( strcmp( argv[ 3 ], "*" ) == 0 ? src : argv[ 3 ] );
1543 	}
1544 
1545 	if ( ( map == at_map )
1546 		&& ( strcasecmp( src, "objectclass" ) == 0
1547 			|| strcasecmp( dst, "objectclass" ) == 0 ) )
1548 	{
1549 		Debug( LDAP_DEBUG_ANY,
1550 			"%s: line %d: objectclass attribute cannot be mapped\n",
1551 			fname, lineno, 0 );
1552 	}
1553 
1554 	mapping = (struct ldapmapping *)ch_calloc( 2,
1555 		sizeof(struct ldapmapping) );
1556 	if ( mapping == NULL ) {
1557 		Debug( LDAP_DEBUG_ANY,
1558 			"%s: line %d: out of memory\n",
1559 			fname, lineno, 0 );
1560 		return 1;
1561 	}
1562 	ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
1563 	ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
1564 	mapping[ 1 ].src = mapping[ 0 ].dst;
1565 	mapping[ 1 ].dst = mapping[ 0 ].src;
1566 
1567 	/*
1568 	 * schema check
1569 	 */
1570 	if ( is_oc ) {
1571 		if ( src[ 0 ] != '\0' ) {
1572 			if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
1573 				Debug( LDAP_DEBUG_ANY,
1574 	"%s: line %d: warning, source objectClass '%s' "
1575 	"should be defined in schema\n",
1576 					fname, lineno, src );
1577 
1578 				/*
1579 				 * FIXME: this should become an err
1580 				 */
1581 				goto error_return;
1582 			}
1583 		}
1584 
1585 		if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
1586 			Debug( LDAP_DEBUG_ANY,
1587 	"%s: line %d: warning, destination objectClass '%s' "
1588 	"is not defined in schema\n",
1589 				fname, lineno, dst );
1590 		}
1591 	} else {
1592 		int			rc;
1593 		const char		*text = NULL;
1594 		AttributeDescription	*ad = NULL;
1595 
1596 		if ( src[ 0 ] != '\0' ) {
1597 			rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
1598 			if ( rc != LDAP_SUCCESS ) {
1599 				Debug( LDAP_DEBUG_ANY,
1600 	"%s: line %d: warning, source attributeType '%s' "
1601 	"should be defined in schema\n",
1602 					fname, lineno, src );
1603 
1604 				/*
1605 				 * FIXME: this should become an err
1606 				 */
1607 				/*
1608 				 * we create a fake "proxied" ad
1609 				 * and add it here.
1610 				 */
1611 
1612 				rc = slap_bv2undef_ad( &mapping[ 0 ].src,
1613 						&ad, &text, SLAP_AD_PROXIED );
1614 				if ( rc != LDAP_SUCCESS ) {
1615 					char	buf[ SLAP_TEXT_BUFLEN ];
1616 
1617 					snprintf( buf, sizeof( buf ),
1618 						"source attributeType \"%s\": %d (%s)",
1619 						src, rc, text ? text : "" );
1620 					Debug( LDAP_DEBUG_ANY,
1621 						"%s: line %d: %s\n",
1622 						fname, lineno, buf );
1623 					goto error_return;
1624 				}
1625 			}
1626 
1627 			ad = NULL;
1628 		}
1629 
1630 		rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
1631 		if ( rc != LDAP_SUCCESS ) {
1632 			Debug( LDAP_DEBUG_ANY,
1633 	"%s: line %d: warning, destination attributeType '%s' "
1634 	"is not defined in schema\n",
1635 				fname, lineno, dst );
1636 
1637 			/*
1638 			 * we create a fake "proxied" ad
1639 			 * and add it here.
1640 			 */
1641 
1642 			rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
1643 					&ad, &text, SLAP_AD_PROXIED );
1644 			if ( rc != LDAP_SUCCESS ) {
1645 				char	buf[ SLAP_TEXT_BUFLEN ];
1646 
1647 				snprintf( buf, sizeof( buf ),
1648 					"source attributeType \"%s\": %d (%s)\n",
1649 					dst, rc, text ? text : "" );
1650 				Debug( LDAP_DEBUG_ANY,
1651 					"%s: line %d: %s\n",
1652 					fname, lineno, buf );
1653 				return 1;
1654 			}
1655 		}
1656 	}
1657 
1658 	if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
1659 			|| avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
1660 	{
1661 		Debug( LDAP_DEBUG_ANY,
1662 			"%s: line %d: duplicate mapping found.\n",
1663 			fname, lineno, 0 );
1664 		goto error_return;
1665 	}
1666 
1667 	if ( src[ 0 ] != '\0' ) {
1668 		avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
1669 					mapping_cmp, mapping_dup );
1670 	}
1671 	avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
1672 				mapping_cmp, mapping_dup );
1673 
1674 success_return:;
1675 	if ( !is_oc && map->map == NULL ) {
1676 		/* only init if required */
1677 		ldap_back_map_init( map, &mapping );
1678 	}
1679 
1680 	return 0;
1681 
1682 error_return:;
1683 	if ( mapping ) {
1684 		ch_free( mapping[ 0 ].src.bv_val );
1685 		ch_free( mapping[ 0 ].dst.bv_val );
1686 		ch_free( mapping );
1687 	}
1688 
1689 	return 1;
1690 }
1691 
1692 
1693 #ifdef ENABLE_REWRITE
1694 static char *
1695 suffix_massage_regexize( const char *s )
1696 {
1697 	char *res, *ptr;
1698 	const char *p, *r;
1699 	int i;
1700 
1701 	if ( s[ 0 ] == '\0' ) {
1702 		return ch_strdup( "^(.+)$" );
1703 	}
1704 
1705 	for ( i = 0, p = s;
1706 			( r = strchr( p, ',' ) ) != NULL;
1707 			p = r + 1, i++ )
1708 		;
1709 
1710 	res = ch_calloc( sizeof( char ),
1711 			strlen( s )
1712 			+ STRLENOF( "((.+),)?" )
1713 			+ STRLENOF( "[ ]?" ) * i
1714 			+ STRLENOF( "$" ) + 1 );
1715 
1716 	ptr = lutil_strcopy( res, "((.+),)?" );
1717 	for ( i = 0, p = s;
1718 			( r = strchr( p, ',' ) ) != NULL;
1719 			p = r + 1 , i++ ) {
1720 		ptr = lutil_strncopy( ptr, p, r - p + 1 );
1721 		ptr = lutil_strcopy( ptr, "[ ]?" );
1722 
1723 		if ( r[ 1 ] == ' ' ) {
1724 			r++;
1725 		}
1726 	}
1727 	ptr = lutil_strcopy( ptr, p );
1728 	ptr[ 0 ] = '$';
1729 	ptr++;
1730 	ptr[ 0 ] = '\0';
1731 
1732 	return res;
1733 }
1734 
1735 static char *
1736 suffix_massage_patternize( const char *s, const char *p )
1737 {
1738 	ber_len_t	len;
1739 	char		*res, *ptr;
1740 
1741 	len = strlen( p );
1742 
1743 	if ( s[ 0 ] == '\0' ) {
1744 		len++;
1745 	}
1746 
1747 	res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
1748 	if ( res == NULL ) {
1749 		return NULL;
1750 	}
1751 
1752 	ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
1753 	if ( s[ 0 ] == '\0' ) {
1754 		ptr[ 0 ] = ',';
1755 		ptr++;
1756 	}
1757 	lutil_strcopy( ptr, p );
1758 
1759 	return res;
1760 }
1761 
1762 int
1763 suffix_massage_config(
1764 		struct rewrite_info *info,
1765 		struct berval *pvnc,
1766 		struct berval *nvnc,
1767 		struct berval *prnc,
1768 		struct berval *nrnc
1769 )
1770 {
1771 	char *rargv[ 5 ];
1772 	int line = 0;
1773 
1774 	rargv[ 0 ] = "rewriteEngine";
1775 	rargv[ 1 ] = "on";
1776 	rargv[ 2 ] = NULL;
1777 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1778 
1779 	rargv[ 0 ] = "rewriteContext";
1780 	rargv[ 1 ] = "default";
1781 	rargv[ 2 ] = NULL;
1782 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1783 
1784 	rargv[ 0 ] = "rewriteRule";
1785 	rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
1786 	rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
1787 	rargv[ 3 ] = ":";
1788 	rargv[ 4 ] = NULL;
1789 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1790 	ch_free( rargv[ 1 ] );
1791 	ch_free( rargv[ 2 ] );
1792 
1793 	if ( BER_BVISEMPTY( pvnc ) ) {
1794 		rargv[ 0 ] = "rewriteRule";
1795 		rargv[ 1 ] = "^$";
1796 		rargv[ 2 ] = prnc->bv_val;
1797 		rargv[ 3 ] = ":";
1798 		rargv[ 4 ] = NULL;
1799 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1800 	}
1801 
1802 	rargv[ 0 ] = "rewriteContext";
1803 	rargv[ 1 ] = "searchEntryDN";
1804 	rargv[ 2 ] = NULL;
1805 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1806 
1807 	rargv[ 0 ] = "rewriteRule";
1808 	rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
1809 	rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
1810 	rargv[ 3 ] = ":";
1811 	rargv[ 4 ] = NULL;
1812 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1813 	ch_free( rargv[ 1 ] );
1814 	ch_free( rargv[ 2 ] );
1815 
1816 	if ( BER_BVISEMPTY( prnc ) ) {
1817 		rargv[ 0 ] = "rewriteRule";
1818 		rargv[ 1 ] = "^$";
1819 		rargv[ 2 ] = pvnc->bv_val;
1820 		rargv[ 3 ] = ":";
1821 		rargv[ 4 ] = NULL;
1822 		rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1823 	}
1824 
1825 	/* backward compatibility */
1826 	rargv[ 0 ] = "rewriteContext";
1827 	rargv[ 1 ] = "searchResult";
1828 	rargv[ 2 ] = "alias";
1829 	rargv[ 3 ] = "searchEntryDN";
1830 	rargv[ 4 ] = NULL;
1831 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1832 
1833 	rargv[ 0 ] = "rewriteContext";
1834 	rargv[ 1 ] = "matchedDN";
1835 	rargv[ 2 ] = "alias";
1836 	rargv[ 3 ] = "searchEntryDN";
1837 	rargv[ 4 ] = NULL;
1838 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1839 
1840 	rargv[ 0 ] = "rewriteContext";
1841 	rargv[ 1 ] = "searchAttrDN";
1842 	rargv[ 2 ] = "alias";
1843 	rargv[ 3 ] = "searchEntryDN";
1844 	rargv[ 4 ] = NULL;
1845 	rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1846 
1847 	/* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
1848 	 * see servers/slapd/overlays/rwm.h for details */
1849         rargv[ 0 ] = "rewriteContext";
1850 	rargv[ 1 ] = "referralAttrDN";
1851 	rargv[ 2 ] = NULL;
1852 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1853 
1854 	rargv[ 0 ] = "rewriteContext";
1855 	rargv[ 1 ] = "referralDN";
1856 	rargv[ 2 ] = NULL;
1857 	rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1858 
1859 	return 0;
1860 }
1861 #endif /* ENABLE_REWRITE */
1862 
1863