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