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