1/*	$NetBSD: bind.c,v 1.3 2021/08/14 16:14:59 christos Exp $	*/
2
3/* bind.c - ldap backend bind function */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1999-2021 The OpenLDAP Foundation.
8 * Portions Copyright 2000-2003 Pierangelo Masarati.
9 * Portions Copyright 1999-2003 Howard Chu.
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/* ACKNOWLEDGEMENTS:
21 * This work was initially developed by Howard Chu for inclusion
22 * in OpenLDAP Software and subsequently enhanced by Pierangelo
23 * Masarati.
24 */
25
26#include <sys/cdefs.h>
27__RCSID("$NetBSD: bind.c,v 1.3 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
37#define AVL_INTERNAL
38#include "slap.h"
39#include "back-ldap.h"
40#include "lutil.h"
41#include "lutil_ldap.h"
42#include "ldap_rq.h"
43
44#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
45
46#ifdef LDAP_DEVEL
47#define SLAP_AUTH_DN 1
48#endif
49
50#if LDAP_BACK_PRINT_CONNTREE > 0
51
52static const struct {
53	slap_mask_t	f;
54	char		c;
55} flagsmap[] = {
56	{ LDAP_BACK_FCONN_ISBOUND,	'B' },
57	{ LDAP_BACK_FCONN_ISANON,	'A' },
58	{ LDAP_BACK_FCONN_ISPRIV,	'P' },
59	{ LDAP_BACK_FCONN_ISTLS,	'T' },
60	{ LDAP_BACK_FCONN_BINDING,	'X' },
61	{ LDAP_BACK_FCONN_TAINTED,	'E' },
62	{ LDAP_BACK_FCONN_ABANDON,	'N' },
63	{ LDAP_BACK_FCONN_ISIDASR,	'S' },
64	{ LDAP_BACK_FCONN_CACHED,	'C' },
65	{ 0,				'\0' }
66};
67
68static void
69ldap_back_conn_print( ldapconn_t *lc )
70{
71	char buf[ SLAP_TEXT_BUFLEN ];
72	char fbuf[ sizeof("BAPTIENSC") ];
73	int i;
74
75	ldap_back_conn2str( &lc->lc_base, buf, sizeof( buf ) );
76	for ( i = 0; flagsmap[ i ].c != '\0'; i++ ) {
77		if ( lc->lc_lcflags & flagsmap[i].f ) {
78			fbuf[i] = flagsmap[i].c;
79
80		} else {
81			fbuf[i] = '.';
82		}
83	}
84	fbuf[i] = '\0';
85
86	fprintf( stderr, "lc=%p %s flags=0x%08x (%s)\n",
87		(void *)lc, buf, lc->lc_lcflags, fbuf );
88}
89
90
91static char* priv2str[] = {
92	"privileged",
93	"privileged/TLS",
94	"anonymous",
95	"anonymous/TLS",
96	"bind",
97	"bind/TLS",
98	NULL
99};
100
101void
102ldap_back_print_conntree( ldapinfo_t *li, char *msg )
103{
104	int	c;
105
106	fprintf( stderr, "========> %s\n", msg );
107
108	for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
109		int		i = 0;
110		ldapconn_t	*lc;
111
112		fprintf( stderr, "  %s[%d]\n", priv2str[ c ], li->li_conn_priv[ c ].lic_num );
113
114		LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q )
115		{
116			fprintf( stderr, "    [%d] ", i );
117			ldap_back_conn_print( lc );
118			i++;
119		}
120	}
121
122	if ( li->li_conninfo.lai_tree == 0 ) {
123		fprintf( stderr, "\t(empty)\n" );
124
125	} else {
126		TAvlnode *edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
127		while ( edge ) {
128			ldap_back_conn_print( (ldapconn_t *)edge->avl_data );
129			edge = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
130		}
131	}
132
133	fprintf( stderr, "<======== %s\n", msg );
134}
135#endif /* LDAP_BACK_PRINT_CONNTREE */
136
137static int
138ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock );
139
140static ldapconn_t *
141ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
142	struct berval *binddn, struct berval *bindcred );
143
144static int
145ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
146	struct berval *binddn, struct berval *bindcred );
147
148static int
149ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs,
150	ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );
151
152static int
153ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs,
154	ldap_back_send_t sendok );
155
156static int
157ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
158
159static void
160ldap_back_conn_prune( ldapinfo_t *li );
161
162static void
163ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc );
164
165ldapconn_t *
166ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc )
167{
168	if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
169		if ( LDAP_BACK_CONN_CACHED( lc ) ) {
170			assert( lc->lc_q.tqe_prev != NULL );
171			assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
172			li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
173			LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
174			LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
175			LDAP_BACK_CONN_CACHED_CLEAR( lc );
176
177		} else {
178			assert( LDAP_BACK_CONN_TAINTED( lc ) );
179			assert( lc->lc_q.tqe_prev == NULL );
180		}
181
182	} else {
183		ldapconn_t	*tmplc = NULL;
184
185		if ( LDAP_BACK_CONN_CACHED( lc ) ) {
186			assert( !LDAP_BACK_CONN_TAINTED( lc ) );
187			tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
188				ldap_back_conndnlc_cmp );
189			assert( tmplc == lc );
190			LDAP_BACK_CONN_CACHED_CLEAR( lc );
191		}
192
193		assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
194	}
195
196	return lc;
197}
198
199int
200ldap_back_bind( Operation *op, SlapReply *rs )
201{
202	ldapinfo_t		*li = (ldapinfo_t *) op->o_bd->be_private;
203	ldapconn_t		*lc;
204
205	LDAPControl		**ctrls = NULL;
206	struct berval		save_o_dn;
207	int			save_o_do_not_cache,
208				rc = 0;
209	ber_int_t		msgid;
210	ldap_back_send_t	retrying = LDAP_BACK_RETRYING;
211
212	/* allow rootdn as a means to auth without the need to actually
213 	 * contact the proxied DSA */
214	switch ( be_rootdn_bind( op, rs ) ) {
215	case SLAP_CB_CONTINUE:
216		break;
217
218	default:
219		return rs->sr_err;
220	}
221
222	lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL );
223	if ( !lc ) {
224		return rs->sr_err;
225	}
226
227	/* we can do (almost) whatever we want with this conn,
228	 * because either it's temporary, or it's marked as binding */
229	if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
230		ch_free( lc->lc_bound_ndn.bv_val );
231		BER_BVZERO( &lc->lc_bound_ndn );
232	}
233	if ( !BER_BVISNULL( &lc->lc_cred ) ) {
234		memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
235		ch_free( lc->lc_cred.bv_val );
236		BER_BVZERO( &lc->lc_cred );
237	}
238	LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
239
240	/* don't add proxyAuthz; set the bindDN */
241	save_o_dn = op->o_dn;
242	save_o_do_not_cache = op->o_do_not_cache;
243	op->o_dn = op->o_req_dn;
244	op->o_do_not_cache = 1;
245
246	ctrls = op->o_ctrls;
247	rc = ldap_back_controls_add( op, rs, lc, &ctrls );
248	op->o_dn = save_o_dn;
249	op->o_do_not_cache = save_o_do_not_cache;
250	if ( rc != LDAP_SUCCESS ) {
251		send_ldap_result( op, rs );
252		ldap_back_release_conn( li, lc );
253		return( rc );
254	}
255
256retry:;
257	/* method is always LDAP_AUTH_SIMPLE if we got here */
258	rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
259			LDAP_SASL_SIMPLE,
260			&op->orb_cred, ctrls, NULL, &msgid );
261	/* FIXME: should we always retry, or only when piping the bind
262	 * in the "override" connection pool? */
263	rc = ldap_back_op_result( lc, op, rs, msgid,
264		li->li_timeout[ SLAP_OP_BIND ],
265		LDAP_BACK_BIND_SERR | retrying );
266	if ( rc == LDAP_UNAVAILABLE && retrying ) {
267		retrying &= ~LDAP_BACK_RETRYING;
268		if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) {
269			goto retry;
270		}
271		if ( !lc )
272			return( rc );
273	}
274
275	ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
276	ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
277	ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
278
279	ldap_back_controls_free( op, rs, &ctrls );
280
281	if ( rc == LDAP_SUCCESS ) {
282		op->o_conn->c_authz_cookie = op->o_bd->be_private;
283
284		/* If defined, proxyAuthz will be used also when
285		 * back-ldap is the authorizing backend; for this
286		 * purpose, after a successful bind the connection
287		 * is left for further binds, and further operations
288		 * on this client connection will use a default
289		 * connection with identity assertion */
290		/* NOTE: use with care */
291		if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
292			ldap_back_release_conn( li, lc );
293			return( rc );
294		}
295
296		/* rebind is now done inside ldap_back_proxy_authz_bind()
297		 * in case of success */
298		LDAP_BACK_CONN_ISBOUND_SET( lc );
299		ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
300
301		if ( !BER_BVISNULL( &lc->lc_cred ) ) {
302			memset( lc->lc_cred.bv_val, 0,
303					lc->lc_cred.bv_len );
304		}
305
306		if ( LDAP_BACK_SAVECRED( li ) ) {
307			ber_bvreplace( &lc->lc_cred, &op->orb_cred );
308			ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
309
310		} else {
311			lc->lc_cred.bv_len = 0;
312		}
313	}
314
315	/* must re-insert if local DN changed as result of bind */
316	if ( !LDAP_BACK_CONN_ISBOUND( lc )
317		|| ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn )
318			&& !LDAP_BACK_PCONN_ISPRIV( lc ) ) )
319	{
320		int		lerr = -1;
321		ldapconn_t	*tmplc;
322
323		/* wait for all other ops to release the connection */
324retry_lock:;
325		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
326		if ( lc->lc_refcnt > 1 ) {
327			ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
328			ldap_pvt_thread_yield();
329			goto retry_lock;
330		}
331
332#if LDAP_BACK_PRINT_CONNTREE > 0
333		ldap_back_print_conntree( li, ">>> ldap_back_bind" );
334#endif /* LDAP_BACK_PRINT_CONNTREE */
335
336		assert( lc->lc_refcnt == 1 );
337		ldap_back_conn_delete( li, lc );
338
339		/* delete all cached connections with the current connection */
340		if ( LDAP_BACK_SINGLECONN( li ) ) {
341			while ( ( tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL )
342			{
343				assert( !LDAP_BACK_PCONN_ISPRIV( lc ) );
344				Debug( LDAP_DEBUG_TRACE,
345					"=>ldap_back_bind: destroying conn %lu (refcnt=%u)\n",
346					lc->lc_conn->c_connid, lc->lc_refcnt );
347
348				if ( tmplc->lc_refcnt != 0 ) {
349					/* taint it */
350					LDAP_BACK_CONN_TAINTED_SET( tmplc );
351					LDAP_BACK_CONN_CACHED_CLEAR( tmplc );
352
353				} else {
354					/*
355					 * Needs a test because the handler may be corrupted,
356					 * and calling ldap_unbind on a corrupted header results
357					 * in a segmentation fault
358					 */
359					ldap_back_conn_free( tmplc );
360				}
361			}
362		}
363
364		if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
365			ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
366			if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
367				LDAP_BACK_PCONN_ROOTDN_SET( lc, op );
368			}
369			lerr = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
370				ldap_back_conndn_cmp, ldap_back_conndn_dup );
371		}
372
373#if LDAP_BACK_PRINT_CONNTREE > 0
374		ldap_back_print_conntree( li, "<<< ldap_back_bind" );
375#endif /* LDAP_BACK_PRINT_CONNTREE */
376
377		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
378		switch ( lerr ) {
379		case 0:
380			LDAP_BACK_CONN_CACHED_SET( lc );
381			break;
382
383		case -1:
384			/* duplicate; someone else successfully bound
385			 * on the same connection with the same identity;
386			 * we can do this because lc_refcnt == 1 */
387			ldap_back_conn_free( lc );
388			lc = NULL;
389		}
390	}
391
392	if ( lc != NULL ) {
393		ldap_back_release_conn( li, lc );
394	}
395
396	return( rc );
397}
398
399/*
400 * ldap_back_conndn_cmp
401 *
402 * compares two ldapconn_t based on the value of the conn pointer
403 * and of the local DN; used by avl stuff for insert, lookup
404 * and direct delete
405 */
406int
407ldap_back_conndn_cmp( const void *c1, const void *c2 )
408{
409	const ldapconn_t	*lc1 = (const ldapconn_t *)c1;
410	const ldapconn_t	*lc2 = (const ldapconn_t *)c2;
411	int rc;
412
413	/* If local DNs don't match, it is definitely not a match */
414	/* For shared sessions, conn is NULL. Only explicitly
415	 * bound sessions will have non-NULL conn.
416	 */
417	rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
418	if ( rc == 0 ) {
419		rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
420	}
421
422	return rc;
423}
424
425/*
426 * ldap_back_conndnlc_cmp
427 *
428 * compares two ldapconn_t based on the value of the conn pointer,
429 * the local DN and the lc pointer; used by avl stuff for insert, lookup
430 * and direct delete
431 */
432static int
433ldap_back_conndnlc_cmp( const void *c1, const void *c2 )
434{
435	const ldapconn_t	*lc1 = (const ldapconn_t *)c1;
436	const ldapconn_t	*lc2 = (const ldapconn_t *)c2;
437	int rc;
438
439	/* If local DNs don't match, it is definitely not a match */
440	/* For shared sessions, conn is NULL. Only explicitly
441	 * bound sessions will have non-NULL conn.
442	 */
443	rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
444	if ( rc == 0 ) {
445		rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
446		if ( rc == 0 ) {
447			rc = SLAP_PTRCMP( lc1, lc2 );
448		}
449	}
450
451	return rc;
452}
453
454/*
455 * ldap_back_conn_cmp
456 *
457 * compares two ldapconn_t based on the value of the conn pointer;
458 * used by avl stuff for delete of all conns with the same connid
459 */
460int
461ldap_back_conn_cmp( const void *c1, const void *c2 )
462{
463	const ldapconn_t	*lc1 = (const ldapconn_t *)c1;
464	const ldapconn_t	*lc2 = (const ldapconn_t *)c2;
465
466	/* For shared sessions, conn is NULL. Only explicitly
467	 * bound sessions will have non-NULL conn.
468	 */
469	return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
470}
471
472/*
473 * ldap_back_conndn_dup
474 *
475 * returns -1 in case a duplicate ldapconn_t has been inserted;
476 * used by avl stuff
477 */
478int
479ldap_back_conndn_dup( void *c1, void *c2 )
480{
481	ldapconn_t	*lc1 = (ldapconn_t *)c1;
482	ldapconn_t	*lc2 = (ldapconn_t *)c2;
483
484	/* Cannot have more than one shared session with same DN */
485	if ( lc1->lc_conn == lc2->lc_conn &&
486		dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) )
487	{
488		return -1;
489	}
490
491	return 0;
492}
493
494static int
495ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock )
496{
497	if ( dolock ) {
498		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
499	}
500
501#if LDAP_BACK_PRINT_CONNTREE > 0
502	ldap_back_print_conntree( li, ">>> ldap_back_freeconn" );
503#endif /* LDAP_BACK_PRINT_CONNTREE */
504
505	(void)ldap_back_conn_delete( li, lc );
506
507	if ( lc->lc_refcnt == 0 ) {
508		ldap_back_conn_free( (void *)lc );
509	}
510
511#if LDAP_BACK_PRINT_CONNTREE > 0
512	ldap_back_print_conntree( li, "<<< ldap_back_freeconn" );
513#endif /* LDAP_BACK_PRINT_CONNTREE */
514
515	if ( dolock ) {
516		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
517	}
518
519	return 0;
520}
521
522#ifdef HAVE_TLS
523static int
524ldap_back_start_tls(
525	LDAP		*ld,
526	int		protocol,
527	int		*is_tls,
528	const char	*url,
529	unsigned	flags,
530	int		timeout,
531	const char	**text )
532{
533	int		rc = LDAP_SUCCESS;
534
535	/* start TLS ("tls-[try-]{start,propagate}" statements) */
536	if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) )
537				&& !ldap_is_ldaps_url( url ) )
538	{
539#ifdef SLAP_STARTTLS_ASYNCHRONOUS
540		/*
541		 * use asynchronous StartTLS
542		 * in case, chase referral (not implemented yet)
543		 */
544		int		msgid;
545
546		if ( protocol == 0 ) {
547			ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
548					(void *)&protocol );
549		}
550
551		if ( protocol < LDAP_VERSION3 ) {
552			/* we should rather bail out... */
553			rc = LDAP_UNWILLING_TO_PERFORM;
554			*text = "invalid protocol version";
555		}
556
557		if ( rc == LDAP_SUCCESS ) {
558			rc = ldap_start_tls( ld, NULL, NULL, &msgid );
559		}
560
561		if ( rc == LDAP_SUCCESS ) {
562			LDAPMessage	*res = NULL;
563			struct timeval	tv;
564
565			if ( timeout ) {
566				tv.tv_sec = timeout;
567				tv.tv_usec = 0;
568			} else {
569				LDAP_BACK_TV_SET( &tv );
570			}
571			rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
572			if ( rc <= 0 ) {
573				rc = LDAP_UNAVAILABLE;
574
575			} else if ( rc == LDAP_RES_EXTENDED ) {
576				struct berval	*data = NULL;
577
578				rc = ldap_parse_extended_result( ld, res,
579						NULL, &data, 0 );
580				if ( rc == LDAP_SUCCESS ) {
581					SlapReply rs;
582					rc = ldap_parse_result( ld, res, &rs.sr_err,
583						NULL, NULL, NULL, NULL, 1 );
584					if ( rc != LDAP_SUCCESS ) {
585						rs.sr_err = rc;
586					}
587					rc = slap_map_api2result( &rs );
588					res = NULL;
589
590					/* FIXME: in case a referral
591					 * is returned, should we try
592					 * using it instead of the
593					 * configured URI? */
594					if ( rc == LDAP_SUCCESS ) {
595						rc = ldap_install_tls( ld );
596
597					} else if ( rc == LDAP_REFERRAL ) {
598						rc = LDAP_UNWILLING_TO_PERFORM;
599						*text = "unwilling to chase referral returned by Start TLS exop";
600					}
601
602					if ( data ) {
603						if ( data->bv_val ) {
604							ber_memfree( data->bv_val );
605						}
606						ber_memfree( data );
607					}
608				}
609
610			} else {
611				rc = LDAP_OTHER;
612			}
613
614			if ( res != NULL ) {
615				ldap_msgfree( res );
616			}
617		}
618#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
619		/*
620		 * use synchronous StartTLS
621		 */
622		rc = ldap_start_tls_s( ld, NULL, NULL );
623#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
624
625		/* if StartTLS is requested, only attempt it if the URL
626		 * is not "ldaps://"; this may occur not only in case
627		 * of misconfiguration, but also when used in the chain
628		 * overlay, where the "uri" can be parsed out of a referral */
629		switch ( rc ) {
630		case LDAP_SUCCESS:
631			*is_tls = 1;
632			break;
633
634		case LDAP_SERVER_DOWN:
635			break;
636
637		default:
638			if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) {
639				*text = "could not start TLS";
640				break;
641			}
642
643			/* in case Start TLS is not critical */
644			*is_tls = 0;
645			rc = LDAP_SUCCESS;
646			break;
647		}
648
649	} else {
650		*is_tls = 0;
651	}
652
653	return rc;
654}
655#endif /* HAVE_TLS */
656
657static int
658ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
659{
660	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
661	int		version;
662	LDAP		*ld = NULL;
663#ifdef HAVE_TLS
664	int		is_tls = op->o_conn->c_is_tls;
665	int		flags = li->li_flags;
666	time_t		lctime = (time_t)(-1);
667	slap_bindconf *sb;
668#endif /* HAVE_TLS */
669
670	ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
671	rs->sr_err = ldap_initialize( &ld, li->li_uri );
672	ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
673	if ( rs->sr_err != LDAP_SUCCESS ) {
674		goto error_return;
675	}
676
677	if ( li->li_urllist_f ) {
678		ldap_set_urllist_proc( ld, li->li_urllist_f, li->li_urllist_p );
679	}
680
681	/* Set LDAP version. This will always succeed: If the client
682	 * bound with a particular version, then so can we.
683	 */
684	if ( li->li_version != 0 ) {
685		version = li->li_version;
686
687	} else if ( op->o_protocol != 0 ) {
688		version = op->o_protocol;
689
690	} else {
691		/* assume it's an internal op; set to LDAPv3 */
692		version = LDAP_VERSION3;
693	}
694	ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version );
695
696	/* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
697	ldap_set_option( ld, LDAP_OPT_REFERRALS,
698		LDAP_BACK_CHASE_REFERRALS( li ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
699
700	if ( li->li_network_timeout > 0 ) {
701		struct timeval		tv;
702
703		tv.tv_sec = li->li_network_timeout;
704		tv.tv_usec = 0;
705		ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (const void *)&tv );
706	}
707
708	/* turn on network keepalive, if configured so */
709	slap_client_keepalive(ld, &li->li_tls.sb_keepalive);
710
711	if ( li->li_tls.sb_tcp_user_timeout > 0 ) {
712		ldap_set_option( ld, LDAP_OPT_TCP_USER_TIMEOUT,
713				&li->li_tls.sb_tcp_user_timeout );
714	}
715
716#ifdef HAVE_TLS
717	if ( LDAP_BACK_CONN_ISPRIV( lc ) ) {
718		/* See "rationale" comment in ldap_back_getconn() */
719		if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
720			 li->li_idassert_authmethod != LDAP_AUTH_NONE )
721			sb = &li->li_idassert.si_bc;
722		else
723			sb = &li->li_acl;
724
725	} else if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
726		sb = &li->li_idassert.si_bc;
727
728	} else {
729		sb = &li->li_tls;
730	}
731
732	bindconf_tls_set( sb, ld );
733
734	/* if required by the bindconf configuration, force TLS */
735	if ( ( sb == &li->li_acl || sb == &li->li_idassert.si_bc ) &&
736		sb->sb_tls_ctx )
737	{
738		flags |= LDAP_BACK_F_USE_TLS;
739	}
740
741	ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
742	assert( li->li_uri_mutex_do_not_lock == 0 );
743	li->li_uri_mutex_do_not_lock = 1;
744	rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
745			li->li_uri, flags, li->li_timeout[ SLAP_OP_BIND ], &rs->sr_text );
746	li->li_uri_mutex_do_not_lock = 0;
747	ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
748	if ( rs->sr_err != LDAP_SUCCESS ) {
749		ldap_unbind_ext( ld, NULL, NULL );
750		rs->sr_text = "Start TLS failed";
751		goto error_return;
752
753	} else if ( li->li_idle_timeout ) {
754		/* only touch when activity actually took place... */
755		lctime = op->o_time;
756	}
757#endif /* HAVE_TLS */
758
759	lc->lc_ld = ld;
760	lc->lc_refcnt = 1;
761#ifdef HAVE_TLS
762	if ( is_tls ) {
763		LDAP_BACK_CONN_ISTLS_SET( lc );
764	} else {
765		LDAP_BACK_CONN_ISTLS_CLEAR( lc );
766	}
767	if ( lctime != (time_t)(-1) ) {
768		lc->lc_time = lctime;
769	}
770#endif /* HAVE_TLS */
771
772error_return:;
773	if ( rs->sr_err != LDAP_SUCCESS ) {
774		rs->sr_err = slap_map_api2result( rs );
775		if ( sendok & LDAP_BACK_SENDERR ) {
776			if ( rs->sr_text == NULL ) {
777				rs->sr_text = "Proxy connection initialization failed";
778			}
779			send_ldap_result( op, rs );
780		}
781
782	} else {
783		if ( li->li_conn_ttl > 0 ) {
784			lc->lc_create_time = op->o_time;
785		}
786	}
787
788	return rs->sr_err;
789}
790
791static ldapconn_t *
792ldap_back_getconn(
793	Operation		*op,
794	SlapReply		*rs,
795	ldap_back_send_t	sendok,
796	struct berval		*binddn,
797	struct berval		*bindcred )
798{
799	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
800	ldapconn_t	*lc = NULL,
801			lc_curr = {{ 0 }};
802	int		refcnt = 1,
803			lookupconn = !( sendok & LDAP_BACK_BINDING );
804
805	/* if the server is quarantined, and
806	 * - the current interval did not expire yet, or
807	 * - no more retries should occur,
808	 * don't return the connection */
809	if ( li->li_isquarantined ) {
810		slap_retry_info_t	*ri = &li->li_quarantine;
811		int			dont_retry = 1;
812
813		if ( li->li_quarantine.ri_interval ) {
814			ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
815			if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
816				dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
817					|| slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
818				if ( !dont_retry ) {
819					Debug( LDAP_DEBUG_ANY,
820						"%s: ldap_back_getconn quarantine "
821						"retry block #%d try #%d.\n",
822						op->o_log_prefix, ri->ri_idx, ri->ri_count );
823					li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
824				}
825			}
826			ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
827		}
828
829		if ( dont_retry ) {
830			rs->sr_err = LDAP_UNAVAILABLE;
831			if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
832				rs->sr_text = "Target is quarantined";
833				send_ldap_result( op, rs );
834			}
835			return NULL;
836		}
837	}
838
839	/* Internal searches are privileged and shared. So is root. */
840	if ( op->o_do_not_cache || be_isroot( op ) ) {
841		LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
842		lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
843		LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
844
845	} else {
846		struct berval	tmpbinddn,
847				tmpbindcred,
848				save_o_dn,
849				save_o_ndn;
850		int		isproxyauthz;
851
852		/* need cleanup */
853		if ( binddn == NULL ) {
854			binddn = &tmpbinddn;
855		}
856		if ( bindcred == NULL ) {
857			bindcred = &tmpbindcred;
858		}
859		if ( op->o_tag == LDAP_REQ_BIND ) {
860			save_o_dn = op->o_dn;
861			save_o_ndn = op->o_ndn;
862			op->o_dn = op->o_req_dn;
863			op->o_ndn = op->o_req_ndn;
864		}
865		isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred );
866		if ( op->o_tag == LDAP_REQ_BIND ) {
867			op->o_dn = save_o_dn;
868			op->o_ndn = save_o_ndn;
869		}
870		if ( isproxyauthz == -1 ) {
871			return NULL;
872		}
873
874		lc_curr.lc_local_ndn = op->o_ndn;
875		/* Explicit binds must not be shared;
876		 * however, explicit binds are piped in a special connection
877		 * when idassert is to occur with "override" set */
878		if ( op->o_tag == LDAP_REQ_BIND && !isproxyauthz ) {
879			lc_curr.lc_conn = op->o_conn;
880
881		} else {
882			if ( isproxyauthz && !( sendok & LDAP_BACK_BINDING ) ) {
883				lc_curr.lc_local_ndn = *binddn;
884				LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
885				LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
886
887			} else if ( isproxyauthz && ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) {
888				lc_curr.lc_local_ndn = slap_empty_bv;
889				LDAP_BACK_PCONN_BIND_SET( &lc_curr, op );
890				LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
891				lookupconn = 1;
892
893			} else if ( SLAP_IS_AUTHZ_BACKEND( op ) ) {
894				lc_curr.lc_conn = op->o_conn;
895
896			} else {
897				LDAP_BACK_PCONN_ANON_SET( &lc_curr, op );
898			}
899		}
900	}
901
902	/* Explicit Bind requests always get their own conn */
903	if ( lookupconn ) {
904retry_lock:
905		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
906		if ( LDAP_BACK_PCONN_ISPRIV( &lc_curr ) ) {
907			/* lookup a conn that's not binding */
908			LDAP_TAILQ_FOREACH( lc,
909				&li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv,
910				lc_q )
911			{
912				if ( !LDAP_BACK_CONN_BINDING( lc ) && lc->lc_refcnt == 0 ) {
913					break;
914				}
915			}
916
917			if ( lc != NULL ) {
918				if ( lc != LDAP_TAILQ_LAST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
919					lc_conn_priv_q ) )
920				{
921					LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
922						lc, lc_q );
923					LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
924					LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
925						lc, lc_q );
926				}
927
928			} else if ( !LDAP_BACK_USE_TEMPORARIES( li )
929				&& li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_num == li->li_conn_priv_max )
930			{
931				lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv );
932			}
933
934		} else {
935
936			/* Searches for a ldapconn in the avl tree */
937			lc = (ldapconn_t *)ldap_tavl_find( li->li_conninfo.lai_tree,
938					(caddr_t)&lc_curr, ldap_back_conndn_cmp );
939		}
940
941		if ( lc != NULL ) {
942			/* Don't reuse connections while they're still binding */
943			if ( LDAP_BACK_CONN_BINDING( lc ) ) {
944				if ( !LDAP_BACK_USE_TEMPORARIES( li ) ) {
945					ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
946
947					ldap_pvt_thread_yield();
948					goto retry_lock;
949				}
950				lc = NULL;
951			}
952
953			if ( lc != NULL ) {
954				if ( op->o_tag == LDAP_REQ_BIND ) {
955					/* right now, this is the only possible case */
956					assert( ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) );
957					LDAP_BACK_CONN_BINDING_SET( lc );
958				}
959
960				refcnt = ++lc->lc_refcnt;
961			}
962		}
963		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
964	}
965
966	/* Looks like we didn't get a bind. Open a new session... */
967	if ( lc == NULL ) {
968		lc = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
969		lc->lc_flags = li->li_flags;
970		lc->lc_lcflags = lc_curr.lc_lcflags;
971		lc->lc_ldapinfo = li;
972		if ( ldap_back_prepare_conn( lc, op, rs, sendok ) != LDAP_SUCCESS ) {
973			ch_free( lc );
974			return NULL;
975		}
976
977		if ( sendok & LDAP_BACK_BINDING ) {
978			LDAP_BACK_CONN_BINDING_SET( lc );
979		}
980
981		lc->lc_conn = lc_curr.lc_conn;
982		ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
983
984		/*
985		 * the rationale is: connections as the rootdn are privileged,
986		 * so li_acl is to be used; however, in some cases
987		 * one already configured identity assertion with a highly
988		 * privileged idassert_authcDN, so if li_acl is not configured
989		 * and idassert is, use idassert instead.
990		 *
991		 * might change in the future, because it's preferable
992		 * to make clear what identity is being used, since
993		 * the only drawback is that one risks to configure
994		 * the same identity twice...
995		 */
996		if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
997			if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
998				 li->li_idassert_authmethod != LDAP_AUTH_NONE ) {
999				ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
1000				ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
1001
1002			} else {
1003				ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
1004				ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
1005			}
1006			LDAP_BACK_CONN_ISPRIV_SET( lc );
1007
1008		} else if ( LDAP_BACK_CONN_ISIDASSERT( &lc_curr ) ) {
1009			if ( !LDAP_BACK_PCONN_ISBIND( &lc_curr ) ) {
1010				ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
1011				ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
1012			}
1013			LDAP_BACK_CONN_ISIDASSERT_SET( lc );
1014
1015		} else {
1016			BER_BVZERO( &lc->lc_cred );
1017			BER_BVZERO( &lc->lc_bound_ndn );
1018			if ( !BER_BVISEMPTY( &op->o_ndn )
1019				&& SLAP_IS_AUTHZ_BACKEND( op ) )
1020			{
1021				ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
1022			}
1023		}
1024
1025#ifdef HAVE_TLS
1026		/* if start TLS failed but it was not mandatory,
1027		 * check if the non-TLS connection was already
1028		 * in cache; in case, destroy the newly created
1029		 * connection and use the existing one */
1030		if ( LDAP_BACK_PCONN_ISTLS( lc )
1031				&& !ldap_tls_inplace( lc->lc_ld ) )
1032		{
1033			ldapconn_t	*tmplc = NULL;
1034			int		idx = LDAP_BACK_CONN2PRIV( &lc_curr ) - 1;
1035
1036			ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1037			LDAP_TAILQ_FOREACH( tmplc,
1038				&li->li_conn_priv[ idx ].lic_priv,
1039				lc_q )
1040			{
1041				if ( !LDAP_BACK_CONN_BINDING( tmplc ) ) {
1042					break;
1043				}
1044			}
1045
1046			if ( tmplc != NULL ) {
1047				refcnt = ++tmplc->lc_refcnt;
1048				ldap_back_conn_free( lc );
1049				lc = tmplc;
1050			}
1051			ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1052
1053			if ( tmplc != NULL ) {
1054				goto done;
1055			}
1056		}
1057#endif /* HAVE_TLS */
1058
1059		/* Inserts the newly created ldapconn in the avl tree */
1060		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1061
1062		LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1063		lc->lc_connid = li->li_conn_nextid++;
1064
1065		assert( lc->lc_refcnt == 1 );
1066
1067#if LDAP_BACK_PRINT_CONNTREE > 0
1068		ldap_back_print_conntree( li, ">>> ldap_back_getconn(insert)" );
1069#endif /* LDAP_BACK_PRINT_CONNTREE */
1070
1071		if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
1072			if ( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num < li->li_conn_priv_max ) {
1073				LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
1074				li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num++;
1075				LDAP_BACK_CONN_CACHED_SET( lc );
1076
1077			} else {
1078				LDAP_BACK_CONN_TAINTED_SET( lc );
1079			}
1080			rs->sr_err = 0;
1081
1082		} else {
1083			rs->sr_err = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
1084				ldap_back_conndn_cmp, ldap_back_conndn_dup );
1085			LDAP_BACK_CONN_CACHED_SET( lc );
1086		}
1087
1088#if LDAP_BACK_PRINT_CONNTREE > 0
1089		ldap_back_print_conntree( li, "<<< ldap_back_getconn(insert)" );
1090#endif /* LDAP_BACK_PRINT_CONNTREE */
1091
1092		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1093
1094		Debug(LDAP_DEBUG_TRACE,
1095		      "=>ldap_back_getconn: %s: lc=%p inserted refcnt=%u rc=%d\n",
1096		      op->o_log_prefix, (void *)lc, refcnt,
1097		      rs->sr_err );
1098
1099		if ( !LDAP_BACK_PCONN_ISPRIV( lc ) ) {
1100			/* Err could be -1 in case a duplicate ldapconn is inserted */
1101			switch ( rs->sr_err ) {
1102			case 0:
1103				break;
1104
1105			case -1:
1106				LDAP_BACK_CONN_CACHED_CLEAR( lc );
1107				if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( li ) ) {
1108					/* duplicate: free and try to get the newly created one */
1109					ldap_back_conn_free( lc );
1110					lc = NULL;
1111					goto retry_lock;
1112				}
1113
1114				/* taint connection, so that it'll be freed when released */
1115				LDAP_BACK_CONN_TAINTED_SET( lc );
1116				break;
1117
1118			default:
1119				LDAP_BACK_CONN_CACHED_CLEAR( lc );
1120				ldap_back_conn_free( lc );
1121				rs->sr_err = LDAP_OTHER;
1122				rs->sr_text = "Proxy bind collision";
1123				if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
1124					send_ldap_result( op, rs );
1125				}
1126				return NULL;
1127			}
1128		}
1129		ldap_back_schedule_conn_expiry( li, lc );
1130
1131	} else {
1132		int	expiring = 0;
1133
1134		if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
1135			|| ( li->li_conn_ttl != 0 && op->o_time > lc->lc_create_time + li->li_conn_ttl ) )
1136		{
1137			expiring = 1;
1138
1139			/* let it be used, but taint/delete it so that
1140			 * no-one else can look it up any further */
1141			ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1142
1143#if LDAP_BACK_PRINT_CONNTREE > 0
1144			ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
1145#endif /* LDAP_BACK_PRINT_CONNTREE */
1146
1147			(void)ldap_back_conn_delete( li, lc );
1148			LDAP_BACK_CONN_TAINTED_SET( lc );
1149
1150#if LDAP_BACK_PRINT_CONNTREE > 0
1151			ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
1152#endif /* LDAP_BACK_PRINT_CONNTREE */
1153
1154			ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1155		}
1156
1157		Debug(LDAP_DEBUG_TRACE,
1158		      "=>ldap_back_getconn: conn %p fetched refcnt=%u%s.\n",
1159		      (void *)lc, refcnt, expiring ? " expiring" : "" );
1160	}
1161
1162#ifdef HAVE_TLS
1163done:;
1164#endif /* HAVE_TLS */
1165
1166	return lc;
1167}
1168
1169void
1170ldap_back_release_conn_lock(
1171	ldapinfo_t		*li,
1172	ldapconn_t		**lcp,
1173	int			dolock )
1174{
1175
1176	ldapconn_t	*lc = *lcp;
1177
1178	if ( dolock ) {
1179		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1180	}
1181	assert( lc->lc_refcnt > 0 );
1182	LDAP_BACK_CONN_BINDING_CLEAR( lc );
1183	lc->lc_refcnt--;
1184	if ( LDAP_BACK_CONN_TAINTED( lc ) ) {
1185		ldap_back_freeconn( li, lc, 0 );
1186		*lcp = NULL;
1187	}
1188	if ( dolock ) {
1189		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1190	}
1191}
1192
1193void
1194ldap_back_quarantine(
1195	Operation	*op,
1196	SlapReply	*rs )
1197{
1198	ldapinfo_t		*li = (ldapinfo_t *)op->o_bd->be_private;
1199
1200	slap_retry_info_t	*ri = &li->li_quarantine;
1201
1202	ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
1203
1204	if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1205		time_t		new_last = slap_get_time();
1206
1207		switch ( li->li_isquarantined ) {
1208		case LDAP_BACK_FQ_NO:
1209			if ( ri->ri_last == new_last ) {
1210				goto done;
1211			}
1212
1213			Debug( LDAP_DEBUG_ANY,
1214				"%s: ldap_back_quarantine enter.\n",
1215				op->o_log_prefix );
1216
1217			ri->ri_idx = 0;
1218			ri->ri_count = 0;
1219			break;
1220
1221		case LDAP_BACK_FQ_RETRYING:
1222			Debug( LDAP_DEBUG_ANY,
1223				"%s: ldap_back_quarantine block #%d try #%d failed.\n",
1224				op->o_log_prefix, ri->ri_idx, ri->ri_count );
1225
1226			++ri->ri_count;
1227			if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
1228				&& ri->ri_count == ri->ri_num[ ri->ri_idx ] )
1229			{
1230				ri->ri_count = 0;
1231				++ri->ri_idx;
1232			}
1233			break;
1234
1235		default:
1236			goto done;
1237		}
1238
1239		li->li_isquarantined = LDAP_BACK_FQ_YES;
1240		ri->ri_last = new_last;
1241
1242	} else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
1243		if ( ri->ri_last == slap_get_time() ) {
1244			goto done;
1245		}
1246
1247		Debug( LDAP_DEBUG_ANY,
1248			"%s: ldap_back_quarantine exit (%d) err=%d.\n",
1249			op->o_log_prefix, li->li_isquarantined, rs->sr_err );
1250
1251		if ( li->li_quarantine_f ) {
1252			(void)li->li_quarantine_f( li, li->li_quarantine_p );
1253		}
1254
1255		ri->ri_count = 0;
1256		ri->ri_idx = 0;
1257		li->li_isquarantined = LDAP_BACK_FQ_NO;
1258	}
1259
1260done:;
1261	ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
1262}
1263
1264static int
1265ldap_back_dobind_cb(
1266	Operation *op,
1267	SlapReply *rs
1268)
1269{
1270	ber_tag_t *tptr = op->o_callback->sc_private;
1271	op->o_tag = *tptr;
1272	rs->sr_tag = slap_req2res( op->o_tag );
1273
1274	return SLAP_CB_CONTINUE;
1275}
1276
1277/*
1278 * ldap_back_dobind_int
1279 *
1280 * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
1281 */
1282static int
1283ldap_back_dobind_int(
1284	ldapconn_t		**lcp,
1285	Operation		*op,
1286	SlapReply		*rs,
1287	ldap_back_send_t	sendok,
1288	int			retries,
1289	int			dolock )
1290{
1291	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
1292
1293	ldapconn_t	*lc;
1294	struct berval	binddn = slap_empty_bv,
1295			bindcred = slap_empty_bv;
1296
1297	int		rc = 0,
1298			isbound,
1299			binding = 0;
1300	ber_int_t	msgid;
1301	ber_tag_t	o_tag = op->o_tag;
1302	slap_callback cb = {0};
1303	char		*tmp_dn;
1304
1305	assert( lcp != NULL );
1306	assert( retries >= 0 );
1307
1308	if ( sendok & LDAP_BACK_GETCONN ) {
1309		assert( *lcp == NULL );
1310
1311		lc = ldap_back_getconn( op, rs, sendok, &binddn, &bindcred );
1312		if ( lc == NULL ) {
1313			return 0;
1314		}
1315		*lcp = lc;
1316
1317	} else {
1318		lc = *lcp;
1319	}
1320
1321	assert( lc != NULL );
1322
1323retry_lock:;
1324 	if ( dolock ) {
1325 		ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1326 	}
1327
1328 	if ( binding == 0 ) {
1329		/* check if already bound */
1330		rc = isbound = LDAP_BACK_CONN_ISBOUND( lc );
1331		if ( isbound ) {
1332 			if ( dolock ) {
1333 				ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1334 			}
1335			return rc;
1336		}
1337
1338		if ( LDAP_BACK_CONN_BINDING( lc ) ) {
1339			/* if someone else is about to bind it, give up and retry */
1340 			if ( dolock ) {
1341 				ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1342 			}
1343			ldap_pvt_thread_yield();
1344			goto retry_lock;
1345
1346		} else {
1347			/* otherwise this thread will bind it */
1348 			LDAP_BACK_CONN_BINDING_SET( lc );
1349			binding = 1;
1350		}
1351	}
1352
1353 	if ( dolock ) {
1354 		ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1355 	}
1356
1357	/*
1358	 * FIXME: we need to let clients use proxyAuthz
1359	 * otherwise we cannot do symmetric pools of servers;
1360	 * we have to live with the fact that a user can
1361	 * authorize itself as any ID that is allowed
1362	 * by the authzTo directive of the "proxyauthzdn".
1363	 */
1364	/*
1365	 * NOTE: current Proxy Authorization specification
1366	 * and implementation do not allow proxy authorization
1367	 * control to be provided with Bind requests
1368	 */
1369	/*
1370	 * if no bind took place yet, but the connection is bound
1371	 * and the "idassert-authcDN" (or other ID) is set,
1372	 * then bind as the asserting identity and explicitly
1373	 * add the proxyAuthz control to every operation with the
1374	 * dn bound to the connection as control value.
1375	 * This is done also if this is the authorizing backend,
1376	 * but the "override" flag is given to idassert.
1377	 * It allows to use SASL bind and yet proxyAuthz users
1378	 */
1379	op->o_tag = LDAP_REQ_BIND;
1380	cb.sc_next = op->o_callback;
1381	cb.sc_private = &o_tag;
1382	cb.sc_response = ldap_back_dobind_cb;
1383	op->o_callback = &cb;
1384
1385	if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
1386		if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) {
1387			/* if we got here, it shouldn't return result */
1388			rc = ldap_back_is_proxy_authz( op, rs,
1389				LDAP_BACK_DONTSEND, &binddn, &bindcred );
1390			if ( rc != 1 ) {
1391				Debug( LDAP_DEBUG_ANY, "Error: ldap_back_is_proxy_authz "
1392					"returned %d, misconfigured URI?\n", rc );
1393				rs->sr_err = LDAP_OTHER;
1394				rs->sr_text = "misconfigured URI?";
1395				LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1396				if ( sendok & LDAP_BACK_SENDERR ) {
1397					send_ldap_result( op, rs );
1398				}
1399				goto done;
1400			}
1401		}
1402		rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred );
1403		goto done;
1404	}
1405
1406#ifdef HAVE_CYRUS_SASL
1407	if ( LDAP_BACK_CONN_ISPRIV( lc )) {
1408	slap_bindconf *sb;
1409	if ( li->li_acl_authmethod != LDAP_AUTH_NONE )
1410		sb = &li->li_acl;
1411	else
1412		sb = &li->li_idassert.si_bc;
1413
1414	if ( sb->sb_method == LDAP_AUTH_SASL ) {
1415		void		*defaults = NULL;
1416
1417		if ( sb->sb_secprops != NULL ) {
1418			rc = ldap_set_option( lc->lc_ld,
1419				LDAP_OPT_X_SASL_SECPROPS, sb->sb_secprops );
1420
1421			if ( rc != LDAP_OPT_SUCCESS ) {
1422				Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
1423					"(SECPROPS,\"%s\") failed!\n",
1424					sb->sb_secprops );
1425				goto done;
1426			}
1427		}
1428
1429		defaults = lutil_sasl_defaults( lc->lc_ld,
1430				sb->sb_saslmech.bv_val,
1431				sb->sb_realm.bv_val,
1432				sb->sb_authcId.bv_val,
1433				sb->sb_cred.bv_val,
1434				NULL );
1435		if ( defaults == NULL ) {
1436			rs->sr_err = LDAP_OTHER;
1437			LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1438			if ( sendok & LDAP_BACK_SENDERR ) {
1439				send_ldap_result( op, rs );
1440			}
1441			goto done;
1442		}
1443
1444		rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
1445				sb->sb_binddn.bv_val,
1446				sb->sb_saslmech.bv_val, NULL, NULL,
1447				LDAP_SASL_QUIET, lutil_sasl_interact,
1448				defaults );
1449
1450		ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
1451		ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
1452		ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
1453
1454		lutil_sasl_freedefs( defaults );
1455
1456		switch ( rs->sr_err ) {
1457		case LDAP_SUCCESS:
1458			LDAP_BACK_CONN_ISBOUND_SET( lc );
1459			break;
1460
1461		case LDAP_LOCAL_ERROR:
1462			/* list client API error codes that require
1463			 * to taint the connection */
1464			/* FIXME: should actually retry? */
1465			LDAP_BACK_CONN_TAINTED_SET( lc );
1466
1467			/* fallthru */
1468
1469		default:
1470			LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
1471			rs->sr_err = slap_map_api2result( rs );
1472			if ( sendok & LDAP_BACK_SENDERR ) {
1473				send_ldap_result( op, rs );
1474			}
1475			break;
1476		}
1477
1478		if ( LDAP_BACK_QUARANTINE( li ) ) {
1479			ldap_back_quarantine( op, rs );
1480		}
1481
1482		goto done;
1483	}
1484	}
1485#endif /* HAVE_CYRUS_SASL */
1486
1487retry:;
1488	if ( BER_BVISNULL( &lc->lc_cred ) ) {
1489		tmp_dn = "";
1490		if ( !BER_BVISNULL( &lc->lc_bound_ndn ) && !BER_BVISEMPTY( &lc->lc_bound_ndn ) ) {
1491			Debug( LDAP_DEBUG_ANY, "%s ldap_back_dobind_int: DN=\"%s\" without creds, binding anonymously",
1492				op->o_log_prefix, lc->lc_bound_ndn.bv_val );
1493		}
1494
1495	} else {
1496		tmp_dn = lc->lc_bound_ndn.bv_val;
1497	}
1498	rs->sr_err = ldap_sasl_bind( lc->lc_ld,
1499			tmp_dn,
1500			LDAP_SASL_SIMPLE, &lc->lc_cred,
1501			NULL, NULL, &msgid );
1502
1503	ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
1504	ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
1505	ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
1506
1507	if ( rs->sr_err == LDAP_SERVER_DOWN ) {
1508		if ( retries != LDAP_BACK_RETRY_NEVER ) {
1509			if ( dolock ) {
1510				ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1511			}
1512
1513			assert( lc->lc_refcnt > 0 );
1514			if ( lc->lc_refcnt == 1 ) {
1515				ldap_unbind_ext( lc->lc_ld, NULL, NULL );
1516				lc->lc_ld = NULL;
1517
1518				/* lc here must be the regular lc, reset and ready for init */
1519				rs->sr_err = ldap_back_prepare_conn( lc, op, rs, sendok );
1520				if ( rs->sr_err != LDAP_SUCCESS ) {
1521					sendok &= ~LDAP_BACK_SENDERR;
1522					lc->lc_refcnt = 0;
1523				}
1524			}
1525
1526			if ( dolock ) {
1527				ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1528			}
1529
1530			if ( rs->sr_err == LDAP_SUCCESS ) {
1531				if ( retries > 0 ) {
1532					retries--;
1533				}
1534				goto retry;
1535			}
1536		}
1537
1538		assert( lc->lc_refcnt == 1 );
1539		lc->lc_refcnt = 0;
1540		ldap_back_freeconn( li, lc, dolock );
1541		*lcp = NULL;
1542		rs->sr_err = slap_map_api2result( rs );
1543
1544		if ( LDAP_BACK_QUARANTINE( li ) ) {
1545			ldap_back_quarantine( op, rs );
1546		}
1547
1548		if ( rs->sr_err != LDAP_SUCCESS &&
1549			( sendok & LDAP_BACK_SENDERR ) )
1550		{
1551			if ( op->o_callback == &cb )
1552				op->o_callback = cb.sc_next;
1553			op->o_tag = o_tag;
1554			rs->sr_text = "Proxy can't contact remote server";
1555			send_ldap_result( op, rs );
1556			/* if we originally bound and wanted rebind-as-user, must drop
1557			 * the connection now because we just discarded the credentials.
1558			 * ITS#7464, #8142
1559			 */
1560			if ( LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
1561				rs->sr_err = SLAPD_DISCONNECT;
1562		}
1563
1564		rc = 0;
1565		goto func_leave;
1566	}
1567
1568	rc = ldap_back_op_result( lc, op, rs, msgid,
1569		-1, ( sendok | LDAP_BACK_BINDING ) );
1570	if ( rc == LDAP_SUCCESS ) {
1571		LDAP_BACK_CONN_ISBOUND_SET( lc );
1572	}
1573
1574done:;
1575	LDAP_BACK_CONN_BINDING_CLEAR( lc );
1576	rc = LDAP_BACK_CONN_ISBOUND( lc );
1577	if ( !rc ) {
1578		ldap_back_release_conn_lock( li, lcp, dolock );
1579
1580	} else if ( LDAP_BACK_SAVECRED( li ) ) {
1581		ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
1582	}
1583
1584func_leave:;
1585	if ( op->o_callback == &cb )
1586		op->o_callback = cb.sc_next;
1587	op->o_tag = o_tag;
1588
1589	return rc;
1590}
1591
1592/*
1593 * ldap_back_dobind
1594 *
1595 * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
1596 */
1597int
1598ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1599{
1600	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
1601
1602	return ldap_back_dobind_int( lcp, op, rs,
1603		( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 );
1604}
1605
1606/*
1607 * ldap_back_default_rebind
1608 *
1609 * This is a callback used for chasing referrals using the same
1610 * credentials as the original user on this session.
1611 */
1612int
1613ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
1614	ber_int_t msgid, void *params )
1615{
1616	ldapconn_t	*lc = (ldapconn_t *)params;
1617
1618#ifdef HAVE_TLS
1619	/* ... otherwise we couldn't get here */
1620	assert( lc != NULL );
1621
1622	if ( !ldap_tls_inplace( ld ) ) {
1623		int		is_tls = LDAP_BACK_CONN_ISTLS( lc ),
1624				rc;
1625		const char	*text = NULL;
1626
1627		rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags,
1628			lc->lc_ldapinfo->li_timeout[ SLAP_OP_BIND ], &text );
1629		if ( rc != LDAP_SUCCESS ) {
1630			return rc;
1631		}
1632	}
1633#endif /* HAVE_TLS */
1634
1635	/* FIXME: add checks on the URL/identity? */
1636	/* TODO: would like to count this bind operation for monitoring
1637	 * too, but where do we get the ldapinfo_t? */
1638
1639	return ldap_sasl_bind_s( ld,
1640			BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
1641			LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
1642}
1643
1644/*
1645 * ldap_back_default_urllist
1646 */
1647int
1648ldap_back_default_urllist(
1649	LDAP		*ld,
1650	LDAPURLDesc	**urllist,
1651	LDAPURLDesc	**url,
1652	void		*params )
1653{
1654	ldapinfo_t	*li = (ldapinfo_t *)params;
1655	LDAPURLDesc	**urltail;
1656
1657	if ( urllist == url ) {
1658		return LDAP_SUCCESS;
1659	}
1660
1661	for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
1662		/* count */ ;
1663
1664	*urltail = *urllist;
1665	*urllist = *url;
1666	*url = NULL;
1667
1668	if ( !li->li_uri_mutex_do_not_lock ) {
1669		ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
1670	}
1671
1672	if ( li->li_uri ) {
1673		ch_free( li->li_uri );
1674	}
1675
1676	ldap_get_option( ld, LDAP_OPT_URI, (void *)&li->li_uri );
1677
1678	if ( !li->li_uri_mutex_do_not_lock ) {
1679		ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
1680	}
1681
1682	return LDAP_SUCCESS;
1683}
1684
1685int
1686ldap_back_cancel(
1687		ldapconn_t		*lc,
1688		Operation		*op,
1689		SlapReply		*rs,
1690		ber_int_t		msgid,
1691		ldap_back_send_t	sendok )
1692{
1693	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
1694
1695	/* default behavior */
1696	if ( LDAP_BACK_ABANDON( li ) ) {
1697		return ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
1698	}
1699
1700	if ( LDAP_BACK_IGNORE( li ) ) {
1701		return ldap_pvt_discard( lc->lc_ld, msgid );
1702	}
1703
1704	if ( LDAP_BACK_CANCEL( li ) ) {
1705		/* FIXME: asynchronous? */
1706		return ldap_cancel_s( lc->lc_ld, msgid, NULL, NULL );
1707	}
1708
1709	assert( 0 );
1710
1711	return LDAP_OTHER;
1712}
1713
1714int
1715ldap_back_op_result(
1716		ldapconn_t		*lc,
1717		Operation		*op,
1718		SlapReply		*rs,
1719		ber_int_t		msgid,
1720		time_t			timeout,
1721		ldap_back_send_t	sendok )
1722{
1723	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
1724
1725	char		*match = NULL;
1726	char		*text = NULL;
1727	char		**refs = NULL;
1728	LDAPControl	**ctrls = NULL;
1729
1730	rs->sr_text = NULL;
1731	rs->sr_matched = NULL;
1732	rs->sr_ref = NULL;
1733	rs->sr_ctrls = NULL;
1734
1735	/* if the error recorded in the reply corresponds
1736	 * to a successful state, get the error from the
1737	 * remote server response */
1738	if ( LDAP_ERR_OK( rs->sr_err ) ) {
1739		int		rc;
1740		struct timeval	tv;
1741		LDAPMessage	*res = NULL;
1742		time_t		stoptime = (time_t)(-1);
1743		int		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
1744					LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
1745		const char	*timeout_text = "Operation timed out";
1746
1747		/* if timeout is not specified, compute and use
1748		 * the one specific to the ongoing operation */
1749		if ( timeout == (time_t)(-1) ) {
1750			slap_op_t	opidx = slap_req2op( op->o_tag );
1751
1752			if ( opidx == SLAP_OP_SEARCH ) {
1753				if ( op->ors_tlimit <= 0 ) {
1754					timeout = 0;
1755
1756				} else {
1757					timeout = op->ors_tlimit;
1758					timeout_err = LDAP_TIMELIMIT_EXCEEDED;
1759					timeout_text = NULL;
1760				}
1761
1762			} else {
1763				timeout = li->li_timeout[ opidx ];
1764			}
1765		}
1766
1767		/* better than nothing :) */
1768		if ( timeout == 0 ) {
1769			if ( li->li_idle_timeout ) {
1770				timeout = li->li_idle_timeout;
1771
1772			} else if ( li->li_conn_ttl ) {
1773				timeout = li->li_conn_ttl;
1774			}
1775		}
1776
1777		if ( timeout ) {
1778			stoptime = op->o_time + timeout;
1779		}
1780
1781		LDAP_BACK_TV_SET( &tv );
1782
1783retry:;
1784		/* if result parsing fails, note the failure reason */
1785		rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
1786		switch ( rc ) {
1787		case 0:
1788			if ( timeout && slap_get_time() > stoptime ) {
1789				if ( sendok & LDAP_BACK_BINDING ) {
1790					ldap_unbind_ext( lc->lc_ld, NULL, NULL );
1791					lc->lc_ld = NULL;
1792
1793					/* let it be used, but taint/delete it so that
1794					 * no-one else can look it up any further */
1795					ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1796
1797#if LDAP_BACK_PRINT_CONNTREE > 0
1798					ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
1799#endif /* LDAP_BACK_PRINT_CONNTREE */
1800
1801					(void)ldap_back_conn_delete( li, lc );
1802					LDAP_BACK_CONN_TAINTED_SET( lc );
1803
1804#if LDAP_BACK_PRINT_CONNTREE > 0
1805					ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
1806#endif /* LDAP_BACK_PRINT_CONNTREE */
1807					ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
1808
1809				} else {
1810					(void)ldap_back_cancel( lc, op, rs, msgid, sendok );
1811				}
1812				rs->sr_err = timeout_err;
1813				rs->sr_text = timeout_text;
1814				break;
1815			}
1816
1817			/* timeout == 0 */
1818			LDAP_BACK_TV_SET( &tv );
1819			ldap_pvt_thread_yield();
1820			goto retry;
1821
1822		case -1:
1823			ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
1824					&rs->sr_err );
1825			break;
1826
1827
1828		/* otherwise get the result; if it is not
1829		 * LDAP_SUCCESS, record it in the reply
1830		 * structure (this includes
1831		 * LDAP_COMPARE_{TRUE|FALSE}) */
1832		default:
1833			/* only touch when activity actually took place... */
1834			if ( li->li_idle_timeout ) {
1835				lc->lc_time = op->o_time;
1836			}
1837
1838			rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
1839					&match, &text, &refs, &ctrls, 1 );
1840			if ( rc == LDAP_SUCCESS ) {
1841				rs->sr_text = text;
1842			} else {
1843				rs->sr_err = rc;
1844			}
1845			rs->sr_err = slap_map_api2result( rs );
1846
1847			/* RFC 4511: referrals can only appear
1848			 * if result code is LDAP_REFERRAL */
1849			if ( refs != NULL
1850				&& refs[ 0 ] != NULL
1851				&& refs[ 0 ][ 0 ] != '\0' )
1852			{
1853				if ( rs->sr_err != LDAP_REFERRAL ) {
1854					Debug( LDAP_DEBUG_ANY,
1855						"%s ldap_back_op_result: "
1856						"got referrals with err=%d\n",
1857						op->o_log_prefix,
1858						rs->sr_err );
1859
1860				} else {
1861					int	i;
1862
1863					for ( i = 0; refs[ i ] != NULL; i++ )
1864						/* count */ ;
1865					rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
1866						op->o_tmpmemctx );
1867					for ( i = 0; refs[ i ] != NULL; i++ ) {
1868						ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
1869					}
1870					BER_BVZERO( &rs->sr_ref[ i ] );
1871				}
1872
1873			} else if ( rs->sr_err == LDAP_REFERRAL ) {
1874				Debug( LDAP_DEBUG_ANY,
1875					"%s ldap_back_op_result: "
1876					"got err=%d with null "
1877					"or empty referrals\n",
1878					op->o_log_prefix,
1879					rs->sr_err );
1880
1881				rs->sr_err = LDAP_NO_SUCH_OBJECT;
1882			}
1883
1884			if ( ctrls != NULL ) {
1885				rs->sr_ctrls = ctrls;
1886			}
1887		}
1888	}
1889
1890	/* if the error in the reply structure is not
1891	 * LDAP_SUCCESS, try to map it from client
1892	 * to server error */
1893	if ( !LDAP_ERR_OK( rs->sr_err ) ) {
1894		rs->sr_err = slap_map_api2result( rs );
1895
1896		/* internal ops ( op->o_conn == NULL )
1897		 * must not reply to client */
1898		if ( op->o_conn && !op->o_do_not_cache && match ) {
1899
1900			/* record the (massaged) matched
1901			 * DN into the reply structure */
1902			rs->sr_matched = match;
1903		}
1904	}
1905
1906	if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1907		if ( !( sendok & LDAP_BACK_RETRYING ) ) {
1908			if ( LDAP_BACK_QUARANTINE( li ) ) {
1909				ldap_back_quarantine( op, rs );
1910			}
1911			if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
1912				if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
1913				send_ldap_result( op, rs );
1914			}
1915		}
1916
1917	} else if ( op->o_conn &&
1918		( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
1919			|| ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
1920	{
1921		send_ldap_result( op, rs );
1922	}
1923
1924	if ( text ) {
1925		ldap_memfree( text );
1926	}
1927	rs->sr_text = NULL;
1928
1929	/* there can't be refs with a (successful) bind */
1930	if ( rs->sr_ref ) {
1931		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
1932		rs->sr_ref = NULL;
1933	}
1934
1935	if ( refs ) {
1936		ber_memvfree( (void **)refs );
1937	}
1938
1939	/* match should not be possible with a successful bind */
1940	if ( match ) {
1941		if ( rs->sr_matched != match ) {
1942			free( (char *)rs->sr_matched );
1943		}
1944		rs->sr_matched = NULL;
1945		ldap_memfree( match );
1946	}
1947
1948	if ( ctrls != NULL ) {
1949		if ( op->o_tag == LDAP_REQ_BIND && rs->sr_err == LDAP_SUCCESS ) {
1950			int i;
1951
1952			for ( i = 0; ctrls[i] != NULL; i++ );
1953
1954			rs->sr_ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( i + 1 ),
1955				op->o_tmpmemctx );
1956			for ( i = 0; ctrls[ i ] != NULL; i++ ) {
1957				char *ptr;
1958				ber_len_t oidlen = strlen( ctrls[i]->ldctl_oid );
1959				ber_len_t size = sizeof( LDAPControl )
1960					+ oidlen + 1
1961					+ ctrls[i]->ldctl_value.bv_len + 1;
1962
1963				rs->sr_ctrls[ i ] = op->o_tmpalloc( size, op->o_tmpmemctx );
1964				rs->sr_ctrls[ i ]->ldctl_oid = (char *)&rs->sr_ctrls[ i ][ 1 ];
1965				lutil_strcopy( rs->sr_ctrls[ i ]->ldctl_oid, ctrls[i]->ldctl_oid );
1966				rs->sr_ctrls[ i ]->ldctl_value.bv_val
1967						= (char *)&rs->sr_ctrls[ i ]->ldctl_oid[oidlen + 1];
1968				rs->sr_ctrls[ i ]->ldctl_value.bv_len
1969					= ctrls[i]->ldctl_value.bv_len;
1970				ptr = lutil_memcopy( rs->sr_ctrls[ i ]->ldctl_value.bv_val,
1971					ctrls[i]->ldctl_value.bv_val, ctrls[i]->ldctl_value.bv_len );
1972				*ptr = '\0';
1973			}
1974			rs->sr_ctrls[ i ] = NULL;
1975			rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
1976
1977		} else {
1978			assert( rs->sr_ctrls != NULL );
1979			rs->sr_ctrls = NULL;
1980		}
1981
1982		ldap_controls_free( ctrls );
1983	}
1984
1985	return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
1986}
1987
1988/* return true if bound, false if failed */
1989int
1990ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1991{
1992	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
1993	int		rc = 0;
1994
1995	assert( lcp != NULL );
1996	assert( *lcp != NULL );
1997
1998	ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
1999
2000	if ( (*lcp)->lc_refcnt == 1 ) {
2001		int binding = LDAP_BACK_CONN_BINDING( *lcp );
2002
2003		ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
2004		Debug( LDAP_DEBUG_ANY,
2005			"%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
2006			op->o_log_prefix, li->li_uri,
2007			BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
2008				"" : (*lcp)->lc_bound_ndn.bv_val );
2009		ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
2010
2011		ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
2012		(*lcp)->lc_ld = NULL;
2013		LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
2014
2015		/* lc here must be the regular lc, reset and ready for init */
2016		rc = ldap_back_prepare_conn( *lcp, op, rs, sendok );
2017		if ( rc != LDAP_SUCCESS ) {
2018			/* freeit, because lc_refcnt == 1 */
2019			(*lcp)->lc_refcnt = 0;
2020			(void)ldap_back_freeconn( li, *lcp, 0 );
2021			*lcp = NULL;
2022			rc = 0;
2023
2024		} else if ( ( sendok & LDAP_BACK_BINDING ) ) {
2025			if ( binding ) {
2026				LDAP_BACK_CONN_BINDING_SET( *lcp );
2027			}
2028			rc = 1;
2029
2030		} else {
2031			rc = ldap_back_dobind_int( lcp, op, rs, sendok, 0, 0 );
2032			if ( rc == 0 && *lcp != NULL ) {
2033				/* freeit, because lc_refcnt == 1 */
2034				(*lcp)->lc_refcnt = 0;
2035				(void)ldap_back_freeconn( li, *lcp, 0 );
2036				*lcp = NULL;
2037			}
2038		}
2039
2040	} else {
2041		Debug( LDAP_DEBUG_TRACE,
2042			"ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
2043			(void *)(*lcp), (*lcp)->lc_refcnt );
2044
2045		LDAP_BACK_CONN_TAINTED_SET( *lcp );
2046		ldap_back_release_conn_lock( li, lcp, 0 );
2047		assert( *lcp == NULL );
2048
2049		if ( sendok & LDAP_BACK_SENDERR ) {
2050			rs->sr_err = LDAP_UNAVAILABLE;
2051			rs->sr_text = "Unable to retry";
2052			send_ldap_result( op, rs );
2053		}
2054	}
2055
2056	ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
2057
2058	return rc;
2059}
2060
2061static int
2062ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
2063	struct berval *binddn, struct berval *bindcred )
2064{
2065	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
2066	struct berval	ndn;
2067	int		dobind = 0;
2068
2069	if ( op->o_conn == NULL || op->o_do_not_cache ) {
2070		goto done;
2071	}
2072
2073	/* don't proxyAuthz if protocol is not LDAPv3 */
2074	switch ( li->li_version ) {
2075	case LDAP_VERSION3:
2076		break;
2077
2078	case 0:
2079		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
2080			break;
2081		}
2082		/* fall thru */
2083
2084	default:
2085		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2086		if ( sendok & LDAP_BACK_SENDERR ) {
2087			send_ldap_result( op, rs );
2088			dobind = -1;
2089		}
2090		goto done;
2091	}
2092
2093	/* safe default */
2094	*binddn = slap_empty_bv;
2095	*bindcred = slap_empty_bv;
2096
2097	if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
2098		ndn = op->o_conn->c_ndn;
2099
2100	} else {
2101		ndn = op->o_ndn;
2102	}
2103
2104	if ( !( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )) {
2105		if ( op->o_tag == LDAP_REQ_BIND && ( sendok & LDAP_BACK_SENDERR )) {
2106			if ( !BER_BVISEMPTY( &ndn )) {
2107				dobind = 0;
2108				goto done;
2109			}
2110		} else if ( SLAP_IS_AUTHZ_BACKEND( op )) {
2111			dobind = 0;
2112			goto done;
2113		}
2114	}
2115
2116	switch ( li->li_idassert_mode ) {
2117	case LDAP_BACK_IDASSERT_LEGACY:
2118		if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
2119			if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
2120			{
2121				*binddn = li->li_idassert_authcDN;
2122				*bindcred = li->li_idassert_passwd;
2123				dobind = 1;
2124			}
2125		}
2126		break;
2127
2128	default:
2129		/* NOTE: rootdn can always idassert */
2130		if ( BER_BVISNULL( &ndn )
2131			&& li->li_idassert_authz == NULL
2132			&& !( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
2133		{
2134			if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
2135				rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
2136				if ( sendok & LDAP_BACK_SENDERR ) {
2137					send_ldap_result( op, rs );
2138					dobind = -1;
2139				}
2140
2141			} else {
2142				rs->sr_err = LDAP_SUCCESS;
2143				*binddn = slap_empty_bv;
2144				*bindcred = slap_empty_bv;
2145				break;
2146			}
2147
2148			goto done;
2149
2150		} else if ( !be_isroot( op ) ) {
2151			if ( li->li_idassert_passthru ) {
2152				struct berval authcDN;
2153
2154				if ( BER_BVISNULL( &ndn ) ) {
2155					authcDN = slap_empty_bv;
2156
2157				} else {
2158					authcDN = ndn;
2159				}
2160				rs->sr_err = slap_sasl_matches( op, li->li_idassert_passthru,
2161						&authcDN, &authcDN );
2162				if ( rs->sr_err == LDAP_SUCCESS ) {
2163					dobind = 0;
2164					break;
2165				}
2166			}
2167
2168			if ( li->li_idassert_authz ) {
2169				struct berval authcDN;
2170
2171				if ( BER_BVISNULL( &ndn ) ) {
2172					authcDN = slap_empty_bv;
2173
2174				} else {
2175					authcDN = ndn;
2176				}
2177				rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz,
2178						&authcDN, &authcDN );
2179				if ( rs->sr_err != LDAP_SUCCESS ) {
2180					if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
2181						if ( sendok & LDAP_BACK_SENDERR ) {
2182							send_ldap_result( op, rs );
2183							dobind = -1;
2184						}
2185
2186					} else {
2187						rs->sr_err = LDAP_SUCCESS;
2188						*binddn = slap_empty_bv;
2189						*bindcred = slap_empty_bv;
2190						break;
2191					}
2192
2193					goto done;
2194				}
2195			}
2196		}
2197
2198		*binddn = li->li_idassert_authcDN;
2199		*bindcred = li->li_idassert_passwd;
2200		dobind = 1;
2201		break;
2202	}
2203
2204done:;
2205	return dobind;
2206}
2207
2208static int
2209ldap_back_proxy_authz_bind(
2210	ldapconn_t		*lc,
2211	Operation		*op,
2212	SlapReply		*rs,
2213	ldap_back_send_t	sendok,
2214	struct berval		*binddn,
2215	struct berval		*bindcred )
2216{
2217	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
2218	struct berval	ndn;
2219	int		msgid;
2220	int		rc;
2221
2222	if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
2223		ndn = op->o_conn->c_ndn;
2224
2225	} else {
2226		ndn = op->o_ndn;
2227	}
2228
2229	if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
2230#ifdef HAVE_CYRUS_SASL
2231		void		*defaults = NULL;
2232		struct berval	authzID = BER_BVNULL;
2233		int		freeauthz = 0;
2234		LDAPControl **ctrlsp = NULL;
2235		LDAPMessage *result = NULL;
2236		const char *rmech = NULL;
2237		const char *save_text = rs->sr_text;
2238
2239#ifdef SLAP_AUTH_DN
2240		LDAPControl ctrl, *ctrls[2];
2241		int msgid;
2242#endif /* SLAP_AUTH_DN */
2243
2244		/* if SASL supports native authz, prepare for it */
2245		if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
2246				( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
2247		{
2248			switch ( li->li_idassert_mode ) {
2249			case LDAP_BACK_IDASSERT_OTHERID:
2250			case LDAP_BACK_IDASSERT_OTHERDN:
2251				authzID = li->li_idassert_authzID;
2252				break;
2253
2254			case LDAP_BACK_IDASSERT_ANONYMOUS:
2255				BER_BVSTR( &authzID, "dn:" );
2256				break;
2257
2258			case LDAP_BACK_IDASSERT_SELF:
2259				if ( BER_BVISNULL( &ndn ) ) {
2260					/* connection is not authc'd, so don't idassert */
2261					BER_BVSTR( &authzID, "dn:" );
2262					break;
2263				}
2264				authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
2265				authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
2266				AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
2267				AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
2268						ndn.bv_val, ndn.bv_len + 1 );
2269				freeauthz = 1;
2270				break;
2271
2272			default:
2273				break;
2274			}
2275		}
2276
2277		if ( li->li_idassert_secprops != NULL ) {
2278			rs->sr_err = ldap_set_option( lc->lc_ld,
2279				LDAP_OPT_X_SASL_SECPROPS,
2280				(void *)li->li_idassert_secprops );
2281
2282			if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
2283				rs->sr_err = LDAP_OTHER;
2284				if ( sendok & LDAP_BACK_SENDERR ) {
2285					send_ldap_result( op, rs );
2286				}
2287				LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
2288				goto done;
2289			}
2290		}
2291
2292		defaults = lutil_sasl_defaults( lc->lc_ld,
2293				li->li_idassert_sasl_mech.bv_val,
2294				li->li_idassert_sasl_realm.bv_val,
2295				li->li_idassert_authcID.bv_val,
2296				li->li_idassert_passwd.bv_val,
2297				authzID.bv_val );
2298		if ( defaults == NULL ) {
2299			rs->sr_err = LDAP_OTHER;
2300			LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
2301			if ( sendok & LDAP_BACK_SENDERR ) {
2302				send_ldap_result( op, rs );
2303			}
2304			goto done;
2305		}
2306
2307#ifdef SLAP_AUTH_DN
2308		if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
2309			assert( BER_BVISNULL( binddn ) );
2310
2311			ctrl.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST;
2312			ctrl.ldctl_iscritical = 0;
2313			BER_BVZERO( &ctrl.ldctl_value );
2314			ctrls[0] = &ctrl;
2315			ctrls[1] = NULL;
2316			ctrlsp = ctrls;
2317		}
2318#endif /* SLAP_AUTH_DN */
2319
2320		do {
2321			rs->sr_err = ldap_sasl_interactive_bind( lc->lc_ld, binddn->bv_val,
2322				li->li_idassert_sasl_mech.bv_val,
2323				ctrlsp, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults,
2324				result, &rmech, &msgid );
2325
2326			if ( rs->sr_err != LDAP_SASL_BIND_IN_PROGRESS )
2327				break;
2328
2329			ldap_msgfree( result );
2330
2331			if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
2332				ldap_get_option( lc->lc_ld, LDAP_OPT_RESULT_CODE, (void*)&rs->sr_err );
2333				ldap_get_option( lc->lc_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&rs->sr_text );
2334				break;
2335			}
2336		} while ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
2337
2338		ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
2339		ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
2340		ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
2341
2342		switch ( rs->sr_err ) {
2343		case LDAP_SUCCESS:
2344#ifdef SLAP_AUTH_DN
2345			/* FIXME: right now, the only reason to check
2346			 * response controls is RFC 3829 authzid */
2347			if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
2348				ctrlsp = NULL;
2349				rc = ldap_parse_result( lc->lc_ld, result, NULL, NULL, NULL, NULL,
2350					&ctrlsp, 0 );
2351				if ( rc == LDAP_SUCCESS && ctrlsp ) {
2352					LDAPControl *ctrl;
2353
2354					ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE,
2355						ctrlsp, NULL );
2356					if ( ctrl ) {
2357						Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (authzid)\n",
2358							op->o_log_prefix, ctrl->ldctl_value.bv_val );
2359						if ( ctrl->ldctl_value.bv_len > STRLENOF("dn:") &&
2360							strncasecmp( ctrl->ldctl_value.bv_val, "dn:", STRLENOF("dn:") ) == 0 )
2361						{
2362							struct berval bv;
2363							bv.bv_val = &ctrl->ldctl_value.bv_val[STRLENOF("dn:")];
2364							bv.bv_len = ctrl->ldctl_value.bv_len - STRLENOF("dn:");
2365							ber_bvreplace( &lc->lc_bound_ndn, &bv );
2366						}
2367					}
2368				}
2369
2370				ldap_controls_free( ctrlsp );
2371
2372			} else if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_WHOAMI ) {
2373				struct berval *val = NULL;
2374				rc = ldap_whoami_s( lc->lc_ld, &val, NULL, NULL );
2375				if ( rc == LDAP_SUCCESS && val != NULL ) {
2376					Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (whoami)\n",
2377						op->o_log_prefix, val->bv_val );
2378					if ( val->bv_len > STRLENOF("dn:") &&
2379						strncasecmp( val->bv_val, "dn:", STRLENOF("dn:") ) == 0 )
2380					{
2381						struct berval bv;
2382						bv.bv_val = &val->bv_val[STRLENOF("dn:")];
2383						bv.bv_len = val->bv_len - STRLENOF("dn:");
2384						ber_bvreplace( &lc->lc_bound_ndn, &bv );
2385					}
2386					ber_bvfree( val );
2387				}
2388			}
2389
2390			if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) &&
2391				BER_BVISNULL( &lc->lc_bound_ndn ) )
2392			{
2393				/* all in all, we only need it to be non-null */
2394				/* FIXME: should this be configurable? */
2395				static struct berval bv = BER_BVC("cn=authzdn");
2396				ber_bvreplace( &lc->lc_bound_ndn, &bv );
2397			}
2398#endif /* SLAP_AUTH_DN */
2399			LDAP_BACK_CONN_ISBOUND_SET( lc );
2400			break;
2401
2402		case LDAP_LOCAL_ERROR:
2403			/* list client API error codes that require
2404			 * to taint the connection */
2405			/* FIXME: should actually retry? */
2406			LDAP_BACK_CONN_TAINTED_SET( lc );
2407
2408			/* fallthru */
2409
2410		default:
2411			LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
2412			rs->sr_err = slap_map_api2result( rs );
2413			if ( sendok & LDAP_BACK_SENDERR ) {
2414				send_ldap_result( op, rs );
2415			}
2416			break;
2417		}
2418
2419		if ( save_text != rs->sr_text ) {
2420			ldap_memfree( (char *)rs->sr_text );
2421			rs->sr_text = save_text;
2422		}
2423
2424		ldap_msgfree( result );
2425
2426		lutil_sasl_freedefs( defaults );
2427		if ( freeauthz ) {
2428			slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
2429		}
2430
2431		goto done;
2432#endif /* HAVE_CYRUS_SASL */
2433	}
2434
2435	switch ( li->li_idassert_authmethod ) {
2436	case LDAP_AUTH_NONE:
2437		/* FIXME: do we really need this? */
2438		BER_BVSTR( binddn, "" );
2439		BER_BVSTR( bindcred, "" );
2440		/* fallthru */
2441
2442	case LDAP_AUTH_SIMPLE:
2443		rs->sr_err = ldap_sasl_bind( lc->lc_ld,
2444				binddn->bv_val, LDAP_SASL_SIMPLE,
2445				bindcred, NULL, NULL, &msgid );
2446		rc = ldap_back_op_result( lc, op, rs, msgid,
2447			-1, ( sendok | LDAP_BACK_BINDING ) );
2448
2449		ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
2450		ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
2451		ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
2452		break;
2453
2454	default:
2455		/* unsupported! */
2456		LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
2457		rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
2458		if ( sendok & LDAP_BACK_SENDERR ) {
2459			send_ldap_result( op, rs );
2460		}
2461		goto done;
2462	}
2463
2464	if ( rc == LDAP_SUCCESS ) {
2465		/* set rebind stuff in case of successful proxyAuthz bind,
2466		 * so that referral chasing is attempted using the right
2467		 * identity */
2468		LDAP_BACK_CONN_ISBOUND_SET( lc );
2469		if ( !BER_BVISNULL( binddn ) ) {
2470			ber_bvreplace( &lc->lc_bound_ndn, binddn );
2471		}
2472
2473		if ( !BER_BVISNULL( &lc->lc_cred ) ) {
2474			memset( lc->lc_cred.bv_val, 0,
2475					lc->lc_cred.bv_len );
2476		}
2477
2478		if ( LDAP_BACK_SAVECRED( li ) ) {
2479			if ( !BER_BVISNULL( bindcred ) ) {
2480				ber_bvreplace( &lc->lc_cred, bindcred );
2481				ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
2482			}
2483
2484		} else {
2485			lc->lc_cred.bv_len = 0;
2486		}
2487	}
2488
2489done:;
2490	return LDAP_BACK_CONN_ISBOUND( lc );
2491}
2492
2493/*
2494 * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
2495 * to existing server-side controls if required; if not,
2496 * the existing server-side controls are placed in *pctrls.
2497 * The caller, after using the controls in client API
2498 * operations, if ( *pctrls != op->o_ctrls ), should
2499 * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
2500 * The function returns success if the control could
2501 * be added if required, or if it did nothing; in the future,
2502 * it might return some error if it failed.
2503 *
2504 * if no bind took place yet, but the connection is bound
2505 * and the "proxyauthzdn" is set, then bind as "proxyauthzdn"
2506 * and explicitly add proxyAuthz the control to every operation
2507 * with the dn bound to the connection as control value.
2508 *
2509 * If no server-side controls are defined for the operation,
2510 * simply add the proxyAuthz control; otherwise, if the
2511 * proxyAuthz control is not already set, add it as
2512 * the first one
2513 *
2514 * FIXME: is controls order significant for security?
2515 * ANSWER: controls ordering and interoperability
2516 * must be indicated by the specs of each control; if none
2517 * is specified, the order is irrelevant.
2518 */
2519int
2520ldap_back_proxy_authz_ctrl(
2521		Operation	*op,
2522		SlapReply	*rs,
2523		struct berval	*bound_ndn,
2524		int		version,
2525		slap_idassert_t	*si,
2526		LDAPControl	*ctrl )
2527{
2528	slap_idassert_mode_t	mode;
2529	struct berval		assertedID,
2530				ndn;
2531	int			isroot = 0;
2532
2533	rs->sr_err = SLAP_CB_CONTINUE;
2534
2535	/* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
2536	 * but if it is not set this test fails.  We need a different
2537	 * means to detect if idassert is enabled */
2538	if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
2539		&& ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
2540		&& BER_BVISNULL( &si->si_bc.sb_saslmech ) )
2541	{
2542		goto done;
2543	}
2544
2545	if ( !op->o_conn || op->o_do_not_cache || ( isroot = be_isroot( op ) ) ) {
2546		goto done;
2547	}
2548
2549	if ( op->o_tag == LDAP_REQ_BIND ) {
2550		ndn = op->o_req_ndn;
2551	} else {
2552		ndn = op->o_ndn;
2553	}
2554
2555	if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
2556		if ( op->o_proxy_authz ) {
2557			/*
2558			 * FIXME: we do not want to perform proxyAuthz
2559			 * on behalf of the client, because this would
2560			 * be performed with "proxyauthzdn" privileges.
2561			 *
2562			 * This might actually be too strict, since
2563			 * the "proxyauthzdn" authzTo, and each entry's
2564			 * authzFrom attributes may be crafted
2565			 * to avoid unwanted proxyAuthz to take place.
2566			 */
2567#if 0
2568			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2569			rs->sr_text = "proxyAuthz not allowed within namingContext";
2570#endif
2571			goto done;
2572		}
2573
2574		if ( !BER_BVISNULL( bound_ndn ) ) {
2575			goto done;
2576		}
2577
2578		if ( BER_BVISNULL( &ndn ) ) {
2579			goto done;
2580		}
2581
2582		if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
2583			goto done;
2584		}
2585
2586	} else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
2587		if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
2588		{
2589			/* already asserted in SASL via native authz */
2590			goto done;
2591		}
2592
2593	} else if ( si->si_authz && !isroot ) {
2594		int		rc;
2595		struct berval authcDN;
2596
2597		if ( BER_BVISNULL( &ndn ) ) {
2598			authcDN = slap_empty_bv;
2599		} else {
2600			authcDN = ndn;
2601		}
2602		rc = slap_sasl_matches( op, si->si_authz,
2603				&authcDN, &authcDN );
2604		if ( rc != LDAP_SUCCESS ) {
2605			if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
2606				/* ndn is not authorized
2607				 * to use idassert */
2608				rs->sr_err = rc;
2609			}
2610			goto done;
2611		}
2612	}
2613
2614	if ( op->o_proxy_authz ) {
2615		/*
2616		 * FIXME: we can:
2617		 * 1) ignore the already set proxyAuthz control
2618		 * 2) leave it in place, and don't set ours
2619		 * 3) add both
2620		 * 4) reject the operation
2621		 *
2622		 * option (4) is very drastic
2623		 * option (3) will make the remote server reject
2624		 * the operation, thus being equivalent to (4)
2625		 * option (2) will likely break the idassert
2626		 * assumptions, so we cannot accept it;
2627		 * option (1) means that we are contradicting
2628		 * the client's request.
2629		 *
2630		 * I think (4) is the only correct choice.
2631		 */
2632		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2633		rs->sr_text = "proxyAuthz not allowed within namingContext";
2634	}
2635
2636	if ( op->o_is_auth_check ) {
2637		mode = LDAP_BACK_IDASSERT_NOASSERT;
2638
2639	} else {
2640		mode = si->si_mode;
2641	}
2642
2643	switch ( mode ) {
2644	case LDAP_BACK_IDASSERT_LEGACY:
2645		/* original behavior:
2646		 * assert the client's identity */
2647	case LDAP_BACK_IDASSERT_SELF:
2648		assertedID = ndn;
2649		break;
2650
2651	case LDAP_BACK_IDASSERT_ANONYMOUS:
2652		/* assert "anonymous" */
2653		assertedID = slap_empty_bv;
2654		break;
2655
2656	case LDAP_BACK_IDASSERT_NOASSERT:
2657		/* don't assert; bind as proxyauthzdn */
2658		goto done;
2659
2660	case LDAP_BACK_IDASSERT_OTHERID:
2661	case LDAP_BACK_IDASSERT_OTHERDN:
2662		/* assert idassert DN */
2663		assertedID = si->si_bc.sb_authzId;
2664		break;
2665
2666	default:
2667		assert( 0 );
2668	}
2669
2670	/* if we got here, "" is allowed to proxyAuthz */
2671	if ( BER_BVISNULL( &assertedID ) ) {
2672		assertedID = slap_empty_bv;
2673	}
2674
2675	/* don't idassert the bound DN (ITS#4497) */
2676	if ( dn_match( &assertedID, bound_ndn ) ) {
2677		goto done;
2678	}
2679
2680	ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
2681	ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
2682
2683	switch ( si->si_mode ) {
2684	/* already in u:ID or dn:DN form */
2685	case LDAP_BACK_IDASSERT_OTHERID:
2686	case LDAP_BACK_IDASSERT_OTHERDN:
2687		ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
2688		rs->sr_err = LDAP_SUCCESS;
2689		break;
2690
2691	/* needs the dn: prefix */
2692	default:
2693		ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
2694		ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
2695				op->o_tmpmemctx );
2696		AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
2697		AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
2698				assertedID.bv_val, assertedID.bv_len + 1 );
2699		rs->sr_err = LDAP_SUCCESS;
2700		break;
2701	}
2702
2703	/* Older versions of <draft-weltman-ldapv3-proxy> required
2704	 * to encode the value of the authzID (and called it proxyDN);
2705	 * this hack provides compatibility with those DSAs that
2706	 * implement it this way */
2707	if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
2708		struct berval		authzID = ctrl->ldctl_value;
2709		BerElementBuffer	berbuf;
2710		BerElement		*ber = (BerElement *)&berbuf;
2711		ber_tag_t		tag;
2712
2713		ber_init2( ber, 0, LBER_USE_DER );
2714		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
2715
2716		tag = ber_printf( ber, "O", &authzID );
2717		if ( tag == LBER_ERROR ) {
2718			rs->sr_err = LDAP_OTHER;
2719			goto free_ber;
2720		}
2721
2722		if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
2723			rs->sr_err = LDAP_OTHER;
2724			goto free_ber;
2725		}
2726
2727		rs->sr_err = LDAP_SUCCESS;
2728
2729free_ber:;
2730		op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
2731		ber_free_buf( ber );
2732
2733		if ( rs->sr_err != LDAP_SUCCESS ) {
2734			goto done;
2735		}
2736
2737	} else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
2738		struct berval		authzID = ctrl->ldctl_value,
2739					tmp;
2740		BerElementBuffer	berbuf;
2741		BerElement		*ber = (BerElement *)&berbuf;
2742		ber_tag_t		tag;
2743
2744		if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
2745			rs->sr_err = LDAP_PROTOCOL_ERROR;
2746			goto done;
2747		}
2748
2749		tmp = authzID;
2750		tmp.bv_val += STRLENOF( "dn:" );
2751		tmp.bv_len -= STRLENOF( "dn:" );
2752
2753		ber_init2( ber, 0, LBER_USE_DER );
2754		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
2755
2756		/* apparently, Mozilla API encodes this
2757		 * as "SEQUENCE { LDAPDN }" */
2758		tag = ber_printf( ber, "{O}", &tmp );
2759		if ( tag == LBER_ERROR ) {
2760			rs->sr_err = LDAP_OTHER;
2761			goto free_ber2;
2762		}
2763
2764		if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
2765			rs->sr_err = LDAP_OTHER;
2766			goto free_ber2;
2767		}
2768
2769		ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
2770		rs->sr_err = LDAP_SUCCESS;
2771
2772free_ber2:;
2773		op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
2774		ber_free_buf( ber );
2775
2776		if ( rs->sr_err != LDAP_SUCCESS ) {
2777			goto done;
2778		}
2779	}
2780
2781done:;
2782
2783	return rs->sr_err;
2784}
2785
2786/*
2787 * Add controls;
2788 *
2789 * if any needs to be added, it is prepended to existing ones,
2790 * in a newly allocated array.  The companion function
2791 * ldap_back_controls_free() must be used to restore the original
2792 * status of op->o_ctrls.
2793 */
2794int
2795ldap_back_controls_add(
2796		Operation	*op,
2797		SlapReply	*rs,
2798		ldapconn_t	*lc,
2799		LDAPControl	***pctrls )
2800{
2801	ldapinfo_t	*li = (ldapinfo_t *)op->o_bd->be_private;
2802
2803	LDAPControl	**ctrls = NULL;
2804	/* set to the maximum number of controls this backend can add */
2805	LDAPControl	c[ 2 ] = { { 0 } };
2806	int		n = 0, i, j1 = 0, j2 = 0, skipped = 0;
2807
2808	*pctrls = NULL;
2809
2810	rs->sr_err = LDAP_SUCCESS;
2811
2812	/* don't add controls if protocol is not LDAPv3 */
2813	switch ( li->li_version ) {
2814	case LDAP_VERSION3:
2815		break;
2816
2817	case 0:
2818		if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
2819			break;
2820		}
2821		/* fall thru */
2822
2823	default:
2824		goto done;
2825	}
2826
2827	/* put controls that go __before__ existing ones here */
2828
2829	/* proxyAuthz for identity assertion */
2830	switch ( ldap_back_proxy_authz_ctrl( op, rs, &lc->lc_bound_ndn,
2831		li->li_version, &li->li_idassert, &c[ j1 ] ) )
2832	{
2833	case SLAP_CB_CONTINUE:
2834		break;
2835
2836	case LDAP_SUCCESS:
2837		j1++;
2838		break;
2839
2840	default:
2841		goto done;
2842	}
2843
2844	/* put controls that go __after__ existing ones here */
2845
2846#ifdef SLAP_CONTROL_X_SESSION_TRACKING
2847	/* FIXME: according to <draft-wahl-ldap-session>,
2848	 * the server should check if the control can be added
2849	 * based on the identity of the client and so */
2850
2851	/* session tracking */
2852	if ( LDAP_BACK_ST_REQUEST( li ) ) {
2853		switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
2854		case SLAP_CB_CONTINUE:
2855			break;
2856
2857		case LDAP_SUCCESS:
2858			j2++;
2859			break;
2860
2861		default:
2862			goto done;
2863		}
2864	}
2865#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
2866
2867	if ( rs->sr_err == SLAP_CB_CONTINUE ) {
2868		rs->sr_err = LDAP_SUCCESS;
2869	}
2870
2871	/* if nothing to do, just bail out */
2872	if ( j1 == 0 && j2 == 0 ) {
2873		goto done;
2874	}
2875
2876	assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
2877
2878	if ( op->o_ctrls ) {
2879		for ( n = 0; op->o_ctrls[ n ]; n++ )
2880			/* just count ctrls */ ;
2881	}
2882
2883	ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
2884			op->o_tmpmemctx );
2885	if ( j1 ) {
2886		ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
2887		*ctrls[ 0 ] = c[ 0 ];
2888		for ( i = 1; i < j1; i++ ) {
2889			ctrls[ i ] = &ctrls[ 0 ][ i ];
2890			*ctrls[ i ] = c[ i ];
2891		}
2892	}
2893
2894	i = 0;
2895	if ( op->o_ctrls ) {
2896		LDAPControl *proxyauthz = ldap_control_find(
2897				LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
2898
2899		for ( i = 0; op->o_ctrls[ i ]; i++ ) {
2900			/* Only replace it if we generated one */
2901			if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
2902				/* Frontend has already checked only one is present */
2903				assert( skipped == 0 );
2904				skipped++;
2905				continue;
2906			}
2907			ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
2908		}
2909	}
2910
2911	n += j1 - skipped;
2912	if ( j2 ) {
2913		ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
2914		*ctrls[ n ] = c[ j1 ];
2915		for ( i = 1; i < j2; i++ ) {
2916			ctrls[ n + i ] = &ctrls[ n ][ i ];
2917			*ctrls[ n + i ] = c[ i ];
2918		}
2919	}
2920
2921	ctrls[ n + j2 ] = NULL;
2922
2923done:;
2924	if ( ctrls == NULL ) {
2925		ctrls = op->o_ctrls;
2926	}
2927
2928	*pctrls = ctrls;
2929
2930	return rs->sr_err;
2931}
2932
2933int
2934ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls )
2935{
2936	LDAPControl	**ctrls = *pctrls;
2937
2938	/* we assume that the controls added by the proxy come first,
2939	 * so as soon as we find op->o_ctrls[ 0 ] we can stop */
2940	if ( ctrls && ctrls != op->o_ctrls ) {
2941		int		i = 0, n = 0, n_added;
2942		LDAPControl	*lower, *upper;
2943
2944		assert( ctrls[ 0 ] != NULL );
2945
2946		for ( n = 0; ctrls[ n ] != NULL; n++ )
2947			/* count 'em */ ;
2948
2949		if ( op->o_ctrls ) {
2950			for ( i = 0; op->o_ctrls[ i ] != NULL; i++ )
2951				/* count 'em */ ;
2952		}
2953
2954		n_added = n - i;
2955		lower = (LDAPControl *)&ctrls[ n ];
2956		upper = &lower[ n_added ];
2957
2958		for ( i = 0; ctrls[ i ] != NULL; i++ ) {
2959			if ( ctrls[ i ] < lower || ctrls[ i ] >= upper ) {
2960				/* original; don't touch */
2961				continue;
2962			}
2963
2964			if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
2965				op->o_tmpfree( ctrls[ i ]->ldctl_value.bv_val, op->o_tmpmemctx );
2966			}
2967		}
2968
2969		op->o_tmpfree( ctrls, op->o_tmpmemctx );
2970	}
2971
2972	*pctrls = NULL;
2973
2974	return 0;
2975}
2976
2977int
2978ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
2979{
2980	char tbuf[ SLAP_TEXT_BUFLEN ];
2981	char *ptr = buf, *end = buf + buflen;
2982	int len;
2983
2984	if ( ptr + sizeof("conn=") > end ) return -1;
2985	ptr = lutil_strcopy( ptr, "conn=" );
2986
2987	len = ldap_back_connid2str( lc, ptr, (ber_len_t)(end - ptr) );
2988	ptr += len;
2989	if ( ptr >= end ) return -1;
2990
2991	if ( !BER_BVISNULL( &lc->lcb_local_ndn ) ) {
2992		if ( ptr + sizeof(" DN=\"\"") + lc->lcb_local_ndn.bv_len > end ) return -1;
2993		ptr = lutil_strcopy( ptr, " DN=\"" );
2994		ptr = lutil_strncopy( ptr, lc->lcb_local_ndn.bv_val, lc->lcb_local_ndn.bv_len );
2995		*ptr++ = '"';
2996	}
2997
2998	if ( lc->lcb_create_time != 0 ) {
2999		len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_create_time );
3000		if ( ptr + sizeof(" created=") + len >= end ) return -1;
3001		ptr = lutil_strcopy( ptr, " created=" );
3002		ptr = lutil_strcopy( ptr, tbuf );
3003	}
3004
3005	if ( lc->lcb_time != 0 ) {
3006		len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_time );
3007		if ( ptr + sizeof(" modified=") + len >= end ) return -1;
3008		ptr = lutil_strcopy( ptr, " modified=" );
3009		ptr = lutil_strcopy( ptr, tbuf );
3010	}
3011
3012	len = snprintf( tbuf, sizeof(tbuf), "%u", lc->lcb_refcnt );
3013	if ( ptr + sizeof(" refcnt=") + len >= end ) return -1;
3014	ptr = lutil_strcopy( ptr, " refcnt=" );
3015	ptr = lutil_strcopy( ptr, tbuf );
3016
3017	return ptr - buf;
3018}
3019
3020int
3021ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
3022{
3023	static struct berval conns[] = {
3024		BER_BVC("ROOTDN"),
3025		BER_BVC("ROOTDN-TLS"),
3026		BER_BVC("ANON"),
3027		BER_BVC("ANON-TLS"),
3028		BER_BVC("BIND"),
3029		BER_BVC("BIND-TLS"),
3030		BER_BVNULL
3031	};
3032
3033	int len = 0;
3034
3035	if ( LDAP_BACK_PCONN_ISPRIV( (const ldapconn_t *)lc ) ) {
3036		long cid;
3037		struct berval *bv;
3038
3039		cid = (long)lc->lcb_conn;
3040		assert( cid >= LDAP_BACK_PCONN_FIRST && cid < LDAP_BACK_PCONN_LAST );
3041
3042		bv = &conns[ cid ];
3043
3044		if ( bv->bv_len >= buflen ) {
3045			return bv->bv_len + 1;
3046		}
3047
3048		len = bv->bv_len;
3049		lutil_strncopy( buf, bv->bv_val, bv->bv_len + 1 );
3050
3051	} else {
3052		len = snprintf( buf, buflen, "%lu", lc->lcb_conn->c_connid );
3053	}
3054
3055	return len;
3056}
3057
3058void *
3059ldap_back_conn_expire_fn( void *ctx, void *arg )
3060{
3061	struct re_s *rtask = arg;
3062	ldapinfo_t *li = (ldapinfo_t *)rtask->arg;
3063	ldap_back_conn_prune( li );
3064
3065	return NULL;
3066}
3067
3068/* Pick which expires first: connection TTL or idle timeout */
3069static time_t
3070ldap_back_conn_expire_time( ldapinfo_t *li, ldapconn_t *lc) {
3071	if ( li->li_conn_ttl != 0 && li->li_idle_timeout != 0 ) {
3072		return ( lc->lc_create_time + li->li_conn_ttl ) < ( lc->lc_time + li->li_idle_timeout ) ?
3073			( lc->lc_create_time + li->li_conn_ttl ) : ( lc->lc_time + li->li_idle_timeout );
3074	} else if ( li->li_conn_ttl != 0 ) {
3075		return lc->lc_create_time + li->li_conn_ttl;
3076	} else if ( li->li_idle_timeout != 0 ) {
3077		return lc->lc_time + li->li_idle_timeout;
3078	}
3079	return -1;
3080}
3081
3082static void
3083ldap_back_conn_prune( ldapinfo_t *li )
3084{
3085	time_t		now = slap_get_time();
3086	time_t 		next_timeout = -1; /* -1 means uninitialized */
3087	TAvlnode	*edge;
3088	int		c;
3089
3090	/*
3091	 * Iterate though connections and close those that are pass the expiry time.
3092	 * Also calculate the time for next connection to to expire.
3093	 */
3094	ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
3095
3096	for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
3097		ldapconn_t *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ c ].lic_priv );
3098
3099		while ( lc ) {
3100			ldapconn_t *next = LDAP_TAILQ_NEXT( lc, lc_q );
3101			time_t conn_expires = ldap_back_conn_expire_time( li, lc );
3102
3103			if ( now >= conn_expires ) {
3104				if ( lc->lc_refcnt == 0 ) {
3105					Debug( LDAP_DEBUG_TRACE,
3106						"ldap_back_conn_prune: closing expired connection lc=%p\n",
3107						lc );
3108					ldap_back_freeconn( li, lc, 0 );
3109				} else {
3110					Debug( LDAP_DEBUG_TRACE,
3111						"ldap_back_conn_prune: tainting expired connection lc=%p\n",
3112						lc );
3113					LDAP_BACK_CONN_TAINTED_SET( lc );
3114				}
3115			} else if ( next_timeout == -1 || conn_expires < next_timeout ) {
3116				/* next_timeout was not yet initialized or current connection expires sooner */
3117				next_timeout = conn_expires;
3118			}
3119
3120			lc = next;
3121		}
3122	}
3123
3124	edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
3125	while ( edge ) {
3126		TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
3127		ldapconn_t *lc = (ldapconn_t *)edge->avl_data;
3128		time_t conn_expires = ldap_back_conn_expire_time( li, lc );
3129
3130		if ( now >= conn_expires ) {
3131			if ( lc->lc_refcnt == 0 ) {
3132				Debug( LDAP_DEBUG_TRACE,
3133					"ldap_back_conn_prune: closing expired connection lc=%p\n",
3134					lc );
3135				ldap_back_freeconn( li, lc, 0 );
3136			} else {
3137				Debug( LDAP_DEBUG_TRACE,
3138					"ldap_back_conn_prune: tainting expired connection lc=%p\n",
3139					lc );
3140				LDAP_BACK_CONN_TAINTED_SET( lc );
3141			}
3142		} else if ( next_timeout == -1 || conn_expires < next_timeout ) {
3143			next_timeout = conn_expires;
3144		}
3145
3146		edge = next;
3147	}
3148
3149	ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
3150
3151	/* Reschedule for next timeout or cancel the task */
3152	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3153	if ( next_timeout > 0 ) {
3154		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
3155			ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
3156		}
3157		li->li_conn_expire_task->interval.tv_sec = next_timeout - now;
3158		ldap_pvt_runqueue_resched( &slapd_rq, li->li_conn_expire_task, 0 );
3159
3160		/*
3161		 * The thread that handles runqueue might have already processed all tasks
3162		 * before we insertered new task or rescheduled the existing task with new
3163		 * timeout period. Wake it up to ensure that the task will be picked up.
3164		 */
3165		slap_wake_listener();
3166		Debug( LDAP_DEBUG_TRACE,
3167			"ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
3168			li->li_conn_expire_task->interval.tv_sec );
3169	} else if ( next_timeout == -1 && li->li_conn_expire_task != NULL ) {
3170		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
3171			ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
3172		}
3173		ldap_pvt_runqueue_remove( &slapd_rq, li->li_conn_expire_task );
3174		li->li_conn_expire_task = NULL;
3175	}
3176	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3177
3178	return;
3179}
3180
3181static void
3182ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc ) {
3183	/* Do nothing if timeouts are not set. */
3184	if ( li->li_conn_ttl == 0 && li->li_idle_timeout == 0 ) {
3185		return;
3186	}
3187
3188	/*
3189	 * If connection expire task is not running, create it and schedule for
3190	 * timeout of this connection.
3191	 *
3192	 * If the task is already running, this connection cannot be next one
3193	 * to expire and therefore timeout does not need to be re-calculated.
3194	 */
3195	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3196	if ( li->li_conn_expire_task == NULL ) {
3197		li->li_conn_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
3198			ldap_back_conn_expire_time( li, lc ) - slap_get_time(),
3199			ldap_back_conn_expire_fn, li, "ldap_back_conn_expire_fn",
3200			"ldap_back_conn_expire_timer" );
3201		slap_wake_listener();
3202		Debug( LDAP_DEBUG_TRACE,
3203			"ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
3204			li->li_conn_expire_task->interval.tv_sec );
3205	}
3206	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3207
3208	return;
3209}