xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ldap/chain.c (revision 7e30e94394d0994ab9534f68a8f91665045c91ce)
1 /*	$NetBSD: chain.c,v 1.1.1.5 2017/02/09 01:47:06 christos Exp $	*/
2 
3 /* chain.c - chain LDAP operations */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2003-2016 The OpenLDAP Foundation.
8  * Portions Copyright 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.
22  * This work was subsequently modified by Pierangelo Masarati.
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: chain.c,v 1.1.1.5 2017/02/09 01:47:06 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/socket.h>
34 
35 #include "lutil.h"
36 #include "slap.h"
37 #include "back-ldap.h"
38 #include "config.h"
39 
40 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
41 #define SLAP_CHAINING_DEFAULT				LDAP_CHAINING_PREFERRED
42 #define SLAP_CH_RESOLVE_SHIFT				SLAP_CONTROL_SHIFT
43 #define SLAP_CH_RESOLVE_MASK				(0x3 << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
45 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
46 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED		(LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
47 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
48 #define SLAP_CH_RESOLVE_DEFAULT				(SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
49 #define	SLAP_CH_CONTINUATION_SHIFT			(SLAP_CH_RESOLVE_SHIFT + 2)
50 #define SLAP_CH_CONTINUATION_MASK			(0x3 << SLAP_CH_CONTINUATION_SHIFT)
51 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
52 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
53 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED	(LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
54 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
55 #define SLAP_CH_CONTINUATION_DEFAULT			(SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
56 
57 #define o_chaining			o_ctrlflag[sc_chainingBehavior]
58 #define get_chaining(op)		((op)->o_chaining & SLAP_CONTROL_MASK)
59 #define get_chainingBehavior(op)	((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
60 #define get_resolveBehavior(op)		((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
61 #define get_continuationBehavior(op)	((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
62 
63 static int		sc_chainingBehavior;
64 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
65 
66 typedef enum {
67 	LDAP_CH_NONE = 0,
68 	LDAP_CH_RES,
69 	LDAP_CH_ERR
70 } ldap_chain_status_t;
71 
72 static BackendInfo	*lback;
73 
74 typedef struct ldap_chain_t {
75 	/*
76 	 * A "template" ldapinfo_t gets all common configuration items;
77 	 * then, for each configured URI, an entry is created in the tree;
78 	 * all the specific configuration items get in the current URI
79 	 * structure.
80 	 *
81  	 * Then, for each referral, extract the URI and lookup the
82 	 * related structure.  If configured to do so, allow URIs
83 	 * not found in the structure to create a temporary one
84 	 * that chains anonymously; maybe it can also be added to
85 	 * the tree?  Should be all configurable.
86 	 */
87 
88 	/* "common" configuration info (anything occurring before an "uri") */
89 	ldapinfo_t		*lc_common_li;
90 
91 	/* current configuration info */
92 	ldapinfo_t		*lc_cfg_li;
93 
94 	/* tree of configured[/generated?] "uri" info */
95 	ldap_avl_info_t		lc_lai;
96 
97 	/* max depth in nested referrals chaining */
98 	int			lc_max_depth;
99 
100 	unsigned		lc_flags;
101 #define LDAP_CHAIN_F_NONE		(0x00U)
102 #define	LDAP_CHAIN_F_CHAINING		(0x01U)
103 #define	LDAP_CHAIN_F_CACHE_URI		(0x02U)
104 #define	LDAP_CHAIN_F_RETURN_ERR		(0x04U)
105 
106 #define LDAP_CHAIN_ISSET(lc, f)		( ( (lc)->lc_flags & (f) ) == (f) )
107 #define	LDAP_CHAIN_CHAINING( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
108 #define	LDAP_CHAIN_CACHE_URI( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
109 #define	LDAP_CHAIN_RETURN_ERR( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
110 
111 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
112 	LDAPControl		lc_chaining_ctrl;
113 	char			lc_chaining_ctrlflag;
114 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
115 } ldap_chain_t;
116 
117 static int ldap_chain_db_init_common( BackendDB	*be );
118 static int ldap_chain_db_init_one( BackendDB *be );
119 static int ldap_chain_db_open_one( BackendDB *be );
120 #define	ldap_chain_db_close_one(be)	(0)
121 #define	ldap_chain_db_destroy_one(be, rs)	(lback)->bi_db_destroy( (be), (rs) )
122 
123 typedef struct ldap_chain_cb_t {
124 	ldap_chain_status_t	lb_status;
125 	ldap_chain_t		*lb_lc;
126 	BI_op_func		*lb_op_f;
127 	int			lb_depth;
128 } ldap_chain_cb_t;
129 
130 static int
131 ldap_chain_op(
132 	Operation	*op,
133 	SlapReply	*rs,
134 	BI_op_func	*op_f,
135 	BerVarray	ref,
136 	int		depth );
137 
138 static int
139 ldap_chain_search(
140 	Operation	*op,
141 	SlapReply	*rs,
142 	BerVarray	ref,
143 	int		depth );
144 
145 static slap_overinst ldapchain;
146 
147 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
148 static int
149 chaining_control_add(
150 		ldap_chain_t	*lc,
151 		Operation 	*op,
152 		LDAPControl	***oldctrlsp )
153 {
154 	LDAPControl	**ctrls = NULL;
155 	int		c = 0;
156 
157 	*oldctrlsp = op->o_ctrls;
158 
159 	/* default chaining control not defined */
160 	if ( !LDAP_CHAIN_CHAINING( lc ) ) {
161 		return 0;
162 	}
163 
164 	/* already present */
165 	if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
166 		return 0;
167 	}
168 
169 	/* FIXME: check other incompatibilities */
170 
171 	/* add to other controls */
172 	if ( op->o_ctrls ) {
173 		for ( c = 0; op->o_ctrls[ c ]; c++ )
174 			/* count them */ ;
175 	}
176 
177 	ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
178 	ctrls[ 0 ] = &lc->lc_chaining_ctrl;
179 	if ( op->o_ctrls ) {
180 		for ( c = 0; op->o_ctrls[ c ]; c++ ) {
181 			ctrls[ c + 1 ] = op->o_ctrls[ c ];
182 		}
183 	}
184 	ctrls[ c + 1 ] = NULL;
185 
186 	op->o_ctrls = ctrls;
187 
188 	op->o_chaining = lc->lc_chaining_ctrlflag;
189 
190 	return 0;
191 }
192 
193 static int
194 chaining_control_remove(
195 		Operation 	*op,
196 		LDAPControl	***oldctrlsp )
197 {
198 	LDAPControl	**oldctrls = *oldctrlsp;
199 
200 	/* we assume that the first control is the chaining control
201 	 * added by the chain overlay, so it's the only one we explicitly
202 	 * free */
203 	if ( op->o_ctrls != oldctrls ) {
204 		if ( op->o_ctrls != NULL ) {
205 			assert( op->o_ctrls[ 0 ] != NULL );
206 
207 			free( op->o_ctrls );
208 
209 			op->o_chaining = 0;
210 		}
211 		op->o_ctrls = oldctrls;
212 	}
213 
214 	*oldctrlsp = NULL;
215 
216 	return 0;
217 }
218 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
219 
220 static int
221 ldap_chain_uri_cmp( const void *c1, const void *c2 )
222 {
223 	const ldapinfo_t	*li1 = (const ldapinfo_t *)c1;
224 	const ldapinfo_t	*li2 = (const ldapinfo_t *)c2;
225 
226 	assert( li1->li_bvuri != NULL );
227 	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
228 	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
229 
230 	assert( li2->li_bvuri != NULL );
231 	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
232 	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
233 
234 	return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
235 }
236 
237 static int
238 ldap_chain_uri_dup( void *c1, void *c2 )
239 {
240 	ldapinfo_t	*li1 = (ldapinfo_t *)c1;
241 	ldapinfo_t	*li2 = (ldapinfo_t *)c2;
242 
243 	assert( li1->li_bvuri != NULL );
244 	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
245 	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
246 
247 	assert( li2->li_bvuri != NULL );
248 	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
249 	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
250 
251 	if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
252 		return -1;
253 	}
254 
255 	return 0;
256 }
257 
258 /*
259  * Search specific response that strips entryDN from entries
260  */
261 static int
262 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
263 {
264 	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
265 
266 	assert( op->o_tag == LDAP_REQ_SEARCH );
267 
268 	/* if in error, don't proceed any further */
269 	if ( lb->lb_status == LDAP_CH_ERR ) {
270 		return 0;
271 	}
272 
273 	if ( rs->sr_type == REP_SEARCH ) {
274 		Attribute	**ap = &rs->sr_entry->e_attrs;
275 
276 		for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
277 			/* will be generated later by frontend
278 			 * (a cleaner solution would be that
279 			 * the frontend checks if it already exists */
280 			if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
281 			{
282 				Attribute *a = *ap;
283 
284 				*ap = (*ap)->a_next;
285 				attr_free( a );
286 
287 				/* there SHOULD be one only! */
288 				break;
289 			}
290 		}
291 
292 		/* tell the frontend not to add generated
293 		 * operational attributes */
294 		rs->sr_flags |= REP_NO_OPERATIONALS;
295 
296 		return SLAP_CB_CONTINUE;
297 
298 	} else if ( rs->sr_type == REP_SEARCHREF ) {
299 		/* if we get it here, it means the library was unable
300 		 * to chase the referral... */
301 		if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
302 			rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
303 		}
304 
305 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
306 		if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
307 			switch ( get_continuationBehavior( op ) ) {
308 			case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
309 				lb->lb_status = LDAP_CH_ERR;
310 				return rs->sr_err = LDAP_X_CANNOT_CHAIN;
311 
312 			default:
313 				break;
314 			}
315 		}
316 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
317 		return SLAP_CB_CONTINUE;
318 
319 	} else if ( rs->sr_type == REP_RESULT ) {
320 		if ( rs->sr_err == LDAP_REFERRAL
321 			&& lb->lb_depth < lb->lb_lc->lc_max_depth
322 			&& rs->sr_ref != NULL )
323 		{
324 			rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
325 		}
326 
327 		/* back-ldap tried to send result */
328 		lb->lb_status = LDAP_CH_RES;
329 		/* don't let other callbacks run, this isn't
330 		 * the real result for this op.
331 		 */
332 		op->o_callback->sc_next = NULL;
333 	}
334 
335 	return 0;
336 }
337 
338 /*
339  * Dummy response that simply traces if back-ldap tried to send
340  * anything to the client
341  */
342 static int
343 ldap_chain_cb_response( Operation *op, SlapReply *rs )
344 {
345 	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
346 
347 	/* if in error, don't proceed any further */
348 	if ( lb->lb_status == LDAP_CH_ERR ) {
349 		return 0;
350 	}
351 
352 	if ( rs->sr_type == REP_RESULT ) {
353 retry:;
354 		switch ( rs->sr_err ) {
355 		case LDAP_COMPARE_TRUE:
356 		case LDAP_COMPARE_FALSE:
357 			if ( op->o_tag != LDAP_REQ_COMPARE ) {
358 				return rs->sr_err;
359 			}
360 			/* fallthru */
361 
362 		case LDAP_SUCCESS:
363 			lb->lb_status = LDAP_CH_RES;
364 			break;
365 
366 		case LDAP_REFERRAL:
367 			if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
368 				rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
369 				goto retry;
370 			}
371 
372 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
373 			if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
374 				switch ( get_continuationBehavior( op ) ) {
375 				case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
376 					lb->lb_status = LDAP_CH_ERR;
377 					return rs->sr_err = LDAP_X_CANNOT_CHAIN;
378 
379 				default:
380 					break;
381 				}
382 			}
383 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
384 			break;
385 
386 		default:
387 			return rs->sr_err;
388 		}
389 
390 	} else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
391 	{
392 		/* strip the entryDN attribute, but keep returning results */
393 		(void)ldap_chain_cb_search_response( op, rs );
394 	}
395 
396 	return SLAP_CB_CONTINUE;
397 }
398 
399 static int
400 ldap_chain_op(
401 	Operation	*op,
402 	SlapReply	*rs,
403 	BI_op_func	*op_f,
404 	BerVarray	ref,
405 	int		depth )
406 {
407 	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
408 	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
409 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
410 	struct berval	odn = op->o_req_dn,
411 			ondn = op->o_req_ndn;
412 	ldapinfo_t	li = { 0 }, *lip = NULL;
413 	struct berval	bvuri[ 2 ] = { { 0 } };
414 
415 	/* NOTE: returned if ref is empty... */
416 	int		rc = LDAP_OTHER,
417 			first_rc;
418 
419 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
420 	LDAPControl	**ctrls = NULL;
421 
422 	(void)chaining_control_add( lc, op, &ctrls );
423 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
424 
425 	li.li_bvuri = bvuri;
426 	first_rc = -1;
427 	for ( ; !BER_BVISNULL( ref ); ref++ ) {
428 		SlapReply	rs2 = { 0 };
429 		LDAPURLDesc	*srv = NULL;
430 		req_search_s	save_oq_search = op->oq_search,
431 				tmp_oq_search = { 0 };
432 		struct berval	dn = BER_BVNULL,
433 				pdn = odn,
434 				ndn = ondn;
435 		char		*filter = NULL;
436 		int		temporary = 0;
437 		int		free_dn = 0;
438 
439 		/* We're setting the URI of the first referral;
440 		 * what if there are more?
441 
442 Document: RFC 4511
443 
444 4.1.10. Referral
445    ...
446    If the client wishes to progress the operation, it MUST follow the
447    referral by contacting one of the supported services. If multiple
448    URIs are present, the client assumes that any supported URI may be
449    used to progress the operation.
450 
451 		 * so we actually need to follow exactly one,
452 		 * and we can assume any is fine.
453 		 */
454 
455 		/* parse reference and use
456 		 * proto://[host][:port]/ only */
457 		rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
458 		if ( rc != LDAP_URL_SUCCESS ) {
459 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
460 				op->o_log_prefix, ref->bv_val, 0 );
461 
462 			/* try next */
463 			rc = LDAP_OTHER;
464 			continue;
465 		}
466 
467 		if ( op->o_tag == LDAP_REQ_SEARCH ) {
468 			if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
469 				/* RFC 4511: if scope is present, use it */
470 				tmp_oq_search.rs_scope = srv->lud_scope;
471 
472 			} else {
473 				/* RFC 4511: if scope is absent, use original */
474 				tmp_oq_search.rs_scope = op->ors_scope;
475 			}
476 		}
477 
478 		rc = LDAP_SUCCESS;
479 		srv->lud_scope = LDAP_SCOPE_DEFAULT;
480 		dn.bv_val = srv->lud_dn;
481 		filter = srv->lud_filter;
482 
483 		/* normalize DN */
484 		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
485 			if ( srv->lud_dn == NULL ) {
486 				srv->lud_dn = "";
487 			}
488 
489 		} else {
490 			ber_str2bv( srv->lud_dn, 0, 0, &dn );
491 			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
492 			if ( rc == LDAP_SUCCESS ) {
493 				/* remove DN essentially because later on
494 				 * ldap_initialize() will parse the URL
495 				 * as a comma-separated URL list */
496 				srv->lud_dn = "";
497 				free_dn = 1;
498 			}
499 		}
500 
501 		/* prepare filter */
502 		if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
503 			/* filter */
504 			if ( srv->lud_filter != NULL
505 				&& srv->lud_filter[0] != '\0'
506 				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
507 			{
508 				/* RFC 4511: if filter is present, use it;
509 				 * otherwise, use original */
510 				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
511 				if ( tmp_oq_search.rs_filter != NULL ) {
512 					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
513 
514 				} else {
515 					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
516 						op->o_log_prefix, ref->bv_val, srv->lud_filter );
517 					rc = LDAP_OTHER;
518 				}
519 			}
520 		}
521 		srv->lud_filter = NULL;
522 
523 		if ( rc == LDAP_SUCCESS ) {
524 			li.li_uri = ldap_url_desc2str( srv );
525 		}
526 
527 		srv->lud_dn = dn.bv_val;
528 		srv->lud_filter = filter;
529 		ldap_free_urldesc( srv );
530 
531 		if ( rc != LDAP_SUCCESS ) {
532 			/* try next */
533 			rc = LDAP_OTHER;
534 			continue;
535 		}
536 
537 		if ( li.li_uri == NULL ) {
538 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
539 				op->o_log_prefix, ref->bv_val, 0 );
540 
541 			/* try next */
542 			rc = LDAP_OTHER;
543 			goto further_cleanup;
544 		}
545 
546 		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
547 			op->o_log_prefix, ref->bv_val, li.li_uri );
548 
549 		op->o_req_dn = pdn;
550 		op->o_req_ndn = ndn;
551 
552 		if ( op->o_tag == LDAP_REQ_SEARCH ) {
553 			op->ors_scope = tmp_oq_search.rs_scope;
554 			if ( tmp_oq_search.rs_filter != NULL ) {
555 				op->ors_filter = tmp_oq_search.rs_filter;
556 				op->ors_filterstr = tmp_oq_search.rs_filterstr;
557 			}
558 		}
559 
560 		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
561 
562 		/* Searches for a ldapinfo in the avl tree */
563 		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
564 		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
565 			(caddr_t)&li, ldap_chain_uri_cmp );
566 		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
567 
568 		if ( lip != NULL ) {
569 			op->o_bd->be_private = (void *)lip;
570 
571 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
572 				op->o_log_prefix, ref->bv_val, li.li_uri );
573 
574 		} else {
575 			rc = ldap_chain_db_init_one( op->o_bd );
576 			if ( rc != 0 ) {
577 				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
578 					op->o_log_prefix, ref->bv_val, li.li_uri );
579 				goto cleanup;
580 			}
581 			lip = (ldapinfo_t *)op->o_bd->be_private;
582 			lip->li_uri = li.li_uri;
583 			lip->li_bvuri = bvuri;
584 			rc = ldap_chain_db_open_one( op->o_bd );
585 			if ( rc != 0 ) {
586 				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
587 					op->o_log_prefix, ref->bv_val, li.li_uri );
588 				lip->li_uri = NULL;
589 				lip->li_bvuri = NULL;
590 				(void)ldap_chain_db_destroy_one( op->o_bd, NULL);
591 				goto cleanup;
592 			}
593 
594 			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
595 				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
596 				if ( avl_insert( &lc->lc_lai.lai_tree,
597 					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
598 				{
599 					/* someone just inserted another;
600 					 * don't bother, use this and then
601 					 * just free it */
602 					temporary = 1;
603 				}
604 				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
605 
606 			} else {
607 				temporary = 1;
608 			}
609 
610 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
611 				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
612 		}
613 
614 		lb->lb_op_f = op_f;
615 		lb->lb_depth = depth + 1;
616 
617 		rc = op_f( op, &rs2 );
618 
619 		/* note the first error */
620 		if ( first_rc == -1 ) {
621 			first_rc = rc;
622 		}
623 
624 cleanup:;
625 		ldap_memfree( li.li_uri );
626 		li.li_uri = NULL;
627 
628 		if ( temporary ) {
629 			lip->li_uri = NULL;
630 			lip->li_bvuri = NULL;
631 			(void)ldap_chain_db_close_one( op->o_bd );
632 			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
633 		}
634 
635 further_cleanup:;
636 		if ( op->o_req_dn.bv_val == pdn.bv_val ) {
637 			op->o_req_dn = odn;
638 			op->o_req_ndn = ondn;
639 		}
640 
641 		if ( free_dn ) {
642 			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
643 			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
644 		}
645 
646 		if ( op->o_tag == LDAP_REQ_SEARCH ) {
647 			if ( tmp_oq_search.rs_filter != NULL ) {
648 				filter_free_x( op, tmp_oq_search.rs_filter, 1 );
649 			}
650 
651 			if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
652 				slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
653 			}
654 
655 			op->oq_search = save_oq_search;
656 		}
657 
658 		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
659 			*rs = rs2;
660 			break;
661 		}
662 
663 		rc = rs2.sr_err;
664 	}
665 
666 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
667 	(void)chaining_control_remove( op, &ctrls );
668 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
669 
670 	if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
671 		rc = first_rc;
672 	}
673 
674 	return rc;
675 }
676 
677 static int
678 ldap_chain_search(
679 	Operation	*op,
680 	SlapReply	*rs,
681 	BerVarray	ref,
682 	int		depth )
683 
684 {
685 	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
686 	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
687 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
688 	ldapinfo_t	li = { 0 }, *lip = NULL;
689 	struct berval	bvuri[ 2 ] = { { 0 } };
690 
691 	struct berval	odn = op->o_req_dn,
692 			ondn = op->o_req_ndn;
693 	Entry		*save_entry = rs->sr_entry;
694 	slap_mask_t	save_flags = rs->sr_flags;
695 
696 	int		rc = LDAP_OTHER,
697 			first_rc = -1;
698 
699 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
700 	LDAPControl	**ctrls = NULL;
701 
702 	(void)chaining_control_add( lc, op, &ctrls );
703 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
704 
705 	assert( rs->sr_type == REP_SEARCHREF );
706 
707 	rs->sr_type = REP_SEARCH;
708 
709 	/* if we parse the URI then by no means
710 	 * we can cache stuff or reuse connections,
711 	 * because in back-ldap there's no caching
712 	 * based on the URI value, which is supposed
713 	 * to be set once for all (correct?) */
714 	li.li_bvuri = bvuri;
715 	for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
716 		SlapReply	rs2 = { REP_RESULT };
717 		LDAPURLDesc	*srv;
718 		req_search_s	save_oq_search = op->oq_search,
719 				tmp_oq_search = { 0 };
720 		struct berval	dn,
721 				pdn = op->o_req_dn,
722 				ndn = op->o_req_ndn;
723 		char		*filter = NULL;
724 		int		temporary = 0;
725 		int		free_dn = 0;
726 
727 		/* parse reference and use
728 		 * proto://[host][:port]/ only */
729 		rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
730 		if ( rc != LDAP_URL_SUCCESS ) {
731 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
732 				op->o_log_prefix, ref->bv_val, 0 );
733 
734 			/* try next */
735 			rs->sr_err = LDAP_OTHER;
736 			continue;
737 		}
738 
739 		if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
740 			/* RFC 4511: if scope is present, use it */
741 			tmp_oq_search.rs_scope = srv->lud_scope;
742 
743 		} else {
744 			/* RFC 4511: if scope is absent, use original */
745 			/* Section 4.5.3: if scope is onelevel, use base */
746 			if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
747 				tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
748 			else
749 				tmp_oq_search.rs_scope = op->ors_scope;
750 		}
751 
752 		rc = LDAP_SUCCESS;
753 		srv->lud_scope = LDAP_SCOPE_DEFAULT;
754 		dn.bv_val = srv->lud_dn;
755 		filter = srv->lud_filter;
756 
757 		/* normalize DN */
758 		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
759 			if ( srv->lud_dn == NULL ) {
760 				srv->lud_dn = "";
761 			}
762 
763 			if ( save_entry != NULL ) {
764 				/* use the "right" DN, if available */
765 				pdn = save_entry->e_name;
766 				ndn = save_entry->e_nname;
767 			} /* else leave the original req DN in place, if any RFC 4511 */
768 
769 		} else {
770 			/* RFC 4511: if DN is present, use it */
771 			ber_str2bv( srv->lud_dn, 0, 0, &dn );
772 			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
773 			if ( rc == LDAP_SUCCESS ) {
774 				/* remove DN essentially because later on
775 				 * ldap_initialize() will parse the URL
776 				 * as a comma-separated URL list */
777 				srv->lud_dn = "";
778 				free_dn = 1;
779 			}
780 		}
781 
782 		/* prepare filter */
783 		if ( rc == LDAP_SUCCESS ) {
784 			/* filter */
785 			if ( srv->lud_filter != NULL
786 				&& srv->lud_filter[0] != '\0'
787 				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
788 			{
789 				/* RFC 4511: if filter is present, use it;
790 				 * otherwise, use original */
791 				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
792 				if ( tmp_oq_search.rs_filter != NULL ) {
793 					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
794 
795 				} else {
796 					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
797 						op->o_log_prefix, ref->bv_val, srv->lud_filter );
798 					rc = LDAP_OTHER;
799 				}
800 			}
801 		}
802 		srv->lud_filter = NULL;
803 
804 		if ( rc == LDAP_SUCCESS ) {
805 			li.li_uri = ldap_url_desc2str( srv );
806 		}
807 
808 		srv->lud_dn = dn.bv_val;
809 		srv->lud_filter = filter;
810 		ldap_free_urldesc( srv );
811 
812 		if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
813 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
814 				op->o_log_prefix, ref->bv_val, 0 );
815 
816 			/* try next */
817 			rc = LDAP_OTHER;
818 			goto further_cleanup;
819 		}
820 
821 		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
822 			op->o_log_prefix, ref->bv_val, li.li_uri );
823 
824 		op->o_req_dn = pdn;
825 		op->o_req_ndn = ndn;
826 		op->ors_scope = tmp_oq_search.rs_scope;
827 		if ( tmp_oq_search.rs_filter != NULL ) {
828 			op->ors_filter = tmp_oq_search.rs_filter;
829 			op->ors_filterstr = tmp_oq_search.rs_filterstr;
830 		}
831 
832 		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
833 
834 		/* Searches for a ldapinfo in the avl tree */
835 		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
836 		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
837 			(caddr_t)&li, ldap_chain_uri_cmp );
838 		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
839 
840 		if ( lip != NULL ) {
841 			op->o_bd->be_private = (void *)lip;
842 
843 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
844 				op->o_log_prefix, ref->bv_val, li.li_uri );
845 
846 		} else {
847 			/* if none is found, create a temporary... */
848 			rc = ldap_chain_db_init_one( op->o_bd );
849 			if ( rc != 0 ) {
850 				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
851 					op->o_log_prefix, ref->bv_val, li.li_uri );
852 				goto cleanup;
853 			}
854 			lip = (ldapinfo_t *)op->o_bd->be_private;
855 			lip->li_uri = li.li_uri;
856 			lip->li_bvuri = bvuri;
857 			rc = ldap_chain_db_open_one( op->o_bd );
858 			if ( rc != 0 ) {
859 				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
860 					op->o_log_prefix, ref->bv_val, li.li_uri );
861 				lip->li_uri = NULL;
862 				lip->li_bvuri = NULL;
863 				(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
864 				goto cleanup;
865 			}
866 
867 			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
868 				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
869 				if ( avl_insert( &lc->lc_lai.lai_tree,
870 					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
871 				{
872 					/* someone just inserted another;
873 					 * don't bother, use this and then
874 					 * just free it */
875 					temporary = 1;
876 				}
877 				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
878 
879 			} else {
880 				temporary = 1;
881 			}
882 
883 			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
884 				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
885 		}
886 
887 		lb->lb_op_f = lback->bi_op_search;
888 		lb->lb_depth = depth + 1;
889 
890 		/* FIXME: should we also copy filter and scope?
891 		 * according to RFC3296, no */
892 		rc = lback->bi_op_search( op, &rs2 );
893 		if ( first_rc == -1 ) {
894 			first_rc = rc;
895 		}
896 
897 cleanup:;
898 		ldap_memfree( li.li_uri );
899 		li.li_uri = NULL;
900 
901 		if ( temporary ) {
902 			lip->li_uri = NULL;
903 			lip->li_bvuri = NULL;
904 			(void)ldap_chain_db_close_one( op->o_bd );
905 			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
906 		}
907 
908 further_cleanup:;
909 		if ( op->o_req_dn.bv_val == pdn.bv_val ) {
910 			op->o_req_dn = odn;
911 			op->o_req_ndn = ondn;
912 		}
913 
914 		if ( free_dn ) {
915 			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
916 			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
917 		}
918 
919 		if ( tmp_oq_search.rs_filter != NULL ) {
920 			filter_free_x( op, tmp_oq_search.rs_filter, 1 );
921 		}
922 
923 		if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
924 			slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
925 		}
926 
927 		op->oq_search = save_oq_search;
928 
929 		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
930 			*rs = rs2;
931 			break;
932 		}
933 
934 		rc = rs2.sr_err;
935 	}
936 
937 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
938 	(void)chaining_control_remove( op, &ctrls );
939 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
940 
941 	rs->sr_type = REP_SEARCHREF;
942 	rs->sr_entry = save_entry;
943 	rs->sr_flags = save_flags;
944 
945 	if ( rc != LDAP_SUCCESS ) {
946 		/* couldn't chase any of the referrals */
947 		if ( first_rc != -1 ) {
948 			rc = first_rc;
949 
950 		} else {
951 			rc = SLAP_CB_CONTINUE;
952 		}
953 	}
954 
955 	return rc;
956 }
957 
958 static int
959 ldap_chain_response( Operation *op, SlapReply *rs )
960 {
961 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
962 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
963 	BackendDB	db, *bd = op->o_bd;
964 	ldap_chain_cb_t	lb = { 0 };
965 	slap_callback	*sc = op->o_callback,
966 			sc2 = { 0 };
967 	int		rc = 0;
968 	const char	*text = NULL;
969 	const char	*matched;
970 	BerVarray	ref;
971 	struct berval	ndn = op->o_ndn;
972 
973 	int		sr_err = rs->sr_err;
974 	slap_reply_t	sr_type = rs->sr_type;
975 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
976 	slap_mask_t	chain_mask = 0;
977 	ber_len_t	chain_shift = 0;
978 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
979 
980 	if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
981 		return SLAP_CB_CONTINUE;
982 	}
983 
984 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
985 	if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986 		switch ( get_resolveBehavior( op ) ) {
987 		case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
988 		case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
989 			return SLAP_CB_CONTINUE;
990 
991 		default:
992 			chain_mask = SLAP_CH_RESOLVE_MASK;
993 			chain_shift = SLAP_CH_RESOLVE_SHIFT;
994 			break;
995 		}
996 
997 	} else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
998 		switch ( get_continuationBehavior( op ) ) {
999 		case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
1000 		case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
1001 			return SLAP_CB_CONTINUE;
1002 
1003 		default:
1004 			chain_mask = SLAP_CH_CONTINUATION_MASK;
1005 			chain_shift = SLAP_CH_CONTINUATION_SHIFT;
1006 			break;
1007 		}
1008 	}
1009 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1010 
1011 	/*
1012 	 * TODO: add checks on who/when chain operations; e.g.:
1013 	 *   a) what identities are authorized
1014 	 *   b) what request DN (e.g. only chain requests rooted at <DN>)
1015 	 *   c) what referral URIs
1016 	 *   d) what protocol scheme (e.g. only ldaps://)
1017 	 *   e) what ssf
1018 	 */
1019 
1020 	db = *op->o_bd;
1021 	SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1022 	op->o_bd = &db;
1023 
1024 	text = rs->sr_text;
1025 	rs->sr_text = NULL;
1026 	matched = rs->sr_matched;
1027 	rs->sr_matched = NULL;
1028 	ref = rs->sr_ref;
1029 	rs->sr_ref = NULL;
1030 
1031 	/* we need this to know if back-ldap returned any result */
1032 	lb.lb_lc = lc;
1033 	sc2.sc_next = sc->sc_next;
1034 	sc2.sc_private = &lb;
1035 	sc2.sc_response = ldap_chain_cb_response;
1036 	op->o_callback = &sc2;
1037 
1038 	/* Chaining can be performed by a privileged user on behalf
1039 	 * of normal users, using the ProxyAuthz control, by exploiting
1040 	 * the identity assertion feature of back-ldap; see idassert-*
1041 	 * directives in slapd-ldap(5).
1042 	 *
1043 	 * FIXME: the idassert-authcDN is one, will it be fine regardless
1044 	 * of the URI we obtain from the referral?
1045 	 */
1046 
1047 	switch ( op->o_tag ) {
1048 	case LDAP_REQ_BIND: {
1049 		struct berval	rndn = op->o_req_ndn;
1050 		Connection	*conn = op->o_conn;
1051 
1052 		/* FIXME: can we really get a referral for binds? */
1053 		op->o_req_ndn = slap_empty_bv;
1054 		op->o_conn = NULL;
1055 		rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1056 		op->o_req_ndn = rndn;
1057 		op->o_conn = conn;
1058 		}
1059 		break;
1060 
1061 	case LDAP_REQ_ADD:
1062 		rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1063 		break;
1064 
1065 	case LDAP_REQ_DELETE:
1066 		rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1067 		break;
1068 
1069 	case LDAP_REQ_MODRDN:
1070 		rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1071 	    	break;
1072 
1073 	case LDAP_REQ_MODIFY:
1074 		rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1075 		break;
1076 
1077 	case LDAP_REQ_COMPARE:
1078 		rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1079 		if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1080 			rc = LDAP_SUCCESS;
1081 		}
1082 		break;
1083 
1084 	case LDAP_REQ_SEARCH:
1085 		if ( rs->sr_type == REP_SEARCHREF ) {
1086 			sc2.sc_response = ldap_chain_cb_search_response;
1087 			rc = ldap_chain_search( op, rs, ref, 0 );
1088 
1089 		} else {
1090 			/* we might get here before any database actually
1091 			 * performed a search; in those cases, we need
1092 			 * to check limits, to make sure safe defaults
1093 			 * are in place */
1094 			if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1095 				rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1096 
1097 			} else {
1098 				rc = SLAP_CB_CONTINUE;
1099 			}
1100 		}
1101 	    	break;
1102 
1103 	case LDAP_REQ_EXTENDED:
1104 		rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1105 		/* FIXME: ldap_back_extended() by design
1106 		 * doesn't send result; frontend is expected
1107 		 * to send it... */
1108 		/* FIXME: what about chaining? */
1109 		if ( rc != SLAPD_ABANDON ) {
1110 			rs->sr_err = rc;
1111 			send_ldap_extended( op, rs );
1112 			rc = LDAP_SUCCESS;
1113 		}
1114 		lb.lb_status = LDAP_CH_RES;
1115 		break;
1116 
1117 	default:
1118 		rc = SLAP_CB_CONTINUE;
1119 		break;
1120 	}
1121 
1122 	switch ( rc ) {
1123 	case SLAPD_ABANDON:
1124 		goto dont_chain;
1125 
1126 	case LDAP_SUCCESS:
1127 	case LDAP_REFERRAL:
1128 		sr_err = rs->sr_err;
1129 		/* slapd-ldap sent response */
1130 		if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1131 			/* FIXME: should we send response? */
1132 			Debug( LDAP_DEBUG_ANY,
1133 				"%s: ldap_chain_response: "
1134 				"overlay should have sent result.\n",
1135 				op->o_log_prefix, 0, 0 );
1136 		}
1137 		break;
1138 
1139 	default:
1140 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1141 		if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1142 			goto cannot_chain;
1143 		}
1144 
1145 		switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1146 		case LDAP_CHAINING_REQUIRED:
1147 cannot_chain:;
1148 			op->o_callback = NULL;
1149 			send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1150 				"operation cannot be completed without chaining" );
1151 			goto dont_chain;
1152 
1153 		default:
1154 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1155 			if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1156 				sr_err = rs->sr_err = rc;
1157 				rs->sr_type = sr_type;
1158 
1159 			} else {
1160 				rc = SLAP_CB_CONTINUE;
1161 				rs->sr_err = sr_err;
1162 				rs->sr_type = sr_type;
1163 				rs->sr_text = text;
1164 				rs->sr_matched = matched;
1165 				rs->sr_ref = ref;
1166 			}
1167 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1168 			break;
1169 		}
1170 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1171 	}
1172 
1173 	if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1174 		/* give the remaining callbacks a chance */
1175 		op->o_callback = sc->sc_next;
1176 		rc = rs->sr_err = slap_map_api2result( rs );
1177 		send_ldap_result( op, rs );
1178 	}
1179 
1180 dont_chain:;
1181 	rs->sr_err = sr_err;
1182 	rs->sr_type = sr_type;
1183 	rs->sr_text = text;
1184 	rs->sr_matched = matched;
1185 	rs->sr_ref = ref;
1186 	op->o_bd = bd;
1187 	op->o_callback = sc;
1188 	op->o_ndn = ndn;
1189 
1190 	return rc;
1191 }
1192 
1193 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1194 static int
1195 ldap_chain_parse_ctrl(
1196 	Operation	*op,
1197 	SlapReply	*rs,
1198 	LDAPControl	*ctrl );
1199 
1200 static int
1201 str2chain( const char *s )
1202 {
1203 	if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1204 		return LDAP_CHAINING_PREFERRED;
1205 
1206 	} else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1207 		return LDAP_CHAINING_REQUIRED;
1208 
1209 	} else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1210 		return LDAP_REFERRALS_PREFERRED;
1211 
1212 	} else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1213 		return LDAP_REFERRALS_REQUIRED;
1214 	}
1215 
1216 	return -1;
1217 }
1218 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1219 
1220 /*
1221  * configuration...
1222  */
1223 
1224 enum {
1225 	CH_CHAINING = 1,
1226 	CH_CACHE_URI,
1227 	CH_MAX_DEPTH,
1228 	CH_RETURN_ERR,
1229 
1230 	CH_LAST
1231 };
1232 
1233 static ConfigDriver chain_cf_gen;
1234 static ConfigCfAdd chain_cfadd;
1235 static ConfigLDAPadd chain_ldadd;
1236 #ifdef SLAP_CONFIG_DELETE
1237 static ConfigLDAPdel chain_lddel;
1238 #endif
1239 
1240 static ConfigTable chaincfg[] = {
1241 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1242 	{ "chain-chaining", "args",
1243 		2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1244 		"( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1245 			"DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1246 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1247 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1248 	{ "chain-cache-uri", "TRUE/FALSE",
1249 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1250 		"( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1251 			"DESC 'Enables caching of URIs not present in configuration' "
1252 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253 	{ "chain-max-depth", "args",
1254 		2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1255 		"( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1256 			"DESC 'max referral depth' "
1257 			"SYNTAX OMsInteger "
1258 			"EQUALITY integerMatch "
1259 			"SINGLE-VALUE )", NULL, NULL },
1260 	{ "chain-return-error", "TRUE/FALSE",
1261 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1262 		"( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1263 			"DESC 'Errors are returned instead of the original referral' "
1264 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1265 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1266 };
1267 
1268 static ConfigOCs chainocs[] = {
1269 	{ "( OLcfgOvOc:3.1 "
1270 		"NAME 'olcChainConfig' "
1271 		"DESC 'Chain configuration' "
1272 		"SUP olcOverlayConfig "
1273 		"MAY ( "
1274 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1275 			"olcChainingBehavior $ "
1276 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1277 			"olcChainCacheURI $ "
1278 			"olcChainMaxReferralDepth $ "
1279 			"olcChainReturnError "
1280 			") )",
1281 		Cft_Overlay, chaincfg, NULL, chain_cfadd },
1282 	{ "( OLcfgOvOc:3.2 "
1283 		"NAME 'olcChainDatabase' "
1284 		"DESC 'Chain remote server configuration' "
1285 		"AUXILIARY )",
1286 		Cft_Misc, olcDatabaseDummy, chain_ldadd
1287 #ifdef SLAP_CONFIG_DELETE
1288 		, NULL, chain_lddel
1289 #endif
1290 	},
1291 	{ NULL, 0, NULL }
1292 };
1293 
1294 static int
1295 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1296 {
1297 	slap_overinst		*on;
1298 	ldap_chain_t		*lc;
1299 
1300 	ldapinfo_t		*li;
1301 
1302 	AttributeDescription	*ad = NULL;
1303 	Attribute		*at;
1304 	const char		*text;
1305 
1306 	int			rc;
1307 
1308 	if ( p->ce_type != Cft_Overlay
1309 		|| !p->ce_bi
1310 		|| p->ce_bi->bi_cf_ocs != chainocs )
1311 	{
1312 		return LDAP_CONSTRAINT_VIOLATION;
1313 	}
1314 
1315 	on = (slap_overinst *)p->ce_bi;
1316 	lc = (ldap_chain_t *)on->on_bi.bi_private;
1317 
1318 	assert( ca->be == NULL );
1319 	ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1320 
1321 	ca->be->bd_info = (BackendInfo *)on;
1322 
1323 	rc = slap_str2ad( "olcDbURI", &ad, &text );
1324 	assert( rc == LDAP_SUCCESS );
1325 
1326 	at = attr_find( e->e_attrs, ad );
1327 #if 0
1328 	if ( lc->lc_common_li == NULL && at != NULL ) {
1329 		/* FIXME: we should generate an empty default entry
1330 		 * if none is supplied */
1331 		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332 			"first underlying database \"%s\" "
1333 			"cannot contain attribute \"%s\".\n",
1334 			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335 		rc = LDAP_CONSTRAINT_VIOLATION;
1336 		goto done;
1337 
1338 	} else
1339 #endif
1340 	if ( lc->lc_common_li != NULL && at == NULL ) {
1341 		/* FIXME: we should generate an empty default entry
1342 		 * if none is supplied */
1343 		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1344 			"subsequent underlying database \"%s\" "
1345 			"must contain attribute \"%s\".\n",
1346 			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1347 		rc = LDAP_CONSTRAINT_VIOLATION;
1348 		goto done;
1349 	}
1350 
1351 	if ( lc->lc_common_li == NULL ) {
1352 		rc = ldap_chain_db_init_common( ca->be );
1353 		if ( rc != 0 )
1354 			goto fail;
1355 		li = ca->be->be_private;
1356 		lc->lc_common_li = lc->lc_cfg_li = li;
1357 
1358 	}
1359 	rc = ldap_chain_db_init_one( ca->be );
1360 
1361 	if ( rc != 0 ) {
1362 fail:
1363 		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1364 			"unable to init %sunderlying database \"%s\".\n",
1365 			lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1366 		return LDAP_CONSTRAINT_VIOLATION;
1367 	}
1368 
1369 	li = ca->be->be_private;
1370 
1371 	if ( at ) {
1372 		li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1373 		value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1374 		if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1375 			ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1376 		{
1377 			Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1378 				"database \"%s\" insert failed.\n",
1379 				e->e_name.bv_val, 0, 0 );
1380 			rc = LDAP_CONSTRAINT_VIOLATION;
1381 			goto done;
1382 		}
1383 	}
1384 
1385 	ca->ca_private = on;
1386 
1387 done:;
1388 	if ( rc != LDAP_SUCCESS ) {
1389 		(void)ldap_chain_db_destroy_one( ca->be, NULL );
1390 		ch_free( ca->be );
1391 		ca->be = NULL;
1392 	}
1393 
1394 	return rc;
1395 }
1396 
1397 typedef struct ldap_chain_cfadd_apply_t {
1398 	Operation	*op;
1399 	SlapReply	*rs;
1400 	Entry		*p;
1401 	ConfigArgs	*ca;
1402 	int		count;
1403 } ldap_chain_cfadd_apply_t;
1404 
1405 static int
1406 ldap_chain_cfadd_apply( void *datum, void *arg )
1407 {
1408 	ldapinfo_t			*li = (ldapinfo_t *)datum;
1409 	ldap_chain_cfadd_apply_t	*lca = (ldap_chain_cfadd_apply_t *)arg;
1410 
1411 	struct berval			bv;
1412 
1413 	/* FIXME: should not hardcode "olcDatabase" here */
1414 	bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1415 		"olcDatabase={%d}%s", lca->count, lback->bi_type );
1416 	bv.bv_val = lca->ca->cr_msg;
1417 
1418 	lca->ca->be->be_private = (void *)li;
1419 	config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1420 		&bv, lback->bi_cf_ocs, &chainocs[1] );
1421 
1422 	lca->count++;
1423 
1424 	return 0;
1425 }
1426 
1427 static int
1428 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1429 {
1430 	CfEntryInfo	*pe = p->e_private;
1431 	slap_overinst	*on = (slap_overinst *)pe->ce_bi;
1432 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1433 	void		*priv = (void *)ca->be->be_private;
1434 
1435 	if ( lback->bi_cf_ocs ) {
1436 		ldap_chain_cfadd_apply_t	lca = { 0 };
1437 
1438 		lca.op = op;
1439 		lca.rs = rs;
1440 		lca.p = p;
1441 		lca.ca = ca;
1442 		lca.count = 0;
1443 
1444 		(void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1445 
1446 		(void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1447 			&lca, 1, AVL_INORDER );
1448 
1449 		ca->be->be_private = priv;
1450 	}
1451 
1452 	return 0;
1453 }
1454 
1455 #ifdef SLAP_CONFIG_DELETE
1456 static int
1457 chain_lddel( CfEntryInfo *ce, Operation *op )
1458 {
1459 	CfEntryInfo	*pe = ce->ce_parent;
1460 	slap_overinst	*on = (slap_overinst *)pe->ce_bi;
1461 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1462 	ldapinfo_t	*li = (ldapinfo_t *) ce->ce_be->be_private;
1463 
1464 	if ( li != lc->lc_common_li ) {
1465 		if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1466 			Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1467 				"\"%s\" not found.\n", li->li_uri, 0, 0 );
1468 			return -1;
1469 		}
1470 	} else if ( lc->lc_lai.lai_tree ) {
1471 		Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1472 			"LDAP database when other databases are still present.\n", 0, 0, 0 );
1473 		return -1;
1474 	} else {
1475 		lc->lc_common_li = NULL;
1476 	}
1477 
1478 	ce->ce_be->bd_info = lback;
1479 
1480 	if ( ce->ce_be->bd_info->bi_db_close ) {
1481 		ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1482 	}
1483 	if ( ce->ce_be->bd_info->bi_db_destroy ) {
1484 		ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1485 	}
1486 
1487 	ch_free(ce->ce_be);
1488 	ce->ce_be = NULL;
1489 
1490 	return LDAP_SUCCESS;
1491 }
1492 #endif /* SLAP_CONFIG_DELETE */
1493 
1494 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1495 static slap_verbmasks chaining_mode[] = {
1496 	{ BER_BVC("referralsRequired"),		LDAP_REFERRALS_REQUIRED },
1497 	{ BER_BVC("referralsPreferred"),	LDAP_REFERRALS_PREFERRED },
1498 	{ BER_BVC("chainingRequired"),		LDAP_CHAINING_REQUIRED },
1499 	{ BER_BVC("chainingPreferred"),		LDAP_CHAINING_PREFERRED },
1500 	{ BER_BVNULL,				0 }
1501 };
1502 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1503 
1504 static int
1505 chain_cf_gen( ConfigArgs *c )
1506 {
1507 	slap_overinst	*on = (slap_overinst *)c->bi;
1508 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1509 
1510 	int		rc = 0;
1511 
1512 	if ( c->op == SLAP_CONFIG_EMIT ) {
1513 		switch( c->type ) {
1514 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1515 		case CH_CHAINING: {
1516 			struct berval	resolve = BER_BVNULL,
1517 					continuation = BER_BVNULL;
1518 
1519 			if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1520 				return 1;
1521 			}
1522 
1523 			enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1524 			enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1525 
1526 			c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1527 				+ STRLENOF( " " )
1528 				+ STRLENOF( "continuation=" ) + continuation.bv_len;
1529 			c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1530 			snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1531 				"resolve=%s continuation=%s",
1532 				resolve.bv_val, continuation.bv_val );
1533 
1534 			if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1535 				c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1536 					c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1537 				AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1538 					" critical", STRLENOF( " critical" ) + 1 );
1539 				c->value_bv.bv_len += STRLENOF( " critical" );
1540 			}
1541 
1542 			break;
1543 		}
1544 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1545 
1546 		case CH_CACHE_URI:
1547 			c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1548 			break;
1549 
1550 		case CH_MAX_DEPTH:
1551 			c->value_int = lc->lc_max_depth;
1552 			break;
1553 
1554 		case CH_RETURN_ERR:
1555 			c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1556 			break;
1557 
1558 		default:
1559 			assert( 0 );
1560 			rc = 1;
1561 		}
1562 		return rc;
1563 
1564 	} else if ( c->op == LDAP_MOD_DELETE ) {
1565 		switch( c->type ) {
1566 		case CH_CHAINING:
1567 			return 1;
1568 
1569 		case CH_CACHE_URI:
1570 			lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1571 			break;
1572 
1573 		case CH_MAX_DEPTH:
1574 			c->value_int = 0;
1575 			break;
1576 
1577 		case CH_RETURN_ERR:
1578 			lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1579 			break;
1580 
1581 		default:
1582 			return 1;
1583 		}
1584 		return rc;
1585 	}
1586 
1587 	switch( c->type ) {
1588 	case CH_CHAINING: {
1589 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1590 		char			**argv = c->argv;
1591 		int			argc = c->argc;
1592 		BerElementBuffer	berbuf;
1593 		BerElement		*ber = (BerElement *)&berbuf;
1594 		int			resolve = -1,
1595 					continuation = -1,
1596 					iscritical = 0;
1597 		Operation		op = { 0 };
1598 		SlapReply		rs = { 0 };
1599 
1600 		lc->lc_chaining_ctrlflag = 0;
1601 
1602 		for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1603 			if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1604 				resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1605 				if ( resolve == -1 ) {
1606 					Debug( LDAP_DEBUG_ANY, "%s: "
1607 						"illegal <resolve> value %s "
1608 						"in \"chain-chaining>\".\n",
1609 						c->log, argv[ 0 ], 0 );
1610 					return 1;
1611 				}
1612 
1613 			} else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1614 				continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1615 				if ( continuation == -1 ) {
1616 					Debug( LDAP_DEBUG_ANY, "%s: "
1617 						"illegal <continuation> value %s "
1618 						"in \"chain-chaining\".\n",
1619 						c->log, argv[ 0 ], 0 );
1620 					return 1;
1621 				}
1622 
1623 			} else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1624 				iscritical = 1;
1625 
1626 			} else {
1627 				Debug( LDAP_DEBUG_ANY, "%s: "
1628 					"unknown option in \"chain-chaining\".\n",
1629 					c->log, 0, 0 );
1630 				return 1;
1631 			}
1632 		}
1633 
1634 		if ( resolve != -1 || continuation != -1 ) {
1635 			int	err;
1636 
1637 			if ( resolve == -1 ) {
1638 				/* default */
1639 				resolve = SLAP_CHAINING_DEFAULT;
1640 			}
1641 
1642 			ber_init2( ber, NULL, LBER_USE_DER );
1643 
1644 			err = ber_printf( ber, "{e" /* } */, resolve );
1645 	    		if ( err == -1 ) {
1646 				ber_free( ber, 1 );
1647 				Debug( LDAP_DEBUG_ANY, "%s: "
1648 					"chaining behavior control encoding error!\n",
1649 					c->log, 0, 0 );
1650 				return 1;
1651 			}
1652 
1653 			if ( continuation > -1 ) {
1654 				err = ber_printf( ber, "e", continuation );
1655 	    			if ( err == -1 ) {
1656 					ber_free( ber, 1 );
1657 					Debug( LDAP_DEBUG_ANY, "%s: "
1658 						"chaining behavior control encoding error!\n",
1659 						c->log, 0, 0 );
1660 					return 1;
1661 				}
1662 			}
1663 
1664 			err = ber_printf( ber, /* { */ "N}" );
1665 	    		if ( err == -1 ) {
1666 				ber_free( ber, 1 );
1667 				Debug( LDAP_DEBUG_ANY, "%s: "
1668 					"chaining behavior control encoding error!\n",
1669 					c->log, 0, 0 );
1670 				return 1;
1671 			}
1672 
1673 			if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1674 				exit( EXIT_FAILURE );
1675 			}
1676 
1677 		} else {
1678 			BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1679 		}
1680 
1681 		lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1682 		lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1683 
1684 		if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1685 		{
1686 			Debug( LDAP_DEBUG_ANY, "%s: "
1687 				"unable to parse chaining control%s%s.\n",
1688 				c->log, rs.sr_text ? ": " : "",
1689 				rs.sr_text ? rs.sr_text : "" );
1690 			return 1;
1691 		}
1692 
1693 		lc->lc_chaining_ctrlflag = op.o_chaining;
1694 
1695 		lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1696 
1697 		rc = 0;
1698 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1699 		Debug( LDAP_DEBUG_ANY, "%s: "
1700 			"\"chaining\" control unsupported (ignored).\n",
1701 			c->log, 0, 0 );
1702 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1703 		} break;
1704 
1705 	case CH_CACHE_URI:
1706 		if ( c->value_int ) {
1707 			lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1708 		} else {
1709 			lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1710 		}
1711 		break;
1712 
1713 	case CH_MAX_DEPTH:
1714 		if ( c->value_int < 0 ) {
1715 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1716 				"<%s> invalid max referral depth %d",
1717 				c->argv[0], c->value_int );
1718 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1719 				c->log, c->cr_msg, 0 );
1720 			rc = 1;
1721 			break;
1722 		}
1723 		lc->lc_max_depth = c->value_int;
1724 
1725 	case CH_RETURN_ERR:
1726 		if ( c->value_int ) {
1727 			lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1728 		} else {
1729 			lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1730 		}
1731 		break;
1732 
1733 	default:
1734 		assert( 0 );
1735 		return 1;
1736 	}
1737 	return rc;
1738 }
1739 
1740 static int
1741 ldap_chain_db_init(
1742 	BackendDB *be,
1743 	ConfigReply *cr )
1744 {
1745 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1746 	ldap_chain_t	*lc = NULL;
1747 
1748 	if ( lback == NULL ) {
1749 		lback = backend_info( "ldap" );
1750 
1751 		if ( lback == NULL ) {
1752 			return 1;
1753 		}
1754 	}
1755 
1756 	lc = ch_malloc( sizeof( ldap_chain_t ) );
1757 	if ( lc == NULL ) {
1758 		return 1;
1759 	}
1760 	memset( lc, 0, sizeof( ldap_chain_t ) );
1761 	lc->lc_max_depth = 1;
1762 	ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1763 
1764 	on->on_bi.bi_private = (void *)lc;
1765 
1766 	return 0;
1767 }
1768 
1769 static int
1770 ldap_chain_db_config(
1771 	BackendDB	*be,
1772 	const char	*fname,
1773 	int		lineno,
1774 	int		argc,
1775 	char		**argv )
1776 {
1777 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1778 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1779 
1780 	int		rc = SLAP_CONF_UNKNOWN;
1781 
1782 	if ( lc->lc_common_li == NULL ) {
1783 		BackendDB db = *be;
1784 		ldap_chain_db_init_common( &db );
1785 		lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1786 	}
1787 
1788 	/* Something for the chain database? */
1789 	if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1790 		char		*save_argv0 = argv[ 0 ];
1791 		BackendDB	db = *be;
1792 		static char	*allowed_argv[] = {
1793 			/* special: put URI here, so in the meanwhile
1794 			 * it detects whether a new URI is being provided */
1795 			"uri",
1796 			"nretries",
1797 			"timeout",
1798 			/* flags */
1799 			"tls",
1800 			/* FIXME: maybe rebind-as-user should be allowed
1801 			 * only within known URIs... */
1802 			"rebind-as-user",
1803 			"chase-referrals",
1804 			"t-f-support",
1805 			"proxy-whoami",
1806 			NULL
1807 		};
1808 		int		which_argv = -1;
1809 
1810 		argv[ 0 ] += STRLENOF( "chain-" );
1811 
1812 		for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1813 			if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1814 				break;
1815 			}
1816 		}
1817 
1818 		if ( allowed_argv[ which_argv ] == NULL ) {
1819 			which_argv = -1;
1820 
1821 			if ( lc->lc_cfg_li == lc->lc_common_li ) {
1822 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1823 					"\"%s\" only allowed within a URI directive.\n.",
1824 					fname, lineno, argv[ 0 ] );
1825 				return 1;
1826 			}
1827 		}
1828 
1829 		if ( which_argv == 0 ) {
1830 			rc = ldap_chain_db_init_one( &db );
1831 			if ( rc != 0 ) {
1832 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1833 					"underlying slapd-ldap initialization failed.\n.",
1834 					fname, lineno, 0 );
1835 				return 1;
1836 			}
1837 			lc->lc_cfg_li = db.be_private;
1838 		}
1839 
1840 		/* TODO: add checks on what other slapd-ldap(5) args
1841 		 * should be put in the template; this is not quite
1842 		 * harmful, because attributes that shouldn't don't
1843 		 * get actually used, but the user should at least
1844 		 * be warned.
1845 		 */
1846 
1847 		db.bd_info = lback;
1848 		db.be_private = (void *)lc->lc_cfg_li;
1849 		db.be_cf_ocs = lback->bi_cf_ocs;
1850 
1851 		rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1852 
1853 		argv[ 0 ] = save_argv0;
1854 
1855 		if ( which_argv == 0 ) {
1856 private_destroy:;
1857 			if ( rc != 0 ) {
1858 				db.bd_info = lback;
1859 				db.be_private = (void *)lc->lc_cfg_li;
1860 				ldap_chain_db_destroy_one( &db, NULL );
1861 				lc->lc_cfg_li = NULL;
1862 			} else {
1863 				if ( lc->lc_cfg_li->li_bvuri == NULL
1864 					|| BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1865 					|| !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1866 				{
1867 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1868 						"no URI list allowed in slapo-chain.\n",
1869 						fname, lineno, 0 );
1870 					rc = 1;
1871 					goto private_destroy;
1872 				}
1873 
1874 				if ( avl_insert( &lc->lc_lai.lai_tree,
1875 					(caddr_t)lc->lc_cfg_li,
1876 					ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1877 				{
1878 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1879 						"duplicate URI in slapo-chain.\n",
1880 						fname, lineno, 0 );
1881 					rc = 1;
1882 					goto private_destroy;
1883 				}
1884 			}
1885 		}
1886 	}
1887 
1888 	return rc;
1889 }
1890 
1891 enum db_which {
1892 	db_open = 0,
1893 	db_close,
1894 	db_destroy,
1895 
1896 	db_last
1897 };
1898 
1899 typedef struct ldap_chain_db_apply_t {
1900 	BackendDB	*be;
1901 	BI_db_func	*func;
1902 } ldap_chain_db_apply_t;
1903 
1904 static int
1905 ldap_chain_db_apply( void *datum, void *arg )
1906 {
1907 	ldapinfo_t		*li = (ldapinfo_t *)datum;
1908 	ldap_chain_db_apply_t	*lca = (ldap_chain_db_apply_t *)arg;
1909 
1910 	lca->be->be_private = (void *)li;
1911 
1912 	return lca->func( lca->be, NULL );
1913 }
1914 
1915 static int
1916 ldap_chain_db_func(
1917 	BackendDB *be,
1918 	enum db_which which
1919 )
1920 {
1921 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1922 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1923 
1924 	int		rc = 0;
1925 
1926 	if ( lc ) {
1927 		BI_db_func	*func = (&lback->bi_db_open)[ which ];
1928 
1929 		if ( func != NULL && lc->lc_common_li != NULL ) {
1930 			BackendDB		db = *be;
1931 
1932 			db.bd_info = lback;
1933 			db.be_private = lc->lc_common_li;
1934 
1935 			rc = func( &db, NULL );
1936 
1937 			if ( rc != 0 ) {
1938 				return rc;
1939 			}
1940 
1941 			if ( lc->lc_lai.lai_tree != NULL ) {
1942 				ldap_chain_db_apply_t	lca;
1943 
1944 				lca.be = &db;
1945 				lca.func = func;
1946 
1947 				rc = avl_apply( lc->lc_lai.lai_tree,
1948 					ldap_chain_db_apply, (void *)&lca,
1949 					1, AVL_INORDER ) != AVL_NOMORE;
1950 			}
1951 		}
1952 	}
1953 
1954 	return rc;
1955 }
1956 
1957 static int
1958 ldap_chain_db_open(
1959 	BackendDB	*be,
1960 	ConfigReply	*cr )
1961 {
1962 	slap_overinst	*on = (slap_overinst *) be->bd_info;
1963 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1964 	slap_mask_t	monitoring;
1965 	int		rc = 0;
1966 
1967 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1968 	rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1969 	if ( rc != 0 ) {
1970 		return rc;
1971 	}
1972 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1973 
1974 	if ( lc->lc_common_li == NULL ) {
1975 		void	*be_private = be->be_private;
1976 		ldap_chain_db_init_common( be );
1977 		lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1978 		be->be_private = be_private;
1979 	}
1980 
1981 	/* filter out and restore monitoring */
1982 	monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1983 	SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1984 	rc = ldap_chain_db_func( be, db_open );
1985 	SLAP_DBFLAGS( be ) |= monitoring;
1986 
1987 	return rc;
1988 }
1989 
1990 static int
1991 ldap_chain_db_close(
1992 	BackendDB	*be,
1993 	ConfigReply	*cr )
1994 {
1995 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1996 #ifdef SLAP_CONFIG_DELETE
1997 	overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1998 #endif /* SLAP_CONFIG_DELETE */
1999 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2000 	return ldap_chain_db_func( be, db_close );
2001 }
2002 
2003 static int
2004 ldap_chain_db_destroy(
2005 	BackendDB	*be,
2006 	ConfigReply	*cr )
2007 {
2008 	slap_overinst	*on = (slap_overinst *) be->bd_info;
2009 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
2010 
2011 	int		rc;
2012 
2013 	rc = ldap_chain_db_func( be, db_destroy );
2014 
2015 	if ( lc ) {
2016 		avl_free( lc->lc_lai.lai_tree, NULL );
2017 		ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2018 		ch_free( lc );
2019 	}
2020 
2021 	return rc;
2022 }
2023 
2024 /*
2025  * inits one instance of the slapd-ldap backend, and stores
2026  * the private info in be_private of the arg
2027  */
2028 static int
2029 ldap_chain_db_init_common(
2030 	BackendDB	*be )
2031 {
2032 	BackendInfo	*bi = be->bd_info;
2033 	ldapinfo_t	*li;
2034 	int		rc;
2035 
2036 	be->bd_info = lback;
2037 	be->be_private = NULL;
2038 	rc = lback->bi_db_init( be, NULL );
2039 	if ( rc != 0 ) {
2040 		return rc;
2041 	}
2042 	li = (ldapinfo_t *)be->be_private;
2043 	li->li_urllist_f = NULL;
2044 	li->li_urllist_p = NULL;
2045 
2046 	be->bd_info = bi;
2047 
2048 	return 0;
2049 }
2050 
2051 /*
2052  * inits one instance of the slapd-ldap backend, stores
2053  * the private info in be_private of the arg and fills
2054  * selected fields with data from the template.
2055  *
2056  * NOTE: add checks about the other fields of the template,
2057  * which are ignored and SHOULD NOT be configured by the user.
2058  */
2059 static int
2060 ldap_chain_db_init_one(
2061 	BackendDB	*be )
2062 {
2063 	slap_overinst	*on = (slap_overinst *)be->bd_info;
2064 	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
2065 
2066 	BackendInfo	*bi = be->bd_info;
2067 	ldapinfo_t	*li;
2068 
2069 	slap_op_t	t;
2070 
2071 	be->bd_info = lback;
2072 	be->be_private = NULL;
2073 	t = lback->bi_db_init( be, NULL );
2074 	if ( t != 0 ) {
2075 		return t;
2076 	}
2077 	li = (ldapinfo_t *)be->be_private;
2078 	li->li_urllist_f = NULL;
2079 	li->li_urllist_p = NULL;
2080 
2081 	/* copy common data */
2082 	li->li_nretries = lc->lc_common_li->li_nretries;
2083 	li->li_flags = lc->lc_common_li->li_flags;
2084 	li->li_version = lc->lc_common_li->li_version;
2085 	for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2086 		li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2087 	}
2088 	be->bd_info = bi;
2089 
2090 	return 0;
2091 }
2092 
2093 static int
2094 ldap_chain_db_open_one(
2095 	BackendDB	*be )
2096 {
2097 	if ( SLAP_DBMONITORING( be ) ) {
2098 		ldapinfo_t	*li = (ldapinfo_t *)be->be_private;
2099 
2100 		if ( li->li_uri == NULL ) {
2101 			ber_str2bv( "cn=Common Connections", 0, 1,
2102 				&li->li_monitor_info.lmi_conn_rdn );
2103 			ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2104 				&li->li_monitor_info.lmi_conn_rdn );
2105 
2106 		} else {
2107 			char		*ptr;
2108 
2109 			li->li_monitor_info.lmi_conn_rdn.bv_len
2110 				= STRLENOF( "cn=" ) + strlen( li->li_uri );
2111 			ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2112 				= ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2113 			ptr = lutil_strcopy( ptr, "cn=" );
2114 			ptr = lutil_strcopy( ptr, li->li_uri );
2115 			ptr[ 0 ] = '\0';
2116 
2117 			li->li_monitor_info.lmi_ops_rdn.bv_len
2118 				= STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2119 			ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2120 				= ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2121 			ptr = lutil_strcopy( ptr, "cn=Operations on " );
2122 			ptr = lutil_strcopy( ptr, li->li_uri );
2123 			ptr[ 0 ] = '\0';
2124 		}
2125 	}
2126 
2127 	return lback->bi_db_open( be, NULL );
2128 }
2129 
2130 typedef struct ldap_chain_conn_apply_t {
2131 	BackendDB	*be;
2132 	Connection	*conn;
2133 } ldap_chain_conn_apply_t;
2134 
2135 static int
2136 ldap_chain_conn_apply( void *datum, void *arg )
2137 {
2138 	ldapinfo_t		*li = (ldapinfo_t *)datum;
2139 	ldap_chain_conn_apply_t	*lca = (ldap_chain_conn_apply_t *)arg;
2140 
2141 	lca->be->be_private = (void *)li;
2142 
2143 	return lback->bi_connection_destroy( lca->be, lca->conn );
2144 }
2145 
2146 static int
2147 ldap_chain_connection_destroy(
2148 	BackendDB *be,
2149 	Connection *conn
2150 )
2151 {
2152 	slap_overinst		*on = (slap_overinst *) be->bd_info;
2153 	ldap_chain_t		*lc = (ldap_chain_t *)on->on_bi.bi_private;
2154 	void			*private = be->be_private;
2155 	ldap_chain_conn_apply_t	lca;
2156 	int			rc;
2157 
2158 	be->be_private = NULL;
2159 	lca.be = be;
2160 	lca.conn = conn;
2161 	ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2162 	rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2163 		(void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2164 	ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2165 	be->be_private = private;
2166 
2167 	return rc;
2168 }
2169 
2170 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2171 static int
2172 ldap_chain_parse_ctrl(
2173 	Operation	*op,
2174 	SlapReply	*rs,
2175 	LDAPControl	*ctrl )
2176 {
2177 	ber_tag_t	tag;
2178 	BerElement	*ber;
2179 	ber_int_t	mode,
2180 			behavior;
2181 
2182 	if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2183 		rs->sr_text = "Chaining behavior control specified multiple times";
2184 		return LDAP_PROTOCOL_ERROR;
2185 	}
2186 
2187 	if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2188 		rs->sr_text = "Chaining behavior control specified with pagedResults control";
2189 		return LDAP_PROTOCOL_ERROR;
2190 	}
2191 
2192 	if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2193 		mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2194 
2195 	} else {
2196 		ber_len_t	len;
2197 
2198 		/* Parse the control value
2199 		 *      ChainingBehavior ::= SEQUENCE {
2200 		 *           resolveBehavior         Behavior OPTIONAL,
2201 		 *           continuationBehavior    Behavior OPTIONAL }
2202 		 *
2203 		 *      Behavior :: = ENUMERATED {
2204 		 *           chainingPreferred       (0),
2205 		 *           chainingRequired        (1),
2206 		 *           referralsPreferred      (2),
2207 		 *           referralsRequired       (3) }
2208 		 */
2209 
2210 		ber = ber_init( &ctrl->ldctl_value );
2211 		if( ber == NULL ) {
2212 			rs->sr_text = "internal error";
2213 			return LDAP_OTHER;
2214 		}
2215 
2216 		tag = ber_scanf( ber, "{e" /* } */, &behavior );
2217 		/* FIXME: since the whole SEQUENCE is optional,
2218 		 * should we accept no enumerations at all? */
2219 		if ( tag != LBER_ENUMERATED ) {
2220 			rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2221 			return LDAP_PROTOCOL_ERROR;
2222 		}
2223 
2224 		switch ( behavior ) {
2225 		case LDAP_CHAINING_PREFERRED:
2226 			mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2227 			break;
2228 
2229 		case LDAP_CHAINING_REQUIRED:
2230 			mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2231 			break;
2232 
2233 		case LDAP_REFERRALS_PREFERRED:
2234 			mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2235 			break;
2236 
2237 		case LDAP_REFERRALS_REQUIRED:
2238 			mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2239 			break;
2240 
2241 		default:
2242 			rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2243 			return LDAP_PROTOCOL_ERROR;
2244 		}
2245 
2246 		tag = ber_peek_tag( ber, &len );
2247 		if ( tag == LBER_ENUMERATED ) {
2248 			tag = ber_scanf( ber, "e", &behavior );
2249 			if ( tag == LBER_ERROR ) {
2250 				rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2251 				return LDAP_PROTOCOL_ERROR;
2252 			}
2253 		}
2254 
2255 		if ( tag == LBER_DEFAULT ) {
2256 			mode |= SLAP_CH_CONTINUATION_DEFAULT;
2257 
2258 		} else {
2259 			switch ( behavior ) {
2260 			case LDAP_CHAINING_PREFERRED:
2261 				mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2262 				break;
2263 
2264 			case LDAP_CHAINING_REQUIRED:
2265 				mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2266 				break;
2267 
2268 			case LDAP_REFERRALS_PREFERRED:
2269 				mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2270 				break;
2271 
2272 			case LDAP_REFERRALS_REQUIRED:
2273 				mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2274 				break;
2275 
2276 			default:
2277 				rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2278 				return LDAP_PROTOCOL_ERROR;
2279 			}
2280 		}
2281 
2282 		if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2283 			rs->sr_text = "Chaining behavior control: decoding error";
2284 			return LDAP_PROTOCOL_ERROR;
2285 		}
2286 
2287 		(void) ber_free( ber, 1 );
2288 	}
2289 
2290 	op->o_chaining = mode | ( ctrl->ldctl_iscritical
2291 			? SLAP_CONTROL_CRITICAL
2292 			: SLAP_CONTROL_NONCRITICAL );
2293 
2294 	return LDAP_SUCCESS;
2295 }
2296 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2297 
2298 int
2299 chain_initialize( void )
2300 {
2301 	int rc;
2302 
2303 	/* Make sure we don't exceed the bits reserved for userland */
2304 	config_check_userland( CH_LAST );
2305 
2306 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2307 	rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2308 			/* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2309 			ldap_chain_parse_ctrl, &sc_chainingBehavior );
2310 	if ( rc != LDAP_SUCCESS ) {
2311 		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2312 			"unable to register chaining behavior control: %d.\n",
2313 			rc, 0, 0 );
2314 		return rc;
2315 	}
2316 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2317 
2318 	ldapchain.on_bi.bi_type = "chain";
2319 	ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2320 	ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2321 	ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2322 	ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2323 	ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2324 
2325 	ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2326 
2327 	ldapchain.on_response = ldap_chain_response;
2328 
2329 	ldapchain.on_bi.bi_cf_ocs = chainocs;
2330 
2331 	rc = config_register_schema( chaincfg, chainocs );
2332 	if ( rc ) {
2333 		return rc;
2334 	}
2335 
2336 	return overlay_register( &ldapchain );
2337 }
2338 
2339