1 /*	$NetBSD: request.c,v 1.3 2021/08/14 16:14:56 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
18  * All rights reserved.
19  */
20 /* This notice applies to changes, created by or for Novell, Inc.,
21  * to preexisting works for which notices appear elsewhere in this file.
22  *
23  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
24  *
25  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
26  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
27  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
28  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
29  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
30  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
31  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
32  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
33  *---
34  * Modification to OpenLDAP source by Novell, Inc.
35  * April 2000 sfs  Added code to chase V3 referrals
36  *  request.c - sending of ldap requests; handling of referrals
37  *---
38  * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
39  * can be found in the file "build/LICENSE-2.0.1" in this distribution
40  * of OpenLDAP Software.
41  */
42 
43 #include <sys/cdefs.h>
44 __RCSID("$NetBSD: request.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
45 
46 #include "portable.h"
47 
48 #include <stdio.h>
49 
50 #include <ac/stdlib.h>
51 
52 #include <ac/errno.h>
53 #include <ac/socket.h>
54 #include <ac/string.h>
55 #include <ac/time.h>
56 #include <ac/unistd.h>
57 
58 #include "ldap-int.h"
59 #include "lber.h"
60 
61 /* used by ldap_send_server_request and ldap_new_connection */
62 #ifdef LDAP_R_COMPILE
63 #define LDAP_CONN_LOCK_IF(nolock) \
64 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
65 #define LDAP_CONN_UNLOCK_IF(nolock) \
66 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
67 #define LDAP_REQ_LOCK_IF(nolock) \
68 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
69 #define LDAP_REQ_UNLOCK_IF(nolock) \
70 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
71 #define LDAP_RES_LOCK_IF(nolock) \
72 	{ if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
73 #define LDAP_RES_UNLOCK_IF(nolock) \
74 	{ if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
75 #else
76 #define LDAP_CONN_LOCK_IF(nolock)
77 #define LDAP_CONN_UNLOCK_IF(nolock)
78 #define LDAP_REQ_LOCK_IF(nolock)
79 #define LDAP_REQ_UNLOCK_IF(nolock)
80 #define LDAP_RES_LOCK_IF(nolock)
81 #define LDAP_RES_UNLOCK_IF(nolock)
82 #endif
83 
84 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
85 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
86 static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
87 
88 static BerElement *
89 re_encode_request( LDAP *ld,
90 	BerElement *origber,
91 	ber_int_t msgid,
92 	int sref,
93 	LDAPURLDesc *srv,
94 	int *type );
95 
96 BerElement *
ldap_alloc_ber_with_options(LDAP * ld)97 ldap_alloc_ber_with_options( LDAP *ld )
98 {
99 	BerElement	*ber;
100 
101 	ber = ber_alloc_t( ld->ld_lberoptions );
102 	if ( ber == NULL ) {
103 		ld->ld_errno = LDAP_NO_MEMORY;
104 	}
105 
106 	return( ber );
107 }
108 
109 
110 void
ldap_set_ber_options(LDAP * ld,BerElement * ber)111 ldap_set_ber_options( LDAP *ld, BerElement *ber )
112 {
113 	/* ld_lberoptions is constant, hence no lock */
114 	ber->ber_options = ld->ld_lberoptions;
115 }
116 
117 
118 /* sets needed mutexes - no mutexes set to this point */
119 ber_int_t
ldap_send_initial_request(LDAP * ld,ber_tag_t msgtype,const char * dn,BerElement * ber,ber_int_t msgid)120 ldap_send_initial_request(
121 	LDAP *ld,
122 	ber_tag_t msgtype,
123 	const char *dn,
124 	BerElement *ber,
125 	ber_int_t msgid)
126 {
127 	int rc = 1;
128 	ber_socket_t sd = AC_SOCKET_INVALID;
129 
130 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n" );
131 
132 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
133 	if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
134 		/* not connected yet */
135 		rc = ldap_open_defconn( ld );
136 		if ( rc == 0 ) {
137 			ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
138 				LBER_SB_OPT_GET_FD, &sd );
139 		}
140 	}
141 	if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
142 		rc = ldap_int_check_async_open( ld, sd );
143 	if( rc < 0 ) {
144 		ber_free( ber, 1 );
145 		LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
146 		return( -1 );
147 	} else if ( rc == 0 ) {
148 		Debug0( LDAP_DEBUG_TRACE,
149 			"ldap_open_defconn: successful\n" );
150 	}
151 
152 #ifdef LDAP_CONNECTIONLESS
153 	if (LDAP_IS_UDP(ld)) {
154 		if (msgtype == LDAP_REQ_BIND) {
155 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
156 			if (ld->ld_options.ldo_cldapdn)
157 				ldap_memfree(ld->ld_options.ldo_cldapdn);
158 			ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
159 			ber_free( ber, 1 );
160 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
161 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
162 			return 0;
163 		}
164 		if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
165 		{
166 			ber_free( ber, 1 );
167 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
168 			return LDAP_PARAM_ERROR;
169 		}
170 	}
171 #endif
172 	LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
173 	rc = ldap_send_server_request( ld, ber, msgid, NULL,
174 		NULL, NULL, NULL, 0, 0 );
175 	LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
176 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
177 	return(rc);
178 }
179 
180 
181 /* protected by conn_mutex */
182 int
ldap_int_flush_request(LDAP * ld,LDAPRequest * lr)183 ldap_int_flush_request(
184 	LDAP *ld,
185 	LDAPRequest *lr )
186 {
187 	LDAPConn *lc = lr->lr_conn;
188 
189 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
190 	if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
191 		if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
192 			/* ENOTCONN is returned in Solaris 10 */
193 			/* need to continue write later */
194 			lr->lr_status = LDAP_REQST_WRITING;
195 			ldap_mark_select_write( ld, lc->lconn_sb );
196 			ld->ld_errno = LDAP_BUSY;
197 			return -2;
198 		} else {
199 			ld->ld_errno = LDAP_SERVER_DOWN;
200 			ldap_free_request( ld, lr );
201 			ldap_free_connection( ld, lc, 0, 0 );
202 			return( -1 );
203 		}
204 	} else {
205 		if ( lr->lr_parent == NULL ) {
206 			lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
207 			lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
208 		}
209 		lr->lr_status = LDAP_REQST_INPROGRESS;
210 
211 		/* sent -- waiting for a response */
212 		ldap_mark_select_read( ld, lc->lconn_sb );
213 		ldap_clear_select_write( ld, lc->lconn_sb );
214 	}
215 	return 0;
216 }
217 
218 /*
219  * protected by req_mutex
220  * if m_noconn then protect using conn_lock
221  * else already protected with conn_lock
222  * if m_res then also protected by res_mutex
223  */
224 
225 int
ldap_send_server_request(LDAP * ld,BerElement * ber,ber_int_t msgid,LDAPRequest * parentreq,LDAPURLDesc ** srvlist,LDAPConn * lc,LDAPreqinfo * bind,int m_noconn,int m_res)226 ldap_send_server_request(
227 	LDAP *ld,
228 	BerElement *ber,
229 	ber_int_t msgid,
230 	LDAPRequest *parentreq,
231 	LDAPURLDesc **srvlist,
232 	LDAPConn *lc,
233 	LDAPreqinfo *bind,
234 	int m_noconn,
235 	int m_res )
236 {
237 	LDAPRequest	*lr;
238 	int		incparent, rc;
239 
240 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
241 	Debug0( LDAP_DEBUG_TRACE, "ldap_send_server_request\n" );
242 
243 	incparent = 0;
244 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
245 
246 	LDAP_CONN_LOCK_IF(m_noconn);
247 	if ( lc == NULL ) {
248 		if ( srvlist == NULL ) {
249 			lc = ld->ld_defconn;
250 		} else {
251 			lc = find_connection( ld, *srvlist, 1 );
252 			if ( lc == NULL ) {
253 				if ( (bind != NULL) && (parentreq != NULL) ) {
254 					/* Remember the bind in the parent */
255 					incparent = 1;
256 					++parentreq->lr_outrefcnt;
257 				}
258 				lc = ldap_new_connection( ld, srvlist, 0,
259 					1, bind, 1, m_res );
260 			}
261 		}
262 	}
263 
264 	/* async connect... */
265 	if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
266 		ber_socket_t	sd = AC_SOCKET_ERROR;
267 		struct timeval	tv = { 0 };
268 
269 		ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
270 
271 		/* poll ... */
272 		switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
273 		case 0:
274 			/* go on! */
275 			lc->lconn_status = LDAP_CONNST_CONNECTED;
276 			break;
277 
278 		case -2:
279 			/* async only occurs if a network timeout is set */
280 
281 			/* honor network timeout */
282 			LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
283 			if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
284 			{
285 				/* caller will have to call again */
286 				ld->ld_errno = LDAP_X_CONNECTING;
287 			}
288 			LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
289 			/* fallthru */
290 
291 		default:
292 			/* error */
293 			break;
294 		}
295 	}
296 
297 	if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
298 		if ( ld->ld_errno == LDAP_SUCCESS ) {
299 			ld->ld_errno = LDAP_SERVER_DOWN;
300 		}
301 
302 		ber_free( ber, 1 );
303 		if ( incparent ) {
304 			/* Forget about the bind */
305 			--parentreq->lr_outrefcnt;
306 		}
307 		LDAP_CONN_UNLOCK_IF(m_noconn);
308 		return( -1 );
309 	}
310 
311 	use_connection( ld, lc );
312 
313 #ifdef LDAP_CONNECTIONLESS
314 	if ( LDAP_IS_UDP( ld )) {
315 		BerElement tmpber = *ber;
316 		ber_rewind( &tmpber );
317 		LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
318 		rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
319 			sizeof( struct sockaddr_storage ), 0 );
320 		LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
321 		if ( rc == -1 ) {
322 			ld->ld_errno = LDAP_ENCODING_ERROR;
323 			ber_free( ber, 1 );
324 			LDAP_CONN_UNLOCK_IF(m_noconn);
325 			return rc;
326 		}
327 	}
328 #endif
329 
330 	/* If we still have an incomplete write, try to finish it before
331 	 * dealing with the new request. If we don't finish here, return
332 	 * LDAP_BUSY and let the caller retry later. We only allow a single
333 	 * request to be in WRITING state.
334 	 */
335 	rc = 0;
336 	if ( ld->ld_requests != NULL ) {
337 		TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
338 		LDAPRequest *lr;
339 
340 		assert( node != NULL );
341 		lr = node->avl_data;
342 		if ( lr->lr_status == LDAP_REQST_WRITING &&
343 				ldap_int_flush_request( ld, lr ) < 0 ) {
344 			rc = -1;
345 		}
346 	}
347 	if ( rc ) {
348 		ber_free( ber, 1 );
349 		LDAP_CONN_UNLOCK_IF(m_noconn);
350 		return rc;
351 	}
352 
353 	lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
354 	if ( lr == NULL ) {
355 		ld->ld_errno = LDAP_NO_MEMORY;
356 		ldap_free_connection( ld, lc, 0, 0 );
357 		ber_free( ber, 1 );
358 		if ( incparent ) {
359 			/* Forget about the bind */
360 			--parentreq->lr_outrefcnt;
361 		}
362 		LDAP_CONN_UNLOCK_IF(m_noconn);
363 		return( -1 );
364 	}
365 	lr->lr_msgid = msgid;
366 	lr->lr_status = LDAP_REQST_INPROGRESS;
367 	lr->lr_res_errno = LDAP_SUCCESS;	/* optimistic */
368 	lr->lr_ber = ber;
369 	lr->lr_conn = lc;
370 	if ( parentreq != NULL ) {	/* sub-request */
371 		if ( !incparent ) {
372 			/* Increment if we didn't do it before the bind */
373 			++parentreq->lr_outrefcnt;
374 		}
375 		lr->lr_origid = parentreq->lr_origid;
376 		lr->lr_parentcnt = ++parentreq->lr_parentcnt;
377 		lr->lr_parent = parentreq;
378 		lr->lr_refnext = parentreq->lr_child;
379 		parentreq->lr_child = lr;
380 	} else {			/* original request */
381 		lr->lr_origid = lr->lr_msgid;
382 	}
383 
384 	/* Extract requestDN for future reference */
385 #ifdef LDAP_CONNECTIONLESS
386 	if ( !LDAP_IS_UDP(ld) )
387 #endif
388 	{
389 		BerElement tmpber = *ber;
390 		ber_int_t	bint;
391 		ber_tag_t	tag, rtag;
392 
393 		ber_reset( &tmpber, 1 );
394 		rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
395 		switch ( tag ) {
396 		case LDAP_REQ_BIND:
397 			rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
398 			break;
399 		case LDAP_REQ_DELETE:
400 			break;
401 		default:
402 			rtag = ber_scanf( &tmpber, "{" /*}*/ );
403 		case LDAP_REQ_ABANDON:
404 			break;
405 		}
406 		if ( tag != LDAP_REQ_ABANDON ) {
407 			ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
408 			lr->lr_dn.bv_val = tmpber.ber_ptr;
409 		}
410 	}
411 
412 	rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error );
413 	assert( rc == LDAP_SUCCESS );
414 
415 	ld->ld_errno = LDAP_SUCCESS;
416 	if ( ldap_int_flush_request( ld, lr ) == -1 ) {
417 		msgid = -1;
418 	}
419 
420 	LDAP_CONN_UNLOCK_IF(m_noconn);
421 	return( msgid );
422 }
423 
424 /* return 0 if no StartTLS ext, 1 if present, 2 if critical */
425 static int
find_tls_ext(LDAPURLDesc * srv)426 find_tls_ext( LDAPURLDesc *srv )
427 {
428 	int i, crit;
429 	char *ext;
430 
431 	if ( !srv->lud_exts )
432 		return 0;
433 
434 	for (i=0; srv->lud_exts[i]; i++) {
435 		crit = 0;
436 		ext = srv->lud_exts[i];
437 		if ( ext[0] == '!') {
438 			ext++;
439 			crit = 1;
440 		}
441 		if ( !strcasecmp( ext, "StartTLS" ) ||
442 			!strcasecmp( ext, "X-StartTLS" ) ||
443 			!strcmp( ext, LDAP_EXOP_START_TLS )) {
444 			return crit + 1;
445 		}
446 	}
447 	return 0;
448 }
449 
450 /*
451  * always protected by conn_mutex
452  * optionally protected by req_mutex and res_mutex
453  */
454 LDAPConn *
ldap_new_connection(LDAP * ld,LDAPURLDesc ** srvlist,int use_ldsb,int connect,LDAPreqinfo * bind,int m_req,int m_res)455 ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
456 	int connect, LDAPreqinfo *bind, int m_req, int m_res )
457 {
458 	LDAPConn	*lc;
459 	int		async = 0;
460 
461 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
462 	Debug3( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
463 		use_ldsb, connect, (bind != NULL) );
464 	/*
465 	 * make a new LDAP server connection
466 	 * XXX open connection synchronously for now
467 	 */
468 	lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
469 	if ( lc == NULL ) {
470 		ld->ld_errno = LDAP_NO_MEMORY;
471 		return( NULL );
472 	}
473 
474 	if ( use_ldsb ) {
475 		assert( ld->ld_sb != NULL );
476 		lc->lconn_sb = ld->ld_sb;
477 
478 	} else {
479 		lc->lconn_sb = ber_sockbuf_alloc();
480 		if ( lc->lconn_sb == NULL ) {
481 			LDAP_FREE( (char *)lc );
482 			ld->ld_errno = LDAP_NO_MEMORY;
483 			return( NULL );
484 		}
485 	}
486 
487 	if ( connect ) {
488 		LDAPURLDesc	**srvp, *srv = NULL;
489 
490 		async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
491 
492 		for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
493 			int		rc;
494 
495 			rc = ldap_int_open_connection( ld, lc, *srvp, async );
496 			if ( rc != -1 ) {
497 				srv = *srvp;
498 
499 				/* If we fully connected, async is moot */
500 				if ( rc == 0 )
501 					async = 0;
502 
503 				if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
504 					ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
505 				}
506 
507 				break;
508 			}
509 		}
510 
511 		if ( srv == NULL ) {
512 			if ( !use_ldsb ) {
513 				ber_sockbuf_free( lc->lconn_sb );
514 			}
515 			LDAP_FREE( (char *)lc );
516 			ld->ld_errno = LDAP_SERVER_DOWN;
517 			return( NULL );
518 		}
519 
520 		lc->lconn_server = ldap_url_dup( srv );
521 		if ( !lc->lconn_server ) {
522 			if ( !use_ldsb )
523 				ber_sockbuf_free( lc->lconn_sb );
524 			LDAP_FREE( (char *)lc );
525 			ld->ld_errno = LDAP_NO_MEMORY;
526 			return( NULL );
527 		}
528 	}
529 
530 	lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
531 	lc->lconn_next = ld->ld_conns;
532 	ld->ld_conns = lc;
533 
534 	if ( connect ) {
535 #ifdef HAVE_TLS
536 		if ( lc->lconn_server->lud_exts ) {
537 			int rc, ext = find_tls_ext( lc->lconn_server );
538 			if ( ext ) {
539 				LDAPConn	*savedefconn;
540 
541 				savedefconn = ld->ld_defconn;
542 				++lc->lconn_refcnt;	/* avoid premature free */
543 				ld->ld_defconn = lc;
544 
545 				LDAP_REQ_UNLOCK_IF(m_req);
546 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
547 				LDAP_RES_UNLOCK_IF(m_res);
548 				rc = ldap_start_tls_s( ld, NULL, NULL );
549 				LDAP_RES_LOCK_IF(m_res);
550 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
551 				LDAP_REQ_LOCK_IF(m_req);
552 				ld->ld_defconn = savedefconn;
553 				--lc->lconn_refcnt;
554 
555 				if ( rc != LDAP_SUCCESS && ext == 2 ) {
556 					ldap_free_connection( ld, lc, 1, 0 );
557 					return NULL;
558 				}
559 			}
560 		}
561 #endif
562 	}
563 
564 	if ( bind != NULL ) {
565 		int		err = 0;
566 		LDAPConn	*savedefconn;
567 
568 		/* Set flag to prevent additional referrals
569 		 * from being processed on this
570 		 * connection until the bind has completed
571 		 */
572 		lc->lconn_rebind_inprogress = 1;
573 		/* V3 rebind function */
574 		if ( ld->ld_rebind_proc != NULL) {
575 			LDAPURLDesc	*srvfunc;
576 
577 			srvfunc = ldap_url_dup( *srvlist );
578 			if ( srvfunc == NULL ) {
579 				ld->ld_errno = LDAP_NO_MEMORY;
580 				err = -1;
581 			} else {
582 				savedefconn = ld->ld_defconn;
583 				++lc->lconn_refcnt;	/* avoid premature free */
584 				ld->ld_defconn = lc;
585 
586 				Debug0( LDAP_DEBUG_TRACE, "Call application rebind_proc\n" );
587 				LDAP_REQ_UNLOCK_IF(m_req);
588 				LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
589 				LDAP_RES_UNLOCK_IF(m_res);
590 				err = (*ld->ld_rebind_proc)( ld,
591 					bind->ri_url, bind->ri_request, bind->ri_msgid,
592 					ld->ld_rebind_params );
593 				LDAP_RES_LOCK_IF(m_res);
594 				LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
595 				LDAP_REQ_LOCK_IF(m_req);
596 
597 				ld->ld_defconn = savedefconn;
598 				--lc->lconn_refcnt;
599 
600 				if ( err != 0 ) {
601 					err = -1;
602 					ldap_free_connection( ld, lc, 1, 0 );
603 					lc = NULL;
604 				}
605 				ldap_free_urldesc( srvfunc );
606 			}
607 
608 		} else {
609 			int		msgid, rc;
610 			struct berval	passwd = BER_BVNULL;
611 
612 			savedefconn = ld->ld_defconn;
613 			++lc->lconn_refcnt;	/* avoid premature free */
614 			ld->ld_defconn = lc;
615 
616 			Debug0( LDAP_DEBUG_TRACE,
617 				"anonymous rebind via ldap_sasl_bind(\"\")\n" );
618 
619 			LDAP_REQ_UNLOCK_IF(m_req);
620 			LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
621 			LDAP_RES_UNLOCK_IF(m_res);
622 			rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
623 				NULL, NULL, &msgid );
624 			if ( rc != LDAP_SUCCESS ) {
625 				err = -1;
626 
627 			} else {
628 				for ( err = 1; err > 0; ) {
629 					struct timeval	tv = { 0, 100000 };
630 					LDAPMessage	*res = NULL;
631 
632 					switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
633 					case -1:
634 						err = -1;
635 						break;
636 
637 					case 0:
638 #ifdef LDAP_R_COMPILE
639 						ldap_pvt_thread_yield();
640 #endif
641 						break;
642 
643 					case LDAP_RES_BIND:
644 						rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
645 						if ( rc != LDAP_SUCCESS ) {
646 							err = -1;
647 
648 						} else if ( err != LDAP_SUCCESS ) {
649 							err = -1;
650 						}
651 						/* else err == LDAP_SUCCESS == 0 */
652 						break;
653 
654 					default:
655 						Debug3( LDAP_DEBUG_TRACE,
656 							"ldap_new_connection %p: "
657 							"unexpected response %d "
658 							"from BIND request id=%d\n",
659 							(void *) ld, ldap_msgtype( res ), msgid );
660 						err = -1;
661 						break;
662 					}
663 				}
664 			}
665 			LDAP_RES_LOCK_IF(m_res);
666 			LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
667 			LDAP_REQ_LOCK_IF(m_req);
668 			ld->ld_defconn = savedefconn;
669 			--lc->lconn_refcnt;
670 
671 			if ( err != 0 ) {
672 				ldap_free_connection( ld, lc, 1, 0 );
673 				lc = NULL;
674 			}
675 		}
676 		if ( lc != NULL )
677 			lc->lconn_rebind_inprogress = 0;
678 	}
679 	return( lc );
680 }
681 
682 
683 /* protected by ld_conn_mutex */
684 static LDAPConn *
find_connection(LDAP * ld,LDAPURLDesc * srv,int any)685 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
686 /*
687  * return an existing connection (if any) to the server srv
688  * if "any" is non-zero, check for any server in the "srv" chain
689  */
690 {
691 	LDAPConn	*lc;
692 	LDAPURLDesc	*lcu, *lsu;
693 	int lcu_port, lsu_port;
694 	int found = 0;
695 
696 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
697 	for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
698 		lcu = lc->lconn_server;
699 		lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
700 			lcu->lud_port );
701 
702 		for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
703 			lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
704 				lsu->lud_port );
705 
706 			if ( lsu_port == lcu_port
707 				&& strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
708 				&& lcu->lud_host != NULL && lsu->lud_host != NULL
709 				&& strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
710 			{
711 				found = 1;
712 				break;
713 			}
714 
715 			if ( !any ) break;
716 		}
717 		if ( found )
718 			break;
719 	}
720 	return lc;
721 }
722 
723 
724 
725 /* protected by ld_conn_mutex */
726 static void
use_connection(LDAP * ld,LDAPConn * lc)727 use_connection( LDAP *ld, LDAPConn *lc )
728 {
729 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
730 	++lc->lconn_refcnt;
731 	lc->lconn_lastused = time( NULL );
732 }
733 
734 
735 /* protected by ld_conn_mutex */
736 void
ldap_free_connection(LDAP * ld,LDAPConn * lc,int force,int unbind)737 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
738 {
739 	LDAPConn	*tmplc, *prevlc;
740 
741 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
742 	Debug2( LDAP_DEBUG_TRACE,
743 		"ldap_free_connection %d %d\n",
744 		force, unbind );
745 
746 	if ( force || --lc->lconn_refcnt <= 0 ) {
747 		/* remove from connections list first */
748 
749 		for ( prevlc = NULL, tmplc = ld->ld_conns;
750 			tmplc != NULL;
751 			tmplc = tmplc->lconn_next )
752 		{
753 			if ( tmplc == lc ) {
754 				if ( prevlc == NULL ) {
755 				    ld->ld_conns = tmplc->lconn_next;
756 				} else {
757 				    prevlc->lconn_next = tmplc->lconn_next;
758 				}
759 				if ( ld->ld_defconn == lc ) {
760 					ld->ld_defconn = NULL;
761 				}
762 				break;
763 			}
764 			prevlc = tmplc;
765 		}
766 
767 		/* process connection callbacks */
768 		{
769 			struct ldapoptions *lo;
770 			ldaplist *ll;
771 			ldap_conncb *cb;
772 
773 			lo = &ld->ld_options;
774 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
775 			if ( lo->ldo_conn_cbs ) {
776 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
777 					cb = ll->ll_data;
778 					cb->lc_del( ld, lc->lconn_sb, cb );
779 				}
780 			}
781 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
782 			lo = LDAP_INT_GLOBAL_OPT();
783 			LDAP_MUTEX_LOCK( &lo->ldo_mutex );
784 			if ( lo->ldo_conn_cbs ) {
785 				for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
786 					cb = ll->ll_data;
787 					cb->lc_del( ld, lc->lconn_sb, cb );
788 				}
789 			}
790 			LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
791 		}
792 
793 		if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
794 			ldap_mark_select_clear( ld, lc->lconn_sb );
795 			if ( unbind ) {
796 				ldap_send_unbind( ld, lc->lconn_sb,
797 						NULL, NULL );
798 			}
799 		}
800 
801 		if ( lc->lconn_ber != NULL ) {
802 			ber_free( lc->lconn_ber, 1 );
803 		}
804 
805 		ldap_int_sasl_close( ld, lc );
806 #ifdef HAVE_GSSAPI
807 		ldap_int_gssapi_close( ld, lc );
808 #endif
809 
810 		ldap_free_urllist( lc->lconn_server );
811 
812 		/* FIXME: is this at all possible?
813 		 * ldap_ld_free() in unbind.c calls ldap_free_connection()
814 		 * with force == 1 __after__ explicitly calling
815 		 * ldap_tavl_free on ld->ld_requests */
816 		if ( force ) {
817 			ldap_tavl_free( ld->ld_requests, ldap_do_free_request );
818 			ld->ld_requests = NULL;
819 		}
820 
821 		if ( lc->lconn_sb != ld->ld_sb ) {
822 			ber_sockbuf_free( lc->lconn_sb );
823 		} else {
824 			ber_int_sb_close( lc->lconn_sb );
825 		}
826 
827 		if ( lc->lconn_rebind_queue != NULL) {
828 			int i;
829 			for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
830 				LDAP_VFREE( lc->lconn_rebind_queue[i] );
831 			}
832 			LDAP_FREE( lc->lconn_rebind_queue );
833 		}
834 
835 		LDAP_FREE( lc );
836 
837 		Debug0( LDAP_DEBUG_TRACE,
838 			"ldap_free_connection: actually freed\n" );
839 
840 	} else {
841 		lc->lconn_lastused = time( NULL );
842 		Debug1( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
843 				lc->lconn_refcnt );
844 	}
845 }
846 
847 
848 /* Protects self with ld_conn_mutex */
849 #ifdef LDAP_DEBUG
850 void
ldap_dump_connection(LDAP * ld,LDAPConn * lconns,int all)851 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
852 {
853 	LDAPConn	*lc;
854    	char		timebuf[32];
855 
856 	Debug2( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "" );
857 	LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
858 	for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
859 		if ( lc->lconn_server != NULL ) {
860 			Debug3( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
861 				( lc->lconn_server->lud_host == NULL ) ? "(null)"
862 				: lc->lconn_server->lud_host,
863 				lc->lconn_server->lud_port, ( lc->lconn_sb ==
864 				ld->ld_sb ) ? "  (default)" : "" );
865 		}
866 		if ( lc->lconn_sb != NULL ) {
867 			char 			from[LDAP_IPADDRLEN];
868 			struct berval 	frombv = BER_BVC(from);
869 			ber_socket_t 	sb;
870 			if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sb ) == 1 ) {
871 				Sockaddr sin;
872 				socklen_t len = sizeof( sin );
873 				if ( getsockname( sb, (struct sockaddr *)&sin, &len ) == 0 ) {
874 					ldap_pvt_sockaddrstr( &sin, &frombv );
875 					Debug1( LDAP_DEBUG_TRACE, "* from: %s\n",
876 						( from == NULL ) ? "(null)" : from );
877 				}
878 			}
879 		}
880 		Debug2( LDAP_DEBUG_TRACE, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
881 			( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
882 				? "NeedSocket" :
883 				( lc->lconn_status == LDAP_CONNST_CONNECTING )
884 					? "Connecting" : "Connected" );
885 		Debug2( LDAP_DEBUG_TRACE, "  last used: %s%s\n",
886 			ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
887 			lc->lconn_rebind_inprogress ? "  rebind in progress" : "" );
888 		if ( lc->lconn_rebind_inprogress ) {
889 			if ( lc->lconn_rebind_queue != NULL) {
890 				int	i;
891 
892 				for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
893 					int	j;
894 					for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
895 						Debug3( LDAP_DEBUG_TRACE, "    queue %d entry %d - %s\n",
896 							i, j, lc->lconn_rebind_queue[i][j] );
897 					}
898 				}
899 			} else {
900 				Debug0( LDAP_DEBUG_TRACE, "    queue is empty\n" );
901 			}
902 		}
903 		Debug0( LDAP_DEBUG_TRACE, "\n" );
904 		if ( !all ) {
905 			break;
906 		}
907 	}
908 	LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
909 }
910 
911 
912 /* protected by req_mutex and res_mutex */
913 void
ldap_dump_requests_and_responses(LDAP * ld)914 ldap_dump_requests_and_responses( LDAP *ld )
915 {
916 	LDAPMessage	*lm, *l;
917 	TAvlnode *node;
918 	int		i;
919 
920 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
921 		(void *)ld );
922 	node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_LEFT );
923 	if ( node == NULL ) {
924 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
925 	}
926 	for ( i = 0 ; node != NULL; i++, node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
927 		LDAPRequest	*lr = node->avl_data;
928 
929 		Debug3( LDAP_DEBUG_TRACE, " * msgid %d,  origid %d, status %s\n",
930 			lr->lr_msgid, lr->lr_origid,
931 			( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
932 			( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
933 			( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
934 			( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
935 			( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
936 				: "InvalidStatus" );
937 		Debug2( LDAP_DEBUG_TRACE, "   outstanding referrals %d, parent count %d\n",
938 			lr->lr_outrefcnt, lr->lr_parentcnt );
939 	}
940 	Debug3( LDAP_DEBUG_TRACE, "  ld %p request count %d (abandoned %lu)\n",
941 		(void *)ld, i, ld->ld_nabandoned );
942 	Debug1( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld );
943 	if ( ( lm = ld->ld_responses ) == NULL ) {
944 		Debug0( LDAP_DEBUG_TRACE, "   Empty\n" );
945 	}
946 	for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
947 		Debug2( LDAP_DEBUG_TRACE, " * msgid %d,  type %lu\n",
948 		    lm->lm_msgid, (unsigned long)lm->lm_msgtype );
949 		if ( lm->lm_chain != NULL ) {
950 			Debug0( LDAP_DEBUG_TRACE, "   chained responses:\n" );
951 			for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
952 				Debug2( LDAP_DEBUG_TRACE,
953 					"  * msgid %d,  type %lu\n",
954 					l->lm_msgid,
955 					(unsigned long)l->lm_msgtype );
956 			}
957 		}
958 	}
959 	Debug2( LDAP_DEBUG_TRACE, "  ld %p response count %d\n", (void *)ld, i );
960 }
961 #endif /* LDAP_DEBUG */
962 
963 /* protected by req_mutex */
964 void
ldap_do_free_request(void * arg)965 ldap_do_free_request( void *arg )
966 {
967 	LDAPRequest *lr = arg;
968 
969 	Debug3( LDAP_DEBUG_TRACE, "ldap_do_free_request: "
970 			"asked to free lr %p msgid %d refcnt %d\n",
971 			lr, lr->lr_msgid, lr->lr_refcnt );
972 	/* if lr_refcnt > 0, the request has been looked up
973 	 * by ldap_find_request_by_msgid(); if in the meanwhile
974 	 * the request is free()'d by someone else, just decrease
975 	 * the reference count; later on, it will be freed. */
976 	if ( lr->lr_refcnt > 0 ) {
977 		assert( lr->lr_refcnt == 1 );
978 		lr->lr_refcnt = -lr->lr_refcnt;
979 		return;
980 	}
981 
982 	if ( lr->lr_ber != NULL ) {
983 		ber_free( lr->lr_ber, 1 );
984 		lr->lr_ber = NULL;
985 	}
986 
987 	if ( lr->lr_res_error != NULL ) {
988 		LDAP_FREE( lr->lr_res_error );
989 		lr->lr_res_error = NULL;
990 	}
991 
992 	if ( lr->lr_res_matched != NULL ) {
993 		LDAP_FREE( lr->lr_res_matched );
994 		lr->lr_res_matched = NULL;
995 	}
996 
997 	LDAP_FREE( lr );
998 }
999 
1000 int
ldap_req_cmp(const void * l,const void * r)1001 ldap_req_cmp( const void *l, const void *r )
1002 {
1003 	const LDAPRequest *left = l, *right = r;
1004 	return left->lr_msgid - right->lr_msgid;
1005 }
1006 
1007 /* protected by req_mutex */
1008 static void
ldap_free_request_int(LDAP * ld,LDAPRequest * lr)1009 ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
1010 {
1011 	LDAPRequest *removed;
1012 
1013 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1014 	removed = ldap_tavl_delete( &ld->ld_requests, lr, ldap_req_cmp );
1015 	assert( !removed || removed == lr );
1016 	Debug3( LDAP_DEBUG_TRACE, "ldap_free_request_int: "
1017 			"lr %p msgid %d%s removed\n",
1018 			lr, lr->lr_msgid, removed ? "" : " not" );
1019 
1020 	ldap_do_free_request( lr );
1021 }
1022 
1023 /* protected by req_mutex */
1024 void
ldap_free_request(LDAP * ld,LDAPRequest * lr)1025 ldap_free_request( LDAP *ld, LDAPRequest *lr )
1026 {
1027 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1028 	Debug2( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
1029 		lr->lr_origid, lr->lr_msgid );
1030 
1031 	/* free all referrals (child requests) */
1032 	while ( lr->lr_child ) {
1033 		ldap_free_request( ld, lr->lr_child );
1034 	}
1035 
1036 	if ( lr->lr_parent != NULL ) {
1037 		LDAPRequest     **lrp;
1038 
1039 		--lr->lr_parent->lr_outrefcnt;
1040 		for ( lrp = &lr->lr_parent->lr_child;
1041 			*lrp && *lrp != lr;
1042 			lrp = &(*lrp)->lr_refnext );
1043 
1044 		if ( *lrp == lr ) {
1045 			*lrp = lr->lr_refnext;
1046 		}
1047 	}
1048 	ldap_free_request_int( ld, lr );
1049 }
1050 
1051 /*
1052  * call first time with *cntp = -1
1053  * when returns *cntp == -1, no referrals are left
1054  *
1055  * NOTE: may replace *refsp, or shuffle the contents
1056  * of the original array.
1057  */
ldap_int_nextref(LDAP * ld,char *** refsp,int * cntp,void * params)1058 static int ldap_int_nextref(
1059 	LDAP			*ld,
1060 	char			***refsp,
1061 	int			*cntp,
1062 	void			*params )
1063 {
1064 	assert( refsp != NULL );
1065 	assert( *refsp != NULL );
1066 	assert( cntp != NULL );
1067 
1068 	if ( *cntp < -1 ) {
1069 		*cntp = -1;
1070 		return -1;
1071 	}
1072 
1073 	(*cntp)++;
1074 
1075 	if ( (*refsp)[ *cntp ] == NULL ) {
1076 		*cntp = -1;
1077 	}
1078 
1079 	return 0;
1080 }
1081 
1082 /*
1083  * Chase v3 referrals
1084  *
1085  * Parameters:
1086  *  (IN) ld = LDAP connection handle
1087  *  (IN) lr = LDAP Request structure
1088  *  (IN) refs = array of pointers to referral strings that we will chase
1089  *              The array will be free'd by this function when no longer needed
1090  *  (IN) sref != 0 if following search reference
1091  *  (OUT) errstrp = Place to return a string of referrals which could not be followed
1092  *  (OUT) hadrefp = 1 if successfully followed referral
1093  *
1094  * Return value - number of referrals followed
1095  *
1096  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
1097  */
1098 int
ldap_chase_v3referrals(LDAP * ld,LDAPRequest * lr,char ** refs,int sref,char ** errstrp,int * hadrefp)1099 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
1100 {
1101 	char		*unfollowed;
1102 	int		 unfollowedcnt = 0;
1103 	LDAPRequest	*origreq;
1104 	LDAPURLDesc	*srv = NULL;
1105 	BerElement	*ber;
1106 	char		**refarray = NULL;
1107 	LDAPConn	*lc;
1108 	int			 rc, count, i, j, id;
1109 	LDAPreqinfo  rinfo;
1110 	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
1111 
1112 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1113 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1114 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1115 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n" );
1116 
1117 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
1118 	*hadrefp = 0;
1119 
1120 	unfollowed = NULL;
1121 	rc = count = 0;
1122 
1123 	/* If no referrals in array, return */
1124 	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
1125 		rc = 0;
1126 		goto done;
1127 	}
1128 
1129 	/* Check for hop limit exceeded */
1130 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
1131 		Debug1( LDAP_DEBUG_ANY,
1132 		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit );
1133 		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
1134 		rc = -1;
1135 		goto done;
1136 	}
1137 
1138 	/* find original request */
1139 	for ( origreq = lr;
1140 		origreq->lr_parent != NULL;
1141 		origreq = origreq->lr_parent )
1142 	{
1143 		/* empty */ ;
1144 	}
1145 
1146 	refarray = refs;
1147 	refs = NULL;
1148 
1149 	/* parse out & follow referrals */
1150 	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
1151 	i = -1;
1152 	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
1153 			i != -1;
1154 			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
1155 	{
1156 
1157 		/* Parse the referral URL */
1158 		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
1159 		if ( rc != LDAP_URL_SUCCESS ) {
1160 			/* ldap_url_parse_ext() returns LDAP_URL_* errors
1161 			 * which do not map on API errors */
1162 			ld->ld_errno = LDAP_PARAM_ERROR;
1163 			rc = -1;
1164 			goto done;
1165 		}
1166 
1167 		if( srv->lud_crit_exts ) {
1168 			int ok = 0;
1169 #ifdef HAVE_TLS
1170 			/* If StartTLS is the only critical ext, OK. */
1171 			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
1172 				ok = 1;
1173 #endif
1174 			if ( !ok ) {
1175 				/* we do not support any other extensions */
1176 				ld->ld_errno = LDAP_NOT_SUPPORTED;
1177 				rc = -1;
1178 				goto done;
1179 			}
1180 		}
1181 
1182 		/* check connection for re-bind in progress */
1183 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
1184 			/* See if we've already requested this DN with this conn */
1185 			LDAPRequest *lp;
1186 			int looped = 0;
1187 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
1188 			for ( lp = origreq; lp; ) {
1189 				if ( lp->lr_conn == lc
1190 					&& len == lp->lr_dn.bv_len
1191 					&& len
1192 					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
1193 				{
1194 					looped = 1;
1195 					break;
1196 				}
1197 				if ( lp == origreq ) {
1198 					lp = lp->lr_child;
1199 				} else {
1200 					lp = lp->lr_refnext;
1201 				}
1202 			}
1203 			if ( looped ) {
1204 				ldap_free_urllist( srv );
1205 				srv = NULL;
1206 				ld->ld_errno = LDAP_CLIENT_LOOP;
1207 				rc = -1;
1208 				continue;
1209 			}
1210 
1211 			if ( lc->lconn_rebind_inprogress ) {
1212 				/* We are already chasing a referral or search reference and a
1213 				 * bind on that connection is in progress.  We must queue
1214 				 * referrals on that connection, so we don't get a request
1215 				 * going out before the bind operation completes. This happens
1216 				 * if two search references come in one behind the other
1217 				 * for the same server with different contexts.
1218 				 */
1219 				Debug1( LDAP_DEBUG_TRACE,
1220 					"ldap_chase_v3referrals: queue referral \"%s\"\n",
1221 					refarray[i] );
1222 				if( lc->lconn_rebind_queue == NULL ) {
1223 					/* Create a referral list */
1224 					lc->lconn_rebind_queue =
1225 						(char ***) LDAP_MALLOC( sizeof(void *) * 2);
1226 
1227 					if( lc->lconn_rebind_queue == NULL) {
1228 						ld->ld_errno = LDAP_NO_MEMORY;
1229 						rc = -1;
1230 						goto done;
1231 					}
1232 
1233 					lc->lconn_rebind_queue[0] = refarray;
1234 					lc->lconn_rebind_queue[1] = NULL;
1235 					refarray = NULL;
1236 
1237 				} else {
1238 					/* Count how many referral arrays we already have */
1239 					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
1240 						/* empty */;
1241 					}
1242 
1243 					/* Add the new referral to the list */
1244 					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
1245 						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
1246 
1247 					if( lc->lconn_rebind_queue == NULL ) {
1248 						ld->ld_errno = LDAP_NO_MEMORY;
1249 						rc = -1;
1250 						goto done;
1251 					}
1252 					lc->lconn_rebind_queue[j] = refarray;
1253 					lc->lconn_rebind_queue[j+1] = NULL;
1254 					refarray = NULL;
1255 				}
1256 
1257 				/* We have queued the referral/reference, now just return */
1258 				rc = 0;
1259 				*hadrefp = 1;
1260 				count = 1; /* Pretend we already followed referral */
1261 				goto done;
1262 			}
1263 		}
1264 		/* Re-encode the request with the new starting point of the search.
1265 		 * Note: In the future we also need to replace the filter if one
1266 		 * was provided with the search reference
1267 		 */
1268 
1269 		/* For references we don't want old dn if new dn empty */
1270 		if ( sref && srv->lud_dn == NULL ) {
1271 			srv->lud_dn = LDAP_STRDUP( "" );
1272 		}
1273 
1274 		LDAP_NEXT_MSGID( ld, id );
1275 		ber = re_encode_request( ld, origreq->lr_ber, id,
1276 			sref, srv, &rinfo.ri_request );
1277 
1278 		if( ber == NULL ) {
1279 			ld->ld_errno = LDAP_ENCODING_ERROR;
1280 			rc = -1;
1281 			goto done;
1282 		}
1283 
1284 		Debug2( LDAP_DEBUG_TRACE,
1285 			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
1286 			lr->lr_msgid, refarray[i] );
1287 
1288 		/* Send the new request to the server - may require a bind */
1289 		rinfo.ri_msgid = origreq->lr_origid;
1290 		rinfo.ri_url = refarray[i];
1291 		rc = ldap_send_server_request( ld, ber, id,
1292 			origreq, &srv, NULL, &rinfo, 0, 1 );
1293 		if ( rc < 0 ) {
1294 			/* Failure, try next referral in the list */
1295 			Debug3( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
1296 				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
1297 			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
1298 			ldap_free_urllist( srv );
1299 			srv = NULL;
1300 			ld->ld_errno = LDAP_REFERRAL;
1301 		} else {
1302 			/* Success, no need to try this referral list further */
1303 			rc = 0;
1304 			++count;
1305 			*hadrefp = 1;
1306 
1307 			/* check if there is a queue of referrals that came in during bind */
1308 			if ( lc == NULL) {
1309 				lc = find_connection( ld, srv, 1 );
1310 				if ( lc == NULL ) {
1311 					ld->ld_errno = LDAP_OPERATIONS_ERROR;
1312 					rc = -1;
1313 					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
1314 					goto done;
1315 				}
1316 			}
1317 
1318 			if ( lc->lconn_rebind_queue != NULL ) {
1319 				/* Release resources of previous list */
1320 				LDAP_VFREE( refarray );
1321 				refarray = NULL;
1322 				ldap_free_urllist( srv );
1323 				srv = NULL;
1324 
1325 				/* Pull entries off end of queue so list always null terminated */
1326 				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
1327 					;
1328 				refarray = lc->lconn_rebind_queue[j - 1];
1329 				lc->lconn_rebind_queue[j-1] = NULL;
1330 				/* we pulled off last entry from queue, free queue */
1331 				if ( j == 1 ) {
1332 					LDAP_FREE( lc->lconn_rebind_queue );
1333 					lc->lconn_rebind_queue = NULL;
1334 				}
1335 				/* restart the loop the with new referral list */
1336 				i = -1;
1337 				continue;
1338 			}
1339 			break; /* referral followed, break out of for loop */
1340 		}
1341 	} /* end for loop */
1342 done:
1343 	LDAP_VFREE( refarray );
1344 	ldap_free_urllist( srv );
1345 	LDAP_FREE( *errstrp );
1346 
1347 	if( rc == 0 ) {
1348 		*errstrp = NULL;
1349 		LDAP_FREE( unfollowed );
1350 		return count;
1351 	} else {
1352 		*errstrp = unfollowed;
1353 		return rc;
1354 	}
1355 }
1356 
1357 /*
1358  * XXX merging of errors in this routine needs to be improved
1359  * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
1360  */
1361 int
ldap_chase_referrals(LDAP * ld,LDAPRequest * lr,char ** errstrp,int sref,int * hadrefp)1362 ldap_chase_referrals( LDAP *ld,
1363 	LDAPRequest *lr,
1364 	char **errstrp,
1365 	int sref,
1366 	int *hadrefp )
1367 {
1368 	int		rc, count, id;
1369 	unsigned	len;
1370 	char		*p, *ref, *unfollowed;
1371 	LDAPRequest	*origreq;
1372 	LDAPURLDesc	*srv;
1373 	BerElement	*ber;
1374 	LDAPreqinfo  rinfo;
1375 	LDAPConn	*lc;
1376 
1377 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
1378 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
1379 	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
1380 	Debug0( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n" );
1381 
1382 	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
1383 	*hadrefp = 0;
1384 
1385 	if ( *errstrp == NULL ) {
1386 		return( 0 );
1387 	}
1388 
1389 	len = strlen( *errstrp );
1390 	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
1391 		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
1392 			*p = '\0';
1393 			p += LDAP_REF_STR_LEN;
1394 			break;
1395 		}
1396 	}
1397 
1398 	if ( len < LDAP_REF_STR_LEN ) {
1399 		return( 0 );
1400 	}
1401 
1402 	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
1403 		Debug1( LDAP_DEBUG_ANY,
1404 		    "more than %d referral hops (dropping)\n",
1405 		    ld->ld_refhoplimit );
1406 		    /* XXX report as error in ld->ld_errno? */
1407 		    return( 0 );
1408 	}
1409 
1410 	/* find original request */
1411 	for ( origreq = lr; origreq->lr_parent != NULL;
1412 	     origreq = origreq->lr_parent ) {
1413 		/* empty */;
1414 	}
1415 
1416 	unfollowed = NULL;
1417 	rc = count = 0;
1418 
1419 	/* parse out & follow referrals */
1420 	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
1421 		p = strchr( ref, '\n' );
1422 		if ( p != NULL ) {
1423 			*p++ = '\0';
1424 		}
1425 
1426 		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
1427 		if ( rc != LDAP_URL_SUCCESS ) {
1428 			Debug2( LDAP_DEBUG_TRACE,
1429 				"ignoring %s referral <%s>\n",
1430 				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect" );
1431 			rc = ldap_append_referral( ld, &unfollowed, ref );
1432 			*hadrefp = 1;
1433 			continue;
1434 		}
1435 
1436 		Debug1( LDAP_DEBUG_TRACE,
1437 		    "chasing LDAP referral: <%s>\n", ref );
1438 
1439 		*hadrefp = 1;
1440 
1441 		/* See if we've already been here */
1442 		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
1443 			LDAPRequest *lp;
1444 			int looped = 0;
1445 			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
1446 			for ( lp = lr; lp; lp = lp->lr_parent ) {
1447 				if ( lp->lr_conn == lc
1448 					&& len == lp->lr_dn.bv_len )
1449 				{
1450 					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
1451 							continue;
1452 					looped = 1;
1453 					break;
1454 				}
1455 			}
1456 			if ( looped ) {
1457 				ldap_free_urllist( srv );
1458 				ld->ld_errno = LDAP_CLIENT_LOOP;
1459 				rc = -1;
1460 				continue;
1461 			}
1462 		}
1463 
1464 		LDAP_NEXT_MSGID( ld, id );
1465 		ber = re_encode_request( ld, origreq->lr_ber,
1466 		    id, sref, srv, &rinfo.ri_request );
1467 
1468 		if ( ber == NULL ) {
1469 			ldap_free_urllist( srv );
1470 			return -1 ;
1471 		}
1472 
1473 		/* copy the complete referral for rebind process */
1474 		rinfo.ri_url = LDAP_STRDUP( ref );
1475 
1476 		rinfo.ri_msgid = origreq->lr_origid;
1477 
1478 		rc = ldap_send_server_request( ld, ber, id,
1479 			lr, &srv, NULL, &rinfo, 0, 1 );
1480 		LDAP_FREE( rinfo.ri_url );
1481 
1482 		if( rc >= 0 ) {
1483 			++count;
1484 		} else {
1485 			Debug3( LDAP_DEBUG_ANY,
1486 				"Unable to chase referral \"%s\" (%d: %s)\n",
1487 				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
1488 			rc = ldap_append_referral( ld, &unfollowed, ref );
1489 		}
1490 
1491 		ldap_free_urllist(srv);
1492 	}
1493 
1494 	LDAP_FREE( *errstrp );
1495 	*errstrp = unfollowed;
1496 
1497 	return(( rc == 0 ) ? count : rc );
1498 }
1499 
1500 
1501 int
ldap_append_referral(LDAP * ld,char ** referralsp,char * s)1502 ldap_append_referral( LDAP *ld, char **referralsp, char *s )
1503 {
1504 	int	first;
1505 
1506 	if ( *referralsp == NULL ) {
1507 		first = 1;
1508 		*referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
1509 		    + 1 );
1510 	} else {
1511 		first = 0;
1512 		*referralsp = (char *)LDAP_REALLOC( *referralsp,
1513 		    strlen( *referralsp ) + strlen( s ) + 2 );
1514 	}
1515 
1516 	if ( *referralsp == NULL ) {
1517 		ld->ld_errno = LDAP_NO_MEMORY;
1518 		return( -1 );
1519 	}
1520 
1521 	if ( first ) {
1522 		strcpy( *referralsp, LDAP_REF_STR );
1523 	} else {
1524 		strcat( *referralsp, "\n" );
1525 	}
1526 	strcat( *referralsp, s );
1527 
1528 	return( 0 );
1529 }
1530 
1531 
1532 
1533 static BerElement *
re_encode_request(LDAP * ld,BerElement * origber,ber_int_t msgid,int sref,LDAPURLDesc * srv,int * type)1534 re_encode_request( LDAP *ld,
1535 	BerElement *origber,
1536 	ber_int_t msgid,
1537 	int sref,
1538 	LDAPURLDesc *srv,
1539 	int *type )
1540 {
1541 	/*
1542 	 * XXX this routine knows way too much about how the lber library works!
1543 	 */
1544 	ber_int_t	along;
1545 	ber_tag_t	tag;
1546 	ber_tag_t	rtag;
1547 	ber_int_t	ver;
1548 	ber_int_t	scope;
1549 	int		rc;
1550 	BerElement	tmpber, *ber;
1551 	struct berval		dn;
1552 
1553 	Debug2( LDAP_DEBUG_TRACE,
1554 	    "re_encode_request: new msgid %ld, new dn <%s>\n",
1555 	    (long) msgid,
1556 		( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn );
1557 
1558 	tmpber = *origber;
1559 
1560 	/*
1561 	 * all LDAP requests are sequences that start with a message id.
1562 	 * For all except delete, this is followed by a sequence that is
1563 	 * tagged with the operation code.  For delete, the provided DN
1564 	 * is not wrapped by a sequence.
1565 	 */
1566 	rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
1567 
1568 	if ( rtag == LBER_ERROR ) {
1569 		ld->ld_errno = LDAP_DECODING_ERROR;
1570 		return( NULL );
1571 	}
1572 
1573 	assert( tag != 0);
1574 	if ( tag == LDAP_REQ_BIND ) {
1575 		/* bind requests have a version number before the DN & other stuff */
1576 		rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
1577 
1578 	} else if ( tag == LDAP_REQ_DELETE ) {
1579 		/* delete requests don't have a DN wrapping sequence */
1580 		rtag = ber_scanf( &tmpber, "m", &dn );
1581 
1582 	} else if ( tag == LDAP_REQ_SEARCH ) {
1583 		/* search requests need to be re-scope-ed */
1584 		rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
1585 
1586 		if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
1587 			/* use the scope provided in reference */
1588 			scope = srv->lud_scope;
1589 
1590 		} else if ( sref ) {
1591 			/* use scope implied by previous operation
1592 			 *   base -> base
1593 			 *   one -> base
1594 			 *   subtree -> subtree
1595 			 *   subordinate -> subtree
1596 			 */
1597 			switch( scope ) {
1598 			default:
1599 			case LDAP_SCOPE_BASE:
1600 			case LDAP_SCOPE_ONELEVEL:
1601 				scope = LDAP_SCOPE_BASE;
1602 				break;
1603 			case LDAP_SCOPE_SUBTREE:
1604 			case LDAP_SCOPE_SUBORDINATE:
1605 				scope = LDAP_SCOPE_SUBTREE;
1606 				break;
1607 			}
1608 		}
1609 
1610 	} else {
1611 		rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
1612 	}
1613 
1614 	if( rtag == LBER_ERROR ) {
1615 		ld->ld_errno = LDAP_DECODING_ERROR;
1616 		return NULL;
1617 	}
1618 
1619 	/* restore character zero'd out by ber_scanf*/
1620 	dn.bv_val[dn.bv_len] = tmpber.ber_tag;
1621 
1622 	if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
1623 		return NULL;
1624 	}
1625 
1626 	if ( srv->lud_dn ) {
1627 		ber_str2bv( srv->lud_dn, 0, 0, &dn );
1628 	}
1629 
1630 	if ( tag == LDAP_REQ_BIND ) {
1631 		rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
1632 	} else if ( tag == LDAP_REQ_DELETE ) {
1633 		rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
1634 	} else if ( tag == LDAP_REQ_SEARCH ) {
1635 		rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
1636 	} else {
1637 		rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
1638 	}
1639 
1640 	if ( rc == -1 ) {
1641 		ld->ld_errno = LDAP_ENCODING_ERROR;
1642 		ber_free( ber, 1 );
1643 		return NULL;
1644 	}
1645 
1646 	if ( tag != LDAP_REQ_DELETE && (
1647 		ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
1648 		!= ( tmpber.ber_end - tmpber.ber_ptr ) ||
1649 	    ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
1650 	{
1651 		ld->ld_errno = LDAP_ENCODING_ERROR;
1652 		ber_free( ber, 1 );
1653 		return NULL;
1654 	}
1655 
1656 #ifdef LDAP_DEBUG
1657 	if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
1658 		Debug0( LDAP_DEBUG_ANY, "re_encode_request new request is:\n" );
1659 		ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
1660 	}
1661 #endif /* LDAP_DEBUG */
1662 
1663 	*type = tag;	/* return request type */
1664 	return ber;
1665 }
1666 
1667 
1668 /* protected by req_mutex */
1669 LDAPRequest *
ldap_find_request_by_msgid(LDAP * ld,ber_int_t msgid)1670 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
1671 {
1672 	LDAPRequest	*lr, needle = {0};
1673 	needle.lr_msgid = msgid;
1674 
1675 	lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
1676 	if ( lr != NULL && lr->lr_status != LDAP_REQST_COMPLETED ) {
1677 		/* try_read1msg is the only user at the moment and we would free it
1678 		 * multiple times if retrieving the request again */
1679 		assert( lr->lr_refcnt == 0 );
1680 		lr->lr_refcnt++;
1681 		Debug3( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
1682 				"msgid %d, lr %p lr->lr_refcnt = %d\n",
1683 				msgid, lr, lr->lr_refcnt );
1684 		return lr;
1685 	}
1686 
1687 	Debug2( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
1688 			"msgid %d, lr %p\n", msgid, lr );
1689 	return NULL;
1690 }
1691 
1692 /* protected by req_mutex */
1693 void
ldap_return_request(LDAP * ld,LDAPRequest * lrx,int freeit)1694 ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
1695 {
1696 	LDAPRequest	*lr;
1697 
1698 	lr = ldap_tavl_find( ld->ld_requests, lrx, ldap_req_cmp );
1699 	Debug2( LDAP_DEBUG_TRACE, "ldap_return_request: "
1700 			"lrx %p, lr %p\n", lrx, lr );
1701 	if ( lr ) {
1702 		assert( lr == lrx );
1703 		if ( lr->lr_refcnt > 0 ) {
1704 			lr->lr_refcnt--;
1705 		} else if ( lr->lr_refcnt < 0 ) {
1706 			lr->lr_refcnt++;
1707 			if ( lr->lr_refcnt == 0 ) {
1708 				lr = NULL;
1709 			}
1710 		}
1711 	}
1712 	Debug3( LDAP_DEBUG_TRACE, "ldap_return_request: "
1713 			"lrx->lr_msgid %d, lrx->lr_refcnt is now %d, lr is %s present\n",
1714 			lrx->lr_msgid, lrx->lr_refcnt, lr ? "still" : "not" );
1715 	/* The request is not tracked anymore */
1716 	if ( lr == NULL ) {
1717 		ldap_free_request_int( ld, lrx );
1718 	} else if ( freeit ) {
1719 		ldap_free_request( ld, lrx );
1720 	}
1721 }
1722