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