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