]> git.sur5r.net Git - openldap/blob - libraries/libldap/cldap.c
Fix NULL pointer deref bugs
[openldap] / libraries / libldap / cldap.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1990, 1994 Regents of the University of Michigan.
8  *  All rights reserved.
9  *
10  *  cldap.c - synchronous, retrying interface to the cldap protocol
11  */
12
13 #include "portable.h"
14
15 #ifdef LDAP_CONNECTIONLESS
16
17 #include <stdio.h>
18
19 #include <ac/stdlib.h>
20
21 #include <ac/errno.h>
22 #include <ac/socket.h>
23 #include <ac/string.h>
24 #include <ac/time.h>
25 #include <ac/unistd.h>
26
27 #include "ldap-int.h"
28
29 #define DEF_CLDAP_TIMEOUT       3
30 #define DEF_CLDAP_TRIES         4
31
32
33 struct cldap_retinfo {
34         int             cri_maxtries;
35         int             cri_try;
36         int             cri_useaddr;
37         long            cri_timeout;
38 };
39
40 static int add_addr LDAP_P((
41         LDAP *ld, struct sockaddr *sap ));
42 static int cldap_result LDAP_P((
43         LDAP *ld, int msgid, LDAPMessage **res,
44         struct cldap_retinfo *crip, const char *base ));
45 static int cldap_parsemsg LDAP_P((
46         LDAP *ld, int msgid, BerElement *ber,
47         LDAPMessage **res, const char *base ));
48
49 /*
50  * cldap_open - initialize and connect to an ldap server.  A magic cookie to
51  * be used for future communication is returned on success, NULL on failure.
52  *
53  * Example:
54  *      LDAP    *ld;
55  *      ld = cldap_open( hostname, port );
56  */
57
58 LDAP *
59 cldap_open( LDAP_CONST char *host, int port )
60 {
61     int                 s;
62     unsigned long       address;
63     struct sockaddr_in  sock;
64     struct hostent      *hp;
65     LDAP                *ld;
66     char                *p;
67     int                 i;
68
69     /* buffers for ldap_pvt_gethostbyname_a ... */
70     struct hostent      he_buf;
71     int                 local_h_errno;
72     char                *ha_buf=NULL;
73
74 #define DO_RETURN(x) if (ha_buf) LDAP_FREE(ha_buf); return (x);
75    
76     Debug( LDAP_DEBUG_TRACE, "ldap_open\n", 0, 0, 0 );
77
78     if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) {
79         return( NULL );
80     }
81
82     sock.sin_addr.s_addr = 0;
83     sock.sin_family = AF_INET;
84     sock.sin_port = 0;
85     if ( bind(s, (struct sockaddr *) &sock, sizeof(sock)) < 0)  {
86         tcp_close( s );
87         return( NULL );
88     }
89     if (( ld = ldap_init( host, port )) == NULL ) {
90         tcp_close( s );
91         return( NULL );
92     }
93         
94     ld->ld_cldapnaddr = 0;
95     ld->ld_cldapaddrs = NULL;
96
97     if (ber_pvt_sb_set_io( &(ld->ld_sb), &ber_pvt_sb_io_udp, NULL )<0) {
98        ldap_ld_free(ld, 1, NULL, NULL );
99        return NULL;
100     }
101         
102     ld->ld_version = LDAP_VERSION2;
103
104     sock.sin_family = AF_INET;
105     sock.sin_port = htons( port );
106
107     /*
108      * 'host' may be a space-separated list.
109      */
110     if ( host != NULL ) {
111         char *host_dup = LDAP_STRDUP( host );
112         host = host_dup;
113         for ( ; host != NULL; host = p ) {
114             if (( p = strchr( host, ' ' )) != NULL ) {
115                 for (*p++ = '\0'; *p == ' '; p++) {
116                     ;
117                 }
118             }
119
120             address = inet_addr( host );
121             /* This was just a test for -1 until OSF1 let inet_addr return
122                unsigned int, which is narrower than 'unsigned long address' */
123             if ( address == 0xffffffff || address == (unsigned long) -1 ) {
124                 if ((ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf,
125                                               &hp,&local_h_errno)<0) || 
126                     (hp==NULL)) {
127                    errno = EHOSTUNREACH;
128                    continue;
129                 }
130
131                 for ( i = 0; hp->h_addr_list[ i ] != 0; ++i ) {
132                     SAFEMEMCPY( (char *)&sock.sin_addr,
133                             (char *)hp->h_addr_list[ i ],
134                             sizeof(sock.sin_addr));
135                     if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
136                         ldap_ld_free( ld, 1, NULL, NULL );
137                         LDAP_FREE( host_dup );
138                         DO_RETURN( NULL );
139                     }
140                 }
141
142             } else {
143                 sock.sin_addr.s_addr = address;
144                 if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
145                     ldap_ld_free( ld, 1, NULL, NULL );
146                     LDAP_FREE( host_dup );
147                     DO_RETURN( NULL );
148                 }
149             }
150
151             if ( ld->ld_host == NULL ) {
152                     ld->ld_host = LDAP_STRDUP( host );
153             }
154         }
155         LDAP_FREE( host_dup );
156     } else {
157         sock.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
158         if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
159             ldap_ld_free( ld, 1, NULL, NULL );
160             DO_RETURN( NULL );
161         }
162     }
163
164     if ( ld->ld_cldapaddrs == NULL
165             || ( ld->ld_defconn = ldap_new_connection( ld, NULL, 1,0,0 )) == NULL
166             ) {
167         ldap_ld_free( ld, 0, NULL, NULL );
168         DO_RETURN( NULL );
169     }
170
171     ber_pvt_sb_udp_set_dst( &ld->ld_sb, ld->ld_cldapaddrs[0] );
172
173     cldap_setretryinfo( ld, 0, 0 );
174
175 #ifdef LDAP_DEBUG
176     putchar( '\n' );
177     for ( i = 0; i < ld->ld_cldapnaddr; ++i ) {
178         Debug( LDAP_DEBUG_TRACE, "end of cldap_open address %d is %s\n",
179                 i, inet_ntoa( ((struct sockaddr_in *)
180                 ld->ld_cldapaddrs[ i ])->sin_addr ), 0 );
181     }
182 #endif
183
184     DO_RETURN( ld );
185 }
186
187 #undef DO_RETURN
188
189 void
190 cldap_close( LDAP *ld )
191 {
192         ldap_ld_free( ld, 0, NULL, NULL );
193 }
194
195
196 void
197 cldap_setretryinfo( LDAP *ld, int tries, int timeout )
198 {
199     ld->ld_cldaptries = ( tries <= 0 ) ? DEF_CLDAP_TRIES : tries;
200     ld->ld_cldaptimeout = ( timeout <= 0 ) ? DEF_CLDAP_TIMEOUT : timeout;
201 }
202
203
204 int
205 cldap_search_s( LDAP *ld,
206         LDAP_CONST char *base,
207         int scope,
208         LDAP_CONST char *filter,
209         char **attrs,
210         int attrsonly,
211         LDAPMessage **res,
212         char *logdn )
213 {
214     int                         ret, msgid;
215     struct cldap_retinfo        cri;
216
217     *res = NULL;
218
219     (void) memset( &cri, 0, sizeof( cri ));
220
221     if ( logdn != NULL ) {
222         ld->ld_cldapdn = logdn;
223     } else if ( ld->ld_cldapdn == NULL ) {
224         ld->ld_cldapdn = "";
225     }
226
227     do {
228         if ( cri.cri_try != 0 ) {
229                 --ld->ld_msgid; /* use same id as before */
230         }
231             
232         ber_pvt_sb_udp_set_dst( &(ld->ld_sb), 
233                         ld->ld_cldapaddrs[ cri.cri_useaddr ] );
234
235         Debug( LDAP_DEBUG_TRACE, "cldap_search_s try %d (to %s)\n",
236             cri.cri_try, inet_ntoa( ((struct sockaddr_in *)
237             ld->ld_cldapaddrs[ cri.cri_useaddr ])->sin_addr), 0 );
238
239             if ( (msgid = ldap_search( ld, base, scope, filter, attrs,
240                 attrsonly )) == -1 ) {
241                     return( ld->ld_errno );
242             }
243 #ifndef LDAP_NOCACHE
244             if ( ld->ld_cache != NULL && ld->ld_responses != NULL ) {
245                 Debug( LDAP_DEBUG_TRACE, "cldap_search_s res from cache\n",
246                         0, 0, 0 );
247                 *res = ld->ld_responses;
248                 ld->ld_responses = ld->ld_responses->lm_next;
249                 return( ldap_result2error( ld, *res, 0 ));
250             }
251 #endif /* LDAP_NOCACHE */
252             ret = cldap_result( ld, msgid, res, &cri, base );
253         } while (ret == -1);
254
255         return( ret );
256 }
257
258
259 static int
260 add_addr( LDAP *ld, struct sockaddr *sap )
261 {
262     struct sockaddr     *newsap, **addrs;
263
264     if (( newsap = (struct sockaddr *)LDAP_MALLOC( sizeof( struct sockaddr )))
265             == NULL ) {
266         ld->ld_errno = LDAP_NO_MEMORY;
267         return( -1 );
268     }
269         
270         addrs = (struct sockaddr **)LDAP_REALLOC( ld->ld_cldapaddrs,
271                 ( ld->ld_cldapnaddr + 1 ) * sizeof(struct sockaddr *));
272
273     if ( addrs == NULL ) {
274         LDAP_FREE( newsap );
275         ld->ld_errno = LDAP_NO_MEMORY;
276         return( -1 );
277     }
278
279     SAFEMEMCPY( (char *)newsap, (char *)sap, sizeof( struct sockaddr ));
280     addrs[ ld->ld_cldapnaddr++ ] = newsap;
281     ld->ld_cldapaddrs = (void **)addrs;
282     return( 0 );
283 }
284
285
286 static int
287 cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
288         struct cldap_retinfo *crip, const char *base )
289 {
290     Sockbuf             *sb = &ld->ld_sb;
291     BerElement          ber;
292     char                *logdn;
293     int                 ret, fromaddr, i;
294         ber_int_t       id;
295     struct timeval      tv;
296
297     fromaddr = -1;
298
299     if ( crip->cri_try == 0 ) {
300         crip->cri_maxtries = ld->ld_cldaptries * ld->ld_cldapnaddr;
301         crip->cri_timeout = ld->ld_cldaptimeout;
302         crip->cri_useaddr = 0;
303         Debug( LDAP_DEBUG_TRACE, "cldap_result tries %d timeout %d\n",
304                 ld->ld_cldaptries, ld->ld_cldaptimeout, 0 );
305     }
306
307     if ((tv.tv_sec = crip->cri_timeout / ld->ld_cldapnaddr) < 1 ) {
308         tv.tv_sec = 1;
309     }
310     tv.tv_usec = 0;
311
312     Debug( LDAP_DEBUG_TRACE,
313             "cldap_result waiting up to %ld seconds for a response\n",
314             (long) tv.tv_sec, 0, 0 );
315     ber_init_w_nullc( &ber, 0 );
316     ldap_set_ber_options( ld, &ber );
317
318     if ( cldap_getmsg( ld, &tv, &ber ) == -1 ) {
319         ret = ld->ld_errno;
320         Debug( LDAP_DEBUG_TRACE, "cldap_getmsg returned -1 (%d)\n",
321                 ret, 0, 0 );
322     } else if ( ld->ld_errno == LDAP_TIMEOUT ) {
323         Debug( LDAP_DEBUG_TRACE,
324             "cldap_result timed out\n", 0, 0, 0 );
325         /*
326          * It timed out; is it time to give up?
327          */
328         if ( ++crip->cri_try >= crip->cri_maxtries ) {
329             ret = LDAP_TIMEOUT;
330             --crip->cri_try;
331         } else {
332             if ( ++crip->cri_useaddr >= ld->ld_cldapnaddr ) {
333                 /*
334                  * new round: reset address to first one and
335                  * double the timeout
336                  */
337                 crip->cri_useaddr = 0;
338                 crip->cri_timeout <<= 1;
339             }
340             ret = -1;
341         }
342
343     } else {
344         /*
345          * Got a response.  It should look like:
346          * { msgid, logdn, { searchresponse...}}
347          */
348         logdn = NULL;
349
350         if ( ber_scanf( &ber, "ia", &id, &logdn ) == LBER_ERROR ) {
351             LDAP_FREE( ber.ber_buf );   /* gack! */
352             ret = LDAP_DECODING_ERROR;
353             Debug( LDAP_DEBUG_TRACE,
354                     "cldap_result: ber_scanf returned LBER_ERROR (%d)\n",
355                     ret, 0, 0 );
356         } else if ( id != msgid ) {
357             LDAP_FREE( ber.ber_buf );   /* gack! */
358             Debug( LDAP_DEBUG_TRACE,
359                     "cldap_result: looking for msgid %d; got %d\n",
360                     msgid, id, 0 );
361             ret = -1;   /* ignore and keep looking */
362         } else {
363             struct sockaddr_in * src;
364             /*
365              * got a result: determine which server it came from
366              * decode into ldap message chain
367              */
368             src = (struct sockaddr_in *) ber_pvt_sb_udp_get_src( sb );
369                 
370             for ( fromaddr = 0; fromaddr < ld->ld_cldapnaddr; ++fromaddr ) {
371                     if ( memcmp( &((struct sockaddr_in *)
372                             ld->ld_cldapaddrs[ fromaddr ])->sin_addr,
373                             &(src->sin_addr),
374                             sizeof( struct in_addr )) == 0 ) {
375                         break;
376                     }
377             }
378             ret = cldap_parsemsg( ld, msgid, &ber, res, base );
379             LDAP_FREE( ber.ber_buf );   /* gack! */
380             Debug( LDAP_DEBUG_TRACE,
381                 "cldap_result got result (%d)\n", ret, 0, 0 );
382         }
383
384         if ( logdn != NULL ) {
385                 LDAP_FREE( logdn );
386         }
387     }
388     
389
390     /*
391      * If we are giving up (successfully or otherwise) then 
392      * abandon any outstanding requests.
393      */
394     if ( ret != -1 ) {
395         i = crip->cri_try;
396         if ( i >= ld->ld_cldapnaddr ) {
397             i = ld->ld_cldapnaddr - 1;
398         }
399
400         for ( ; i >= 0; --i ) {
401             if ( i == fromaddr ) {
402                 continue;
403             }
404             ber_pvt_sb_udp_set_dst( sb, ld->ld_cldapaddrs[i] );
405
406             Debug( LDAP_DEBUG_TRACE, "cldap_result abandoning id %d (to %s)\n",
407                 msgid, inet_ntoa( ((struct sockaddr_in *)
408                 ld->ld_cldapaddrs[i])->sin_addr ), 0 );
409             (void) ldap_abandon( ld, msgid );
410         }
411     }
412
413     return( ld->ld_errno = ret );
414 }
415
416
417 static int
418 cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber,
419         LDAPMessage **res, const char *base )
420 {
421     ber_tag_t   tag;
422         ber_len_t       len;
423     int                 baselen, slen;
424         ber_tag_t       rc;
425     char                *dn, *p, *cookie;
426     LDAPMessage         *chain, *prev, *ldm;
427     struct berval       *bv;
428
429     rc = LDAP_DECODING_ERROR;   /* pessimistic */
430     ldm = chain = prev = NULL;
431     baselen = ( base == NULL ) ? 0 : strlen( base );
432     bv = NULL;
433
434     for ( tag = ber_first_element( ber, &len, &cookie );
435             tag != LBER_DEFAULT && rc != LDAP_SUCCESS;
436             tag = ber_next_element( ber, &len, cookie )) {
437         if (( ldm = (LDAPMessage *)LDAP_CALLOC( 1, sizeof(LDAPMessage)))
438                 == NULL || ( ldm->lm_ber = ldap_alloc_ber_with_options( ld ))
439                 == NULL ) {
440             rc = LDAP_NO_MEMORY;
441             break;      /* return w/error*/
442         }
443         ldm->lm_msgid = msgid;
444         ldm->lm_msgtype = tag;
445
446         if ( tag == LDAP_RES_SEARCH_RESULT ) {
447             Debug( LDAP_DEBUG_TRACE, "cldap_parsemsg got search result\n",
448                     0, 0, 0 );
449
450             if ( ber_get_stringal( ber, &bv ) == LBER_DEFAULT ) {
451                 break;  /* return w/error */
452             }
453
454             if ( ber_printf( ldm->lm_ber, "tO", tag, bv ) == -1 ) {
455                 break;  /* return w/error */
456             }
457             ber_bvfree( bv );
458             bv = NULL;
459             rc = LDAP_SUCCESS;
460
461         } else if ( tag == LDAP_RES_SEARCH_ENTRY ) {
462             if ( ber_scanf( ber, "{aO" /*}*/, &dn, &bv ) == LBER_ERROR ) {
463                 break;  /* return w/error */
464             }
465             Debug( LDAP_DEBUG_TRACE, "cldap_parsemsg entry %s\n", dn, 0, 0 );
466             if ( dn != NULL && *(dn + ( slen = strlen(dn)) - 1) == '*' &&
467                     baselen > 0 ) {
468                 /*
469                  * substitute original searchbase for trailing '*'
470                  */
471                 if (( p = (char *)LDAP_MALLOC( slen + baselen )) == NULL ) {
472                     rc = LDAP_NO_MEMORY;
473                     LDAP_FREE( dn );
474                     break;      /* return w/error */
475                 }
476                 strcpy( p, dn );
477                 strcpy( p + slen - 1, base );
478                 LDAP_FREE( dn );
479                 dn = p;
480             }
481
482             if ( ber_printf( ldm->lm_ber, "t{so}", tag, dn, bv->bv_val,
483                     bv->bv_len ) == -1 ) {
484                 break;  /* return w/error */
485             }
486             LDAP_FREE( dn );
487             ber_bvfree( bv );
488             bv = NULL;
489                 
490 #ifdef notyet
491         } else if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
492 #endif
493         } else {
494             Debug( LDAP_DEBUG_TRACE, "cldap_parsemsg got unknown tag %lu\n",
495                     tag, 0, 0 );
496             rc = LDAP_DECODING_ERROR;
497             break;      /* return w/error */
498         }
499
500         /* Reset message ber so we can read from it later.  Gack! */
501         ldm->lm_ber->ber_end = ldm->lm_ber->ber_ptr;
502         ldm->lm_ber->ber_ptr = ldm->lm_ber->ber_buf;
503
504 #ifdef LDAP_DEBUG
505         if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
506             fprintf( stderr, "cldap_parsemsg add message id %ld type %ld:\n",
507                     (long) ldm->lm_msgid, (long) ldm->lm_msgtype  );
508             ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ldm->lm_ber, 1 );
509         }
510 #endif /* LDAP_DEBUG */
511
512 #ifndef LDAP_NOCACHE
513             if ( ld->ld_cache != NULL ) {
514                 ldap_add_result_to_cache( ld, ldm );
515             }
516 #endif /* LDAP_NOCACHE */
517
518         if ( chain == NULL ) {
519             chain = ldm;
520         } else {
521             prev->lm_chain = ldm;
522         }
523         prev = ldm;
524         ldm = NULL;
525     }
526
527     /* dispose of any leftovers */
528     if ( ldm != NULL ) {
529         if ( ldm->lm_ber != NULL ) {
530             ber_free( ldm->lm_ber, 1 );
531         }
532         LDAP_FREE( ldm );
533     }
534     if ( bv != NULL ) {
535         ber_bvfree( bv );
536     }
537
538     /* return chain, calling result2error if we got anything at all */
539     *res = chain;
540     return(( *res == NULL ) ? rc : ldap_result2error( ld, *res, 0 ));
541 }
542 #endif /* LDAP_CONNECTIONLESS */