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