Esempio n. 1
0
/*
 * meta_back_dobind
 */
int
meta_back_dobind(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
	ldap_back_send_t	sendok )
{
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;

	int			bound = 0,
				i,
				isroot = 0;

	SlapReply		*candidates;

	if ( be_isroot( op ) ) {
		isroot = 1;
	}

	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char buf[STRLENOF("4294967295U") + 1] = { 0 };
		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );

		Debug( LDAP_DEBUG_TRACE,
			"%s meta_back_dobind: conn=%s%s\n",
			op->o_log_prefix, buf,
			isroot ? " (isroot)" : "" );
	}

	/*
	 * all the targets are bound as pseudoroot
	 */
	if ( mc->mc_authz_target == META_BOUND_ALL ) {
		bound = 1;
		goto done;
	}

	candidates = meta_back_candidates_get( op );

	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		metatarget_t		*mt = mi->mi_targets[ i ];
		metasingleconn_t	*msc = &mc->mc_conns[ i ];
		int			rc;

		/*
		 * Not a candidate
		 */
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}

		assert( msc->msc_ld != NULL );

		/*
		 * If the target is already bound it is skipped
		 */

retry_binding:;
		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		if ( LDAP_BACK_CONN_ISBOUND( msc )
			|| ( LDAP_BACK_CONN_ISANON( msc )
				&& mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
		{
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
			++bound;
			continue;

		} else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
		{
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
			ldap_pvt_thread_yield();
			goto retry_binding;

		}

		LDAP_BACK_CONN_BINDING_SET( msc );
		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );

		rc = meta_back_single_dobind( op, rs, &mc, i,
			LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
		/*
		 * NOTE: meta_back_single_dobind() already retries;
		 * in case of failure, it resets mc...
		 */
		if ( rc != LDAP_SUCCESS ) {
			char		buf[ SLAP_TEXT_BUFLEN ];

			if ( mc == NULL ) {
				/* meta_back_single_dobind() already sent
				 * response and released connection */
				goto send_err;
			}


			if ( rc == LDAP_UNAVAILABLE ) {
				/* FIXME: meta_back_retry() already re-calls
				 * meta_back_single_dobind() */
				if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
					goto retry_ok;
				}

				if ( mc != NULL ) {
					ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
					LDAP_BACK_CONN_BINDING_CLEAR( msc );
					meta_back_release_conn_lock( mi, mc, 0 );
					ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
				}

				return 0;
			}

			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
			LDAP_BACK_CONN_BINDING_CLEAR( msc );
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );

			snprintf( buf, sizeof( buf ),
				"meta_back_dobind[%d]: (%s) err=%d (%s).",
				i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
				rc, ldap_err2string( rc ) );
			Debug( LDAP_DEBUG_ANY,
				"%s %s\n",
				op->o_log_prefix, buf );

			/*
			 * null cred bind should always succeed
			 * as anonymous, so a failure means
			 * the target is no longer candidate possibly
			 * due to technical reasons (remote host down?)
			 * so better clear the handle
			 */
			/* leave the target candidate, but record the error for later use */
			candidates[ i ].sr_err = rc;
			if ( META_BACK_ONERR_STOP( mi ) ) {
				bound = 0;
				goto done;
			}

			continue;
		} /* else */

retry_ok:;
		Debug( LDAP_DEBUG_TRACE,
			"%s meta_back_dobind[%d]: "
			"(%s)\n",
			op->o_log_prefix, i,
			isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );

		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		LDAP_BACK_CONN_BINDING_CLEAR( msc );
		if ( isroot ) {
			LDAP_BACK_CONN_ISBOUND_SET( msc );
		} else {
			LDAP_BACK_CONN_ISANON_SET( msc );
		}
		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
		++bound;
	}

done:;
	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char buf[STRLENOF("4294967295U") + 1] = { 0 };
		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );

		Debug( LDAP_DEBUG_TRACE,
			"%s meta_back_dobind: conn=%s bound=%d\n",
			op->o_log_prefix, buf, bound );
	}

	if ( bound == 0 ) {
		meta_back_release_conn( mi, mc );

send_err:;
		if ( sendok & LDAP_BACK_SENDERR ) {
			if ( rs->sr_err == LDAP_SUCCESS ) {
				rs->sr_err = LDAP_BUSY;
			}
			send_ldap_result( op, rs );
		}

		return 0;
	}

	return ( bound > 0 );
}
Esempio n. 2
0
/*
 * meta_back_single_dobind
 */
int
meta_back_single_dobind(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		**mcp,
	int			candidate,
	ldap_back_send_t	sendok,
	int			nretries,
	int			dolock )
{
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
	metatarget_t		*mt = mi->mi_targets[ candidate ];
	metaconn_t		*mc = *mcp;
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	int			msgid;

	assert( !LDAP_BACK_CONN_ISBOUND( msc ) );

	/* NOTE: this obsoletes pseudorootdn */
	if ( op->o_conn != NULL &&
		!op->o_do_not_cache &&
		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
			( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
	{
		(void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );

	} else {
		char *binddn = "";
		struct berval cred = BER_BVC( "" );

		/* use credentials if available */
		if ( !BER_BVISNULL( &msc->msc_bound_ndn )
			&& !BER_BVISNULL( &msc->msc_cred ) )
		{
			binddn = msc->msc_bound_ndn.bv_val;
			cred = msc->msc_cred;
		}

		/* FIXME: should we check if at least some of the op->o_ctrls
		 * can/should be passed? */
		if(!dolock) {
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
		}

		for (;;) {
			rs->sr_err = ldap_sasl_bind( msc->msc_ld,
				binddn, LDAP_SASL_SIMPLE, &cred,
				NULL, NULL, &msgid );
			if ( rs->sr_err != LDAP_X_CONNECTING ) {
				break;
			}
			ldap_pvt_thread_yield();
		}

		if(!dolock) {
			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		}

		rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );

		/* if bind succeeded, but anonymous, clear msc_bound_ndn */
		if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
				ber_memfree( msc->msc_bound_ndn.bv_val );
				BER_BVZERO( &msc->msc_bound_ndn );
			}

			if ( !BER_BVISNULL( &msc->msc_cred ) ) {
				memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
				ber_memfree( msc->msc_cred.bv_val );
				BER_BVZERO( &msc->msc_cred );
			}
		}
	}

	if ( rs->sr_err != LDAP_SUCCESS ) {
		if ( dolock ) {
			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		}
		LDAP_BACK_CONN_BINDING_CLEAR( msc );
		if ( META_BACK_ONERR_STOP( mi ) ) {
			LDAP_BACK_CONN_TAINTED_SET( mc );
			meta_back_release_conn_lock( mi, mc, 0 );
			*mcp = NULL;
		}
		if ( dolock ) {
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
		}
	}

	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		meta_back_quarantine( op, rs, candidate );
	}

	return rs->sr_err;
}
Esempio n. 3
0
/*
 * meta_back_single_bind
 *
 * attempts to perform a bind with creds
 */
static int
meta_back_single_bind(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
	int			candidate )
{
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
	metatarget_t		*mt = mi->mi_targets[ candidate ];
	struct berval		mdn = BER_BVNULL;
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	int			msgid;
	dncookie		dc;
	struct berval		save_o_dn;
	int			save_o_do_not_cache;
	LDAPControl		**ctrls = NULL;

	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
		ch_free( msc->msc_bound_ndn.bv_val );
		BER_BVZERO( &msc->msc_bound_ndn );
	}

	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
		/* destroy sensitive data */
		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
		ch_free( msc->msc_cred.bv_val );
		BER_BVZERO( &msc->msc_cred );
	}

	/*
	 * Rewrite the bind dn if needed
	 */
	dc.target = mt;
	dc.conn = op->o_conn;
	dc.rs = rs;
	dc.ctx = "bindDN";

	if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
		rs->sr_text = "DN rewrite error";
		rs->sr_err = LDAP_OTHER;
		return rs->sr_err;
	}

	/* don't add proxyAuthz; set the bindDN */
	save_o_dn = op->o_dn;
	save_o_do_not_cache = op->o_do_not_cache;
	op->o_do_not_cache = 1;
	op->o_dn = op->o_req_dn;

	ctrls = op->o_ctrls;
	rs->sr_err = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
	op->o_dn = save_o_dn;
	op->o_do_not_cache = save_o_do_not_cache;
	if ( rs->sr_err != LDAP_SUCCESS ) {
		goto return_results;
	}

	/* FIXME: this fixes the bind problem right now; we need
	 * to use the asynchronous version to get the "matched"
	 * and more in case of failure ... */
	/* FIXME: should we check if at least some of the op->o_ctrls
	 * can/should be passed? */
	for (;;) {
		rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
			LDAP_SASL_SIMPLE, &op->orb_cred,
			ctrls, NULL, &msgid );
		if ( rs->sr_err != LDAP_X_CONNECTING ) {
			break;
		}
		ldap_pvt_thread_yield();
	}

	mi->mi_ldap_extra->controls_free( op, rs, &ctrls );

	meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
	if ( rs->sr_err != LDAP_SUCCESS ) {
		goto return_results;
	}

	/* If defined, proxyAuthz will be used also when
	 * back-ldap is the authorizing backend; for this
	 * purpose, a successful bind is followed by a
	 * bind with the configured identity assertion */
	/* NOTE: use with care */
	if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
		meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
		if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
			goto return_results;
		}
		goto cache_refresh;
	}

	ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
	LDAP_BACK_CONN_ISBOUND_SET( msc );
	mc->mc_authz_target = candidate;

	if ( META_BACK_TGT_SAVECRED( mt ) ) {
		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
			memset( msc->msc_cred.bv_val, 0,
				msc->msc_cred.bv_len );
		}
		ber_bvreplace( &msc->msc_cred, &op->orb_cred );
		ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
	}

cache_refresh:;
	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
			&& !BER_BVISEMPTY( &op->o_req_ndn ) )
	{
		( void )meta_dncache_update_entry( &mi->mi_cache,
				&op->o_req_ndn, candidate );
	}

return_results:;
	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
		free( mdn.bv_val );
	}

	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		meta_back_quarantine( op, rs, candidate );
	}

	return rs->sr_err;
}
Esempio n. 4
0
static int
meta_back_proxy_authz_bind(
	metaconn_t *mc,
	int candidate,
	Operation *op,
	SlapReply *rs,
	ldap_back_send_t sendok,
	int dolock )
{
	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;
	metatarget_t		*mt = mi->mi_targets[ candidate ];
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	struct berval		binddn = BER_BVC( "" ),
				cred = BER_BVC( "" );
	int			method = LDAP_AUTH_NONE,
				rc;

	rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
	if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
		int	msgid;

		switch ( method ) {
		case LDAP_AUTH_NONE:
		case LDAP_AUTH_SIMPLE:

			if(!dolock) {
				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
			}

			for (;;) {
				rs->sr_err = ldap_sasl_bind( msc->msc_ld,
					binddn.bv_val, LDAP_SASL_SIMPLE,
					&cred, NULL, NULL, &msgid );
				if ( rs->sr_err != LDAP_X_CONNECTING ) {
					break;
				}
				ldap_pvt_thread_yield();
			}

			if(!dolock) {
				ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
			}

			rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
			if ( rc == LDAP_SUCCESS ) {
				/* set rebind stuff in case of successful proxyAuthz bind,
				 * so that referral chasing is attempted using the right
				 * identity */
				LDAP_BACK_CONN_ISBOUND_SET( msc );
				ber_bvreplace( &msc->msc_bound_ndn, &binddn );

				if ( META_BACK_TGT_SAVECRED( mt ) ) {
					if ( !BER_BVISNULL( &msc->msc_cred ) ) {
						memset( msc->msc_cred.bv_val, 0,
							msc->msc_cred.bv_len );
					}
					ber_bvreplace( &msc->msc_cred, &cred );
					ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
				}
			}
			break;

		default:
			LDAP_BUG();
			break;
		}
	}

	return LDAP_BACK_CONN_ISBOUND( msc );
}
Esempio n. 5
0
/*
 * asyncmeta_back_single_dobind
 */
int
asyncmeta_back_single_dobind(
	Operation		*op,
	SlapReply		*rs,
	a_metaconn_t		**mcp,
	int			candidate,
	ldap_back_send_t	sendok,
	int			nretries,
	int			dolock )
{
	a_metaconn_t		*mc = *mcp;
	a_metainfo_t		*mi = mc->mc_info;
	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	int			msgid;

	assert( !LDAP_BACK_CONN_ISBOUND( msc ) );

	if ( op->o_conn != NULL &&
		!op->o_do_not_cache &&
		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
			( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
	{
		(void)asyncmeta_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );

	} else {
		char *binddn = "";
		struct berval cred = BER_BVC( "" );

		/* use credentials if available */
		if ( !BER_BVISNULL( &msc->msc_bound_ndn )
			&& !BER_BVISNULL( &msc->msc_cred ) )
		{
			binddn = msc->msc_bound_ndn.bv_val;
			cred = msc->msc_cred;
		}

		for (;;) {
			rs->sr_err = ldap_sasl_bind( msc->msc_ld,
				binddn, LDAP_SASL_SIMPLE, &cred,
				NULL, NULL, &msgid );
			if ( rs->sr_err != LDAP_X_CONNECTING ) {
				break;
			}
			ldap_pvt_thread_yield();
		}

		rs->sr_err = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );

		/* if bind succeeded, but anonymous, clear msc_bound_ndn */
		if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
				ber_memfree( msc->msc_bound_ndn.bv_val );
				BER_BVZERO( &msc->msc_bound_ndn );
			}

			if ( !BER_BVISNULL( &msc->msc_cred ) ) {
				memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
				ber_memfree( msc->msc_cred.bv_val );
				BER_BVZERO( &msc->msc_cred );
			}
		}
	}

	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		asyncmeta_quarantine( op, mi, rs, candidate );
	}

	return rs->sr_err;
}
Esempio n. 6
0
/*
 * asyncmeta_dobind_init()
 *
 * initiates bind for a candidate target
 */
meta_search_candidate_t
asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
{
	SlapReply		*candidates = bc->candidates;
	a_metainfo_t		*mi = ( a_metainfo_t * )mc->mc_info;
	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	ber_socket_t s;
	struct berval		binddn = msc->msc_bound_ndn,
				cred = msc->msc_cred;
	int			method;

	int			rc;
	ber_int_t	msgid;

	meta_search_candidate_t	retcode;

	Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_search_dobind_init[%d]\n",
		op->o_log_prefix, candidate, 0 );

	if ( mc->mc_authz_target == META_BOUND_ALL ) {
		return META_SEARCH_CANDIDATE;
	}

	retcode = META_SEARCH_BINDING;
	if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
		/* already bound (or anonymous) */

#ifdef DEBUG_205
		char	buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
		int	bound = 0;

		if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
			bound = 1;
		}

		snprintf( buf, sizeof( buf ), " mc=%p ld=%p%s DN=\"%s\"",
			(void *)mc, (void *)msc->msc_ld,
			bound ? " bound" : " anonymous",
			bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
		Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
			op->o_log_prefix, candidate, buf );
#endif /* DEBUG_205 */

		retcode = META_SEARCH_CANDIDATE;

	} else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
		/* another thread is binding the target for this conn; wait */

#ifdef DEBUG_205
		char	buf[ SLAP_TEXT_BUFLEN ] = { '\0' };

		snprintf( buf, sizeof( buf ), " mc=%p ld=%p needbind",
			(void *)mc, (void *)msc->msc_ld );
		Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
			op->o_log_prefix, candidate, buf );
#endif /* DEBUG_205 */

		candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
		retcode = META_SEARCH_NEED_BIND;

	} else {
		/* we'll need to bind the target for this conn */

#ifdef DEBUG_205
		char buf[ SLAP_TEXT_BUFLEN ];

		snprintf( buf, sizeof( buf ), " mc=%p ld=%p binding",
			(void *)mc, (void *)msc->msc_ld );
		Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
			op->o_log_prefix, candidate, buf );
#endif /* DEBUG_205 */

		if ( msc->msc_ld == NULL ) {
			/* for some reason (e.g. because formerly in "binding"
			 * state, with eventual connection expiration or invalidation)
			 * it was not initialized as expected */

			Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p ld=NULL\n",
				op->o_log_prefix, candidate, (void *)mc );

			rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
				LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
			switch ( rc ) {
			case LDAP_SUCCESS:
				assert( msc->msc_ld != NULL );
				break;

			case LDAP_SERVER_DOWN:
			case LDAP_UNAVAILABLE:
				goto down;

			default:
				goto other;
			}
		}

		LDAP_BACK_CONN_BINDING_SET( msc );
	}

	if ( retcode != META_SEARCH_BINDING ) {
		return retcode;
	}

	if ( op->o_conn != NULL &&
		!op->o_do_not_cache &&
		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
	{
		rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
		switch ( rc ) {
		case LDAP_SUCCESS:
			break;
		case LDAP_UNAVAILABLE:
			goto down;
		default:
			goto other;
		}

		/* NOTE: we copy things here, even if bind didn't succeed yet,
		 * because the connection is not shared until bind is over */
		if ( !BER_BVISNULL( &binddn ) ) {
			ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);

			ber_bvreplace( &msc->msc_bound_ndn, &binddn );
			if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
					memset( msc->msc_cred.bv_val, 0,
						msc->msc_cred.bv_len );
				}
				ber_bvreplace( &msc->msc_cred, &cred );
			}
			ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
		}
		if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
			/* apparently, idassert was configured with SASL bind,
			 * so bind occurred inside meta_back_proxy_authz_cred() */
			LDAP_BACK_CONN_BINDING_CLEAR( msc );
			return META_SEARCH_CANDIDATE;
		}

		/* paranoid */
		switch ( method ) {
		case LDAP_AUTH_NONE:
		case LDAP_AUTH_SIMPLE:
			/* do a simple bind with binddn, cred */
			break;

		default:
			assert( 0 );
			break;
		}
	}

	assert( msc->msc_ld != NULL );

	if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
		/* bind anonymously? */
		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
			"non-empty dn with empty cred; binding anonymously\n",
			op->o_log_prefix, candidate, (void *)mc );
		cred = slap_empty_bv;

	} else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
		/* error */
		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
			"empty dn with non-empty cred: error\n",
			op->o_log_prefix, candidate, (void *)mc );
		rc = LDAP_OTHER;
		goto other;
	}
retry_bind:
	rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
			NULL, NULL, &msgid );
	ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc );

	if (rc == LDAP_SERVER_DOWN ) {
		goto down;
	}
	candidates[ candidate ].sr_msgid = msgid;
	asyncmeta_set_msc_time(msc);
#ifdef DEBUG_205
	{
		char buf[ SLAP_TEXT_BUFLEN ];

		snprintf( buf, sizeof( buf ), "asyncmeta_search_dobind_init[%d] mc=%p ld=%p rc=%d",
			candidate, (void *)mc, (void *)mc->mc_conns[ candidate ].msc_ld, rc );
		Debug( LDAP_DEBUG_ANY, "### %s %s\n",
			op->o_log_prefix, buf, 0 );
	}
#endif /* DEBUG_205 */

	switch ( rc ) {
	case LDAP_SUCCESS:
		assert( msgid >= 0 );
		META_BINDING_SET( &candidates[ candidate ] );
		rs->sr_err = LDAP_SUCCESS;
		return META_SEARCH_BINDING;

	case LDAP_X_CONNECTING:
		/* must retry, same conn */
		candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING;
		LDAP_BACK_CONN_BINDING_CLEAR( msc );
		goto retry_bind;

	case LDAP_SERVER_DOWN:
down:;
		retcode = META_SEARCH_ERR;
		rs->sr_err = LDAP_UNAVAILABLE;
		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
		break;

		/* fall thru */

	default:
other:;
		rs->sr_err = rc;
		rc = slap_map_api2result( rs );
		candidates[ candidate ].sr_err = rc;
		if ( META_BACK_ONERR_STOP( mi ) ) {
			retcode = META_SEARCH_ERR;

		} else {
			retcode = META_SEARCH_NOT_CANDIDATE;
		}
		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
		break;
	}

	return retcode;
}