Ejemplo n.º 1
0
static int
asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
{
	a_metainfo_t	*mi = mc->mc_info;
	int i;
	SlapReply *candidates = bc->candidates;
	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}
		if (candidates[ i ].sr_msgid != META_MSGID_IGNORE ||
		    candidates[ i ].sr_type != REP_RESULT) {
			return 1;
		}
	}
	return 0;
}
Ejemplo n.º 2
0
static void
asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres)
{
	a_metainfo_t	*mi = mc->mc_info;
	Operation *op = bc->op;
	SlapReply *rs = &bc->rs;
	int	   i;
	SlapReply *candidates = bc->candidates;
	char *matched = NULL;

	if ( bc->candidate_match > 0 ) {
		struct berval	pmatched = BER_BVNULL;

		/* we use the first one */
		for ( i = 0; i < mi->mi_ntargets; i++ ) {
			if ( META_IS_CANDIDATE( &candidates[ i ] )
					&& candidates[ i ].sr_matched != NULL )
			{
				struct berval	bv, pbv;
				int		rc;

				/* if we got success, and this target
				 * returned noSuchObject, and its suffix
				 * is a superior of the searchBase,
				 * ignore the matchedDN */
				if ( sres == LDAP_SUCCESS
					&& candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
					&& op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
				{
					free( (char *)candidates[ i ].sr_matched );
					candidates[ i ].sr_matched = NULL;
					continue;
				}

				ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
				rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );

				if ( rc == LDAP_SUCCESS ) {

					/* NOTE: if they all are superiors
					 * of the baseDN, the shorter is also
					 * superior of the longer... */
					if ( pbv.bv_len > pmatched.bv_len ) {
						if ( !BER_BVISNULL( &pmatched ) ) {
							op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
						}
						pmatched = pbv;

					} else {
						op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
					}
				}

				if ( candidates[ i ].sr_matched != NULL ) {
					free( (char *)candidates[ i ].sr_matched );
					candidates[ i ].sr_matched = NULL;
				}
			}
		}

		if ( !BER_BVISNULL( &pmatched ) ) {
			matched = pmatched.bv_val;
		}

	} else if ( sres == LDAP_NO_SUCH_OBJECT ) {
		matched = mi->mi_suffix.bv_val;
	}

	/*
	 * In case we returned at least one entry, we return LDAP_SUCCESS
	 * otherwise, the latter error code we got
	 */

	if ( sres == LDAP_SUCCESS ) {
		if ( rs->sr_v2ref ) {
			sres = LDAP_REFERRAL;
		}

		if ( META_BACK_ONERR_REPORT( mi ) ) {
			/*
			 * Report errors, if any
			 *
			 * FIXME: we should handle error codes and return the more
			 * important/reasonable
			 */
			for ( i = 0; i < mi->mi_ntargets; i++ ) {
				if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
					continue;
				}

				if ( candidates[ i ].sr_err != LDAP_SUCCESS
					&& candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
				{
					sres = candidates[ i ].sr_err;
					break;
				}
			}
		}
	}
	Debug( LDAP_DEBUG_TRACE,
	       "%s asyncmeta_search_last_result(\"%d\"): "
	       ".\n",
	       op->o_log_prefix, candidate );
	rs->sr_err = sres;
	rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
	rs->sr_text =  ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text );
	rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
	asyncmeta_send_ldap_result(bc, op, rs);
	rs->sr_text = NULL;
	rs->sr_matched = NULL;
	rs->sr_ref = NULL;
}
Ejemplo n.º 3
0
/* This takes care to clean out the outbound queue in case we have a read error
 * sending back responses to the client */
int
asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
{
	bm_context_t *bc, *onext;
	int cleanup;
	Operation *op;
	SlapReply *rs;
	SlapReply *candidates;
	/* no outstanding ops, nothing to do but log */
	Debug( LDAP_DEBUG_TRACE,
	       "asyncmeta_op_read_error: ldr=%p\n",
	       mc->mc_conns[candidate].msc_ldr );

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	/*someone may be trying to write */
	if (mc->mc_conns[candidate].msc_active <= 1) {
		asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
	} else {
		META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
	}

	if (mc->pending_ops <= 0) {
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
		return LDAP_SUCCESS;
	}

	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
		onext = LDAP_STAILQ_NEXT(bc, bc_next);
		cleanup = 0;
		candidates = bc->candidates;
		/* was this op affected? */
		if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
			continue;

		if (bc->op->o_abandon) {
			bc->bc_invalid = 1;
			continue;
		}

		if (bc->bc_active > 0) {
			continue;
		}

		bc->op->o_threadctx = ctx;
		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);

		op = bc->op;
		rs = &bc->rs;
		switch (op->o_tag) {
		case LDAP_REQ_ADD:
		case LDAP_REQ_MODIFY:
		case LDAP_REQ_MODRDN:
		case LDAP_REQ_COMPARE:
		case LDAP_REQ_DELETE:
			rs->sr_err = LDAP_UNAVAILABLE;
			rs->sr_text = "Read error on connection to target";
			asyncmeta_send_ldap_result( bc, op, rs );
			cleanup = 1;
			break;
		case LDAP_REQ_SEARCH:
		{
			a_metainfo_t *mi = mc->mc_info;
			rs->sr_err = LDAP_UNAVAILABLE;
			rs->sr_text = "Read error on connection to target";
			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
			candidates[ candidate ].sr_type = REP_RESULT;
			if ( (META_BACK_ONERR_STOP( mi ) ||
			      asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
				asyncmeta_send_ldap_result( bc, op, rs );
				cleanup = 1;
			}
		}
			break;
		default:
			break;
		}

		if (cleanup) {
			int j;
			a_metainfo_t *mi = mc->mc_info;
			for (j=0; j<mi->mi_ntargets; j++) {
				if (j != candidate && bc->candidates[j].sr_msgid >= 0
				    && mc->mc_conns[j].msc_ld != NULL) {
					asyncmeta_back_cancel( mc, op,
							       bc->candidates[ j ].sr_msgid, j );
				}
			}
			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
			mc->pending_ops--;
			asyncmeta_clear_bm_context(bc);
		}
	}
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	return LDAP_SUCCESS;
}
Ejemplo n.º 4
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 );
}
Ejemplo n.º 5
0
int
meta_back_bind( Operation *op, SlapReply *rs )
{
	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
	metaconn_t	*mc = NULL;

	int		rc = LDAP_OTHER,
			i,
			gotit = 0,
			isroot = 0;

	SlapReply	*candidates;

	rs->sr_err = LDAP_SUCCESS;

	Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
		op->o_log_prefix, op->o_req_dn.bv_val );

	/* the test on the bind method should be superfluous */
	switch ( be_rootdn_bind( op, rs ) ) {
	case LDAP_SUCCESS:
		if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
			/* frontend will return success */
			return rs->sr_err;
		}

		isroot = 1;
		/* fallthru */

	case SLAP_CB_CONTINUE:
		break;

	default:
		/* be_rootdn_bind() sent result */
		return rs->sr_err;
	}

	/* we need meta_back_getconn() not send result even on error,
	 * because we want to intercept the error and make it
	 * invalidCredentials */
	mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
	if ( !mc ) {
		if ( LogTest( LDAP_DEBUG_ANY ) ) {
			char	buf[ SLAP_TEXT_BUFLEN ];

			snprintf( buf, sizeof( buf ),
				"meta_back_bind: no target "
				"for dn \"%s\" (%d%s%s).",
				op->o_req_dn.bv_val, rs->sr_err,
				rs->sr_text ? ". " : "",
				rs->sr_text ? rs->sr_text : "" );
			Debug( LDAP_DEBUG_ANY,
				"%s %s\n",
				op->o_log_prefix, buf );
		}

		/* FIXME: there might be cases where we don't want
		 * to map the error onto invalidCredentials */
		switch ( rs->sr_err ) {
		case LDAP_NO_SUCH_OBJECT:
		case LDAP_UNWILLING_TO_PERFORM:
			rs->sr_err = LDAP_INVALID_CREDENTIALS;
			rs->sr_text = NULL;
			break;
		}
		send_ldap_result( op, rs );
		return rs->sr_err;
	}

	candidates = meta_back_candidates_get( op );

	/*
	 * Each target is scanned ...
	 */
	mc->mc_authz_target = META_BOUND_NONE;
	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		metatarget_t	*mt = mi->mi_targets[ i ];
		int		lerr;

		/*
		 * Skip non-candidates
		 */
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}

		if ( gotit == 0 ) {
			/* set rc to LDAP_SUCCESS only if at least
			 * one candidate has been tried */
			rc = LDAP_SUCCESS;
			gotit = 1;

		} else if ( !isroot ) {
			/*
			 * A bind operation is expected to have
			 * ONE CANDIDATE ONLY!
			 */
			Debug( LDAP_DEBUG_ANY,
				"### %s meta_back_bind: more than one"
				" candidate selected...\n",
				op->o_log_prefix );
		}

		if ( isroot ) {
			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
				|| BER_BVISNULL( &mt->mt_idassert_authcDN ) )
			{
				metasingleconn_t	*msc = &mc->mc_conns[ i ];

				/* skip the target if no pseudorootdn is provided */
				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 );
				}

				continue;
			}


			(void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
			lerr = rs->sr_err;

		} else {
			lerr = meta_back_single_bind( op, rs, mc, i );
		}

		if ( lerr != LDAP_SUCCESS ) {
			rc = rs->sr_err = lerr;

			/* FIXME: in some cases (e.g. unavailable)
			 * do not assume it's not candidate; rather
			 * mark this as an error to be eventually
			 * reported to client */
			META_CANDIDATE_CLEAR( &candidates[ i ] );
			break;
		}
	}

	/* must re-insert if local DN changed as result of bind */
	if ( rc == LDAP_SUCCESS ) {
		if ( isroot ) {
			mc->mc_authz_target = META_BOUND_ALL;
		}

		if ( !LDAP_BACK_PCONN_ISPRIV( mc )
			&& !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) )
		{
			int		lerr;

			/* wait for all other ops to release the connection */
			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
			assert( mc->mc_refcnt == 1 );
#if META_BACK_PRINT_CONNTREE > 0
			meta_back_print_conntree( mi, ">>> meta_back_bind" );
#endif /* META_BACK_PRINT_CONNTREE */

			/* delete all cached connections with the current connection */
			if ( LDAP_BACK_SINGLECONN( mi ) ) {
				metaconn_t	*tmpmc;

				while ( ( tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
				{
					assert( !LDAP_BACK_PCONN_ISPRIV( mc ) );
					Debug( LDAP_DEBUG_TRACE,
						"=>meta_back_bind: destroying conn %lu (refcnt=%u)\n",
						mc->mc_conn->c_connid, mc->mc_refcnt );

					if ( tmpmc->mc_refcnt != 0 ) {
						/* taint it */
						LDAP_BACK_CONN_TAINTED_SET( tmpmc );

					} else {
						/*
						 * Needs a test because the handler may be corrupted,
						 * and calling ldap_unbind on a corrupted header results
						 * in a segmentation fault
						 */
						meta_back_conn_free( tmpmc );
					}
				}
			}

			ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
			lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
				meta_back_conndn_cmp, meta_back_conndn_dup );
#if META_BACK_PRINT_CONNTREE > 0
			meta_back_print_conntree( mi, "<<< meta_back_bind" );
#endif /* META_BACK_PRINT_CONNTREE */
			if ( lerr == 0 ) {
#if 0
				/* NOTE: a connection cannot be privileged
				 * and be in the avl tree at the same time
				 */
				if ( isroot ) {
					LDAP_BACK_CONN_ISPRIV_SET( mc );
					LDAP_BACK_PCONN_SET( mc, op );
				}
#endif
				LDAP_BACK_CONN_CACHED_SET( mc );

			} else {
				LDAP_BACK_CONN_CACHED_CLEAR( mc );
			}
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
		}
	}

	if ( mc != NULL ) {
		meta_back_release_conn( mi, mc );
	}

	/*
	 * rc is LDAP_SUCCESS if at least one bind succeeded,
	 * err is the last error that occurred during a bind;
	 * if at least (and at most?) one bind succeeds, fine.
	 */
	if ( rc != LDAP_SUCCESS ) {

		/*
		 * deal with bind failure ...
		 */

		/*
		 * no target was found within the naming context,
		 * so bind must fail with invalid credentials
		 */
		if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
			rs->sr_err = LDAP_INVALID_CREDENTIALS;
		} else {
			rs->sr_err = slap_map_api2result( rs );
		}
		send_ldap_result( op, rs );
		return rs->sr_err;

	}

	return LDAP_SUCCESS;
}
Ejemplo n.º 6
0
int
asyncmeta_back_bind( Operation *op, SlapReply *rs )
{
	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
	a_metaconn_t	*mc = NULL;

	int		rc = LDAP_OTHER,
			i,
			gotit = 0,
			isroot = 0;

	SlapReply	*candidates;

	candidates = op->o_tmpcalloc(mi->mi_ntargets, sizeof(SlapReply),op->o_tmpmemctx);
	rs->sr_err = LDAP_SUCCESS;

	Debug( LDAP_DEBUG_ARGS, "%s asyncmeta_back_bind: dn=\"%s\".\n",
		op->o_log_prefix, op->o_req_dn.bv_val, 0 );

	/* the test on the bind method should be superfluous */
	switch ( be_rootdn_bind( op, rs ) ) {
	case LDAP_SUCCESS:
		if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
			/* frontend will return success */
			return rs->sr_err;
		}

		isroot = 1;
		/* fallthru */

	case SLAP_CB_CONTINUE:
		break;

	default:
		/* be_rootdn_bind() sent result */
		return rs->sr_err;
	}

	/* we need asyncmeta_getconn() not send result even on error,
	 * because we want to intercept the error and make it
	 * invalidCredentials */
	mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_BIND_DONTSEND, 1 );
	if ( !mc ) {
		if ( LogTest( LDAP_DEBUG_ANY ) ) {
			char	buf[ SLAP_TEXT_BUFLEN ];

			snprintf( buf, sizeof( buf ),
				"asyncmeta_back_bind: no target "
				"for dn \"%s\" (%d%s%s).",
				op->o_req_dn.bv_val, rs->sr_err,
				rs->sr_text ? ". " : "",
				rs->sr_text ? rs->sr_text : "" );
			Debug( LDAP_DEBUG_ANY,
				"%s %s\n",
				op->o_log_prefix, buf, 0 );
		}

		/* FIXME: there might be cases where we don't want
		 * to map the error onto invalidCredentials */
		switch ( rs->sr_err ) {
		case LDAP_NO_SUCH_OBJECT:
		case LDAP_UNWILLING_TO_PERFORM:
			rs->sr_err = LDAP_INVALID_CREDENTIALS;
			rs->sr_text = NULL;
			break;
		}
		send_ldap_result( op, rs );
		return rs->sr_err;
	}

	/*
	 * Each target is scanned ...
	 */
	mc->mc_authz_target = META_BOUND_NONE;
	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		a_metatarget_t	*mt = mi->mi_targets[ i ];
		int		lerr;

		/*
		 * Skip non-candidates
		 */
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}

		if ( gotit == 0 ) {
			/* set rc to LDAP_SUCCESS only if at least
			 * one candidate has been tried */
			rc = LDAP_SUCCESS;
			gotit = 1;

		} else if ( !isroot ) {
			/*
			 * A bind operation is expected to have
			 * ONE CANDIDATE ONLY!
			 */
			Debug( LDAP_DEBUG_ANY,
				"### %s asyncmeta_back_bind: more than one"
				" candidate selected...\n",
				op->o_log_prefix, 0, 0 );
		}

		if ( isroot ) {
			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
				|| BER_BVISNULL( &mt->mt_idassert_authcDN ) )
			{
				a_metasingleconn_t	*msc = &mc->mc_conns[ i ];

				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 );
				}

				continue;
			}


			(void)asyncmeta_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
			lerr = rs->sr_err;

		} else {
			lerr = asyncmeta_single_bind( op, rs, mc, i );
		}

		if ( lerr != LDAP_SUCCESS ) {
			rc = rs->sr_err = lerr;

			/* FIXME: in some cases (e.g. unavailable)
			 * do not assume it's not candidate; rather
			 * mark this as an error to be eventually
			 * reported to client */
			META_CANDIDATE_CLEAR( &candidates[ i ] );
			break;
		}
	}

	if ( mc != NULL ) {
		for ( i = 0; i < mi->mi_ntargets; i++ ) {
			a_metasingleconn_t	*msc = &mc->mc_conns[ i ];
			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
				ch_free( msc->msc_bound_ndn.bv_val );
			}

			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 );
			}
		}
		asyncmeta_back_conn_free( mc );
	}

	/*
	 * rc is LDAP_SUCCESS if at least one bind succeeded,
	 * err is the last error that occurred during a bind;
	 * if at least (and at most?) one bind succeeds, fine.
	 */
	if ( rc != LDAP_SUCCESS ) {

		/*
		 * deal with bind failure ...
		 */

		/*
		 * no target was found within the naming context,
		 * so bind must fail with invalid credentials
		 */
		if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
			rs->sr_err = LDAP_INVALID_CREDENTIALS;
		} else {
			rs->sr_err = slap_map_api2result( rs );
		}
		send_ldap_result( op, rs );
		return rs->sr_err;

	}
	return LDAP_SUCCESS;
}
Ejemplo n.º 7
0
int
asyncmeta_back_search( Operation *op, SlapReply *rs )
{
	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
	struct timeval	save_tv = { 0, 0 },
			tv;
	time_t		stoptime = (time_t)(-1),
			lastres_time = slap_get_time(),
			timeout = 0;
	int		rc = 0, sres = LDAP_SUCCESS;
	char		*matched = NULL;
	int		last = 0, ncandidates = 0,
			initial_candidates = 0, candidate_match = 0,
			needbind = 0;
	ldap_back_send_t	sendok = LDAP_BACK_SENDERR;
	long		i,j;
	int		is_ok = 0;
	void		*savepriv;
	SlapReply	*candidates = NULL;
	int		do_taint = 0;
	bm_context_t *bc;
	a_metaconn_t *mc;
	slap_callback *cb = op->o_callback;

	rs_assert_ready( rs );
	rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */

	/*
	 * controls are set in ldap_back_dobind()
	 *
	 * FIXME: in case of values return filter, we might want
	 * to map attrs and maybe rewrite value
	 */

	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
	if (bc == NULL) {
		rs->sr_err = LDAP_OTHER;
		send_ldap_result(op, rs);
		return rs->sr_err;
	}

	candidates = bc->candidates;
	mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
		op->o_callback = cb;
		send_ldap_result(op, rs);
		asyncmeta_clear_bm_context(bc);
		return rs->sr_err;
	}

	/*
	 * Inits searches
	 */

	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		/* reset sr_msgid; it is used in most loops
		 * to check if that target is still to be considered */
		candidates[i].sr_msgid = META_MSGID_UNDEFINED;
		/* a target is marked as candidate by asyncmeta_getconn();
		 * if for any reason (an error, it's over or so) it is
		 * no longer active, sr_msgid is set to META_MSGID_IGNORE
		 * but it remains candidate, which means it has been active
		 * at some point during the operation.  This allows to
		 * use its response code and more to compute the final
		 * response */
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}

		candidates[ i ].sr_matched = NULL;
		candidates[ i ].sr_text = NULL;
		candidates[ i ].sr_ref = NULL;
		candidates[ i ].sr_ctrls = NULL;
		candidates[ i ].sr_nentries = 0;
		candidates[ i ].sr_type = -1;

		/* get largest timeout among candidates */
		if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
			&& mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
		{
			timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
		}
	}

	bc->timeout = timeout;
	bc->stoptime = op->o_time + bc->timeout;

	if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
		stoptime = op->o_time + op->ors_tlimit;
		if (stoptime < bc->stoptime) {
			bc->stoptime = stoptime;
			bc->searchtime = 1;
			bc->timeout = op->ors_tlimit;
		}
	}

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	rc = asyncmeta_add_message_queue(mc, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);

	if (rc != LDAP_SUCCESS) {
		rs->sr_err = LDAP_BUSY;
		rs->sr_text = "Maximum pending ops limit exceeded";
		asyncmeta_clear_bm_context(bc);
		op->o_callback = cb;
		send_ldap_result(op, rs);
		goto finish;
	}

	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		if ( !META_IS_CANDIDATE( &candidates[ i ] )
			|| candidates[ i ].sr_err != LDAP_SUCCESS )
		{
			continue;
		}

		rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
		switch (rc)
		{
		case META_SEARCH_CANDIDATE:
			/* target is already bound, just send the search request */
			ncandidates++;
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
			       "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);

			rc = asyncmeta_back_search_start( op, rs, mc, bc, i,  NULL, 0 );
			if (rc == META_SEARCH_ERR) {
				META_CANDIDATE_CLEAR(&candidates[i]);
				candidates[ i ].sr_msgid = META_MSGID_IGNORE;
				if ( META_BACK_ONERR_STOP( mi ) ) {
					asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
					goto finish;
				}
				else {
					continue;
				}
			}
			break;
		case META_SEARCH_NOT_CANDIDATE:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
			       "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);
			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
			break;

		case META_SEARCH_NEED_BIND:
		case META_SEARCH_CONNECTING:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NEED_BIND "
			       "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
			ncandidates++;
			rc = asyncmeta_dobind_init(op, rs, bc, mc, i);
			if (rc == META_SEARCH_ERR) {
				candidates[ i ].sr_msgid = META_MSGID_IGNORE;
				if ( META_BACK_ONERR_STOP( mi ) ) {
					asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
					goto finish;
				}
				else {
					continue;
				}
			}
			break;
		case META_SEARCH_BINDING:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
			       "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
			ncandidates++;
			/* Todo add the context to the message queue but do not send the request
			 the receiver must send this when we are done binding */
			/* question - how would do receiver know to which targets??? */
			break;

		case META_SEARCH_ERR:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
			       "cnd=\"%ldd\"\n", op->o_log_prefix, i , 0);
			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
			candidates[ i ].sr_type = REP_RESULT;

			if ( META_BACK_ONERR_STOP( mi ) ) {
				asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
				goto finish;
			}
			else {
				continue;
			}
			break;

		default:
			assert( 0 );
			break;
		}
	}

	initial_candidates = ncandidates;

	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char	cnd[ SLAP_TEXT_BUFLEN ];
		int	c;

		for ( c = 0; c < mi->mi_ntargets; c++ ) {
			if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
				cnd[ c ] = '*';
			} else {
				cnd[ c ] = ' ';
			}
		}
		cnd[ c ] = '\0';

		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
			"cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
	}

	if ( initial_candidates == 0 ) {
		/* NOTE: here we are not sending any matchedDN;
		 * this is intended, because if the back-meta
		 * is serving this search request, but no valid
		 * candidate could be looked up, it means that
		 * there is a hole in the mapping of the targets
		 * and thus no knowledge of any remote superior
		 * is available */
		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
			"base=\"%s\" scope=%d: "
			"no candidate could be selected\n",
			op->o_log_prefix, op->o_req_dn.bv_val,
			op->ors_scope );

		/* FIXME: we're sending the first error we encounter;
		 * maybe we should pick the worst... */
		rc = LDAP_NO_SUCH_OBJECT;
		for ( i = 0; i < mi->mi_ntargets; i++ ) {
			if ( META_IS_CANDIDATE( &candidates[ i ] )
				&& candidates[ i ].sr_err != LDAP_SUCCESS )
			{
				rc = candidates[ i ].sr_err;
				break;
			}
		}
		rs->sr_err = rc;
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
		asyncmeta_drop_bc(mc, bc);
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
		op->o_callback = cb;
		send_ldap_result(op, rs);
		asyncmeta_clear_bm_context(bc);
		goto finish;
	}
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	asyncmeta_start_listeners(mc, candidates, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
finish:
	return rs->sr_err;
}