1*cf1d77f7Schristos /*	$NetBSD: bind.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
292cfeba6Schristos 
392cfeba6Schristos /* bind.c - bind request handler functions for binding
492cfeba6Schristos  * to remote targets for back-asyncmeta */
592cfeba6Schristos /* $OpenLDAP$ */
692cfeba6Schristos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
792cfeba6Schristos  *
892cfeba6Schristos  * Copyright 2016-2021 The OpenLDAP Foundation.
992cfeba6Schristos  * Portions Copyright 2016 Symas Corporation.
1092cfeba6Schristos  * All rights reserved.
1192cfeba6Schristos  *
1292cfeba6Schristos  * Redistribution and use in source and binary forms, with or without
1392cfeba6Schristos  * modification, are permitted only as authorized by the OpenLDAP
1492cfeba6Schristos  * Public License.
1592cfeba6Schristos  *
1692cfeba6Schristos  * A copy of this license is available in the file LICENSE in the
1792cfeba6Schristos  * top-level directory of the distribution or, alternatively, at
1892cfeba6Schristos  * <http://www.OpenLDAP.org/license.html>.
1992cfeba6Schristos  */
2092cfeba6Schristos 
2192cfeba6Schristos /* ACKNOWLEDGEMENTS:
2292cfeba6Schristos  * This work was developed by Symas Corporation
2392cfeba6Schristos  * based on back-meta module for inclusion in OpenLDAP Software.
2492cfeba6Schristos  * This work was sponsored by Ericsson. */
2592cfeba6Schristos 
2692cfeba6Schristos #include <sys/cdefs.h>
27*cf1d77f7Schristos __RCSID("$NetBSD: bind.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
2892cfeba6Schristos 
2992cfeba6Schristos #include "portable.h"
3092cfeba6Schristos 
3192cfeba6Schristos #include <stdio.h>
3292cfeba6Schristos 
3392cfeba6Schristos #include <ac/errno.h>
3492cfeba6Schristos #include <ac/socket.h>
3592cfeba6Schristos #include <ac/string.h>
3692cfeba6Schristos #include "slap.h"
3792cfeba6Schristos #include "../../../libraries/libldap/ldap-int.h"
3892cfeba6Schristos 
3992cfeba6Schristos #define AVL_INTERNAL
4092cfeba6Schristos #include "../back-ldap/back-ldap.h"
4192cfeba6Schristos #include "back-asyncmeta.h"
4292cfeba6Schristos #include "lutil_ldap.h"
4392cfeba6Schristos 
4492cfeba6Schristos #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
4592cfeba6Schristos 
4692cfeba6Schristos static int
4792cfeba6Schristos asyncmeta_proxy_authz_bind(
4892cfeba6Schristos 	a_metaconn_t		*mc,
4992cfeba6Schristos 	int			candidate,
5092cfeba6Schristos 	Operation		*op,
5192cfeba6Schristos 	SlapReply		*rs,
5292cfeba6Schristos 	ldap_back_send_t	sendok,
5392cfeba6Schristos 	int			dolock );
5492cfeba6Schristos 
5592cfeba6Schristos static int
5692cfeba6Schristos asyncmeta_single_bind(
5792cfeba6Schristos 	Operation		*op,
5892cfeba6Schristos 	SlapReply		*rs,
5992cfeba6Schristos 	a_metaconn_t		*mc,
6092cfeba6Schristos 	int			candidate );
6192cfeba6Schristos 
6292cfeba6Schristos int
asyncmeta_back_bind(Operation * op,SlapReply * rs)6392cfeba6Schristos asyncmeta_back_bind( Operation *op, SlapReply *rs )
6492cfeba6Schristos {
6592cfeba6Schristos 	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
6692cfeba6Schristos 	a_metaconn_t	*mc = NULL;
6792cfeba6Schristos 
6892cfeba6Schristos 	int		rc = LDAP_OTHER,
6992cfeba6Schristos 			i,
7092cfeba6Schristos 			gotit = 0,
7192cfeba6Schristos 			isroot = 0;
7292cfeba6Schristos 
7392cfeba6Schristos 	SlapReply	*candidates;
7492cfeba6Schristos 
7592cfeba6Schristos 	candidates = op->o_tmpcalloc(mi->mi_ntargets, sizeof(SlapReply),op->o_tmpmemctx);
7692cfeba6Schristos 	rs->sr_err = LDAP_SUCCESS;
7792cfeba6Schristos 
7892cfeba6Schristos 	Debug( LDAP_DEBUG_ARGS, "%s asyncmeta_back_bind: dn=\"%s\".\n",
7992cfeba6Schristos 		op->o_log_prefix, op->o_req_dn.bv_val );
8092cfeba6Schristos 
8192cfeba6Schristos 	/* the test on the bind method should be superfluous */
8292cfeba6Schristos 	switch ( be_rootdn_bind( op, rs ) ) {
8392cfeba6Schristos 	case LDAP_SUCCESS:
8492cfeba6Schristos 		if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
8592cfeba6Schristos 			/* frontend will return success */
8692cfeba6Schristos 			return rs->sr_err;
8792cfeba6Schristos 		}
8892cfeba6Schristos 
8992cfeba6Schristos 		isroot = 1;
9092cfeba6Schristos 		/* fallthru */
9192cfeba6Schristos 
9292cfeba6Schristos 	case SLAP_CB_CONTINUE:
9392cfeba6Schristos 		break;
9492cfeba6Schristos 
9592cfeba6Schristos 	default:
9692cfeba6Schristos 		/* be_rootdn_bind() sent result */
9792cfeba6Schristos 		return rs->sr_err;
9892cfeba6Schristos 	}
9992cfeba6Schristos 
10092cfeba6Schristos 	/* we need asyncmeta_getconn() not send result even on error,
10192cfeba6Schristos 	 * because we want to intercept the error and make it
10292cfeba6Schristos 	 * invalidCredentials */
10392cfeba6Schristos 	mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_BIND_DONTSEND, 1 );
10492cfeba6Schristos 	if ( !mc ) {
10592cfeba6Schristos 		Debug(LDAP_DEBUG_ANY,
10692cfeba6Schristos 		      "%s asyncmeta_back_bind: no target " "for dn \"%s\" (%d%s%s).\n",
10792cfeba6Schristos 		      op->o_log_prefix, op->o_req_dn.bv_val,
10892cfeba6Schristos 		      rs->sr_err, rs->sr_text ? ". " : "",
10992cfeba6Schristos 		      rs->sr_text ? rs->sr_text : "" );
11092cfeba6Schristos 
11192cfeba6Schristos 		/* FIXME: there might be cases where we don't want
11292cfeba6Schristos 		 * to map the error onto invalidCredentials */
11392cfeba6Schristos 		switch ( rs->sr_err ) {
11492cfeba6Schristos 		case LDAP_NO_SUCH_OBJECT:
11592cfeba6Schristos 		case LDAP_UNWILLING_TO_PERFORM:
11692cfeba6Schristos 			rs->sr_err = LDAP_INVALID_CREDENTIALS;
11792cfeba6Schristos 			rs->sr_text = NULL;
11892cfeba6Schristos 			break;
11992cfeba6Schristos 		}
12092cfeba6Schristos 		send_ldap_result( op, rs );
12192cfeba6Schristos 		return rs->sr_err;
12292cfeba6Schristos 	}
12392cfeba6Schristos 
12492cfeba6Schristos 	/*
12592cfeba6Schristos 	 * Each target is scanned ...
12692cfeba6Schristos 	 */
12792cfeba6Schristos 	mc->mc_authz_target = META_BOUND_NONE;
12892cfeba6Schristos 	for ( i = 0; i < mi->mi_ntargets; i++ ) {
12992cfeba6Schristos 		a_metatarget_t	*mt = mi->mi_targets[ i ];
13092cfeba6Schristos 		int		lerr;
13192cfeba6Schristos 
13292cfeba6Schristos 		/*
13392cfeba6Schristos 		 * Skip non-candidates
13492cfeba6Schristos 		 */
13592cfeba6Schristos 		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
13692cfeba6Schristos 			continue;
13792cfeba6Schristos 		}
13892cfeba6Schristos 
13992cfeba6Schristos 		if ( gotit == 0 ) {
14092cfeba6Schristos 			/* set rc to LDAP_SUCCESS only if at least
14192cfeba6Schristos 			 * one candidate has been tried */
14292cfeba6Schristos 			rc = LDAP_SUCCESS;
14392cfeba6Schristos 			gotit = 1;
14492cfeba6Schristos 
14592cfeba6Schristos 		} else if ( !isroot ) {
14692cfeba6Schristos 			/*
14792cfeba6Schristos 			 * A bind operation is expected to have
14892cfeba6Schristos 			 * ONE CANDIDATE ONLY!
14992cfeba6Schristos 			 */
15092cfeba6Schristos 			Debug( LDAP_DEBUG_ANY,
15192cfeba6Schristos 				"### %s asyncmeta_back_bind: more than one"
15292cfeba6Schristos 				" candidate selected...\n",
15392cfeba6Schristos 				op->o_log_prefix );
15492cfeba6Schristos 		}
15592cfeba6Schristos 
15692cfeba6Schristos 		if ( isroot ) {
15792cfeba6Schristos 			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
15892cfeba6Schristos 				|| BER_BVISNULL( &mt->mt_idassert_authcDN ) )
15992cfeba6Schristos 			{
16092cfeba6Schristos 				a_metasingleconn_t	*msc = &mc->mc_conns[ i ];
16192cfeba6Schristos 
16292cfeba6Schristos 				if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
16392cfeba6Schristos 					ch_free( msc->msc_bound_ndn.bv_val );
16492cfeba6Schristos 					BER_BVZERO( &msc->msc_bound_ndn );
16592cfeba6Schristos 				}
16692cfeba6Schristos 
16792cfeba6Schristos 				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
16892cfeba6Schristos 					/* destroy sensitive data */
16992cfeba6Schristos 					memset( msc->msc_cred.bv_val, 0,
17092cfeba6Schristos 						msc->msc_cred.bv_len );
17192cfeba6Schristos 					ch_free( msc->msc_cred.bv_val );
17292cfeba6Schristos 					BER_BVZERO( &msc->msc_cred );
17392cfeba6Schristos 				}
17492cfeba6Schristos 
17592cfeba6Schristos 				continue;
17692cfeba6Schristos 			}
17792cfeba6Schristos 
17892cfeba6Schristos 
17992cfeba6Schristos 			(void)asyncmeta_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
18092cfeba6Schristos 			lerr = rs->sr_err;
18192cfeba6Schristos 
18292cfeba6Schristos 		} else {
18392cfeba6Schristos 			lerr = asyncmeta_single_bind( op, rs, mc, i );
18492cfeba6Schristos 		}
18592cfeba6Schristos 
18692cfeba6Schristos 		if ( lerr != LDAP_SUCCESS ) {
18792cfeba6Schristos 			rc = rs->sr_err = lerr;
18892cfeba6Schristos 
18992cfeba6Schristos 			/* FIXME: in some cases (e.g. unavailable)
19092cfeba6Schristos 			 * do not assume it's not candidate; rather
19192cfeba6Schristos 			 * mark this as an error to be eventually
19292cfeba6Schristos 			 * reported to client */
19392cfeba6Schristos 			META_CANDIDATE_CLEAR( &candidates[ i ] );
19492cfeba6Schristos 			break;
19592cfeba6Schristos 		}
19692cfeba6Schristos 	}
19792cfeba6Schristos 
19892cfeba6Schristos 	if ( mc != NULL ) {
19992cfeba6Schristos 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
20092cfeba6Schristos 			a_metasingleconn_t	*msc = &mc->mc_conns[ i ];
20192cfeba6Schristos 			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
20292cfeba6Schristos 				ch_free( msc->msc_bound_ndn.bv_val );
20392cfeba6Schristos 			}
20492cfeba6Schristos 
20592cfeba6Schristos 			if ( !BER_BVISNULL( &msc->msc_cred ) ) {
20692cfeba6Schristos 				/* destroy sensitive data */
20792cfeba6Schristos 				memset( msc->msc_cred.bv_val, 0,
20892cfeba6Schristos 					msc->msc_cred.bv_len );
20992cfeba6Schristos 				ch_free( msc->msc_cred.bv_val );
21092cfeba6Schristos 			}
21192cfeba6Schristos 		}
21292cfeba6Schristos 		asyncmeta_back_conn_free( mc );
21392cfeba6Schristos 	}
21492cfeba6Schristos 
21592cfeba6Schristos 	/*
21692cfeba6Schristos 	 * rc is LDAP_SUCCESS if at least one bind succeeded,
21792cfeba6Schristos 	 * err is the last error that occurred during a bind;
21892cfeba6Schristos 	 * if at least (and at most?) one bind succeeds, fine.
21992cfeba6Schristos 	 */
22092cfeba6Schristos 	if ( rc != LDAP_SUCCESS ) {
22192cfeba6Schristos 
22292cfeba6Schristos 		/*
22392cfeba6Schristos 		 * deal with bind failure ...
22492cfeba6Schristos 		 */
22592cfeba6Schristos 
22692cfeba6Schristos 		/*
22792cfeba6Schristos 		 * no target was found within the naming context,
22892cfeba6Schristos 		 * so bind must fail with invalid credentials
22992cfeba6Schristos 		 */
23092cfeba6Schristos 		if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
23192cfeba6Schristos 			rs->sr_err = LDAP_INVALID_CREDENTIALS;
23292cfeba6Schristos 		} else {
23392cfeba6Schristos 			rs->sr_err = slap_map_api2result( rs );
23492cfeba6Schristos 		}
23592cfeba6Schristos 		send_ldap_result( op, rs );
23692cfeba6Schristos 		return rs->sr_err;
23792cfeba6Schristos 
23892cfeba6Schristos 	}
23992cfeba6Schristos 	return LDAP_SUCCESS;
24092cfeba6Schristos }
24192cfeba6Schristos 
24292cfeba6Schristos static int
asyncmeta_bind_op_result(Operation * op,SlapReply * rs,a_metaconn_t * mc,int candidate,int msgid,ldap_back_send_t sendok,int dolock)24392cfeba6Schristos asyncmeta_bind_op_result(
24492cfeba6Schristos 	Operation		*op,
24592cfeba6Schristos 	SlapReply		*rs,
24692cfeba6Schristos 	a_metaconn_t		*mc,
24792cfeba6Schristos 	int			candidate,
24892cfeba6Schristos 	int			msgid,
24992cfeba6Schristos 	ldap_back_send_t	sendok,
25092cfeba6Schristos 	int			dolock )
25192cfeba6Schristos {
25292cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
25392cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
25492cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
25592cfeba6Schristos 	LDAPMessage		*res;
25692cfeba6Schristos 	struct timeval		tv;
25792cfeba6Schristos 	int			rc;
25892cfeba6Schristos 	int			nretries = mt->mt_nretries;
25992cfeba6Schristos 
26092cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE,
26192cfeba6Schristos 		">>> %s asyncmeta_bind_op_result[%d]\n",
26292cfeba6Schristos 		op->o_log_prefix, candidate );
26392cfeba6Schristos 
26492cfeba6Schristos 	/* make sure this is clean */
26592cfeba6Schristos 	assert( rs->sr_ctrls == NULL );
26692cfeba6Schristos 
26792cfeba6Schristos 	if ( rs->sr_err == LDAP_SUCCESS ) {
26892cfeba6Schristos 		time_t		stoptime = (time_t)(-1),
26992cfeba6Schristos 				timeout;
27092cfeba6Schristos 		int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
27192cfeba6Schristos 				LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
27292cfeba6Schristos 		const char	*timeout_text = "Operation timed out";
27392cfeba6Schristos 		slap_op_t	opidx = slap_req2op( op->o_tag );
27492cfeba6Schristos 
27592cfeba6Schristos 		/* since timeout is not specified, compute and use
27692cfeba6Schristos 		 * the one specific to the ongoing operation */
27792cfeba6Schristos 		if ( opidx == LDAP_REQ_SEARCH ) {
27892cfeba6Schristos 			if ( op->ors_tlimit <= 0 ) {
27992cfeba6Schristos 				timeout = 0;
28092cfeba6Schristos 
28192cfeba6Schristos 			} else {
28292cfeba6Schristos 				timeout = op->ors_tlimit;
28392cfeba6Schristos 				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
28492cfeba6Schristos 				timeout_text = NULL;
28592cfeba6Schristos 			}
28692cfeba6Schristos 
28792cfeba6Schristos 		} else {
28892cfeba6Schristos 			timeout = mt->mt_timeout[ opidx ];
28992cfeba6Schristos 		}
29092cfeba6Schristos 
29192cfeba6Schristos 		/* better than nothing :) */
29292cfeba6Schristos 		if ( timeout == 0 ) {
29392cfeba6Schristos 			if ( mi->mi_idle_timeout ) {
29492cfeba6Schristos 				timeout = mi->mi_idle_timeout;
29592cfeba6Schristos 
29692cfeba6Schristos 			}
29792cfeba6Schristos 		}
29892cfeba6Schristos 
29992cfeba6Schristos 		if ( timeout ) {
30092cfeba6Schristos 			stoptime = op->o_time + timeout;
30192cfeba6Schristos 		}
30292cfeba6Schristos 
30392cfeba6Schristos 		LDAP_BACK_TV_SET( &tv );
30492cfeba6Schristos 
30592cfeba6Schristos 		/*
30692cfeba6Schristos 		 * handle response!!!
30792cfeba6Schristos 		 */
30892cfeba6Schristos retry:;
30992cfeba6Schristos 		rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
31092cfeba6Schristos 		switch ( rc ) {
31192cfeba6Schristos 		case 0:
31292cfeba6Schristos 			if ( nretries != META_RETRY_NEVER
31392cfeba6Schristos 				|| ( timeout && slap_get_time() <= stoptime ) )
31492cfeba6Schristos 			{
31592cfeba6Schristos 				ldap_pvt_thread_yield();
31692cfeba6Schristos 				if ( nretries > 0 ) {
31792cfeba6Schristos 					nretries--;
31892cfeba6Schristos 				}
31992cfeba6Schristos 				tv = mt->mt_bind_timeout;
32092cfeba6Schristos 				goto retry;
32192cfeba6Schristos 			}
32292cfeba6Schristos 
32392cfeba6Schristos 			/* don't let anyone else use this handler,
32492cfeba6Schristos 			 * because there's a pending bind that will not
32592cfeba6Schristos 			 * be acknowledged */
32692cfeba6Schristos 			assert( LDAP_BACK_CONN_BINDING( msc ) );
32792cfeba6Schristos 
32892cfeba6Schristos #ifdef DEBUG_205
32992cfeba6Schristos 			Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
33092cfeba6Schristos 				op->o_log_prefix, candidate, (void *)msc->msc_ld );
33192cfeba6Schristos #endif /* DEBUG_205 */
33292cfeba6Schristos 
33392cfeba6Schristos 			rs->sr_err = timeout_err;
33492cfeba6Schristos 			rs->sr_text = timeout_text;
33592cfeba6Schristos 			break;
33692cfeba6Schristos 
33792cfeba6Schristos 		case -1:
33892cfeba6Schristos 			ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
33992cfeba6Schristos 				&rs->sr_err );
34092cfeba6Schristos 
34192cfeba6Schristos 			Debug( LDAP_DEBUG_ANY,
34292cfeba6Schristos 			      "### %s asyncmeta_bind_op_result[%d]: err=%d (%s) nretries=%d.\n",
34392cfeba6Schristos 			      op->o_log_prefix, candidate, rs->sr_err,
34492cfeba6Schristos 			      ldap_err2string(rs->sr_err), nretries );
34592cfeba6Schristos 			break;
34692cfeba6Schristos 
34792cfeba6Schristos 		default:
34892cfeba6Schristos 			/* only touch when activity actually took place... */
34992cfeba6Schristos 			if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
35092cfeba6Schristos 				msc->msc_time = op->o_time;
35192cfeba6Schristos 			}
35292cfeba6Schristos 
35392cfeba6Schristos 			/* FIXME: matched? referrals? response controls? */
35492cfeba6Schristos 			rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
35592cfeba6Schristos 					NULL, NULL, NULL, NULL, 1 );
35692cfeba6Schristos 			if ( rc != LDAP_SUCCESS ) {
35792cfeba6Schristos 				rs->sr_err = rc;
35892cfeba6Schristos 			}
35992cfeba6Schristos 			rs->sr_err = slap_map_api2result( rs );
36092cfeba6Schristos 			break;
36192cfeba6Schristos 		}
36292cfeba6Schristos 	}
36392cfeba6Schristos 
36492cfeba6Schristos 	rs->sr_err = slap_map_api2result( rs );
36592cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE,
36692cfeba6Schristos 		"<<< %s asyncmeta_bind_op_result[%d] err=%d\n",
36792cfeba6Schristos 		op->o_log_prefix, candidate, rs->sr_err );
36892cfeba6Schristos 
36992cfeba6Schristos 	return rs->sr_err;
37092cfeba6Schristos }
37192cfeba6Schristos 
37292cfeba6Schristos /*
37392cfeba6Schristos  * asyncmeta_single_bind
37492cfeba6Schristos  *
37592cfeba6Schristos  * attempts to perform a bind with creds
37692cfeba6Schristos  */
37792cfeba6Schristos static int
asyncmeta_single_bind(Operation * op,SlapReply * rs,a_metaconn_t * mc,int candidate)37892cfeba6Schristos asyncmeta_single_bind(
37992cfeba6Schristos 	Operation		*op,
38092cfeba6Schristos 	SlapReply		*rs,
38192cfeba6Schristos 	a_metaconn_t		*mc,
38292cfeba6Schristos 	int			candidate )
38392cfeba6Schristos {
38492cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
38592cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
38692cfeba6Schristos 	struct berval		mdn = BER_BVNULL;
38792cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
38892cfeba6Schristos 	int			msgid;
38992cfeba6Schristos 	a_dncookie		dc;
39092cfeba6Schristos 	struct berval		save_o_dn;
39192cfeba6Schristos 	int			save_o_do_not_cache;
39292cfeba6Schristos 	LDAPControl		**ctrls = NULL;
39392cfeba6Schristos 
39492cfeba6Schristos 	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
39592cfeba6Schristos 		ch_free( msc->msc_bound_ndn.bv_val );
39692cfeba6Schristos 		BER_BVZERO( &msc->msc_bound_ndn );
39792cfeba6Schristos 	}
39892cfeba6Schristos 
39992cfeba6Schristos 	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
40092cfeba6Schristos 		/* destroy sensitive data */
40192cfeba6Schristos 		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
40292cfeba6Schristos 		ch_free( msc->msc_cred.bv_val );
40392cfeba6Schristos 		BER_BVZERO( &msc->msc_cred );
40492cfeba6Schristos 	}
40592cfeba6Schristos 
40692cfeba6Schristos 	/*
40792cfeba6Schristos 	 * Rewrite the bind dn if needed
40892cfeba6Schristos 	 */
40992cfeba6Schristos 	dc.op = op;
41092cfeba6Schristos 	dc.target = mt;
41192cfeba6Schristos 	dc.memctx = op->o_tmpmemctx;
41292cfeba6Schristos 	dc.to_from = MASSAGE_REQ;
41392cfeba6Schristos 
41492cfeba6Schristos 	asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
41592cfeba6Schristos 
41692cfeba6Schristos 	/* don't add proxyAuthz; set the bindDN */
41792cfeba6Schristos 	save_o_dn = op->o_dn;
41892cfeba6Schristos 	save_o_do_not_cache = op->o_do_not_cache;
41992cfeba6Schristos 	op->o_do_not_cache = 1;
42092cfeba6Schristos 	op->o_dn = op->o_req_dn;
42192cfeba6Schristos 
42292cfeba6Schristos 	ctrls = op->o_ctrls;
42392cfeba6Schristos 	rs->sr_err = asyncmeta_controls_add( op, rs, mc, candidate, be_isroot(op), &ctrls );
42492cfeba6Schristos 	op->o_dn = save_o_dn;
42592cfeba6Schristos 	op->o_do_not_cache = save_o_do_not_cache;
42692cfeba6Schristos 	if ( rs->sr_err != LDAP_SUCCESS ) {
42792cfeba6Schristos 		goto return_results;
42892cfeba6Schristos 	}
42992cfeba6Schristos 
43092cfeba6Schristos 	/* FIXME: this fixes the bind problem right now; we need
43192cfeba6Schristos 	 * to use the asynchronous version to get the "matched"
43292cfeba6Schristos 	 * and more in case of failure ... */
43392cfeba6Schristos 	/* FIXME: should we check if at least some of the op->o_ctrls
43492cfeba6Schristos 	 * can/should be passed? */
43592cfeba6Schristos 	for (;;) {
43692cfeba6Schristos 		rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
43792cfeba6Schristos 			LDAP_SASL_SIMPLE, &op->orb_cred,
43892cfeba6Schristos 			ctrls, NULL, &msgid );
43992cfeba6Schristos 		if ( rs->sr_err != LDAP_X_CONNECTING ) {
44092cfeba6Schristos 			break;
44192cfeba6Schristos 		}
44292cfeba6Schristos 		ldap_pvt_thread_yield();
44392cfeba6Schristos 	}
44492cfeba6Schristos 
44592cfeba6Schristos 	mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
44692cfeba6Schristos 
44792cfeba6Schristos 	asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
44892cfeba6Schristos 	if ( rs->sr_err != LDAP_SUCCESS ) {
44992cfeba6Schristos 		goto return_results;
45092cfeba6Schristos 	}
45192cfeba6Schristos 
45292cfeba6Schristos 	/* If defined, proxyAuthz will be used also when
45392cfeba6Schristos 	 * back-ldap is the authorizing backend; for this
45492cfeba6Schristos 	 * purpose, a successful bind is followed by a
45592cfeba6Schristos 	 * bind with the configured identity assertion */
45692cfeba6Schristos 	/* NOTE: use with care */
45792cfeba6Schristos 	if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
45892cfeba6Schristos 		asyncmeta_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
45992cfeba6Schristos 		if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
46092cfeba6Schristos 			goto return_results;
46192cfeba6Schristos 		}
46292cfeba6Schristos 		goto cache_refresh;
46392cfeba6Schristos 	}
46492cfeba6Schristos 
46592cfeba6Schristos 	ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
46692cfeba6Schristos 	LDAP_BACK_CONN_ISBOUND_SET( msc );
46792cfeba6Schristos 	mc->mc_authz_target = candidate;
46892cfeba6Schristos 
46992cfeba6Schristos 	if ( META_BACK_TGT_SAVECRED( mt ) ) {
47092cfeba6Schristos 		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
47192cfeba6Schristos 			memset( msc->msc_cred.bv_val, 0,
47292cfeba6Schristos 				msc->msc_cred.bv_len );
47392cfeba6Schristos 		}
47492cfeba6Schristos 		ber_bvreplace( &msc->msc_cred, &op->orb_cred );
47592cfeba6Schristos 		ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
47692cfeba6Schristos 	}
47792cfeba6Schristos 
47892cfeba6Schristos cache_refresh:;
47992cfeba6Schristos 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
48092cfeba6Schristos 			&& !BER_BVISEMPTY( &op->o_req_ndn ) )
48192cfeba6Schristos 	{
48292cfeba6Schristos 		( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
48392cfeba6Schristos 				&op->o_req_ndn, candidate );
48492cfeba6Schristos 	}
48592cfeba6Schristos 
48692cfeba6Schristos return_results:;
48792cfeba6Schristos 	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
48892cfeba6Schristos 		op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
48992cfeba6Schristos 	}
49092cfeba6Schristos 
49192cfeba6Schristos 	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
49292cfeba6Schristos 		asyncmeta_quarantine( op, mi, rs, candidate );
49392cfeba6Schristos 	}
49492cfeba6Schristos 	ldap_unbind_ext( msc->msc_ld, NULL, NULL );
49592cfeba6Schristos 	msc->msc_ld = NULL;
49692cfeba6Schristos 	ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
49792cfeba6Schristos 	msc->msc_ldr = NULL;
49892cfeba6Schristos 	return rs->sr_err;
49992cfeba6Schristos }
50092cfeba6Schristos 
50192cfeba6Schristos 
50292cfeba6Schristos /*
50392cfeba6Schristos  * asyncmeta_back_default_rebind
50492cfeba6Schristos  *
50592cfeba6Schristos  * This is a callback used for chasing referrals using the same
50692cfeba6Schristos  * credentials as the original user on this session.
50792cfeba6Schristos  */
50892cfeba6Schristos int
asyncmeta_back_default_rebind(LDAP * ld,LDAP_CONST char * url,ber_tag_t request,ber_int_t msgid,void * params)50992cfeba6Schristos asyncmeta_back_default_rebind(
51092cfeba6Schristos 	LDAP			*ld,
51192cfeba6Schristos 	LDAP_CONST char		*url,
51292cfeba6Schristos 	ber_tag_t		request,
51392cfeba6Schristos 	ber_int_t		msgid,
51492cfeba6Schristos 	void			*params )
51592cfeba6Schristos {
51692cfeba6Schristos 	a_metasingleconn_t	*msc = ( a_metasingleconn_t * )params;
51792cfeba6Schristos 
51892cfeba6Schristos 	return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
51992cfeba6Schristos 			LDAP_SASL_SIMPLE, &msc->msc_cred,
52092cfeba6Schristos 			NULL, NULL, NULL );
52192cfeba6Schristos }
52292cfeba6Schristos 
52392cfeba6Schristos /*
52492cfeba6Schristos  * meta_back_default_urllist
52592cfeba6Schristos  *
52692cfeba6Schristos  * This is a callback used for mucking with the urllist
52792cfeba6Schristos  */
52892cfeba6Schristos int
asyncmeta_back_default_urllist(LDAP * ld,LDAPURLDesc ** urllist,LDAPURLDesc ** url,void * params)52992cfeba6Schristos asyncmeta_back_default_urllist(
53092cfeba6Schristos 	LDAP		*ld,
53192cfeba6Schristos 	LDAPURLDesc	**urllist,
53292cfeba6Schristos 	LDAPURLDesc	**url,
53392cfeba6Schristos 	void		*params )
53492cfeba6Schristos {
53592cfeba6Schristos 	a_metatarget_t	*mt = (a_metatarget_t *)params;
53692cfeba6Schristos 	LDAPURLDesc	**urltail;
53792cfeba6Schristos 
53892cfeba6Schristos 	if ( urllist == url ) {
53992cfeba6Schristos 		return LDAP_SUCCESS;
54092cfeba6Schristos 	}
54192cfeba6Schristos 
54292cfeba6Schristos 	for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
54392cfeba6Schristos 		/* count */ ;
54492cfeba6Schristos 
54592cfeba6Schristos 	*urltail = *urllist;
54692cfeba6Schristos 	*urllist = *url;
54792cfeba6Schristos 	*url = NULL;
54892cfeba6Schristos 
54992cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
55092cfeba6Schristos 	if ( mt->mt_uri ) {
55192cfeba6Schristos 		ch_free( mt->mt_uri );
55292cfeba6Schristos 	}
55392cfeba6Schristos 
55492cfeba6Schristos 	ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
55592cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
55692cfeba6Schristos 
55792cfeba6Schristos 	return LDAP_SUCCESS;
55892cfeba6Schristos }
55992cfeba6Schristos 
56092cfeba6Schristos int
asyncmeta_back_cancel(a_metaconn_t * mc,Operation * op,ber_int_t msgid,int candidate)56192cfeba6Schristos asyncmeta_back_cancel(
56292cfeba6Schristos 	a_metaconn_t		*mc,
56392cfeba6Schristos 	Operation		*op,
56492cfeba6Schristos 	ber_int_t		msgid,
56592cfeba6Schristos 	int			candidate )
56692cfeba6Schristos {
56792cfeba6Schristos 
56892cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
56992cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
57092cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
57192cfeba6Schristos 
57292cfeba6Schristos 	int			rc = LDAP_OTHER;
57392cfeba6Schristos 	struct timeval tv = { 0, 0 };
57492cfeba6Schristos 	ber_socket_t s;
57592cfeba6Schristos 
57692cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n",
57792cfeba6Schristos 		op->o_log_prefix, candidate, msgid );
57892cfeba6Schristos 
57992cfeba6Schristos 	if (!( LDAP_BACK_CONN_ISBOUND( msc )
58092cfeba6Schristos 	       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
58192cfeba6Schristos 		Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n already reset",
58292cfeba6Schristos 		op->o_log_prefix, candidate, msgid );
58392cfeba6Schristos 		return LDAP_SUCCESS;
58492cfeba6Schristos 	}
58592cfeba6Schristos 
58692cfeba6Schristos 	ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
58792cfeba6Schristos 	if (s < 0) {
58892cfeba6Schristos 		return rc;
58992cfeba6Schristos 	}
59092cfeba6Schristos 	rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
59192cfeba6Schristos 	if (rc < 0) {
59292cfeba6Schristos 		rc = LDAP_SERVER_DOWN;
59392cfeba6Schristos 		return rc;
59492cfeba6Schristos 	}
59592cfeba6Schristos 	/* default behavior */
59692cfeba6Schristos 	if ( META_BACK_TGT_ABANDON( mt ) ) {
59792cfeba6Schristos 		rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
59892cfeba6Schristos 
59992cfeba6Schristos 	} else if ( META_BACK_TGT_IGNORE( mt ) ) {
60092cfeba6Schristos 		rc = ldap_pvt_discard( msc->msc_ld, msgid );
60192cfeba6Schristos 
60292cfeba6Schristos 	} else if ( META_BACK_TGT_CANCEL( mt ) ) {
60392cfeba6Schristos 		rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
60492cfeba6Schristos 
60592cfeba6Schristos 	} else {
60692cfeba6Schristos 		assert( 0 );
60792cfeba6Schristos 	}
60892cfeba6Schristos 
60992cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_cancel[%d] err=%d\n",
61092cfeba6Schristos 		op->o_log_prefix, candidate, rc );
61192cfeba6Schristos 
61292cfeba6Schristos 	return rc;
61392cfeba6Schristos }
61492cfeba6Schristos 
61592cfeba6Schristos 
61692cfeba6Schristos 
61792cfeba6Schristos /*
61892cfeba6Schristos  * asyncmeta_back_proxy_authz_cred()
61992cfeba6Schristos  *
62092cfeba6Schristos  * prepares credentials & method for meta_back_proxy_authz_bind();
62192cfeba6Schristos  * or, if method is SASL, performs the SASL bind directly.
62292cfeba6Schristos  */
62392cfeba6Schristos int
asyncmeta_back_proxy_authz_cred(a_metaconn_t * mc,int candidate,Operation * op,SlapReply * rs,ldap_back_send_t sendok,struct berval * binddn,struct berval * bindcred,int * method)62492cfeba6Schristos asyncmeta_back_proxy_authz_cred(
62592cfeba6Schristos 	a_metaconn_t		*mc,
62692cfeba6Schristos 	int			candidate,
62792cfeba6Schristos 	Operation		*op,
62892cfeba6Schristos 	SlapReply		*rs,
62992cfeba6Schristos 	ldap_back_send_t	sendok,
63092cfeba6Schristos 	struct berval		*binddn,
63192cfeba6Schristos 	struct berval		*bindcred,
63292cfeba6Schristos 	int			*method )
63392cfeba6Schristos {
63492cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
63592cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
63692cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
63792cfeba6Schristos 	struct berval		ndn;
63892cfeba6Schristos 	int			dobind = 0;
63992cfeba6Schristos 	struct timeval old_tv = {0, 0};
64092cfeba6Schristos 	struct timeval bind_tv = { mt->mt_timeout[ SLAP_OP_BIND ], 0};
64192cfeba6Schristos 	/* don't proxyAuthz if protocol is not LDAPv3 */
64292cfeba6Schristos 	switch ( mt->mt_version ) {
64392cfeba6Schristos 	case LDAP_VERSION3:
64492cfeba6Schristos 		break;
64592cfeba6Schristos 
64692cfeba6Schristos 	case 0:
64792cfeba6Schristos 		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
64892cfeba6Schristos 			break;
64992cfeba6Schristos 		}
65092cfeba6Schristos 		/* fall thru */
65192cfeba6Schristos 
65292cfeba6Schristos 	default:
65392cfeba6Schristos 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
65492cfeba6Schristos 		if ( sendok & LDAP_BACK_SENDERR ) {
65592cfeba6Schristos 			send_ldap_result( op, rs );
65692cfeba6Schristos 		}
65792cfeba6Schristos 		LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
65892cfeba6Schristos 		goto done;
65992cfeba6Schristos 	}
66092cfeba6Schristos 
66192cfeba6Schristos 	if ( op->o_tag == LDAP_REQ_BIND ) {
66292cfeba6Schristos 		ndn = op->o_req_ndn;
66392cfeba6Schristos 
66492cfeba6Schristos 	} else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
66592cfeba6Schristos 		ndn = op->o_conn->c_ndn;
66692cfeba6Schristos 
66792cfeba6Schristos 	} else {
66892cfeba6Schristos 		ndn = op->o_ndn;
66992cfeba6Schristos 	}
67092cfeba6Schristos 	rs->sr_err = LDAP_SUCCESS;
67192cfeba6Schristos 
67292cfeba6Schristos 	/*
67392cfeba6Schristos 	 * FIXME: we need to let clients use proxyAuthz
67492cfeba6Schristos 	 * otherwise we cannot do symmetric pools of servers;
67592cfeba6Schristos 	 * we have to live with the fact that a user can
67692cfeba6Schristos 	 * authorize itself as any ID that is allowed
67792cfeba6Schristos 	 * by the authzTo directive of the "proxyauthzdn".
67892cfeba6Schristos 	 */
67992cfeba6Schristos 	/*
68092cfeba6Schristos 	 * NOTE: current Proxy Authorization specification
68192cfeba6Schristos 	 * and implementation do not allow proxy authorization
68292cfeba6Schristos 	 * control to be provided with Bind requests
68392cfeba6Schristos 	 */
68492cfeba6Schristos 	/*
68592cfeba6Schristos 	 * if no bind took place yet, but the connection is bound
68692cfeba6Schristos 	 * and the "proxyauthzdn" is set, then bind as
68792cfeba6Schristos 	 * "proxyauthzdn" and explicitly add the proxyAuthz
68892cfeba6Schristos 	 * control to every operation with the dn bound
68992cfeba6Schristos 	 * to the connection as control value.
69092cfeba6Schristos 	 */
69192cfeba6Schristos 
69292cfeba6Schristos 	/* bind as proxyauthzdn only if no idassert mode
69392cfeba6Schristos 	 * is requested, or if the client's identity
69492cfeba6Schristos 	 * is authorized */
69592cfeba6Schristos 	switch ( mt->mt_idassert_mode ) {
69692cfeba6Schristos 	case LDAP_BACK_IDASSERT_LEGACY:
69792cfeba6Schristos 		if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
69892cfeba6Schristos 			if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
69992cfeba6Schristos 			{
70092cfeba6Schristos 				*binddn = mt->mt_idassert_authcDN;
70192cfeba6Schristos 				*bindcred = mt->mt_idassert_passwd;
70292cfeba6Schristos 				dobind = 1;
70392cfeba6Schristos 			}
70492cfeba6Schristos 		}
70592cfeba6Schristos 		break;
70692cfeba6Schristos 
70792cfeba6Schristos 	default:
70892cfeba6Schristos 		/* NOTE: rootdn can always idassert */
70992cfeba6Schristos 		if ( BER_BVISNULL( &ndn )
71092cfeba6Schristos 			&& mt->mt_idassert_authz == NULL
71192cfeba6Schristos 			&& !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
71292cfeba6Schristos 		{
71392cfeba6Schristos 			if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
71492cfeba6Schristos 				rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
71592cfeba6Schristos 				if ( sendok & LDAP_BACK_SENDERR ) {
71692cfeba6Schristos 					send_ldap_result( op, rs );
71792cfeba6Schristos 				}
71892cfeba6Schristos 				LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
71992cfeba6Schristos 				goto done;
72092cfeba6Schristos 
72192cfeba6Schristos 			}
72292cfeba6Schristos 
72392cfeba6Schristos 			rs->sr_err = LDAP_SUCCESS;
72492cfeba6Schristos 			*binddn = slap_empty_bv;
72592cfeba6Schristos 			*bindcred = slap_empty_bv;
72692cfeba6Schristos 			break;
72792cfeba6Schristos 
72892cfeba6Schristos 		} else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
72992cfeba6Schristos 			struct berval authcDN;
73092cfeba6Schristos 
73192cfeba6Schristos 			if ( BER_BVISNULL( &ndn ) ) {
73292cfeba6Schristos 				authcDN = slap_empty_bv;
73392cfeba6Schristos 
73492cfeba6Schristos 			} else {
73592cfeba6Schristos 				authcDN = ndn;
73692cfeba6Schristos 			}
73792cfeba6Schristos 			rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
73892cfeba6Schristos 					&authcDN, &authcDN );
73992cfeba6Schristos 			if ( rs->sr_err != LDAP_SUCCESS ) {
74092cfeba6Schristos 				if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
74192cfeba6Schristos 					if ( sendok & LDAP_BACK_SENDERR ) {
74292cfeba6Schristos 						send_ldap_result( op, rs );
74392cfeba6Schristos 					}
74492cfeba6Schristos 					LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
74592cfeba6Schristos 					goto done;
74692cfeba6Schristos 				}
74792cfeba6Schristos 
74892cfeba6Schristos 				rs->sr_err = LDAP_SUCCESS;
74992cfeba6Schristos 				*binddn = slap_empty_bv;
75092cfeba6Schristos 				*bindcred = slap_empty_bv;
75192cfeba6Schristos 				break;
75292cfeba6Schristos 			}
75392cfeba6Schristos 		}
75492cfeba6Schristos 
75592cfeba6Schristos 		*binddn = mt->mt_idassert_authcDN;
75692cfeba6Schristos 		*bindcred = mt->mt_idassert_passwd;
75792cfeba6Schristos 		dobind = 1;
75892cfeba6Schristos 		break;
75992cfeba6Schristos 	}
76092cfeba6Schristos 
76192cfeba6Schristos 	if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
76292cfeba6Schristos #ifdef HAVE_CYRUS_SASL
76392cfeba6Schristos 		void		*defaults = NULL;
76492cfeba6Schristos 		struct berval	authzID = BER_BVNULL;
76592cfeba6Schristos 		int		freeauthz = 0;
76692cfeba6Schristos 
76792cfeba6Schristos 		/* if SASL supports native authz, prepare for it */
76892cfeba6Schristos 		if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
76992cfeba6Schristos 				( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
77092cfeba6Schristos 		{
77192cfeba6Schristos 			switch ( mt->mt_idassert_mode ) {
77292cfeba6Schristos 			case LDAP_BACK_IDASSERT_OTHERID:
77392cfeba6Schristos 			case LDAP_BACK_IDASSERT_OTHERDN:
77492cfeba6Schristos 				authzID = mt->mt_idassert_authzID;
77592cfeba6Schristos 				break;
77692cfeba6Schristos 
77792cfeba6Schristos 			case LDAP_BACK_IDASSERT_ANONYMOUS:
77892cfeba6Schristos 				BER_BVSTR( &authzID, "dn:" );
77992cfeba6Schristos 				break;
78092cfeba6Schristos 
78192cfeba6Schristos 			case LDAP_BACK_IDASSERT_SELF:
78292cfeba6Schristos 				if ( BER_BVISNULL( &ndn ) ) {
78392cfeba6Schristos 					/* connection is not authc'd, so don't idassert */
78492cfeba6Schristos 					BER_BVSTR( &authzID, "dn:" );
78592cfeba6Schristos 					break;
78692cfeba6Schristos 				}
78792cfeba6Schristos 				authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
78892cfeba6Schristos 				authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
78992cfeba6Schristos 				AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
79092cfeba6Schristos 				AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
79192cfeba6Schristos 						ndn.bv_val, ndn.bv_len + 1 );
79292cfeba6Schristos 				freeauthz = 1;
79392cfeba6Schristos 				break;
79492cfeba6Schristos 
79592cfeba6Schristos 			default:
79692cfeba6Schristos 				break;
79792cfeba6Schristos 			}
79892cfeba6Schristos 		}
79992cfeba6Schristos 
80092cfeba6Schristos 		if ( mt->mt_idassert_secprops != NULL ) {
80192cfeba6Schristos 			rs->sr_err = ldap_set_option( msc->msc_ld,
80292cfeba6Schristos 				LDAP_OPT_X_SASL_SECPROPS,
80392cfeba6Schristos 				(void *)mt->mt_idassert_secprops );
80492cfeba6Schristos 
80592cfeba6Schristos 			if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
80692cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
80792cfeba6Schristos 				if ( sendok & LDAP_BACK_SENDERR ) {
80892cfeba6Schristos 					send_ldap_result( op, rs );
80992cfeba6Schristos 				}
81092cfeba6Schristos 				LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
81192cfeba6Schristos 				goto done;
81292cfeba6Schristos 			}
81392cfeba6Schristos 		}
81492cfeba6Schristos 
81592cfeba6Schristos 		ldap_get_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv);
81692cfeba6Schristos 
81792cfeba6Schristos 		if (mt->mt_timeout[ SLAP_OP_BIND ] > 0 ) {
81892cfeba6Schristos 			rs->sr_err = ldap_set_option( msc->msc_ld,
81992cfeba6Schristos 				LDAP_OPT_TIMEOUT,
82092cfeba6Schristos 				(void *)&bind_tv );
82192cfeba6Schristos 
82292cfeba6Schristos 			if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
82392cfeba6Schristos 				rs->sr_err = LDAP_OTHER;
82492cfeba6Schristos 				if ( sendok & LDAP_BACK_SENDERR ) {
82592cfeba6Schristos 					send_ldap_result( op, rs );
82692cfeba6Schristos 				}
82792cfeba6Schristos 				LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
82892cfeba6Schristos 				goto done;
82992cfeba6Schristos 			}
83092cfeba6Schristos 		}
83192cfeba6Schristos 		defaults = lutil_sasl_defaults( msc->msc_ld,
83292cfeba6Schristos 				mt->mt_idassert_sasl_mech.bv_val,
83392cfeba6Schristos 				mt->mt_idassert_sasl_realm.bv_val,
83492cfeba6Schristos 				mt->mt_idassert_authcID.bv_val,
83592cfeba6Schristos 				mt->mt_idassert_passwd.bv_val,
83692cfeba6Schristos 				authzID.bv_val );
83792cfeba6Schristos 		if ( defaults == NULL ) {
83892cfeba6Schristos 			rs->sr_err = LDAP_OTHER;
83992cfeba6Schristos 			LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
84092cfeba6Schristos 			if ( sendok & LDAP_BACK_SENDERR ) {
84192cfeba6Schristos 				send_ldap_result( op, rs );
84292cfeba6Schristos 			}
84392cfeba6Schristos 			goto done;
84492cfeba6Schristos 		}
84592cfeba6Schristos 
84692cfeba6Schristos 		rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
84792cfeba6Schristos 				mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
84892cfeba6Schristos 				LDAP_SASL_QUIET, lutil_sasl_interact,
84992cfeba6Schristos 				defaults );
85092cfeba6Schristos 
85192cfeba6Schristos 		/* restore the old timeout just in case */
85292cfeba6Schristos 		ldap_set_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv );
85392cfeba6Schristos 
85492cfeba6Schristos 		rs->sr_err = slap_map_api2result( rs );
85592cfeba6Schristos 		if ( rs->sr_err != LDAP_SUCCESS ) {
85692cfeba6Schristos 			if ( LogTest( asyncmeta_debug ) ) {
85792cfeba6Schristos 				char	time_buf[ SLAP_TEXT_BUFLEN ];
85892cfeba6Schristos 				asyncmeta_get_timestamp(time_buf);
85992cfeba6Schristos 				Debug( asyncmeta_debug, "[%s] asyncmeta_back_proxy_authz_cred failed bind msc: %p\n",
86092cfeba6Schristos 				      time_buf, msc );
86192cfeba6Schristos 			}
86292cfeba6Schristos 			LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
86392cfeba6Schristos 			if ( sendok & LDAP_BACK_SENDERR ) {
86492cfeba6Schristos 				send_ldap_result( op, rs );
86592cfeba6Schristos 			}
86692cfeba6Schristos 
86792cfeba6Schristos 		} else {
86892cfeba6Schristos 			LDAP_BACK_CONN_ISBOUND_SET( msc );
86992cfeba6Schristos 		}
87092cfeba6Schristos 
87192cfeba6Schristos 		lutil_sasl_freedefs( defaults );
87292cfeba6Schristos 		if ( freeauthz ) {
87392cfeba6Schristos 			slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
87492cfeba6Schristos 		}
87592cfeba6Schristos 
87692cfeba6Schristos 		goto done;
87792cfeba6Schristos #endif /* HAVE_CYRUS_SASL */
87892cfeba6Schristos 	}
87992cfeba6Schristos 
88092cfeba6Schristos 	*method = mt->mt_idassert_authmethod;
88192cfeba6Schristos 	switch ( mt->mt_idassert_authmethod ) {
88292cfeba6Schristos 	case LDAP_AUTH_NONE:
88392cfeba6Schristos 		BER_BVSTR( binddn, "" );
88492cfeba6Schristos 		BER_BVSTR( bindcred, "" );
88592cfeba6Schristos 		/* fallthru */
88692cfeba6Schristos 
88792cfeba6Schristos 	case LDAP_AUTH_SIMPLE:
88892cfeba6Schristos 		break;
88992cfeba6Schristos 
89092cfeba6Schristos 	default:
89192cfeba6Schristos 		/* unsupported! */
89292cfeba6Schristos 		LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
89392cfeba6Schristos 		rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
89492cfeba6Schristos 		if ( sendok & LDAP_BACK_SENDERR ) {
89592cfeba6Schristos 			send_ldap_result( op, rs );
89692cfeba6Schristos 		}
89792cfeba6Schristos 		break;
89892cfeba6Schristos 	}
89992cfeba6Schristos 
90092cfeba6Schristos done:;
90192cfeba6Schristos 
90292cfeba6Schristos 	if ( !BER_BVISEMPTY( binddn ) ) {
90392cfeba6Schristos 		LDAP_BACK_CONN_ISIDASSERT_SET( msc );
90492cfeba6Schristos 	}
90592cfeba6Schristos 
90692cfeba6Schristos 	return rs->sr_err;
90792cfeba6Schristos }
90892cfeba6Schristos 
90992cfeba6Schristos static int
asyncmeta_proxy_authz_bind(a_metaconn_t * mc,int candidate,Operation * op,SlapReply * rs,ldap_back_send_t sendok,int dolock)91092cfeba6Schristos asyncmeta_proxy_authz_bind(
91192cfeba6Schristos 	a_metaconn_t *mc,
91292cfeba6Schristos 	int candidate,
91392cfeba6Schristos 	Operation *op,
91492cfeba6Schristos 	SlapReply *rs,
91592cfeba6Schristos 	ldap_back_send_t sendok,
91692cfeba6Schristos 	int dolock )
91792cfeba6Schristos {
91892cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
91992cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
92092cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
92192cfeba6Schristos 	struct berval		binddn = BER_BVC( "" ),
92292cfeba6Schristos 				cred = BER_BVC( "" );
92392cfeba6Schristos 	int			method = LDAP_AUTH_NONE,
92492cfeba6Schristos 				rc;
92592cfeba6Schristos 
92692cfeba6Schristos 	rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
92792cfeba6Schristos 	if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
92892cfeba6Schristos 		int	msgid;
92992cfeba6Schristos 
93092cfeba6Schristos 		switch ( method ) {
93192cfeba6Schristos 		case LDAP_AUTH_NONE:
93292cfeba6Schristos 		case LDAP_AUTH_SIMPLE:
93392cfeba6Schristos 			for (;;) {
93492cfeba6Schristos 				rs->sr_err = ldap_sasl_bind( msc->msc_ld,
93592cfeba6Schristos 					binddn.bv_val, LDAP_SASL_SIMPLE,
93692cfeba6Schristos 					&cred, NULL, NULL, &msgid );
93792cfeba6Schristos 				if ( rs->sr_err != LDAP_X_CONNECTING ) {
93892cfeba6Schristos 					break;
93992cfeba6Schristos 				}
94092cfeba6Schristos 				ldap_pvt_thread_yield();
94192cfeba6Schristos 			}
94292cfeba6Schristos 
94392cfeba6Schristos 			rc = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
94492cfeba6Schristos 			if ( rc == LDAP_SUCCESS ) {
94592cfeba6Schristos 				/* set rebind stuff in case of successful proxyAuthz bind,
94692cfeba6Schristos 				 * so that referral chasing is attempted using the right
94792cfeba6Schristos 				 * identity */
94892cfeba6Schristos 				LDAP_BACK_CONN_ISBOUND_SET( msc );
94992cfeba6Schristos 				ber_bvreplace( &msc->msc_bound_ndn, &binddn );
95092cfeba6Schristos 
95192cfeba6Schristos 				if ( META_BACK_TGT_SAVECRED( mt ) ) {
95292cfeba6Schristos 					if ( !BER_BVISNULL( &msc->msc_cred ) ) {
95392cfeba6Schristos 						memset( msc->msc_cred.bv_val, 0,
95492cfeba6Schristos 							msc->msc_cred.bv_len );
95592cfeba6Schristos 					}
95692cfeba6Schristos 					ber_bvreplace( &msc->msc_cred, &cred );
95792cfeba6Schristos 					ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
95892cfeba6Schristos 				}
95992cfeba6Schristos 			}
96092cfeba6Schristos 			break;
96192cfeba6Schristos 
96292cfeba6Schristos 		default:
96392cfeba6Schristos 			assert( 0 );
96492cfeba6Schristos 			break;
96592cfeba6Schristos 		}
96692cfeba6Schristos 	}
96792cfeba6Schristos 
96892cfeba6Schristos 	return LDAP_BACK_CONN_ISBOUND( msc );
96992cfeba6Schristos }
97092cfeba6Schristos 
97192cfeba6Schristos 
97292cfeba6Schristos static int
asyncmeta_back_proxy_authz_ctrl(Operation * op,SlapReply * rs,struct berval * bound_ndn,int version,int isroot,slap_idassert_t * si,LDAPControl * ctrl)97392cfeba6Schristos asyncmeta_back_proxy_authz_ctrl(Operation *op,
97492cfeba6Schristos 				SlapReply *rs,
97592cfeba6Schristos 				struct berval	*bound_ndn,
97692cfeba6Schristos 				int		version,
97792cfeba6Schristos 				int             isroot,
97892cfeba6Schristos 				slap_idassert_t	*si,
97992cfeba6Schristos 				LDAPControl	*ctrl )
98092cfeba6Schristos {
98192cfeba6Schristos 	slap_idassert_mode_t	mode;
98292cfeba6Schristos 	struct berval		assertedID,
98392cfeba6Schristos 				ndn;
98492cfeba6Schristos 
98592cfeba6Schristos 	rs->sr_err = SLAP_CB_CONTINUE;
98692cfeba6Schristos 
98792cfeba6Schristos 	/* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
98892cfeba6Schristos 	 * but if it is not set this test fails.  We need a different
98992cfeba6Schristos 	 * means to detect if idassert is enabled */
99092cfeba6Schristos 	if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
99192cfeba6Schristos 		&& ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
99292cfeba6Schristos 		&& BER_BVISNULL( &si->si_bc.sb_saslmech ) )
99392cfeba6Schristos 	{
99492cfeba6Schristos 		goto done;
99592cfeba6Schristos 	}
99692cfeba6Schristos 
99792cfeba6Schristos 	if ( !op->o_conn || op->o_do_not_cache || ( isroot ) ) {
99892cfeba6Schristos 		goto done;
99992cfeba6Schristos 	}
100092cfeba6Schristos 
100192cfeba6Schristos 	if ( op->o_tag == LDAP_REQ_BIND ) {
100292cfeba6Schristos 		ndn = op->o_req_ndn;
100392cfeba6Schristos 
100492cfeba6Schristos #if 0
100592cfeba6Schristos 	} else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
100692cfeba6Schristos 		ndn = op->o_conn->c_ndn;
100792cfeba6Schristos #endif
100892cfeba6Schristos 	} else {
100992cfeba6Schristos 		ndn = op->o_ndn;
101092cfeba6Schristos 	}
101192cfeba6Schristos 
101292cfeba6Schristos 	if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
101392cfeba6Schristos 		if ( op->o_proxy_authz ) {
101492cfeba6Schristos 			/*
101592cfeba6Schristos 			 * FIXME: we do not want to perform proxyAuthz
101692cfeba6Schristos 			 * on behalf of the client, because this would
101792cfeba6Schristos 			 * be performed with "proxyauthzdn" privileges.
101892cfeba6Schristos 			 *
101992cfeba6Schristos 			 * This might actually be too strict, since
102092cfeba6Schristos 			 * the "proxyauthzdn" authzTo, and each entry's
102192cfeba6Schristos 			 * authzFrom attributes may be crafted
102292cfeba6Schristos 			 * to avoid unwanted proxyAuthz to take place.
102392cfeba6Schristos 			 */
102492cfeba6Schristos #if 0
102592cfeba6Schristos 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
102692cfeba6Schristos 			rs->sr_text = "proxyAuthz not allowed within namingContext";
102792cfeba6Schristos #endif
102892cfeba6Schristos 			goto done;
102992cfeba6Schristos 		}
103092cfeba6Schristos 
103192cfeba6Schristos 		if ( !BER_BVISNULL( bound_ndn ) ) {
103292cfeba6Schristos 			goto done;
103392cfeba6Schristos 		}
103492cfeba6Schristos 
103592cfeba6Schristos 		if ( BER_BVISNULL( &ndn ) ) {
103692cfeba6Schristos 			goto done;
103792cfeba6Schristos 		}
103892cfeba6Schristos 
103992cfeba6Schristos 		if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
104092cfeba6Schristos 			goto done;
104192cfeba6Schristos 		}
104292cfeba6Schristos 
104392cfeba6Schristos 	} else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
104492cfeba6Schristos 		if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
104592cfeba6Schristos 		{
104692cfeba6Schristos 			/* already asserted in SASL via native authz */
104792cfeba6Schristos 			goto done;
104892cfeba6Schristos 		}
104992cfeba6Schristos 
105092cfeba6Schristos 	} else if ( si->si_authz && !isroot ) {
105192cfeba6Schristos 		int		rc;
105292cfeba6Schristos 		struct berval authcDN;
105392cfeba6Schristos 
105492cfeba6Schristos 		if ( BER_BVISNULL( &ndn ) ) {
105592cfeba6Schristos 			authcDN = slap_empty_bv;
105692cfeba6Schristos 		} else {
105792cfeba6Schristos 			authcDN = ndn;
105892cfeba6Schristos 		}
105992cfeba6Schristos 		rc = slap_sasl_matches( op, si->si_authz,
106092cfeba6Schristos 				&authcDN, &authcDN );
106192cfeba6Schristos 		if ( rc != LDAP_SUCCESS ) {
106292cfeba6Schristos 			if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
106392cfeba6Schristos 				/* ndn is not authorized
106492cfeba6Schristos 				 * to use idassert */
106592cfeba6Schristos 				rs->sr_err = rc;
106692cfeba6Schristos 			}
106792cfeba6Schristos 			goto done;
106892cfeba6Schristos 		}
106992cfeba6Schristos 	}
107092cfeba6Schristos 
107192cfeba6Schristos 	if ( op->o_proxy_authz ) {
107292cfeba6Schristos 		/*
107392cfeba6Schristos 		 * FIXME: we can:
107492cfeba6Schristos 		 * 1) ignore the already set proxyAuthz control
107592cfeba6Schristos 		 * 2) leave it in place, and don't set ours
107692cfeba6Schristos 		 * 3) add both
107792cfeba6Schristos 		 * 4) reject the operation
107892cfeba6Schristos 		 *
107992cfeba6Schristos 		 * option (4) is very drastic
108092cfeba6Schristos 		 * option (3) will make the remote server reject
108192cfeba6Schristos 		 * the operation, thus being equivalent to (4)
108292cfeba6Schristos 		 * option (2) will likely break the idassert
108392cfeba6Schristos 		 * assumptions, so we cannot accept it;
108492cfeba6Schristos 		 * option (1) means that we are contradicting
108592cfeba6Schristos 		 * the client's request.
108692cfeba6Schristos 		 *
108792cfeba6Schristos 		 * I think (4) is the only correct choice.
108892cfeba6Schristos 		 */
108992cfeba6Schristos 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
109092cfeba6Schristos 		rs->sr_text = "proxyAuthz not allowed within namingContext";
109192cfeba6Schristos 	}
109292cfeba6Schristos 
109392cfeba6Schristos 	if ( op->o_is_auth_check ) {
109492cfeba6Schristos 		mode = LDAP_BACK_IDASSERT_NOASSERT;
109592cfeba6Schristos 
109692cfeba6Schristos 	} else {
109792cfeba6Schristos 		mode = si->si_mode;
109892cfeba6Schristos 	}
109992cfeba6Schristos 
110092cfeba6Schristos 	switch ( mode ) {
110192cfeba6Schristos 	case LDAP_BACK_IDASSERT_LEGACY:
110292cfeba6Schristos 		/* original behavior:
110392cfeba6Schristos 		 * assert the client's identity */
110492cfeba6Schristos 	case LDAP_BACK_IDASSERT_SELF:
110592cfeba6Schristos 		assertedID = ndn;
110692cfeba6Schristos 		break;
110792cfeba6Schristos 
110892cfeba6Schristos 	case LDAP_BACK_IDASSERT_ANONYMOUS:
110992cfeba6Schristos 		/* assert "anonymous" */
111092cfeba6Schristos 		assertedID = slap_empty_bv;
111192cfeba6Schristos 		break;
111292cfeba6Schristos 
111392cfeba6Schristos 	case LDAP_BACK_IDASSERT_NOASSERT:
111492cfeba6Schristos 		/* don't assert; bind as proxyauthzdn */
111592cfeba6Schristos 		goto done;
111692cfeba6Schristos 
111792cfeba6Schristos 	case LDAP_BACK_IDASSERT_OTHERID:
111892cfeba6Schristos 	case LDAP_BACK_IDASSERT_OTHERDN:
111992cfeba6Schristos 		/* assert idassert DN */
112092cfeba6Schristos 		assertedID = si->si_bc.sb_authzId;
112192cfeba6Schristos 		break;
112292cfeba6Schristos 
112392cfeba6Schristos 	default:
112492cfeba6Schristos 		assert( 0 );
112592cfeba6Schristos 	}
112692cfeba6Schristos 
112792cfeba6Schristos 	/* if we got here, "" is allowed to proxyAuthz */
112892cfeba6Schristos 	if ( BER_BVISNULL( &assertedID ) ) {
112992cfeba6Schristos 		assertedID = slap_empty_bv;
113092cfeba6Schristos 	}
113192cfeba6Schristos 
113292cfeba6Schristos 	/* don't idassert the bound DN (ITS#4497) */
113392cfeba6Schristos 	if ( dn_match( &assertedID, bound_ndn ) ) {
113492cfeba6Schristos 		goto done;
113592cfeba6Schristos 	}
113692cfeba6Schristos 
113792cfeba6Schristos 	ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
113892cfeba6Schristos 	ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
113992cfeba6Schristos 
114092cfeba6Schristos 	switch ( si->si_mode ) {
114192cfeba6Schristos 	/* already in u:ID or dn:DN form */
114292cfeba6Schristos 	case LDAP_BACK_IDASSERT_OTHERID:
114392cfeba6Schristos 	case LDAP_BACK_IDASSERT_OTHERDN:
114492cfeba6Schristos 		ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
114592cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
114692cfeba6Schristos 		break;
114792cfeba6Schristos 
114892cfeba6Schristos 	/* needs the dn: prefix */
114992cfeba6Schristos 	default:
115092cfeba6Schristos 		ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
115192cfeba6Schristos 		ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
115292cfeba6Schristos 				op->o_tmpmemctx );
115392cfeba6Schristos 		AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
115492cfeba6Schristos 		AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
115592cfeba6Schristos 				assertedID.bv_val, assertedID.bv_len + 1 );
115692cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
115792cfeba6Schristos 		break;
115892cfeba6Schristos 	}
115992cfeba6Schristos 
116092cfeba6Schristos 	/* Older versions of <draft-weltman-ldapv3-proxy> required
116192cfeba6Schristos 	 * to encode the value of the authzID (and called it proxyDN);
116292cfeba6Schristos 	 * this hack provides compatibility with those DSAs that
116392cfeba6Schristos 	 * implement it this way */
116492cfeba6Schristos 	if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
116592cfeba6Schristos 		struct berval		authzID = ctrl->ldctl_value;
116692cfeba6Schristos 		BerElementBuffer	berbuf;
116792cfeba6Schristos 		BerElement		*ber = (BerElement *)&berbuf;
116892cfeba6Schristos 		ber_tag_t		tag;
116992cfeba6Schristos 
117092cfeba6Schristos 		ber_init2( ber, 0, LBER_USE_DER );
117192cfeba6Schristos 		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
117292cfeba6Schristos 
117392cfeba6Schristos 		tag = ber_printf( ber, "O", &authzID );
117492cfeba6Schristos 		if ( tag == LBER_ERROR ) {
117592cfeba6Schristos 			rs->sr_err = LDAP_OTHER;
117692cfeba6Schristos 			goto free_ber;
117792cfeba6Schristos 		}
117892cfeba6Schristos 
117992cfeba6Schristos 		if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
118092cfeba6Schristos 			rs->sr_err = LDAP_OTHER;
118192cfeba6Schristos 			goto free_ber;
118292cfeba6Schristos 		}
118392cfeba6Schristos 
118492cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
118592cfeba6Schristos 
118692cfeba6Schristos free_ber:;
118792cfeba6Schristos 		op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
118892cfeba6Schristos 		ber_free_buf( ber );
118992cfeba6Schristos 
119092cfeba6Schristos 		if ( rs->sr_err != LDAP_SUCCESS ) {
119192cfeba6Schristos 			goto done;
119292cfeba6Schristos 		}
119392cfeba6Schristos 
119492cfeba6Schristos 	} else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
119592cfeba6Schristos 		struct berval		authzID = ctrl->ldctl_value,
119692cfeba6Schristos 					tmp;
119792cfeba6Schristos 		BerElementBuffer	berbuf;
119892cfeba6Schristos 		BerElement		*ber = (BerElement *)&berbuf;
119992cfeba6Schristos 		ber_tag_t		tag;
120092cfeba6Schristos 
120192cfeba6Schristos 		if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
120292cfeba6Schristos 			rs->sr_err = LDAP_PROTOCOL_ERROR;
120392cfeba6Schristos 			goto done;
120492cfeba6Schristos 		}
120592cfeba6Schristos 
120692cfeba6Schristos 		tmp = authzID;
120792cfeba6Schristos 		tmp.bv_val += STRLENOF( "dn:" );
120892cfeba6Schristos 		tmp.bv_len -= STRLENOF( "dn:" );
120992cfeba6Schristos 
121092cfeba6Schristos 		ber_init2( ber, 0, LBER_USE_DER );
121192cfeba6Schristos 		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
121292cfeba6Schristos 
121392cfeba6Schristos 		/* apparently, Mozilla API encodes this
121492cfeba6Schristos 		 * as "SEQUENCE { LDAPDN }" */
121592cfeba6Schristos 		tag = ber_printf( ber, "{O}", &tmp );
121692cfeba6Schristos 		if ( tag == LBER_ERROR ) {
121792cfeba6Schristos 			rs->sr_err = LDAP_OTHER;
121892cfeba6Schristos 			goto free_ber2;
121992cfeba6Schristos 		}
122092cfeba6Schristos 
122192cfeba6Schristos 		if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
122292cfeba6Schristos 			rs->sr_err = LDAP_OTHER;
122392cfeba6Schristos 			goto free_ber2;
122492cfeba6Schristos 		}
122592cfeba6Schristos 
122692cfeba6Schristos 		ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
122792cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
122892cfeba6Schristos 
122992cfeba6Schristos free_ber2:;
123092cfeba6Schristos 		op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
123192cfeba6Schristos 		ber_free_buf( ber );
123292cfeba6Schristos 
123392cfeba6Schristos 		if ( rs->sr_err != LDAP_SUCCESS ) {
123492cfeba6Schristos 			goto done;
123592cfeba6Schristos 		}
123692cfeba6Schristos 	}
123792cfeba6Schristos 
123892cfeba6Schristos done:;
123992cfeba6Schristos 
124092cfeba6Schristos 	return rs->sr_err;
124192cfeba6Schristos }
124292cfeba6Schristos 
124392cfeba6Schristos /*
124492cfeba6Schristos  * Add controls;
124592cfeba6Schristos  *
124692cfeba6Schristos  * if any needs to be added, it is prepended to existing ones,
124792cfeba6Schristos  * in a newly allocated array.  The companion function
124892cfeba6Schristos  * mi->mi_ldap_extra->controls_free() must be used to restore the original
124992cfeba6Schristos  * status of op->o_ctrls.
125092cfeba6Schristos  */
125192cfeba6Schristos int
asyncmeta_controls_add(Operation * op,SlapReply * rs,a_metaconn_t * mc,int candidate,int isroot,LDAPControl *** pctrls)125292cfeba6Schristos asyncmeta_controls_add( Operation *op,
125392cfeba6Schristos 			SlapReply *rs,
125492cfeba6Schristos 			a_metaconn_t	*mc,
125592cfeba6Schristos 			int		candidate,
125692cfeba6Schristos 			int             isroot,
125792cfeba6Schristos 			LDAPControl	***pctrls )
125892cfeba6Schristos {
125992cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
126092cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
126192cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
126292cfeba6Schristos 
126392cfeba6Schristos 	LDAPControl		**ctrls = NULL;
126492cfeba6Schristos 	/* set to the maximum number of controls this backend can add */
126592cfeba6Schristos 	LDAPControl		c[ 2 ] = {{ 0 }};
126692cfeba6Schristos 	int			n = 0, i, j1 = 0, j2 = 0, skipped = 0;
126792cfeba6Schristos 
126892cfeba6Schristos 	*pctrls = NULL;
126992cfeba6Schristos 
127092cfeba6Schristos 	rs->sr_err = LDAP_SUCCESS;
127192cfeba6Schristos 
127292cfeba6Schristos 	/* don't add controls if protocol is not LDAPv3 */
127392cfeba6Schristos 	switch ( mt->mt_version ) {
127492cfeba6Schristos 	case LDAP_VERSION3:
127592cfeba6Schristos 		break;
127692cfeba6Schristos 
127792cfeba6Schristos 	case 0:
127892cfeba6Schristos 		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
127992cfeba6Schristos 			break;
128092cfeba6Schristos 		}
128192cfeba6Schristos 		/* fall thru */
128292cfeba6Schristos 
128392cfeba6Schristos 	default:
128492cfeba6Schristos 		goto done;
128592cfeba6Schristos 	}
128692cfeba6Schristos 
128792cfeba6Schristos 	/* put controls that go __before__ existing ones here */
128892cfeba6Schristos 
128992cfeba6Schristos 	/* proxyAuthz for identity assertion */
129092cfeba6Schristos 	switch ( asyncmeta_back_proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
129192cfeba6Schristos 					     mt->mt_version, isroot, &mt->mt_idassert, &c[ j1 ] ) )
129292cfeba6Schristos 	{
129392cfeba6Schristos 	case SLAP_CB_CONTINUE:
129492cfeba6Schristos 		break;
129592cfeba6Schristos 
129692cfeba6Schristos 	case LDAP_SUCCESS:
129792cfeba6Schristos 		j1++;
129892cfeba6Schristos 		break;
129992cfeba6Schristos 
130092cfeba6Schristos 	default:
130192cfeba6Schristos 		goto done;
130292cfeba6Schristos 	}
130392cfeba6Schristos 
130492cfeba6Schristos 	/* put controls that go __after__ existing ones here */
130592cfeba6Schristos 
130692cfeba6Schristos #ifdef SLAP_CONTROL_X_SESSION_TRACKING
130792cfeba6Schristos 	/* session tracking */
130892cfeba6Schristos 	if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
130992cfeba6Schristos 		switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
131092cfeba6Schristos 		case SLAP_CB_CONTINUE:
131192cfeba6Schristos 			break;
131292cfeba6Schristos 
131392cfeba6Schristos 		case LDAP_SUCCESS:
131492cfeba6Schristos 			j2++;
131592cfeba6Schristos 			break;
131692cfeba6Schristos 
131792cfeba6Schristos 		default:
131892cfeba6Schristos 			goto done;
131992cfeba6Schristos 		}
132092cfeba6Schristos 	}
132192cfeba6Schristos #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
132292cfeba6Schristos 
132392cfeba6Schristos 	if ( rs->sr_err == SLAP_CB_CONTINUE ) {
132492cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
132592cfeba6Schristos 	}
132692cfeba6Schristos 
132792cfeba6Schristos 	/* if nothing to do, just bail out */
132892cfeba6Schristos 	if ( j1 == 0 && j2 == 0 ) {
132992cfeba6Schristos 		goto done;
133092cfeba6Schristos 	}
133192cfeba6Schristos 
133292cfeba6Schristos 	assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
133392cfeba6Schristos 
133492cfeba6Schristos 	if ( op->o_ctrls ) {
133592cfeba6Schristos 		for ( n = 0; op->o_ctrls[ n ]; n++ )
133692cfeba6Schristos 			/* just count ctrls */ ;
133792cfeba6Schristos 	}
133892cfeba6Schristos 
133992cfeba6Schristos 	ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
134092cfeba6Schristos 			op->o_tmpmemctx );
134192cfeba6Schristos 	if ( j1 ) {
134292cfeba6Schristos 		ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
134392cfeba6Schristos 		*ctrls[ 0 ] = c[ 0 ];
134492cfeba6Schristos 		for ( i = 1; i < j1; i++ ) {
134592cfeba6Schristos 			ctrls[ i ] = &ctrls[ 0 ][ i ];
134692cfeba6Schristos 			*ctrls[ i ] = c[ i ];
134792cfeba6Schristos 		}
134892cfeba6Schristos 	}
134992cfeba6Schristos 
135092cfeba6Schristos 	i = 0;
135192cfeba6Schristos 	if ( op->o_ctrls ) {
135292cfeba6Schristos 		LDAPControl *proxyauthz = ldap_control_find(
135392cfeba6Schristos 				LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
135492cfeba6Schristos 
135592cfeba6Schristos 		for ( i = 0; op->o_ctrls[ i ]; i++ ) {
135692cfeba6Schristos 			/* Only replace it if we generated one */
135792cfeba6Schristos 			if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
135892cfeba6Schristos 				/* Frontend has already checked only one is present */
135992cfeba6Schristos 				assert( skipped == 0 );
136092cfeba6Schristos 				skipped++;
136192cfeba6Schristos 				continue;
136292cfeba6Schristos 			}
136392cfeba6Schristos 			ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
136492cfeba6Schristos 		}
136592cfeba6Schristos 	}
136692cfeba6Schristos 
136792cfeba6Schristos 	n += j1 - skipped;
136892cfeba6Schristos 	if ( j2 ) {
136992cfeba6Schristos 		ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
137092cfeba6Schristos 		*ctrls[ n ] = c[ j1 ];
137192cfeba6Schristos 		for ( i = 1; i < j2; i++ ) {
137292cfeba6Schristos 			ctrls[ n + i ] = &ctrls[ n ][ i ];
137392cfeba6Schristos 			*ctrls[ n + i ] = c[ i ];
137492cfeba6Schristos 		}
137592cfeba6Schristos 	}
137692cfeba6Schristos 
137792cfeba6Schristos 	ctrls[ n + j2 ] = NULL;
137892cfeba6Schristos 
137992cfeba6Schristos done:;
138092cfeba6Schristos 	if ( ctrls == NULL ) {
138192cfeba6Schristos 		ctrls = op->o_ctrls;
138292cfeba6Schristos 	}
138392cfeba6Schristos 
138492cfeba6Schristos 	*pctrls = ctrls;
138592cfeba6Schristos 
138692cfeba6Schristos 	return rs->sr_err;
138792cfeba6Schristos }
138892cfeba6Schristos 
138992cfeba6Schristos 
139092cfeba6Schristos /*
139192cfeba6Schristos  * asyncmeta_dobind_init()
139292cfeba6Schristos  *
139392cfeba6Schristos  * initiates bind for a candidate target
139492cfeba6Schristos  */
139592cfeba6Schristos meta_search_candidate_t
asyncmeta_dobind_init(Operation * op,SlapReply * rs,bm_context_t * bc,a_metaconn_t * mc,int candidate)139692cfeba6Schristos asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
139792cfeba6Schristos {
139892cfeba6Schristos 	SlapReply		*candidates = bc->candidates;
139992cfeba6Schristos 	a_metainfo_t		*mi = ( a_metainfo_t * )mc->mc_info;
140092cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
140192cfeba6Schristos 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
140292cfeba6Schristos 	struct berval		binddn = msc->msc_bound_ndn,
140392cfeba6Schristos 				cred = msc->msc_cred;
140492cfeba6Schristos 	int			method;
140592cfeba6Schristos 
140692cfeba6Schristos 	int			rc;
140792cfeba6Schristos 	ber_int_t	msgid;
140892cfeba6Schristos 
140992cfeba6Schristos 	meta_search_candidate_t	retcode;
141092cfeba6Schristos 
141192cfeba6Schristos 	Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_dobind_init[%d] msc %p\n",
141292cfeba6Schristos 		op->o_log_prefix, candidate, msc );
141392cfeba6Schristos 
141492cfeba6Schristos 	if ( mc->mc_authz_target == META_BOUND_ALL ) {
141592cfeba6Schristos 		return META_SEARCH_CANDIDATE;
141692cfeba6Schristos 	}
141792cfeba6Schristos 
141892cfeba6Schristos 	if ( slapd_shutdown ) {
141992cfeba6Schristos 		rs->sr_err = LDAP_UNAVAILABLE;
142092cfeba6Schristos 		return META_SEARCH_ERR;
142192cfeba6Schristos 	}
142292cfeba6Schristos 
142392cfeba6Schristos 	retcode = META_SEARCH_BINDING;
142492cfeba6Schristos 	if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
142592cfeba6Schristos 		/* already bound (or anonymous) */
142692cfeba6Schristos 
142792cfeba6Schristos #ifdef DEBUG_205
142892cfeba6Schristos 		char	buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
142992cfeba6Schristos 		int	bound = 0;
143092cfeba6Schristos 
143192cfeba6Schristos 		if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
143292cfeba6Schristos 			bound = 1;
143392cfeba6Schristos 		}
143492cfeba6Schristos 
143592cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
143692cfeba6Schristos 		       "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p%s DN=\"%s\"\n",
143792cfeba6Schristos 		       op->o_log_prefix, candidate, (void *)mc,
143892cfeba6Schristos 		       (void *)msc->msc_ld, bound ? " bound" : " anonymous",
143992cfeba6Schristos 		       bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
144092cfeba6Schristos #endif /* DEBUG_205 */
144192cfeba6Schristos 
144292cfeba6Schristos 		retcode = META_SEARCH_CANDIDATE;
144392cfeba6Schristos 
144492cfeba6Schristos 	} else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
144592cfeba6Schristos 		/* another thread is binding the target for this conn; wait */
144692cfeba6Schristos 
144792cfeba6Schristos #ifdef DEBUG_205
144892cfeba6Schristos 
144992cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
145092cfeba6Schristos 		       "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p needbind\n",
145192cfeba6Schristos 		       op->o_log_prefix, candidate, (void *)mc,
145292cfeba6Schristos 		       (void *)msc->msc_ld );
145392cfeba6Schristos #endif /* DEBUG_205 */
145492cfeba6Schristos 
145592cfeba6Schristos 		candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
145692cfeba6Schristos 		retcode = META_SEARCH_NEED_BIND;
145792cfeba6Schristos 	} else {
145892cfeba6Schristos 		/* we'll need to bind the target for this conn */
145992cfeba6Schristos 
146092cfeba6Schristos #ifdef DEBUG_205
146192cfeba6Schristos 
146292cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
146392cfeba6Schristos 		       "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p binding\n",
146492cfeba6Schristos 		       op->o_log_prefix, candidate, (void *)mc,
146592cfeba6Schristos 		       (void *)msc->msc_ld );
146692cfeba6Schristos #endif /* DEBUG_205 */
146792cfeba6Schristos 
146892cfeba6Schristos 		if ( msc->msc_ld == NULL ) {
146992cfeba6Schristos 			/* for some reason (e.g. because formerly in "binding"
147092cfeba6Schristos 			 * state, with eventual connection expiration or invalidation)
147192cfeba6Schristos 			 * it was not initialized as expected */
147292cfeba6Schristos 
147392cfeba6Schristos 			Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p ld=NULL\n",
147492cfeba6Schristos 				op->o_log_prefix, candidate, (void *)mc );
147592cfeba6Schristos 
147692cfeba6Schristos 			rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
147792cfeba6Schristos 						      LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
147892cfeba6Schristos 
147992cfeba6Schristos 			switch ( rc ) {
148092cfeba6Schristos 			case LDAP_SUCCESS:
148192cfeba6Schristos 				assert( msc->msc_ld != NULL );
148292cfeba6Schristos 				break;
148392cfeba6Schristos 
148492cfeba6Schristos 			case LDAP_SERVER_DOWN:
148592cfeba6Schristos 			case LDAP_UNAVAILABLE:
148692cfeba6Schristos 				goto down;
148792cfeba6Schristos 
148892cfeba6Schristos 			default:
148992cfeba6Schristos 				goto other;
149092cfeba6Schristos 			}
149192cfeba6Schristos 		}
149292cfeba6Schristos 
149392cfeba6Schristos 		LDAP_BACK_CONN_BINDING_SET( msc );
149492cfeba6Schristos 	}
149592cfeba6Schristos 
149692cfeba6Schristos 	if ( retcode != META_SEARCH_BINDING ) {
149792cfeba6Schristos 		return retcode;
149892cfeba6Schristos 	}
149992cfeba6Schristos 
150092cfeba6Schristos 	if ( op->o_conn != NULL &&
150192cfeba6Schristos 		!op->o_do_not_cache &&
150292cfeba6Schristos 		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
150392cfeba6Schristos 			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
150492cfeba6Schristos 			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
150592cfeba6Schristos 	{
150692cfeba6Schristos 		rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
150792cfeba6Schristos 		switch ( rc ) {
150892cfeba6Schristos 		case LDAP_SUCCESS:
150992cfeba6Schristos 			break;
151092cfeba6Schristos 		case LDAP_UNAVAILABLE:
151192cfeba6Schristos 			goto down;
151292cfeba6Schristos 		default:
151392cfeba6Schristos 			goto other;
151492cfeba6Schristos 		}
151592cfeba6Schristos 
151692cfeba6Schristos 		/* NOTE: we copy things here, even if bind didn't succeed yet,
151792cfeba6Schristos 		 * because the connection is not shared until bind is over */
151892cfeba6Schristos 		if ( !BER_BVISNULL( &binddn ) ) {
151992cfeba6Schristos 			ber_bvreplace( &msc->msc_bound_ndn, &binddn );
152092cfeba6Schristos 			if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
152192cfeba6Schristos 				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
152292cfeba6Schristos 					memset( msc->msc_cred.bv_val, 0,
152392cfeba6Schristos 						msc->msc_cred.bv_len );
152492cfeba6Schristos 				}
152592cfeba6Schristos 				ber_bvreplace( &msc->msc_cred, &cred );
152692cfeba6Schristos 			}
152792cfeba6Schristos 		}
152892cfeba6Schristos 		if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
152992cfeba6Schristos 			/* apparently, idassert was configured with SASL bind,
153092cfeba6Schristos 			 * so bind occurred inside meta_back_proxy_authz_cred() */
153192cfeba6Schristos 			LDAP_BACK_CONN_BINDING_CLEAR( msc );
153292cfeba6Schristos 			return META_SEARCH_CANDIDATE;
153392cfeba6Schristos 		}
153492cfeba6Schristos 
153592cfeba6Schristos 		/* paranoid */
153692cfeba6Schristos 		switch ( method ) {
153792cfeba6Schristos 		case LDAP_AUTH_NONE:
153892cfeba6Schristos 		case LDAP_AUTH_SIMPLE:
153992cfeba6Schristos 			/* do a simple bind with binddn, cred */
154092cfeba6Schristos 			break;
154192cfeba6Schristos 
154292cfeba6Schristos 		default:
154392cfeba6Schristos 			assert( 0 );
154492cfeba6Schristos 			break;
154592cfeba6Schristos 		}
154692cfeba6Schristos 	}
154792cfeba6Schristos 
154892cfeba6Schristos 	assert( msc->msc_ld != NULL );
154992cfeba6Schristos 
155092cfeba6Schristos 	if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
155192cfeba6Schristos 		/* bind anonymously? */
155292cfeba6Schristos 		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
155392cfeba6Schristos 			"non-empty dn with empty cred; binding anonymously\n",
155492cfeba6Schristos 			op->o_log_prefix, candidate, (void *)mc );
155592cfeba6Schristos 		cred = slap_empty_bv;
155692cfeba6Schristos 
155792cfeba6Schristos 	} else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
155892cfeba6Schristos 		/* error */
155992cfeba6Schristos 		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
156092cfeba6Schristos 			"empty dn with non-empty cred: error\n",
156192cfeba6Schristos 			op->o_log_prefix, candidate, (void *)mc );
156292cfeba6Schristos 		rc = LDAP_OTHER;
156392cfeba6Schristos 		goto other;
156492cfeba6Schristos 	}
156592cfeba6Schristos retry_bind:
156692cfeba6Schristos 	if ( LogTest( asyncmeta_debug ) ) {
156792cfeba6Schristos 		char	time_buf[ SLAP_TEXT_BUFLEN ];
156892cfeba6Schristos 		asyncmeta_get_timestamp(time_buf);
156992cfeba6Schristos 		Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind msc: %p\n",
157092cfeba6Schristos 		      time_buf, msc );
157192cfeba6Schristos 	}
157292cfeba6Schristos 	rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
157392cfeba6Schristos 			NULL, NULL, &msgid );
157492cfeba6Schristos 	ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc );
157592cfeba6Schristos 	if ( LogTest( asyncmeta_debug ) ) {
157692cfeba6Schristos 		char	time_buf[ SLAP_TEXT_BUFLEN ];
157792cfeba6Schristos 		asyncmeta_get_timestamp(time_buf);
157892cfeba6Schristos 		Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init rc=%d msc: %p\n",
157992cfeba6Schristos 		      time_buf, rc, msc );
158092cfeba6Schristos 	}
158192cfeba6Schristos 	if ( LogTest( LDAP_DEBUG_TRACE )) {
158292cfeba6Schristos 		ber_socket_t s;
158392cfeba6Schristos 		char sockname[LDAP_IPADDRLEN];
158492cfeba6Schristos 		struct berval sockbv = BER_BVC( sockname );
158592cfeba6Schristos 		Sockaddr addr;
158692cfeba6Schristos 		socklen_t len = sizeof( addr );
158792cfeba6Schristos 
158892cfeba6Schristos 		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
158992cfeba6Schristos 		getsockname( s, &addr.sa_addr, &len );
159092cfeba6Schristos 		ldap_pvt_sockaddrstr( &addr, &sockbv );
159192cfeba6Schristos 		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_dobind_init msc %p ld %p ldr %p fd %d addr %s\n",
159292cfeba6Schristos 			op->o_log_prefix, msc, msc->msc_ld, msc->msc_ldr, s, sockname );
159392cfeba6Schristos 	}
159492cfeba6Schristos 
159592cfeba6Schristos 	if (rc == LDAP_SERVER_DOWN ) {
159692cfeba6Schristos 		goto down;
159792cfeba6Schristos 	} else if (rc == LDAP_BUSY) {
159892cfeba6Schristos 		if (rs->sr_text == NULL) {
159992cfeba6Schristos 			rs->sr_text = "Unable to establish LDAP connection to target within the specified network timeout.";
160092cfeba6Schristos 		}
160192cfeba6Schristos 		LDAP_BACK_CONN_BINDING_CLEAR( msc );
160292cfeba6Schristos 		goto other;
160392cfeba6Schristos 	}
160492cfeba6Schristos 	/* mark as need bind so it gets send when the bind response is received */
160592cfeba6Schristos 	candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
160692cfeba6Schristos 	asyncmeta_set_msc_time(msc);
160792cfeba6Schristos #ifdef DEBUG_205
160892cfeba6Schristos 	Debug( LDAP_DEBUG_ANY,
160992cfeba6Schristos 	       "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p rc=%d\n",
161092cfeba6Schristos 	       op->o_log_prefix, candidate, (void *)mc,
161192cfeba6Schristos 	       (void *)mc->mc_conns[candidate].msc_ld, rc );
161292cfeba6Schristos #endif /* DEBUG_205 */
161392cfeba6Schristos 
161492cfeba6Schristos 	switch ( rc ) {
161592cfeba6Schristos 	case LDAP_SUCCESS:
161692cfeba6Schristos 		assert( msgid >= 0 );
161792cfeba6Schristos 		if ( LogTest( asyncmeta_debug ) ) {
161892cfeba6Schristos 			char	time_buf[ SLAP_TEXT_BUFLEN ];
161992cfeba6Schristos 			asyncmeta_get_timestamp(time_buf);
162092cfeba6Schristos 			Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind success msc: %p\n",
162192cfeba6Schristos 			      time_buf, msc );
162292cfeba6Schristos 		}
162392cfeba6Schristos 		META_BINDING_SET( &candidates[ candidate ] );
162492cfeba6Schristos 		rs->sr_err = LDAP_SUCCESS;
162592cfeba6Schristos 		msc->msc_binding_time = slap_get_time();
162692cfeba6Schristos 		return META_SEARCH_BINDING;
162792cfeba6Schristos 
162892cfeba6Schristos 	case LDAP_X_CONNECTING:
162992cfeba6Schristos 		/* must retry, same conn */
163092cfeba6Schristos 		candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING;
163192cfeba6Schristos 		LDAP_BACK_CONN_BINDING_CLEAR( msc );
163292cfeba6Schristos 		goto retry_bind;
163392cfeba6Schristos 
163492cfeba6Schristos 	case LDAP_SERVER_DOWN:
163592cfeba6Schristos down:;
163692cfeba6Schristos 		retcode = META_SEARCH_ERR;
163792cfeba6Schristos 		rs->sr_err = LDAP_UNAVAILABLE;
163892cfeba6Schristos 		if (rs->sr_text == NULL) {
163992cfeba6Schristos 			rs->sr_text = "Unable to bind to remote target - target down or unavailable";
164092cfeba6Schristos 		}
164192cfeba6Schristos 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
164292cfeba6Schristos                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
164392cfeba6Schristos 		break;
164492cfeba6Schristos 
164592cfeba6Schristos 		/* fall thru */
164692cfeba6Schristos 
164792cfeba6Schristos 	default:
164892cfeba6Schristos other:;
164992cfeba6Schristos 		rs->sr_err = rc;
165092cfeba6Schristos 		rc = slap_map_api2result( rs );
165192cfeba6Schristos 		candidates[ candidate ].sr_err = rc;
165292cfeba6Schristos 		if ( META_BACK_ONERR_STOP( mi ) ) {
165392cfeba6Schristos 			retcode = META_SEARCH_ERR;
165492cfeba6Schristos 
165592cfeba6Schristos 		} else {
165692cfeba6Schristos 			retcode = META_SEARCH_NOT_CANDIDATE;
165792cfeba6Schristos 		}
165892cfeba6Schristos 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
165992cfeba6Schristos                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
166092cfeba6Schristos 		break;
166192cfeba6Schristos 	}
166292cfeba6Schristos 
166392cfeba6Schristos 	return retcode;
166492cfeba6Schristos }
166592cfeba6Schristos 
166692cfeba6Schristos 
166792cfeba6Schristos 
166892cfeba6Schristos 
166992cfeba6Schristos meta_search_candidate_t
asyncmeta_dobind_init_with_retry(Operation * op,SlapReply * rs,bm_context_t * bc,a_metaconn_t * mc,int candidate)167092cfeba6Schristos asyncmeta_dobind_init_with_retry(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
167192cfeba6Schristos {
167292cfeba6Schristos 
167392cfeba6Schristos 	int rc;
167492cfeba6Schristos 	a_metasingleconn_t *msc = &mc->mc_conns[candidate];
167592cfeba6Schristos 	a_metainfo_t		*mi = mc->mc_info;
167692cfeba6Schristos 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
167792cfeba6Schristos 
167892cfeba6Schristos 	if (META_BACK_CONN_INVALID(msc) || (LDAP_BACK_CONN_BINDING( msc ) && msc->msc_binding_time > 0
167992cfeba6Schristos 					    && (msc->msc_binding_time + mt->mt_timeout[ SLAP_OP_BIND ]) < slap_get_time())) {
168092cfeba6Schristos 		char	buf[ SLAP_TEXT_BUFLEN ];
168192cfeba6Schristos 		snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
168292cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
168392cfeba6Schristos 		asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
168492cfeba6Schristos 
168592cfeba6Schristos 		rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
168692cfeba6Schristos 					      LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
168792cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
168892cfeba6Schristos 	}
168992cfeba6Schristos 
169092cfeba6Schristos 	if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
169192cfeba6Schristos 		if ( mc->pending_ops > 1 ) {
169292cfeba6Schristos 			asyncmeta_send_all_pending_ops( mc, candidate, op->o_threadctx, 1 );
169392cfeba6Schristos 		}
169492cfeba6Schristos 		return META_SEARCH_CANDIDATE;
169592cfeba6Schristos 	}
169692cfeba6Schristos 
169792cfeba6Schristos retry_dobind:
169892cfeba6Schristos 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
169992cfeba6Schristos 	rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
170092cfeba6Schristos 	if (rs->sr_err != LDAP_UNAVAILABLE && rs->sr_err != LDAP_BUSY) {
170192cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
170292cfeba6Schristos 		return rc;
170392cfeba6Schristos 	} else if (bc->nretries[candidate] == 0) {
170492cfeba6Schristos 		char	buf[ SLAP_TEXT_BUFLEN ];
170592cfeba6Schristos 		snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
170692cfeba6Schristos 		asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
170792cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
170892cfeba6Schristos 		return rc;
170992cfeba6Schristos 	}
171092cfeba6Schristos 	/* need to retry */
171192cfeba6Schristos 	bc->nretries[candidate]--;
171292cfeba6Schristos 	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
171392cfeba6Schristos 		/* this lock is required; however,
171492cfeba6Schristos 		 * it's invoked only when logging is on */
171592cfeba6Schristos 		ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
171692cfeba6Schristos 		Debug( LDAP_DEBUG_ANY,
171792cfeba6Schristos 		       "%s asyncmeta_dobind_init_with_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
171892cfeba6Schristos 		       op->o_log_prefix, candidate, mt->mt_uri,
171992cfeba6Schristos 		       BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
172092cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
172192cfeba6Schristos 	}
172292cfeba6Schristos 
172392cfeba6Schristos 	asyncmeta_reset_msc(NULL, mc, candidate, 0,  __FUNCTION__);
172492cfeba6Schristos 	rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
172592cfeba6Schristos 				      LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
172692cfeba6Schristos 
172792cfeba6Schristos 	if (rs->sr_err != LDAP_SUCCESS) {
172892cfeba6Schristos 		asyncmeta_reset_msc(NULL, mc, candidate, 0,  __FUNCTION__);
172992cfeba6Schristos 		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
173092cfeba6Schristos 		return META_SEARCH_ERR;
173192cfeba6Schristos 	}
173292cfeba6Schristos 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
173392cfeba6Schristos 	goto retry_dobind;
173492cfeba6Schristos 	return rc;
173592cfeba6Schristos }
1736