Home | History | Annotate | Line # | Download | only in back-meta
      1 /*	$NetBSD: bind.c,v 1.4 2025/09/05 21:16:28 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1999-2024 The OpenLDAP Foundation.
      7  * Portions Copyright 2001-2003 Pierangelo Masarati.
      8  * Portions Copyright 1999-2003 Howard Chu.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* ACKNOWLEDGEMENTS:
     20  * This work was initially developed by the Howard Chu for inclusion
     21  * in OpenLDAP Software and subsequently enhanced by Pierangelo
     22  * Masarati.
     23  */
     24 
     25 #include <sys/cdefs.h>
     26 __RCSID("$NetBSD: bind.c,v 1.4 2025/09/05 21:16:28 christos Exp $");
     27 
     28 #include "portable.h"
     29 
     30 #include <stdio.h>
     31 
     32 #include <ac/errno.h>
     33 #include <ac/socket.h>
     34 #include <ac/string.h>
     35 
     36 
     37 #define AVL_INTERNAL
     38 #include "slap.h"
     39 #include "../back-ldap/back-ldap.h"
     40 #include "back-meta.h"
     41 
     42 #include "lutil_ldap.h"
     43 
     44 static int
     45 meta_back_proxy_authz_bind(
     46 	metaconn_t		*mc,
     47 	int			candidate,
     48 	Operation		*op,
     49 	SlapReply		*rs,
     50 	ldap_back_send_t	sendok,
     51 	int			dolock );
     52 
     53 static int
     54 meta_back_single_bind(
     55 	Operation		*op,
     56 	SlapReply		*rs,
     57 	metaconn_t		*mc,
     58 	int			candidate );
     59 
     60 int
     61 meta_back_bind( Operation *op, SlapReply *rs )
     62 {
     63 	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
     64 	metaconn_t	*mc = NULL;
     65 
     66 	int		rc = LDAP_OTHER,
     67 			i,
     68 			gotit = 0,
     69 			isroot = 0;
     70 
     71 	SlapReply	*candidates;
     72 
     73 	rs->sr_err = LDAP_SUCCESS;
     74 
     75 	Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
     76 		op->o_log_prefix, op->o_req_dn.bv_val );
     77 
     78 	/* the test on the bind method should be superfluous */
     79 	switch ( be_rootdn_bind( op, rs ) ) {
     80 	case LDAP_SUCCESS:
     81 		if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
     82 			/* frontend will return success */
     83 			return rs->sr_err;
     84 		}
     85 
     86 		isroot = 1;
     87 		/* fallthru */
     88 
     89 	case SLAP_CB_CONTINUE:
     90 		break;
     91 
     92 	default:
     93 		/* be_rootdn_bind() sent result */
     94 		return rs->sr_err;
     95 	}
     96 
     97 	candidates = meta_back_candidates_get( op );
     98 	/* we need meta_back_getconn() not send result even on error,
     99 	 * because we want to intercept the error and make it
    100 	 * invalidCredentials */
    101 	mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND, candidates );
    102 	if ( !mc ) {
    103 		Debug(LDAP_DEBUG_ANY,
    104 		      "%s meta_back_bind: no target " "for dn \"%s\" (%d%s%s).\n",
    105 		      op->o_log_prefix, op->o_req_dn.bv_val,
    106 		      rs->sr_err, rs->sr_text ? ". " : "",
    107 		      rs->sr_text ? rs->sr_text : "" );
    108 
    109 		/* FIXME: there might be cases where we don't want
    110 		 * to map the error onto invalidCredentials */
    111 		switch ( rs->sr_err ) {
    112 		case LDAP_NO_SUCH_OBJECT:
    113 		case LDAP_UNWILLING_TO_PERFORM:
    114 			rs->sr_err = LDAP_INVALID_CREDENTIALS;
    115 			rs->sr_text = NULL;
    116 			break;
    117 		}
    118 		send_ldap_result( op, rs );
    119 		op->o_tmpfree( candidates, op->o_tmpmemctx );
    120 		return rs->sr_err;
    121 	}
    122 
    123 	/*
    124 	 * Each target is scanned ...
    125 	 */
    126 	mc->mc_authz_target = META_BOUND_NONE;
    127 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
    128 		metatarget_t	*mt = mi->mi_targets[ i ];
    129 		int		lerr;
    130 
    131 		/*
    132 		 * Skip non-candidates
    133 		 */
    134 		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
    135 			continue;
    136 		}
    137 
    138 		if ( gotit == 0 ) {
    139 			/* set rc to LDAP_SUCCESS only if at least
    140 			 * one candidate has been tried */
    141 			rc = LDAP_SUCCESS;
    142 			gotit = 1;
    143 
    144 		} else if ( !isroot ) {
    145 			/*
    146 			 * A bind operation is expected to have
    147 			 * ONE CANDIDATE ONLY!
    148 			 */
    149 			Debug( LDAP_DEBUG_ANY,
    150 				"### %s meta_back_bind: more than one"
    151 				" candidate selected...\n",
    152 				op->o_log_prefix );
    153 		}
    154 
    155 		if ( isroot ) {
    156 			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
    157 				|| BER_BVISNULL( &mt->mt_idassert_authcDN ) )
    158 			{
    159 				metasingleconn_t	*msc = &mc->mc_conns[ i ];
    160 
    161 				/* skip the target if no pseudorootdn is provided */
    162 				if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
    163 					ch_free( msc->msc_bound_ndn.bv_val );
    164 					BER_BVZERO( &msc->msc_bound_ndn );
    165 				}
    166 
    167 				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
    168 					/* destroy sensitive data */
    169 					memset( msc->msc_cred.bv_val, 0,
    170 						msc->msc_cred.bv_len );
    171 					ch_free( msc->msc_cred.bv_val );
    172 					BER_BVZERO( &msc->msc_cred );
    173 				}
    174 
    175 				continue;
    176 			}
    177 
    178 
    179 			(void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
    180 			lerr = rs->sr_err;
    181 
    182 		} else {
    183 			lerr = meta_back_single_bind( op, rs, mc, i );
    184 		}
    185 
    186 		if ( lerr != LDAP_SUCCESS ) {
    187 			rc = rs->sr_err = lerr;
    188 
    189 			/* FIXME: in some cases (e.g. unavailable)
    190 			 * do not assume it's not candidate; rather
    191 			 * mark this as an error to be eventually
    192 			 * reported to client */
    193 			META_CANDIDATE_CLEAR( &candidates[ i ] );
    194 			break;
    195 		}
    196 	}
    197 
    198 	/* must re-insert if local DN changed as result of bind */
    199 	if ( rc == LDAP_SUCCESS ) {
    200 		if ( isroot ) {
    201 			mc->mc_authz_target = META_BOUND_ALL;
    202 		}
    203 
    204 		if ( !LDAP_BACK_PCONN_ISPRIV( mc )
    205 			&& !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) )
    206 		{
    207 			int		lerr;
    208 
    209 			/* wait for all other ops to release the connection */
    210 			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    211 			assert( mc->mc_refcnt == 1 );
    212 #if META_BACK_PRINT_CONNTREE > 0
    213 			meta_back_print_conntree( mi, ">>> meta_back_bind" );
    214 #endif /* META_BACK_PRINT_CONNTREE */
    215 
    216 			/* delete all cached connections with the current connection */
    217 			if ( LDAP_BACK_SINGLECONN( mi ) ) {
    218 				metaconn_t	*tmpmc;
    219 
    220 				while ( ( tmpmc = ldap_tavl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
    221 				{
    222 					assert( !LDAP_BACK_PCONN_ISPRIV( mc ) );
    223 					Debug( LDAP_DEBUG_TRACE,
    224 						"=>meta_back_bind: destroying conn %lu (refcnt=%u)\n",
    225 						mc->mc_conn->c_connid, mc->mc_refcnt );
    226 
    227 					if ( tmpmc->mc_refcnt != 0 ) {
    228 						/* taint it */
    229 						LDAP_BACK_CONN_TAINTED_SET( tmpmc );
    230 
    231 					} else {
    232 						/*
    233 						 * Needs a test because the handler may be corrupted,
    234 						 * and calling ldap_unbind on a corrupted header results
    235 						 * in a segmentation fault
    236 						 */
    237 						meta_back_conn_free( tmpmc );
    238 					}
    239 				}
    240 			}
    241 
    242 			ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
    243 			lerr = ldap_tavl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
    244 				meta_back_conndn_cmp, meta_back_conndn_dup );
    245 #if META_BACK_PRINT_CONNTREE > 0
    246 			meta_back_print_conntree( mi, "<<< meta_back_bind" );
    247 #endif /* META_BACK_PRINT_CONNTREE */
    248 			if ( lerr == 0 ) {
    249 #if 0
    250 				/* NOTE: a connection cannot be privileged
    251 				 * and be in the avl tree at the same time
    252 				 */
    253 				if ( isroot ) {
    254 					LDAP_BACK_CONN_ISPRIV_SET( mc );
    255 					LDAP_BACK_PCONN_SET( mc, op );
    256 				}
    257 #endif
    258 				LDAP_BACK_CONN_CACHED_SET( mc );
    259 
    260 			} else {
    261 				LDAP_BACK_CONN_CACHED_CLEAR( mc );
    262 			}
    263 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    264 		}
    265 	}
    266 
    267 	if ( mc != NULL ) {
    268 		meta_back_release_conn( mi, mc );
    269 	}
    270 
    271 	/*
    272 	 * rc is LDAP_SUCCESS if at least one bind succeeded,
    273 	 * err is the last error that occurred during a bind;
    274 	 * if at least (and at most?) one bind succeeds, fine.
    275 	 */
    276 	if ( rc != LDAP_SUCCESS ) {
    277 
    278 		/*
    279 		 * deal with bind failure ...
    280 		 */
    281 
    282 		/*
    283 		 * no target was found within the naming context,
    284 		 * so bind must fail with invalid credentials
    285 		 */
    286 		if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
    287 			rs->sr_err = LDAP_INVALID_CREDENTIALS;
    288 		} else {
    289 			rs->sr_err = slap_map_api2result( rs );
    290 		}
    291 		send_ldap_result( op, rs );
    292 		op->o_tmpfree( candidates, op->o_tmpmemctx );
    293 		return rs->sr_err;
    294 
    295 	}
    296 
    297 	op->o_tmpfree( candidates, op->o_tmpmemctx );
    298 	return LDAP_SUCCESS;
    299 }
    300 
    301 static int
    302 meta_back_bind_op_result(
    303 	Operation		*op,
    304 	SlapReply		*rs,
    305 	metaconn_t		*mc,
    306 	int			candidate,
    307 	int			msgid,
    308 	ldap_back_send_t	sendok,
    309 	int			dolock )
    310 {
    311 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
    312 	metatarget_t		*mt = mi->mi_targets[ candidate ];
    313 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
    314 	LDAPMessage		*res;
    315 	struct timeval		tv;
    316 	int			rc;
    317 	int			nretries = mt->mt_nretries;
    318 
    319 	Debug( LDAP_DEBUG_TRACE,
    320 		">>> %s meta_back_bind_op_result[%d]\n",
    321 		op->o_log_prefix, candidate );
    322 
    323 	/* make sure this is clean */
    324 	assert( rs->sr_ctrls == NULL );
    325 
    326 	if ( rs->sr_err == LDAP_SUCCESS ) {
    327 		time_t		stoptime = (time_t)(-1),
    328 				timeout;
    329 		int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
    330 				LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
    331 		const char	*timeout_text = "Operation timed out";
    332 		slap_op_t	opidx = slap_req2op( op->o_tag );
    333 
    334 		/* since timeout is not specified, compute and use
    335 		 * the one specific to the ongoing operation */
    336 		if ( opidx == LDAP_REQ_SEARCH ) {
    337 			if ( op->ors_tlimit <= 0 ) {
    338 				timeout = 0;
    339 
    340 			} else {
    341 				timeout = op->ors_tlimit;
    342 				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
    343 				timeout_text = NULL;
    344 			}
    345 
    346 		} else {
    347 			timeout = mt->mt_timeout[ opidx ];
    348 		}
    349 
    350 		/* better than nothing :) */
    351 		if ( timeout == 0 ) {
    352 			if ( mi->mi_idle_timeout ) {
    353 				timeout = mi->mi_idle_timeout;
    354 
    355 			} else if ( mi->mi_conn_ttl ) {
    356 				timeout = mi->mi_conn_ttl;
    357 			}
    358 		}
    359 
    360 		if ( timeout ) {
    361 			stoptime = op->o_time + timeout;
    362 		}
    363 
    364 		LDAP_BACK_TV_SET( &tv );
    365 
    366 		/*
    367 		 * handle response!!!
    368 		 */
    369 retry:;
    370 		rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
    371 		switch ( rc ) {
    372 		case 0:
    373 			if ( nretries != META_RETRY_NEVER
    374 				|| ( timeout && slap_get_time() <= stoptime ) )
    375 			{
    376 				ldap_pvt_thread_yield();
    377 				if ( nretries > 0 ) {
    378 					nretries--;
    379 				}
    380 				tv = mt->mt_bind_timeout;
    381 				goto retry;
    382 			}
    383 
    384 			/* don't let anyone else use this handler,
    385 			 * because there's a pending bind that will not
    386 			 * be acknowledged */
    387 			if ( dolock) {
    388 				ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    389 			}
    390 			assert( LDAP_BACK_CONN_BINDING( msc ) );
    391 
    392 #ifdef DEBUG_205
    393 			Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
    394 				op->o_log_prefix, candidate, (void *)msc->msc_ld );
    395 #endif /* DEBUG_205 */
    396 
    397 			meta_clear_one_candidate( op, mc, candidate );
    398 			if ( dolock ) {
    399 				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    400 			}
    401 
    402 			rs->sr_err = timeout_err;
    403 			rs->sr_text = timeout_text;
    404 			break;
    405 
    406 		case -1:
    407 			ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
    408 				&rs->sr_err );
    409 
    410 			Debug(LDAP_DEBUG_ANY,
    411 			      "### %s meta_back_bind_op_result[%d]: err=%d (%s) nretries=%d.\n",
    412 			      op->o_log_prefix, candidate, rs->sr_err,
    413 			      ldap_err2string(rs->sr_err), nretries );
    414 			break;
    415 
    416 		default:
    417 			/* only touch when activity actually took place... */
    418 			if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
    419 				msc->msc_time = op->o_time;
    420 			}
    421 
    422 			/* FIXME: matched? referrals? response controls? */
    423 			rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
    424 					NULL, NULL, NULL, NULL, 1 );
    425 			if ( rc != LDAP_SUCCESS ) {
    426 				rs->sr_err = rc;
    427 			}
    428 			rs->sr_err = slap_map_api2result( rs );
    429 			break;
    430 		}
    431 	}
    432 
    433 	rs->sr_err = slap_map_api2result( rs );
    434 
    435 	Debug( LDAP_DEBUG_TRACE,
    436 		"<<< %s meta_back_bind_op_result[%d] err=%d\n",
    437 		op->o_log_prefix, candidate, rs->sr_err );
    438 
    439 	return rs->sr_err;
    440 }
    441 
    442 /*
    443  * meta_back_single_bind
    444  *
    445  * attempts to perform a bind with creds
    446  */
    447 static int
    448 meta_back_single_bind(
    449 	Operation		*op,
    450 	SlapReply		*rs,
    451 	metaconn_t		*mc,
    452 	int			candidate )
    453 {
    454 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
    455 	metatarget_t		*mt = mi->mi_targets[ candidate ];
    456 	struct berval		mdn = BER_BVNULL;
    457 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
    458 	int			msgid;
    459 	dncookie		dc;
    460 	struct berval		save_o_dn;
    461 	int			save_o_do_not_cache;
    462 	LDAPControl		**ctrls = NULL;
    463 
    464 	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
    465 		ch_free( msc->msc_bound_ndn.bv_val );
    466 		BER_BVZERO( &msc->msc_bound_ndn );
    467 	}
    468 
    469 	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
    470 		/* destroy sensitive data */
    471 		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
    472 		ch_free( msc->msc_cred.bv_val );
    473 		BER_BVZERO( &msc->msc_cred );
    474 	}
    475 
    476 	/*
    477 	 * Rewrite the bind dn if needed
    478 	 */
    479 	dc.target = mt;
    480 	dc.conn = op->o_conn;
    481 	dc.rs = rs;
    482 	dc.ctx = "bindDN";
    483 
    484 	if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
    485 		rs->sr_text = "DN rewrite error";
    486 		rs->sr_err = LDAP_OTHER;
    487 		return rs->sr_err;
    488 	}
    489 
    490 	/* don't add proxyAuthz; set the bindDN */
    491 	save_o_dn = op->o_dn;
    492 	save_o_do_not_cache = op->o_do_not_cache;
    493 	op->o_do_not_cache = 1;
    494 	op->o_dn = op->o_req_dn;
    495 
    496 	ctrls = op->o_ctrls;
    497 	rs->sr_err = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
    498 	op->o_dn = save_o_dn;
    499 	op->o_do_not_cache = save_o_do_not_cache;
    500 	if ( rs->sr_err != LDAP_SUCCESS ) {
    501 		goto return_results;
    502 	}
    503 
    504 	/* FIXME: this fixes the bind problem right now; we need
    505 	 * to use the asynchronous version to get the "matched"
    506 	 * and more in case of failure ... */
    507 	/* FIXME: should we check if at least some of the op->o_ctrls
    508 	 * can/should be passed? */
    509 	for (;;) {
    510 		rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
    511 			LDAP_SASL_SIMPLE, &op->orb_cred,
    512 			ctrls, NULL, &msgid );
    513 		if ( rs->sr_err != LDAP_X_CONNECTING ) {
    514 			break;
    515 		}
    516 		ldap_pvt_thread_yield();
    517 	}
    518 
    519 	mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
    520 
    521 	meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
    522 	if ( rs->sr_err != LDAP_SUCCESS ) {
    523 		goto return_results;
    524 	}
    525 
    526 	/* If defined, proxyAuthz will be used also when
    527 	 * back-ldap is the authorizing backend; for this
    528 	 * purpose, a successful bind is followed by a
    529 	 * bind with the configured identity assertion */
    530 	/* NOTE: use with care */
    531 	if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
    532 		meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
    533 		if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
    534 			goto return_results;
    535 		}
    536 		goto cache_refresh;
    537 	}
    538 
    539 	ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
    540 	LDAP_BACK_CONN_ISBOUND_SET( msc );
    541 	mc->mc_authz_target = candidate;
    542 
    543 	if ( META_BACK_TGT_SAVECRED( mt ) ) {
    544 		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
    545 			memset( msc->msc_cred.bv_val, 0,
    546 				msc->msc_cred.bv_len );
    547 		}
    548 		ber_bvreplace( &msc->msc_cred, &op->orb_cred );
    549 		ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
    550 	}
    551 
    552 cache_refresh:;
    553 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
    554 			&& !BER_BVISEMPTY( &op->o_req_ndn ) )
    555 	{
    556 		( void )meta_dncache_update_entry( &mi->mi_cache,
    557 				&op->o_req_ndn, candidate );
    558 	}
    559 
    560 return_results:;
    561 	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
    562 		free( mdn.bv_val );
    563 	}
    564 
    565 	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
    566 		meta_back_quarantine( op, rs, candidate );
    567 	}
    568 
    569 	return rs->sr_err;
    570 }
    571 
    572 /*
    573  * meta_back_single_dobind
    574  */
    575 int
    576 meta_back_single_dobind(
    577 	Operation		*op,
    578 	SlapReply		*rs,
    579 	metaconn_t		**mcp,
    580 	int			candidate,
    581 	ldap_back_send_t	sendok,
    582 	int			nretries,
    583 	int			dolock )
    584 {
    585 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
    586 	metatarget_t		*mt = mi->mi_targets[ candidate ];
    587 	metaconn_t		*mc = *mcp;
    588 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
    589 	int			msgid;
    590 
    591 	assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
    592 
    593 	/* NOTE: this obsoletes pseudorootdn */
    594 	if ( op->o_conn != NULL &&
    595 		!op->o_do_not_cache &&
    596 		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
    597 			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
    598 			( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
    599 			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
    600 	{
    601 		(void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
    602 
    603 	} else {
    604 		char *binddn = "";
    605 		struct berval cred = BER_BVC( "" );
    606 
    607 		/* use credentials if available */
    608 		if ( !BER_BVISNULL( &msc->msc_bound_ndn )
    609 			&& !BER_BVISNULL( &msc->msc_cred ) )
    610 		{
    611 			binddn = msc->msc_bound_ndn.bv_val;
    612 			cred = msc->msc_cred;
    613 		}
    614 
    615 		/* FIXME: should we check if at least some of the op->o_ctrls
    616 		 * can/should be passed? */
    617 		if(!dolock) {
    618 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    619 		}
    620 
    621 		for (;;) {
    622 			rs->sr_err = ldap_sasl_bind( msc->msc_ld,
    623 				binddn, LDAP_SASL_SIMPLE, &cred,
    624 				NULL, NULL, &msgid );
    625 			if ( rs->sr_err != LDAP_X_CONNECTING ) {
    626 				break;
    627 			}
    628 			ldap_pvt_thread_yield();
    629 		}
    630 
    631 		if(!dolock) {
    632 			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    633 		}
    634 
    635 		rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
    636 
    637 		/* if bind succeeded, but anonymous, clear msc_bound_ndn */
    638 		if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
    639 			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
    640 				ber_memfree( msc->msc_bound_ndn.bv_val );
    641 				BER_BVZERO( &msc->msc_bound_ndn );
    642 			}
    643 
    644 			if ( !BER_BVISNULL( &msc->msc_cred ) ) {
    645 				memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
    646 				ber_memfree( msc->msc_cred.bv_val );
    647 				BER_BVZERO( &msc->msc_cred );
    648 			}
    649 		}
    650 	}
    651 
    652 	if ( rs->sr_err != LDAP_SUCCESS ) {
    653 		if ( dolock ) {
    654 			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    655 		}
    656 		LDAP_BACK_CONN_BINDING_CLEAR( msc );
    657 		if ( META_BACK_ONERR_STOP( mi ) ) {
    658 			LDAP_BACK_CONN_TAINTED_SET( mc );
    659 			meta_back_release_conn_lock( mi, mc, 0 );
    660 			*mcp = NULL;
    661 		}
    662 		if ( dolock ) {
    663 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    664 		}
    665 	}
    666 
    667 	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
    668 		meta_back_quarantine( op, rs, candidate );
    669 	}
    670 
    671 	return rs->sr_err;
    672 }
    673 
    674 /*
    675  * meta_back_dobind
    676  */
    677 int
    678 meta_back_dobind(
    679 	Operation		*op,
    680 	SlapReply		*rs,
    681 	metaconn_t		*mc,
    682 	ldap_back_send_t	sendok,
    683 	SlapReply		*candidates )
    684 {
    685 	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
    686 
    687 	int			bound = 0,
    688 				i,
    689 				isroot = 0;
    690 
    691 	if ( be_isroot( op ) ) {
    692 		isroot = 1;
    693 	}
    694 
    695 	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
    696 		char buf[STRLENOF("4294967295U") + 1] = { 0 };
    697 		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
    698 
    699 		Debug( LDAP_DEBUG_TRACE,
    700 			"%s meta_back_dobind: conn=%s%s\n",
    701 			op->o_log_prefix, buf,
    702 			isroot ? " (isroot)" : "" );
    703 	}
    704 
    705 	/*
    706 	 * all the targets are bound as pseudoroot
    707 	 */
    708 	if ( mc->mc_authz_target == META_BOUND_ALL ) {
    709 		bound = 1;
    710 		goto done;
    711 	}
    712 
    713 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
    714 		metatarget_t		*mt = mi->mi_targets[ i ];
    715 		metasingleconn_t	*msc = &mc->mc_conns[ i ];
    716 		int			rc;
    717 
    718 		/*
    719 		 * Not a candidate
    720 		 */
    721 		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
    722 			continue;
    723 		}
    724 
    725 		assert( msc->msc_ld != NULL );
    726 
    727 		/*
    728 		 * If the target is already bound it is skipped
    729 		 */
    730 
    731 retry_binding:;
    732 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    733 		if ( LDAP_BACK_CONN_ISBOUND( msc )
    734 			|| ( LDAP_BACK_CONN_ISANON( msc )
    735 				&& mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
    736 		{
    737 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    738 			++bound;
    739 			continue;
    740 
    741 		} else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
    742 		{
    743 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    744 			ldap_pvt_thread_yield();
    745 			goto retry_binding;
    746 
    747 		}
    748 
    749 		LDAP_BACK_CONN_BINDING_SET( msc );
    750 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    751 
    752 		rc = meta_back_single_dobind( op, rs, &mc, i,
    753 			LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
    754 		/*
    755 		 * NOTE: meta_back_single_dobind() already retries;
    756 		 * in case of failure, it resets mc...
    757 		 */
    758 		if ( rc != LDAP_SUCCESS ) {
    759 			if ( mc == NULL ) {
    760 				/* meta_back_single_dobind() already sent
    761 				 * response and released connection */
    762 				goto send_err;
    763 			}
    764 
    765 			if ( rc == LDAP_UNAVAILABLE ) {
    766 				/* FIXME: meta_back_retry() already re-calls
    767 				 * meta_back_single_dobind() */
    768 				if ( meta_back_retry( op, rs, &mc, i, sendok, candidates ) ) {
    769 					goto retry_ok;
    770 				}
    771 
    772 				if ( mc != NULL ) {
    773 					ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    774 					LDAP_BACK_CONN_BINDING_CLEAR( msc );
    775 					meta_back_release_conn_lock( mi, mc, 0 );
    776 					ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    777 				}
    778 
    779 				return 0;
    780 			}
    781 
    782 			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    783 			LDAP_BACK_CONN_BINDING_CLEAR( msc );
    784 			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    785 
    786 			Debug(LDAP_DEBUG_ANY,
    787 			      "%s meta_back_dobind[%d]: (%s) err=%d (%s).\n",
    788 			      op->o_log_prefix, i,
    789 			      isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
    790 			      rc, ldap_err2string(rc) );
    791 
    792 			/*
    793 			 * null cred bind should always succeed
    794 			 * as anonymous, so a failure means
    795 			 * the target is no longer candidate possibly
    796 			 * due to technical reasons (remote host down?)
    797 			 * so better clear the handle
    798 			 */
    799 			/* leave the target candidate, but record the error for later use */
    800 			candidates[ i ].sr_err = rc;
    801 			if ( META_BACK_ONERR_STOP( mi ) ) {
    802 				bound = 0;
    803 				goto done;
    804 			}
    805 
    806 			continue;
    807 		} /* else */
    808 
    809 retry_ok:;
    810 		Debug( LDAP_DEBUG_TRACE,
    811 			"%s meta_back_dobind[%d]: "
    812 			"(%s)\n",
    813 			op->o_log_prefix, i,
    814 			isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
    815 
    816 		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
    817 		LDAP_BACK_CONN_BINDING_CLEAR( msc );
    818 		if ( isroot ) {
    819 			LDAP_BACK_CONN_ISBOUND_SET( msc );
    820 		} else {
    821 			LDAP_BACK_CONN_ISANON_SET( msc );
    822 		}
    823 		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
    824 		++bound;
    825 	}
    826 
    827 done:;
    828 	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
    829 		char buf[STRLENOF("4294967295U") + 1] = { 0 };
    830 		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
    831 
    832 		Debug( LDAP_DEBUG_TRACE,
    833 			"%s meta_back_dobind: conn=%s bound=%d\n",
    834 			op->o_log_prefix, buf, bound );
    835 	}
    836 
    837 	if ( bound == 0 ) {
    838 		meta_back_release_conn( mi, mc );
    839 
    840 send_err:;
    841 		if ( sendok & LDAP_BACK_SENDERR ) {
    842 			if ( rs->sr_err == LDAP_SUCCESS ) {
    843 				rs->sr_err = LDAP_BUSY;
    844 			}
    845 			send_ldap_result( op, rs );
    846 		}
    847 
    848 		return 0;
    849 	}
    850 
    851 	return ( bound > 0 );
    852 }
    853 
    854 /*
    855  * meta_back_default_rebind
    856  *
    857  * This is a callback used for chasing referrals using the same
    858  * credentials as the original user on this session.
    859  */
    860 int
    861 meta_back_default_rebind(
    862 	LDAP			*ld,
    863 	LDAP_CONST char		*url,
    864 	ber_tag_t		request,
    865 	ber_int_t		msgid,
    866 	void			*params )
    867 {
    868 	metasingleconn_t	*msc = ( metasingleconn_t * )params;
    869 
    870 	return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
    871 			LDAP_SASL_SIMPLE, &msc->msc_cred,
    872 			NULL, NULL, NULL );
    873 }
    874 
    875 /*
    876  * meta_back_default_urllist
    877  *
    878  * This is a callback used for mucking with the urllist
    879  */
    880 int
    881 meta_back_default_urllist(
    882 	LDAP		*ld,
    883 	LDAPURLDesc	**urllist,
    884 	LDAPURLDesc	**url,
    885 	void		*params )
    886 {
    887 	metatarget_t	*mt = (metatarget_t *)params;
    888 	LDAPURLDesc	**urltail;
    889 
    890 	if ( urllist == url ) {
    891 		return LDAP_SUCCESS;
    892 	}
    893 
    894 	for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
    895 		/* count */ ;
    896 
    897 	*urltail = *urllist;
    898 	*urllist = *url;
    899 	*url = NULL;
    900 
    901 	ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
    902 	if ( mt->mt_uri ) {
    903 		ch_free( mt->mt_uri );
    904 	}
    905 
    906 	ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
    907 	ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
    908 
    909 	return LDAP_SUCCESS;
    910 }
    911 
    912 int
    913 meta_back_cancel(
    914 	metaconn_t		*mc,
    915 	Operation		*op,
    916 	SlapReply		*rs,
    917 	ber_int_t		msgid,
    918 	int			candidate,
    919 	ldap_back_send_t	sendok )
    920 {
    921 	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
    922 
    923 	metatarget_t		*mt = mi->mi_targets[ candidate ];
    924 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
    925 
    926 	int			rc = LDAP_OTHER;
    927 
    928 	Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n",
    929 		op->o_log_prefix, candidate, msgid );
    930 
    931 	/* default behavior */
    932 	if ( META_BACK_TGT_ABANDON( mt ) ) {
    933 		rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
    934 
    935 	} else if ( META_BACK_TGT_IGNORE( mt ) ) {
    936 		rc = ldap_pvt_discard( msc->msc_ld, msgid );
    937 
    938 	} else if ( META_BACK_TGT_CANCEL( mt ) ) {
    939 		rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
    940 
    941 	} else {
    942 		assert( 0 );
    943 	}
    944 
    945 	Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
    946 		op->o_log_prefix, candidate, rc );
    947 
    948 	return rc;
    949 }
    950 
    951 
    952 
    953 /*
    954  * FIXME: error return must be handled in a cleaner way ...
    955  */
    956 int
    957 meta_back_op_result(
    958 	metaconn_t		*mc,
    959 	Operation		*op,
    960 	SlapReply		*rs,
    961 	int			candidate,
    962 	ber_int_t		msgid,
    963 	time_t			timeout,
    964 	ldap_back_send_t	sendok )
    965 {
    966 	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
    967 
    968 	const char	*save_text = rs->sr_text,
    969 			*save_matched = rs->sr_matched;
    970 	BerVarray	save_ref = rs->sr_ref;
    971 	LDAPControl	**save_ctrls = rs->sr_ctrls;
    972 	void		*matched_ctx = NULL;
    973 
    974 	char		*matched = NULL;
    975 	char		*text = NULL;
    976 	char		**refs = NULL;
    977 	LDAPControl	**ctrls = NULL;
    978 
    979 	assert( mc != NULL );
    980 
    981 	rs->sr_text = NULL;
    982 	rs->sr_matched = NULL;
    983 	rs->sr_ref = NULL;
    984 	rs->sr_ctrls = NULL;
    985 
    986 	if ( candidate != META_TARGET_NONE ) {
    987 		metatarget_t		*mt = mi->mi_targets[ candidate ];
    988 		metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
    989 
    990 		if ( LDAP_ERR_OK( rs->sr_err ) ) {
    991 			int		rc;
    992 			struct timeval	tv;
    993 			LDAPMessage	*res = NULL;
    994 			time_t		stoptime = (time_t)(-1);
    995 			int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
    996 						LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
    997 			const char	*timeout_text = "Operation timed out";
    998 
    999 			/* if timeout is not specified, compute and use
   1000 			 * the one specific to the ongoing operation */
   1001 			if ( timeout == (time_t)(-1) ) {
   1002 				slap_op_t	opidx = slap_req2op( op->o_tag );
   1003 
   1004 				if ( opidx == SLAP_OP_SEARCH ) {
   1005 					if ( op->ors_tlimit <= 0 ) {
   1006 						timeout = 0;
   1007 
   1008 					} else {
   1009 						timeout = op->ors_tlimit;
   1010 						timeout_err = LDAP_TIMELIMIT_EXCEEDED;
   1011 						timeout_text = NULL;
   1012 					}
   1013 
   1014 				} else {
   1015 					timeout = mt->mt_timeout[ opidx ];
   1016 				}
   1017 			}
   1018 
   1019 			/* better than nothing :) */
   1020 			if ( timeout == 0 ) {
   1021 				if ( mi->mi_idle_timeout ) {
   1022 					timeout = mi->mi_idle_timeout;
   1023 
   1024 				} else if ( mi->mi_conn_ttl ) {
   1025 					timeout = mi->mi_conn_ttl;
   1026 				}
   1027 			}
   1028 
   1029 			if ( timeout ) {
   1030 				stoptime = op->o_time + timeout;
   1031 			}
   1032 
   1033 			LDAP_BACK_TV_SET( &tv );
   1034 
   1035 retry:;
   1036 			rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
   1037 			switch ( rc ) {
   1038 			case 0:
   1039 				if ( timeout && slap_get_time() > stoptime ) {
   1040 					(void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
   1041 					rs->sr_err = timeout_err;
   1042 					rs->sr_text = timeout_text;
   1043 					break;
   1044 				}
   1045 
   1046 				LDAP_BACK_TV_SET( &tv );
   1047 				ldap_pvt_thread_yield();
   1048 				goto retry;
   1049 
   1050 			case -1:
   1051 				ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
   1052 						&rs->sr_err );
   1053 				break;
   1054 
   1055 
   1056 			/* otherwise get the result; if it is not
   1057 			 * LDAP_SUCCESS, record it in the reply
   1058 			 * structure (this includes
   1059 			 * LDAP_COMPARE_{TRUE|FALSE}) */
   1060 			default:
   1061 				/* only touch when activity actually took place... */
   1062 				if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
   1063 					msc->msc_time = op->o_time;
   1064 				}
   1065 
   1066 				rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
   1067 						&matched, &text, &refs, &ctrls, 1 );
   1068 				res = NULL;
   1069 				if ( rc == LDAP_SUCCESS ) {
   1070 					rs->sr_text = text;
   1071 				} else {
   1072 					rs->sr_err = rc;
   1073 				}
   1074 				rs->sr_err = slap_map_api2result( rs );
   1075 
   1076 				/* RFC 4511: referrals can only appear
   1077 				 * if result code is LDAP_REFERRAL */
   1078 				if ( refs != NULL
   1079 					&& refs[ 0 ] != NULL
   1080 					&& refs[ 0 ][ 0 ] != '\0' )
   1081 				{
   1082 					if ( rs->sr_err != LDAP_REFERRAL ) {
   1083 						Debug( LDAP_DEBUG_ANY,
   1084 							"%s meta_back_op_result[%d]: "
   1085 							"got referrals with err=%d\n",
   1086 							op->o_log_prefix,
   1087 							candidate, rs->sr_err );
   1088 
   1089 					} else {
   1090 						int	i;
   1091 
   1092 						for ( i = 0; refs[ i ] != NULL; i++ )
   1093 							/* count */ ;
   1094 						rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
   1095 							op->o_tmpmemctx );
   1096 						for ( i = 0; refs[ i ] != NULL; i++ ) {
   1097 							ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
   1098 						}
   1099 						BER_BVZERO( &rs->sr_ref[ i ] );
   1100 					}
   1101 
   1102 				} else if ( rs->sr_err == LDAP_REFERRAL ) {
   1103 					Debug( LDAP_DEBUG_ANY,
   1104 						"%s meta_back_op_result[%d]: "
   1105 						"got err=%d with null "
   1106 						"or empty referrals\n",
   1107 						op->o_log_prefix,
   1108 						candidate, rs->sr_err );
   1109 
   1110 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
   1111 				}
   1112 
   1113 				if ( ctrls != NULL ) {
   1114 					rs->sr_ctrls = ctrls;
   1115 				}
   1116 			}
   1117 
   1118 			assert( res == NULL );
   1119 		}
   1120 
   1121 		/* if the error in the reply structure is not
   1122 		 * LDAP_SUCCESS, try to map it from client
   1123 		 * to server error */
   1124 		if ( !LDAP_ERR_OK( rs->sr_err ) ) {
   1125 			rs->sr_err = slap_map_api2result( rs );
   1126 
   1127 			/* internal ops ( op->o_conn == NULL )
   1128 			 * must not reply to client */
   1129 			if ( op->o_conn && !op->o_do_not_cache && matched ) {
   1130 
   1131 				/* record the (massaged) matched
   1132 				 * DN into the reply structure */
   1133 				rs->sr_matched = matched;
   1134 			}
   1135 		}
   1136 
   1137 		if ( META_BACK_TGT_QUARANTINE( mt ) ) {
   1138 			meta_back_quarantine( op, rs, candidate );
   1139 		}
   1140 
   1141 	} else {
   1142 		int	i,
   1143 			err = rs->sr_err;
   1144 
   1145 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
   1146 			metasingleconn_t	*msc = &mc->mc_conns[ i ];
   1147 			char			*xtext = NULL;
   1148 			char			*xmatched = NULL;
   1149 
   1150 			if ( msc->msc_ld == NULL ) {
   1151 				continue;
   1152 			}
   1153 
   1154 			rs->sr_err = LDAP_SUCCESS;
   1155 
   1156 			ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err );
   1157 			if ( rs->sr_err != LDAP_SUCCESS ) {
   1158 				/*
   1159 				 * better check the type of error. In some cases
   1160 				 * (search ?) it might be better to return a
   1161 				 * success if at least one of the targets gave
   1162 				 * positive result ...
   1163 				 */
   1164 				ldap_get_option( msc->msc_ld,
   1165 						LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext );
   1166 				if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
   1167 					ldap_memfree( xtext );
   1168 					xtext = NULL;
   1169 				}
   1170 
   1171 				ldap_get_option( msc->msc_ld,
   1172 						LDAP_OPT_MATCHED_DN, &xmatched );
   1173 				if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
   1174 					ldap_memfree( xmatched );
   1175 					xmatched = NULL;
   1176 				}
   1177 
   1178 				rs->sr_err = slap_map_api2result( rs );
   1179 
   1180 				Debug(LDAP_DEBUG_ANY,
   1181 				      "%s meta_back_op_result[%d] " "err=%d text=\"%s\" matched=\"%s\".\n",
   1182 				      op->o_log_prefix, i, rs->sr_err,
   1183 				      (xtext ? xtext : ""),
   1184 				      (xmatched ? xmatched : "") );
   1185 
   1186 				/*
   1187 				 * FIXME: need to rewrite "match" (need rwinfo)
   1188 				 */
   1189 				switch ( rs->sr_err ) {
   1190 				default:
   1191 					err = rs->sr_err;
   1192 					if ( xtext != NULL ) {
   1193 						if ( text ) {
   1194 							ldap_memfree( text );
   1195 						}
   1196 						text = xtext;
   1197 						xtext = NULL;
   1198 					}
   1199 					if ( xmatched != NULL ) {
   1200 						if ( matched ) {
   1201 							ldap_memfree( matched );
   1202 						}
   1203 						matched = xmatched;
   1204 						xmatched = NULL;
   1205 					}
   1206 					break;
   1207 				}
   1208 
   1209 				if ( xtext ) {
   1210 					ldap_memfree( xtext );
   1211 				}
   1212 
   1213 				if ( xmatched ) {
   1214 					ldap_memfree( xmatched );
   1215 				}
   1216 			}
   1217 
   1218 			if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
   1219 				meta_back_quarantine( op, rs, i );
   1220 			}
   1221 		}
   1222 
   1223 		if ( err != LDAP_SUCCESS ) {
   1224 			rs->sr_err = err;
   1225 		}
   1226 	}
   1227 
   1228 	if ( matched != NULL ) {
   1229 		struct berval	dn, pdn;
   1230 
   1231 		ber_str2bv( matched, 0, 0, &dn );
   1232 		if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
   1233 			ldap_memfree( matched );
   1234 			matched_ctx = op->o_tmpmemctx;
   1235 			matched = pdn.bv_val;
   1236 		}
   1237 		rs->sr_matched = matched;
   1238 	}
   1239 
   1240 	if ( rs->sr_err == LDAP_UNAVAILABLE ) {
   1241 		if ( !( sendok & LDAP_BACK_RETRYING ) ) {
   1242 			if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
   1243 				if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
   1244 				send_ldap_result( op, rs );
   1245 			}
   1246 		}
   1247 
   1248 	} else if ( op->o_conn &&
   1249 		( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
   1250 			|| ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
   1251 	{
   1252 		send_ldap_result( op, rs );
   1253 	}
   1254 	if ( matched ) {
   1255 		op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
   1256 	}
   1257 	if ( text ) {
   1258 		ldap_memfree( text );
   1259 	}
   1260 	if ( rs->sr_ref ) {
   1261 		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
   1262 		rs->sr_ref = NULL;
   1263 	}
   1264 	if ( refs ) {
   1265 		ber_memvfree( (void **)refs );
   1266 	}
   1267 	if ( ctrls ) {
   1268 		assert( rs->sr_ctrls != NULL );
   1269 		ldap_controls_free( ctrls );
   1270 	}
   1271 
   1272 	rs->sr_text = save_text;
   1273 	rs->sr_matched = save_matched;
   1274 	rs->sr_ref = save_ref;
   1275 	rs->sr_ctrls = save_ctrls;
   1276 
   1277 	return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
   1278 }
   1279 
   1280 /*
   1281  * meta_back_proxy_authz_cred()
   1282  *
   1283  * prepares credentials & method for meta_back_proxy_authz_bind();
   1284  * or, if method is SASL, performs the SASL bind directly.
   1285  */
   1286 int
   1287 meta_back_proxy_authz_cred(
   1288 	metaconn_t		*mc,
   1289 	int			candidate,
   1290 	Operation		*op,
   1291 	SlapReply		*rs,
   1292 	ldap_back_send_t	sendok,
   1293 	struct berval		*binddn,
   1294 	struct berval		*bindcred,
   1295 	int			*method )
   1296 {
   1297 	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
   1298 	metatarget_t		*mt = mi->mi_targets[ candidate ];
   1299 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
   1300 	struct berval		ndn;
   1301 	int			dobind = 0;
   1302 
   1303 	/* don't proxyAuthz if protocol is not LDAPv3 */
   1304 	switch ( mt->mt_version ) {
   1305 	case LDAP_VERSION3:
   1306 		break;
   1307 
   1308 	case 0:
   1309 		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
   1310 			break;
   1311 		}
   1312 		/* fall thru */
   1313 
   1314 	default:
   1315 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
   1316 		if ( sendok & LDAP_BACK_SENDERR ) {
   1317 			send_ldap_result( op, rs );
   1318 		}
   1319 		LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1320 		goto done;
   1321 	}
   1322 
   1323 	if ( op->o_tag == LDAP_REQ_BIND ) {
   1324 		ndn = op->o_req_ndn;
   1325 
   1326 	} else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
   1327 		ndn = op->o_conn->c_ndn;
   1328 
   1329 	} else {
   1330 		ndn = op->o_ndn;
   1331 	}
   1332 	rs->sr_err = LDAP_SUCCESS;
   1333 
   1334 	/*
   1335 	 * FIXME: we need to let clients use proxyAuthz
   1336 	 * otherwise we cannot do symmetric pools of servers;
   1337 	 * we have to live with the fact that a user can
   1338 	 * authorize itself as any ID that is allowed
   1339 	 * by the authzTo directive of the "proxyauthzdn".
   1340 	 */
   1341 	/*
   1342 	 * NOTE: current Proxy Authorization specification
   1343 	 * and implementation do not allow proxy authorization
   1344 	 * control to be provided with Bind requests
   1345 	 */
   1346 	/*
   1347 	 * if no bind took place yet, but the connection is bound
   1348 	 * and the "proxyauthzdn" is set, then bind as
   1349 	 * "proxyauthzdn" and explicitly add the proxyAuthz
   1350 	 * control to every operation with the dn bound
   1351 	 * to the connection as control value.
   1352 	 */
   1353 
   1354 	/* bind as proxyauthzdn only if no idassert mode
   1355 	 * is requested, or if the client's identity
   1356 	 * is authorized */
   1357 	switch ( mt->mt_idassert_mode ) {
   1358 	case LDAP_BACK_IDASSERT_LEGACY:
   1359 		if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
   1360 			if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
   1361 			{
   1362 				*binddn = mt->mt_idassert_authcDN;
   1363 				*bindcred = mt->mt_idassert_passwd;
   1364 				dobind = 1;
   1365 			}
   1366 		}
   1367 		break;
   1368 
   1369 	default:
   1370 		/* NOTE: rootdn can always idassert */
   1371 		if ( BER_BVISNULL( &ndn )
   1372 			&& mt->mt_idassert_authz == NULL
   1373 			&& !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
   1374 		{
   1375 			if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
   1376 				rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
   1377 				if ( sendok & LDAP_BACK_SENDERR ) {
   1378 					send_ldap_result( op, rs );
   1379 				}
   1380 				LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1381 				goto done;
   1382 
   1383 			}
   1384 
   1385 			rs->sr_err = LDAP_SUCCESS;
   1386 			*binddn = slap_empty_bv;
   1387 			*bindcred = slap_empty_bv;
   1388 			break;
   1389 
   1390 		} else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
   1391 			struct berval authcDN;
   1392 
   1393 			if ( BER_BVISNULL( &ndn ) ) {
   1394 				authcDN = slap_empty_bv;
   1395 
   1396 			} else {
   1397 				authcDN = ndn;
   1398 			}
   1399 			rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
   1400 					&authcDN, &authcDN );
   1401 			if ( rs->sr_err != LDAP_SUCCESS ) {
   1402 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
   1403 					if ( sendok & LDAP_BACK_SENDERR ) {
   1404 						send_ldap_result( op, rs );
   1405 					}
   1406 					LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1407 					goto done;
   1408 				}
   1409 
   1410 				rs->sr_err = LDAP_SUCCESS;
   1411 				*binddn = slap_empty_bv;
   1412 				*bindcred = slap_empty_bv;
   1413 				break;
   1414 			}
   1415 		}
   1416 
   1417 		*binddn = mt->mt_idassert_authcDN;
   1418 		*bindcred = mt->mt_idassert_passwd;
   1419 		dobind = 1;
   1420 		break;
   1421 	}
   1422 
   1423 	if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
   1424 #ifdef HAVE_CYRUS_SASL
   1425 		void		*defaults = NULL;
   1426 		struct berval	authzID = BER_BVNULL;
   1427 		int		freeauthz = 0;
   1428 
   1429 		/* if SASL supports native authz, prepare for it */
   1430 		if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
   1431 				( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
   1432 		{
   1433 			switch ( mt->mt_idassert_mode ) {
   1434 			case LDAP_BACK_IDASSERT_OTHERID:
   1435 			case LDAP_BACK_IDASSERT_OTHERDN:
   1436 				authzID = mt->mt_idassert_authzID;
   1437 				break;
   1438 
   1439 			case LDAP_BACK_IDASSERT_ANONYMOUS:
   1440 				BER_BVSTR( &authzID, "dn:" );
   1441 				break;
   1442 
   1443 			case LDAP_BACK_IDASSERT_SELF:
   1444 				if ( BER_BVISNULL( &ndn ) ) {
   1445 					/* connection is not authc'd, so don't idassert */
   1446 					BER_BVSTR( &authzID, "dn:" );
   1447 					break;
   1448 				}
   1449 				authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
   1450 				authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
   1451 				AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
   1452 				AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
   1453 						ndn.bv_val, ndn.bv_len + 1 );
   1454 				freeauthz = 1;
   1455 				break;
   1456 
   1457 			default:
   1458 				break;
   1459 			}
   1460 		}
   1461 
   1462 		if ( mt->mt_idassert_secprops != NULL ) {
   1463 			rs->sr_err = ldap_set_option( msc->msc_ld,
   1464 				LDAP_OPT_X_SASL_SECPROPS,
   1465 				(void *)mt->mt_idassert_secprops );
   1466 
   1467 			if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
   1468 				rs->sr_err = LDAP_OTHER;
   1469 				if ( sendok & LDAP_BACK_SENDERR ) {
   1470 					send_ldap_result( op, rs );
   1471 				}
   1472 				LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1473 				goto done;
   1474 			}
   1475 		}
   1476 
   1477 		defaults = lutil_sasl_defaults( msc->msc_ld,
   1478 				mt->mt_idassert_sasl_mech.bv_val,
   1479 				mt->mt_idassert_sasl_realm.bv_val,
   1480 				mt->mt_idassert_authcID.bv_val,
   1481 				mt->mt_idassert_passwd.bv_val,
   1482 				authzID.bv_val );
   1483 		if ( defaults == NULL ) {
   1484 			rs->sr_err = LDAP_OTHER;
   1485 			LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1486 			if ( sendok & LDAP_BACK_SENDERR ) {
   1487 				send_ldap_result( op, rs );
   1488 			}
   1489 			goto done;
   1490 		}
   1491 
   1492 		rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
   1493 				mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
   1494 				LDAP_SASL_QUIET, lutil_sasl_interact,
   1495 				defaults );
   1496 
   1497 		rs->sr_err = slap_map_api2result( rs );
   1498 		if ( rs->sr_err != LDAP_SUCCESS ) {
   1499 			LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1500 			if ( sendok & LDAP_BACK_SENDERR ) {
   1501 				send_ldap_result( op, rs );
   1502 			}
   1503 
   1504 		} else {
   1505 			LDAP_BACK_CONN_ISBOUND_SET( msc );
   1506 		}
   1507 
   1508 		lutil_sasl_freedefs( defaults );
   1509 		if ( freeauthz ) {
   1510 			slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
   1511 		}
   1512 
   1513 		goto done;
   1514 #endif /* HAVE_CYRUS_SASL */
   1515 	}
   1516 
   1517 	*method = mt->mt_idassert_authmethod;
   1518 	switch ( mt->mt_idassert_authmethod ) {
   1519 	case LDAP_AUTH_NONE:
   1520 		BER_BVSTR( binddn, "" );
   1521 		BER_BVSTR( bindcred, "" );
   1522 		/* fallthru */
   1523 
   1524 	case LDAP_AUTH_SIMPLE:
   1525 		break;
   1526 
   1527 	default:
   1528 		/* unsupported! */
   1529 		LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
   1530 		rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
   1531 		if ( sendok & LDAP_BACK_SENDERR ) {
   1532 			send_ldap_result( op, rs );
   1533 		}
   1534 		break;
   1535 	}
   1536 
   1537 done:;
   1538 
   1539 	if ( !BER_BVISEMPTY( binddn ) ) {
   1540 		LDAP_BACK_CONN_ISIDASSERT_SET( msc );
   1541 	}
   1542 
   1543 	return rs->sr_err;
   1544 }
   1545 
   1546 static int
   1547 meta_back_proxy_authz_bind(
   1548 	metaconn_t *mc,
   1549 	int candidate,
   1550 	Operation *op,
   1551 	SlapReply *rs,
   1552 	ldap_back_send_t sendok,
   1553 	int dolock )
   1554 {
   1555 	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
   1556 	metatarget_t		*mt = mi->mi_targets[ candidate ];
   1557 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
   1558 	struct berval		binddn = BER_BVC( "" ),
   1559 				cred = BER_BVC( "" );
   1560 	int			method = LDAP_AUTH_NONE,
   1561 				rc;
   1562 
   1563 	rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
   1564 	if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
   1565 		int	msgid;
   1566 
   1567 		switch ( method ) {
   1568 		case LDAP_AUTH_NONE:
   1569 		case LDAP_AUTH_SIMPLE:
   1570 
   1571 			if(!dolock) {
   1572 				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
   1573 			}
   1574 
   1575 			for (;;) {
   1576 				rs->sr_err = ldap_sasl_bind( msc->msc_ld,
   1577 					binddn.bv_val, LDAP_SASL_SIMPLE,
   1578 					&cred, NULL, NULL, &msgid );
   1579 				if ( rs->sr_err != LDAP_X_CONNECTING ) {
   1580 					break;
   1581 				}
   1582 				ldap_pvt_thread_yield();
   1583 			}
   1584 
   1585 			if(!dolock) {
   1586 				ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
   1587 			}
   1588 
   1589 			rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
   1590 			if ( rc == LDAP_SUCCESS ) {
   1591 				/* set rebind stuff in case of successful proxyAuthz bind,
   1592 				 * so that referral chasing is attempted using the right
   1593 				 * identity */
   1594 				LDAP_BACK_CONN_ISBOUND_SET( msc );
   1595 				ber_bvreplace( &msc->msc_bound_ndn, &binddn );
   1596 
   1597 				if ( META_BACK_TGT_SAVECRED( mt ) ) {
   1598 					if ( !BER_BVISNULL( &msc->msc_cred ) ) {
   1599 						memset( msc->msc_cred.bv_val, 0,
   1600 							msc->msc_cred.bv_len );
   1601 					}
   1602 					ber_bvreplace( &msc->msc_cred, &cred );
   1603 					ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
   1604 				}
   1605 			}
   1606 			break;
   1607 
   1608 		default:
   1609 			assert( 0 );
   1610 			break;
   1611 		}
   1612 	}
   1613 
   1614 	return LDAP_BACK_CONN_ISBOUND( msc );
   1615 }
   1616 
   1617 /*
   1618  * Add controls;
   1619  *
   1620  * if any needs to be added, it is prepended to existing ones,
   1621  * in a newly allocated array.  The companion function
   1622  * mi->mi_ldap_extra->controls_free() must be used to restore the original
   1623  * status of op->o_ctrls.
   1624  */
   1625 int
   1626 meta_back_controls_add(
   1627 		Operation	*op,
   1628 		SlapReply	*rs,
   1629 		metaconn_t	*mc,
   1630 		int		candidate,
   1631 		LDAPControl	***pctrls )
   1632 {
   1633 	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
   1634 	metatarget_t		*mt = mi->mi_targets[ candidate ];
   1635 	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
   1636 
   1637 	LDAPControl		**ctrls = NULL;
   1638 	/* set to the maximum number of controls this backend can add */
   1639 	LDAPControl		c[ 2 ] = {{ 0 }};
   1640 	int			n = 0, i, j1 = 0, j2 = 0, skipped = 0;
   1641 
   1642 	*pctrls = NULL;
   1643 
   1644 	rs->sr_err = LDAP_SUCCESS;
   1645 
   1646 	/* don't add controls if protocol is not LDAPv3 */
   1647 	switch ( mt->mt_version ) {
   1648 	case LDAP_VERSION3:
   1649 		break;
   1650 
   1651 	case 0:
   1652 		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
   1653 			break;
   1654 		}
   1655 		/* fall thru */
   1656 
   1657 	default:
   1658 		goto done;
   1659 	}
   1660 
   1661 	/* put controls that go __before__ existing ones here */
   1662 
   1663 	/* proxyAuthz for identity assertion */
   1664 	switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
   1665 		mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
   1666 	{
   1667 	case SLAP_CB_CONTINUE:
   1668 		break;
   1669 
   1670 	case LDAP_SUCCESS:
   1671 		j1++;
   1672 		break;
   1673 
   1674 	default:
   1675 		goto done;
   1676 	}
   1677 
   1678 	/* put controls that go __after__ existing ones here */
   1679 
   1680 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
   1681 	/* session tracking */
   1682 	if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
   1683 		switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
   1684 		case SLAP_CB_CONTINUE:
   1685 			break;
   1686 
   1687 		case LDAP_SUCCESS:
   1688 			j2++;
   1689 			break;
   1690 
   1691 		default:
   1692 			goto done;
   1693 		}
   1694 	}
   1695 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
   1696 
   1697 	if ( rs->sr_err == SLAP_CB_CONTINUE ) {
   1698 		rs->sr_err = LDAP_SUCCESS;
   1699 	}
   1700 
   1701 	/* if nothing to do, just bail out */
   1702 	if ( j1 == 0 && j2 == 0 ) {
   1703 		goto done;
   1704 	}
   1705 
   1706 	assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
   1707 
   1708 	if ( op->o_ctrls ) {
   1709 		for ( n = 0; op->o_ctrls[ n ]; n++ )
   1710 			/* just count ctrls */ ;
   1711 	}
   1712 
   1713 	ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
   1714 			op->o_tmpmemctx );
   1715 	if ( j1 ) {
   1716 		ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
   1717 		*ctrls[ 0 ] = c[ 0 ];
   1718 		for ( i = 1; i < j1; i++ ) {
   1719 			ctrls[ i ] = &ctrls[ 0 ][ i ];
   1720 			*ctrls[ i ] = c[ i ];
   1721 		}
   1722 	}
   1723 
   1724 	i = 0;
   1725 	if ( op->o_ctrls ) {
   1726 		LDAPControl *proxyauthz = ldap_control_find(
   1727 				LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
   1728 
   1729 		for ( i = 0; op->o_ctrls[ i ]; i++ ) {
   1730 			/* Only replace it if we generated one */
   1731 			if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
   1732 				/* Frontend has already checked only one is present */
   1733 				assert( skipped == 0 );
   1734 				skipped++;
   1735 				continue;
   1736 			}
   1737 			ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
   1738 		}
   1739 	}
   1740 
   1741 	n += j1 - skipped;
   1742 	if ( j2 ) {
   1743 		ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
   1744 		*ctrls[ n ] = c[ j1 ];
   1745 		for ( i = 1; i < j2; i++ ) {
   1746 			ctrls[ n + i ] = &ctrls[ n ][ i ];
   1747 			*ctrls[ n + i ] = c[ i ];
   1748 		}
   1749 	}
   1750 
   1751 	ctrls[ n + j2 ] = NULL;
   1752 
   1753 done:;
   1754 	if ( ctrls == NULL ) {
   1755 		ctrls = op->o_ctrls;
   1756 	}
   1757 
   1758 	*pctrls = ctrls;
   1759 
   1760 	return rs->sr_err;
   1761 }
   1762 
   1763