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