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