bind.c 41.5 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
2
3
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
4
 * Copyright 1999-2013 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
6
 * Portions Copyright 2001-2003 Pierangelo Masarati.
 * Portions Copyright 1999-2003 Howard Chu.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7
 * All rights reserved.
Pierangelo Masarati's avatar
Pierangelo Masarati committed
8
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by the Howard Chu for inclusion
 * in OpenLDAP Software and subsequently enhanced by Pierangelo
 * Masarati.
 */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
22
23
24
25
26

#include "portable.h"

#include <stdio.h>

Pierangelo Masarati's avatar
Pierangelo Masarati committed
27
#include <ac/errno.h>
Pierangelo Masarati's avatar
Pierangelo Masarati committed
28
29
30
31
32
33
34
35
36
#include <ac/socket.h>
#include <ac/string.h>


#define AVL_INTERNAL
#include "slap.h"
#include "../back-ldap/back-ldap.h"
#include "back-meta.h"

37
38
39
40
41
42
43
44
#include "lutil_ldap.h"

static int
meta_back_proxy_authz_bind(
	metaconn_t		*mc,
	int			candidate,
	Operation		*op,
	SlapReply		*rs,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
45
46
	ldap_back_send_t	sendok,
	int			dolock );
47

48
49
50
51
52
53
54
static int
meta_back_single_bind(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
	int			candidate );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
55
int
56
meta_back_bind( Operation *op, SlapReply *rs )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
57
{
58
	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
59
	metaconn_t	*mc = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
60

61
	int		rc = LDAP_OTHER,
62
63
64
			i,
			gotit = 0,
			isroot = 0;
65

66
	SlapReply	*candidates;
67
68

	rs->sr_err = LDAP_SUCCESS;
69

70
71
	Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
		op->o_log_prefix, op->o_req_dn.bv_val, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
72

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

81
		isroot = 1;
82
83
84
85
86
87
88
89
		/* fallthru */

	case SLAP_CB_CONTINUE:
		break;

	default:
		/* be_rootdn_bind() sent result */
		return rs->sr_err;
90
	}
91
92
93
94

	/* we need meta_back_getconn() not send result even on error,
	 * because we want to intercept the error and make it
	 * invalidCredentials */
95
	mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
96
	if ( !mc ) {
97
98
99
100
101
102
103
104
105
106
107
108
109
		if ( LogTest( LDAP_DEBUG_ANY ) ) {
			char	buf[ SLAP_TEXT_BUFLEN ];

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

111
112
113
114
115
116
117
118
119
		/* FIXME: there might be cases where we don't want
		 * to map the error onto invalidCredentials */
		switch ( rs->sr_err ) {
		case LDAP_NO_SUCH_OBJECT:
		case LDAP_UNWILLING_TO_PERFORM:
			rs->sr_err = LDAP_INVALID_CREDENTIALS;
			rs->sr_text = NULL;
			break;
		}
120
		send_ldap_result( op, rs );
121
		return rs->sr_err;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
122
123
	}

124
125
	candidates = meta_back_candidates_get( op );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
126
127
128
	/*
	 * Each target is scanned ...
	 */
129
	mc->mc_authz_target = META_BOUND_NONE;
130
	for ( i = 0; i < mi->mi_ntargets; i++ ) {
131
		metatarget_t	*mt = mi->mi_targets[ i ];
132
		int		lerr;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
133
134
135
136

		/*
		 * Skip non-candidates
		 */
137
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
138
139
140
141
			continue;
		}

		if ( gotit == 0 ) {
142
143
144
			/* set rc to LDAP_SUCCESS only if at least
			 * one candidate has been tried */
			rc = LDAP_SUCCESS;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
145
			gotit = 1;
146

147
		} else if ( !isroot ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
148
149
150
151
152
			/*
			 * A bind operation is expected to have
			 * ONE CANDIDATE ONLY!
			 */
			Debug( LDAP_DEBUG_ANY,
153
				"### %s meta_back_bind: more than one"
154
				" candidate selected...\n",
155
				op->o_log_prefix, 0, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
156
157
		}

158
		if ( isroot ) {
159
160
			if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
				|| BER_BVISNULL( &mt->mt_idassert_authcDN ) )
161
162
163
164
165
166
167
168
169
			{
				metasingleconn_t	*msc = &mc->mc_conns[ i ];

				/* skip the target if no pseudorootdn is provided */
				if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
					ch_free( msc->msc_bound_ndn.bv_val );
					BER_BVZERO( &msc->msc_bound_ndn );
				}

170
				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
171
172
173
174
175
176
177
178
179
180
					/* destroy sensitive data */
					memset( msc->msc_cred.bv_val, 0,
						msc->msc_cred.bv_len );
					ch_free( msc->msc_cred.bv_val );
					BER_BVZERO( &msc->msc_cred );
				}

				continue;
			}

181
			
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
182
			(void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
183
			lerr = rs->sr_err;
184

185
186
		} else {
			lerr = meta_back_single_bind( op, rs, mc, i );
187
		}
188

Pierangelo Masarati's avatar
Pierangelo Masarati committed
189
		if ( lerr != LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
190
			rc = rs->sr_err = lerr;
191

192
193
194
195
			/* FIXME: in some cases (e.g. unavailable)
			 * do not assume it's not candidate; rather
			 * mark this as an error to be eventually
			 * reported to client */
196
			META_CANDIDATE_CLEAR( &candidates[ i ] );
197
			break;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
198
199
200
		}
	}

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

207
208
209
		if ( !LDAP_BACK_PCONN_ISPRIV( mc )
			&& !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) )
		{
210
211
212
			int		lerr;

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

219
220
			/* delete all cached connections with the current connection */
			if ( LDAP_BACK_SINGLECONN( mi ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
221
222
				metaconn_t	*tmpmc;

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

Pierangelo Masarati's avatar
Pierangelo Masarati committed
230
					if ( tmpmc->mc_refcnt != 0 ) {
231
232
233
234
235
236
237
238
239
240
241
242
243
244
						/* taint it */
						LDAP_BACK_CONN_TAINTED_SET( tmpmc );

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

245
			ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
246
			lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
247
				meta_back_conndn_cmp, meta_back_conndn_dup );
248
#if META_BACK_PRINT_CONNTREE > 0
249
			meta_back_print_conntree( mi, "<<< meta_back_bind" );
250
#endif /* META_BACK_PRINT_CONNTREE */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
251
			if ( lerr == 0 ) {
252
253
254
255
#if 0
				/* NOTE: a connection cannot be privileged
				 * and be in the avl tree at the same time
				 */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
256
257
258
259
				if ( isroot ) {
					LDAP_BACK_CONN_ISPRIV_SET( mc );
					LDAP_BACK_PCONN_SET( mc, op );
				}
260
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
261
262
263
264
				LDAP_BACK_CONN_CACHED_SET( mc );

			} else {
				LDAP_BACK_CONN_CACHED_CLEAR( mc );
265
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
266
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
267
		}
268
269
	}

270
	if ( mc != NULL ) {
271
		meta_back_release_conn( mi, mc );
272
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
273

274
275
276
	/*
	 * rc is LDAP_SUCCESS if at least one bind succeeded,
	 * err is the last error that occurred during a bind;
277
	 * if at least (and at most?) one bind succeeds, fine.
278
	 */
279
	if ( rc != LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
280
281
282
283
		
		/*
		 * deal with bind failure ...
		 */
284
285
286
287
288

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

Pierangelo Masarati's avatar
Pierangelo Masarati committed
297
298
	}

299
	return LDAP_SUCCESS;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
300
301
}

302
303
304
305
306
307
308
static int
meta_back_bind_op_result(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
	int			candidate,
	int			msgid,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
309
310
	ldap_back_send_t	sendok,
	int			dolock )
311
312
313
314
315
316
317
318
319
320
{
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
	metatarget_t		*mt = mi->mi_targets[ candidate ];
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
	LDAPMessage		*res;
	struct timeval		tv;
	int			rc;
	int			nretries = mt->mt_nretries;
	char			buf[ SLAP_TEXT_BUFLEN ];

321
322
323
324
	Debug( LDAP_DEBUG_TRACE,
		">>> %s meta_back_bind_op_result[%d]\n",
		op->o_log_prefix, candidate, 0 );

325
326
327
	/* make sure this is clean */
	assert( rs->sr_ctrls == NULL );

328
	if ( rs->sr_err == LDAP_SUCCESS ) {
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
		time_t		stoptime = (time_t)(-1),
				timeout;
		int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
				LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
		const char	*timeout_text = "Operation timed out";
		slap_op_t	opidx = slap_req2op( op->o_tag );

		/* since timeout is not specified, compute and use
		 * the one specific to the ongoing operation */
		if ( opidx == LDAP_REQ_SEARCH ) {
			if ( op->ors_tlimit <= 0 ) {
				timeout = 0;

			} else {
				timeout = op->ors_tlimit;
				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
				timeout_text = NULL;
			}

		} else {
			timeout = mt->mt_timeout[ opidx ];
		}

		/* better than nothing :) */
		if ( timeout == 0 ) {
			if ( mi->mi_idle_timeout ) {
				timeout = mi->mi_idle_timeout;

			} else if ( mi->mi_conn_ttl ) {
				timeout = mi->mi_conn_ttl;
			}
		}

		if ( timeout ) {
			stoptime = op->o_time + timeout;
		}

366
367
368
369
370
371
		LDAP_BACK_TV_SET( &tv );

		/*
		 * handle response!!!
		 */
retry:;
372
373
		rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
		switch ( rc ) {
374
		case 0:
375
376
377
			if ( nretries != META_RETRY_NEVER 
				|| ( timeout && slap_get_time() <= stoptime ) )
			{
378
379
380
381
382
383
384
385
				ldap_pvt_thread_yield();
				if ( nretries > 0 ) {
					nretries--;
				}
				tv = mt->mt_bind_timeout;
				goto retry;
			}

386
387
388
			/* don't let anyone else use this handler,
			 * because there's a pending bind that will not
			 * be acknowledged */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
389
390
391
			if ( dolock) {
				ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
			}
392
393
			assert( LDAP_BACK_CONN_BINDING( msc ) );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
394
#ifdef DEBUG_205
395
396
			Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
				op->o_log_prefix, candidate, (void *)msc->msc_ld );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
397
#endif /* DEBUG_205 */
398

399
			meta_clear_one_candidate( op, mc, candidate );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
400
401
402
			if ( dolock ) {
				ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
			}
403
404
405

			rs->sr_err = timeout_err;
			rs->sr_text = timeout_text;
406
407
408
			break;

		case -1:
409
			ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
410
411
412
413
414
415
				&rs->sr_err );

			snprintf( buf, sizeof( buf ),
				"err=%d (%s) nretries=%d",
				rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
			Debug( LDAP_DEBUG_ANY,
416
				"### %s meta_back_bind_op_result[%d]: %s.\n",
417
418
419
420
				op->o_log_prefix, candidate, buf );
			break;

		default:
421
422
423
424
425
			/* only touch when activity actually took place... */
			if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
				msc->msc_time = op->o_time;
			}

426
			/* FIXME: matched? referrals? response controls? */
427
428
429
430
431
			rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
					NULL, NULL, NULL, NULL, 1 );
			if ( rc != LDAP_SUCCESS ) {
				rs->sr_err = rc;
			}
432
			rs->sr_err = slap_map_api2result( rs );
433
434
435
436
			break;
		}
	}

437
438
439
440
441
442
443
	rs->sr_err = slap_map_api2result( rs );

	Debug( LDAP_DEBUG_TRACE,
		"<<< %s meta_back_bind_op_result[%d] err=%d\n",
		op->o_log_prefix, candidate, rs->sr_err );

	return rs->sr_err;
444
445
}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
446
/*
447
 * meta_back_single_bind
Pierangelo Masarati's avatar
Pierangelo Masarati committed
448
449
450
 *
 * attempts to perform a bind with creds
 */
451
static int
452
meta_back_single_bind(
453
454
455
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
456
	int			candidate )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
457
{
458
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
459
	metatarget_t		*mt = mi->mi_targets[ candidate ];
460
	struct berval		mdn = BER_BVNULL;
461
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
462
	int			msgid;
463
	dncookie		dc;
464
465
466
	struct berval		save_o_dn;
	int			save_o_do_not_cache;
	LDAPControl		**ctrls = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
467
	
468
469
470
471
472
	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
		ch_free( msc->msc_bound_ndn.bv_val );
		BER_BVZERO( &msc->msc_bound_ndn );
	}

473
	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
474
475
476
477
478
479
		/* destroy sensitive data */
		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
		ch_free( msc->msc_cred.bv_val );
		BER_BVZERO( &msc->msc_cred );
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
480
481
482
	/*
	 * Rewrite the bind dn if needed
	 */
483
484
485
486
487
488
489
490
491
	dc.target = mt;
	dc.conn = op->o_conn;
	dc.rs = rs;
	dc.ctx = "bindDN";

	if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
		rs->sr_text = "DN rewrite error";
		rs->sr_err = LDAP_OTHER;
		return rs->sr_err;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
492
493
	}

494
495
496
497
498
499
500
501
502
503
504
505
506
507
	/* don't add proxyAuthz; set the bindDN */
	save_o_dn = op->o_dn;
	save_o_do_not_cache = op->o_do_not_cache;
	op->o_do_not_cache = 1;
	op->o_dn = op->o_req_dn;

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

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
523
	mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
524

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
525
	meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
526
	if ( rs->sr_err != LDAP_SUCCESS ) {
527
528
		goto return_results;
	}
529

530
531
532
533
534
535
	/* If defined, proxyAuthz will be used also when
	 * back-ldap is the authorizing backend; for this
	 * purpose, a successful bind is followed by a
	 * bind with the configured identity assertion */
	/* NOTE: use with care */
	if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
536
		meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
537
538
539
540
541
542
543
		if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
			goto return_results;
		}
		goto cache_refresh;
	}

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
547
	if ( META_BACK_TGT_SAVECRED( mt ) ) {
548
549
550
551
		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
			memset( msc->msc_cred.bv_val, 0,
				msc->msc_cred.bv_len );
		}
552
		ber_bvreplace( &msc->msc_cred, &op->orb_cred );
553
		ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
554
	}
555

556
cache_refresh:;
557
	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
558
			&& !BER_BVISEMPTY( &op->o_req_ndn ) )
559
	{
560
		( void )meta_dncache_update_entry( &mi->mi_cache,
561
				&op->o_req_ndn, candidate );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
562
	}
563
564

return_results:;
565
	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
566
		free( mdn.bv_val );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
567
568
	}

569
570
	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		meta_back_quarantine( op, rs, candidate );
571
572
	}

573
	return rs->sr_err;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
574
575
}

576
577
578
579
580
581
/*
 * meta_back_single_dobind
 */
int
meta_back_single_dobind(
	Operation		*op,
582
	SlapReply		*rs,
583
	metaconn_t		**mcp,
584
	int			candidate,
585
	ldap_back_send_t	sendok,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
586
587
	int			nretries,
	int			dolock )
588
{
589
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;
590
	metatarget_t		*mt = mi->mi_targets[ candidate ];
591
	metaconn_t		*mc = *mcp;
592
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
593
	int			msgid;
594

595
596
	assert( !LDAP_BACK_CONN_ISBOUND( msc ) );

597
598
599
600
601
	/* NOTE: this obsoletes pseudorootdn */
	if ( op->o_conn != NULL &&
		!op->o_do_not_cache &&
		( BER_BVISNULL( &msc->msc_bound_ndn ) ||
			BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
Pierangelo Masarati's avatar
Pierangelo Masarati committed
602
			( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
603
			( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
604
	{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
605
		(void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
606

607
	} else {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
608
609
610
611
612
613
614
615
616
617
		char *binddn = "";
		struct berval cred = BER_BVC( "" );

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

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

625
626
		for (;;) {
			rs->sr_err = ldap_sasl_bind( msc->msc_ld,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
627
				binddn, LDAP_SASL_SIMPLE, &cred,
628
629
630
631
632
633
634
				NULL, NULL, &msgid );
			if ( rs->sr_err != LDAP_X_CONNECTING ) {
				break;
			}
			ldap_pvt_thread_yield();
		}

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
639
		rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
640
641

		/* if bind succeeded, but anonymous, clear msc_bound_ndn */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
642
643
		if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
			if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
644
645
646
				ber_memfree( msc->msc_bound_ndn.bv_val );
				BER_BVZERO( &msc->msc_bound_ndn );
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
647
648
649
650
651
652

			if ( !BER_BVISNULL( &msc->msc_cred ) ) {
				memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
				ber_memfree( msc->msc_cred.bv_val );
				BER_BVZERO( &msc->msc_cred );
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
653
		}
654
655
656
	}

	if ( rs->sr_err != LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
657
658
659
		if ( dolock ) {
			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
660
		LDAP_BACK_CONN_BINDING_CLEAR( msc );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
661
		if ( META_BACK_ONERR_STOP( mi ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
662
			LDAP_BACK_CONN_TAINTED_SET( mc );
663
			meta_back_release_conn_lock( mi, mc, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
664
665
666
667
668
			*mcp = NULL;
		}
		if ( dolock ) {
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
		}
669
670
	}

671
672
	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		meta_back_quarantine( op, rs, candidate );
673
674
	}

675
	return rs->sr_err;
676
677
}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
678
679
680
681
/*
 * meta_back_dobind
 */
int
682
683
684
685
686
meta_back_dobind(
	Operation		*op,
	SlapReply		*rs,
	metaconn_t		*mc,
	ldap_back_send_t	sendok )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
687
{
688
689
	metainfo_t		*mi = ( metainfo_t * )op->o_bd->be_private;

690
691
692
	int			bound = 0,
				i,
				isroot = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
693

694
	SlapReply		*candidates;
695

696
697
698
	if ( be_isroot( op ) ) {
		isroot = 1;
	}
699

700
701
702
703
704
705
706
707
708
	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char buf[STRLENOF("4294967295U") + 1] = { 0 };
		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );

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

710
711
712
	/*
	 * all the targets are bound as pseudoroot
	 */
713
	if ( mc->mc_authz_target == META_BOUND_ALL ) {
714
715
		bound = 1;
		goto done;
716
717
	}

718
719
	candidates = meta_back_candidates_get( op );

720
	for ( i = 0; i < mi->mi_ntargets; i++ ) {
721
		metatarget_t		*mt = mi->mi_targets[ i ];
722
		metasingleconn_t	*msc = &mc->mc_conns[ i ];
723
		int			rc;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
724
725

		/*
726
		 * Not a candidate
Pierangelo Masarati's avatar
Pierangelo Masarati committed
727
		 */
728
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
729
730
731
			continue;
		}

732
733
		assert( msc->msc_ld != NULL );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
734
735
736
		/*
		 * If the target is already bound it is skipped
		 */
737
738
739

retry_binding:;
		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
740
741
742
743
		if ( LDAP_BACK_CONN_ISBOUND( msc )
			|| ( LDAP_BACK_CONN_ISANON( msc )
				&& mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
		{
744
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
745
746
			++bound;
			continue;
747

748
749
		} else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
		{
750
751
752
753
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
			ldap_pvt_thread_yield();
			goto retry_binding;

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
754
755
756
757
		}

		LDAP_BACK_CONN_BINDING_SET( msc );
		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
758

759
760
761
762
763
764
		rc = meta_back_single_dobind( op, rs, &mc, i,
			LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
		/*
		 * NOTE: meta_back_single_dobind() already retries;
		 * in case of failure, it resets mc...
		 */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
765
		if ( rc != LDAP_SUCCESS ) {
766
767
			char		buf[ SLAP_TEXT_BUFLEN ];

768
769
770
771
772
773
			if ( mc == NULL ) {
				/* meta_back_single_dobind() already sent 
				 * response and released connection */
				goto send_err;
			}

774

775
			if ( rc == LDAP_UNAVAILABLE ) {
776
				/* FIXME: meta_back_retry() already re-calls
777
				 * meta_back_single_dobind() */
778
				if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
779
					goto retry_ok;
780
				}
781
782
783
784

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

789
				return 0;
790
791
			}

792
793
794
795
			ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
			LDAP_BACK_CONN_BINDING_CLEAR( msc );
			ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );

796
			snprintf( buf, sizeof( buf ),
797
				"meta_back_dobind[%d]: (%s) err=%d (%s).",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
798
				i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
799
				rc, ldap_err2string( rc ) );
800
801
802
			Debug( LDAP_DEBUG_ANY,
				"%s %s\n",
				op->o_log_prefix, buf, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
803
804
805
806
807
808
809
810

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

Pierangelo Masarati's avatar
Pierangelo Masarati committed
818
819
			continue;
		} /* else */
820
821

retry_ok:;
822
823
824
825
		Debug( LDAP_DEBUG_TRACE,
			"%s meta_back_dobind[%d]: "
			"(%s)\n",
			op->o_log_prefix, i,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
826
			isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
827

828
829
		ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
		LDAP_BACK_CONN_BINDING_CLEAR( msc );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
830
		if ( isroot ) {
831
832
833
834
			LDAP_BACK_CONN_ISBOUND_SET( msc );
		} else {
			LDAP_BACK_CONN_ISANON_SET( msc );
		}
835
		ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
836
837
838
		++bound;
	}

839
done:;
840
841
842
843
844
845
846
847
	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char buf[STRLENOF("4294967295U") + 1] = { 0 };
		mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );

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

Pierangelo Masarati's avatar
Pierangelo Masarati committed
849
	if ( bound == 0 ) {
850
		meta_back_release_conn( mi, mc );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
851

852
send_err:;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
853
854
855
856
857
		if ( sendok & LDAP_BACK_SENDERR ) {
			if ( rs->sr_err == LDAP_SUCCESS ) {
				rs->sr_err = LDAP_BUSY;
			}
			send_ldap_result( op, rs );
858
		}
859
860

		return 0;
861
862
	}

863
	return ( bound > 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
864
865
}

866
/*
867
 * meta_back_default_rebind
868
869
870
871
 *
 * This is a callback used for chasing referrals using the same
 * credentials as the original user on this session.
 */
872
int 
873
meta_back_default_rebind(
874
875
876
877
878
	LDAP			*ld,
	LDAP_CONST char		*url,
	ber_tag_t		request,
	ber_int_t		msgid,
	void			*params )
879
{
880
	metasingleconn_t	*msc = ( metasingleconn_t * )params;
881

882
883
	return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
			LDAP_SASL_SIMPLE, &msc->msc_cred,
884
			NULL, NULL, NULL );
885
886
}

887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
/*
 * meta_back_default_urllist
 *
 * This is a callback used for mucking with the urllist
 */
int 
meta_back_default_urllist(
	LDAP		*ld,
	LDAPURLDesc	**urllist,
	LDAPURLDesc	**url,
	void		*params )
{
	metatarget_t	*mt = (metatarget_t *)params;
	LDAPURLDesc	**urltail;

	if ( urllist == url ) {
		return LDAP_SUCCESS;
	}

	for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
		/* count */ ;

	*urltail = *urllist;
	*urllist = *url;
	*url = NULL;

	ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
	if ( mt->mt_uri ) {
		ch_free( mt->mt_uri );
	}

	ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
	ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );

	return LDAP_SUCCESS;
}

924
925
926
927
928
929
930
931
932
933
934
935
936
937
int
meta_back_cancel(
	metaconn_t		*mc,
	Operation		*op,
	SlapReply		*rs,
	ber_int_t		msgid,
	int			candidate,
	ldap_back_send_t	sendok )
{
	metainfo_t		*mi = (metainfo_t *)op->o_bd->be_private;

	metatarget_t		*mt = mi->mi_targets[ candidate ];
	metasingleconn_t	*msc = &mc->mc_conns[ candidate ];

938
939
940
941
942
	int			rc = LDAP_OTHER;

	Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n",
		op->o_log_prefix, candidate, msgid );

943
944
	/* default behavior */
	if ( META_BACK_TGT_ABANDON( mt ) ) {
945
		rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
946

947
	} else if ( META_BACK_TGT_IGNORE( mt ) ) {
948
		rc = ldap_pvt_discard( msc->msc_ld, msgid );
949

950
951
952
953
954
	} else if ( META_BACK_TGT_CANCEL( mt ) ) {
		rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );

	} else {
		assert( 0 );
955
956
	}

957
958
	Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
		op->o_log_prefix, candidate, rc );
959

960
	return rc;
961
962
963
964
}



Pierangelo Masarati's avatar
Pierangelo Masarati committed
965
966
967
968
/*
 * FIXME: error return must be handled in a cleaner way ...
 */
int
969
meta_back_op_result(
970
971
972
973
974
975
976
	metaconn_t		*mc,
	Operation		*op,
	SlapReply		*rs,
	int			candidate,
	ber_int_t		msgid,
	time_t			timeout,
	ldap_back_send_t	sendok )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
977
{
978
979
980
981
982
983
984
	metainfo_t	*mi = ( metainfo_t * )op->o_bd->be_private;

	const char	*save_text = rs->sr_text,
			*save_matched = rs->sr_matched;
	BerVarray	save_ref = rs->sr_ref;
	LDAPControl	**save_ctrls = rs->sr_ctrls;
	void		*matched_ctx = NULL;
985

986
987
988
989
	char		*matched = NULL;
	char		*text = NULL;
	char		**refs = NULL;
	LDAPControl	**ctrls = NULL;
990

991
992
	assert( mc != NULL );

993
994
995
996
997
	rs->sr_text = NULL;
	rs->sr_matched = NULL;
	rs->sr_ref = NULL;
	rs->sr_ctrls = NULL;

998
	if ( candidate != META_TARGET_NONE ) {
999
		metatarget_t		*mt = mi->mi_targets[ candidate ];
1000
		metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
1001

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1002
		if ( LDAP_ERR_OK( rs->sr_err ) ) {
1003
1004
1005
			int		rc;
			struct timeval	tv;
			LDAPMessage	*res = NULL;
1006
1007
1008
1009
			time_t		stoptime = (time_t)(-1);
			int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
						LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
			const char	*timeout_text = "Operation timed out";
1010

1011
1012
1013
1014
			/* if timeout is not specified, compute and use
			 * the one specific to the ongoing operation */
			if ( timeout == (time_t)(-1) ) {
				slap_op_t	opidx = slap_req2op( op->o_tag );
1015

1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
				if ( opidx == SLAP_OP_SEARCH ) {
					if ( op->ors_tlimit <= 0 ) {
						timeout = 0;

					} else {
						timeout = op->ors_tlimit;
						timeout_err = LDAP_TIMELIMIT_EXCEEDED;
						timeout_text = NULL;
					}

				} else {
					timeout = mt->mt_timeout[ opidx ];
				}
			}

			/* better than nothing :) */
			if ( timeout == 0 ) {
				if ( mi->mi_idle_timeout ) {
					timeout = mi->mi_idle_timeout;

				} else if ( mi->mi_conn_ttl ) {
					timeout = mi->mi_conn_ttl;
				}
			}

			if ( timeout ) {
				stoptime = op->o_time + timeout;
1043
1044
			}

1045
1046
			LDAP_BACK_TV_SET( &tv );

1047
1048
1049
1050
retry:;
			rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
			switch ( rc ) {
			case 0:
1051
				if ( timeout && slap_get_time() > stoptime ) {
1052
					(void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
1053
1054
					rs->sr_err = timeout_err;
					rs->sr_text = timeout_text;
1055
1056
1057
1058
1059
1060
1061
1062
					break;
				}

				LDAP_BACK_TV_SET( &tv );
				ldap_pvt_thread_yield();
				goto retry;

			case -1:
1063
				ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
1064
1065
1066
1067
1068