Imported Upstream version 7.53.1
[platform/upstream/curl.git] / lib / ldap.c
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                 \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
26
27 /*
28  * Notice that USE_OPENLDAP is only a source code selection switch. When
29  * libcurl is built with USE_OPENLDAP defined the libcurl source code that
30  * gets compiled is the code from openldap.c, otherwise the code that gets
31  * compiled is the code from ldap.c.
32  *
33  * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
34  * might be required for compilation and runtime. In order to use ancient
35  * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
36  */
37
38 #ifdef USE_WIN32_LDAP           /* Use Windows LDAP implementation. */
39 # include <winldap.h>
40 # ifndef LDAP_VENDOR_NAME
41 #  error Your Platform SDK is NOT sufficient for LDAP support! \
42          Update your Platform SDK, or disable LDAP support!
43 # else
44 #  include <winber.h>
45 # endif
46 #else
47 # define LDAP_DEPRECATED 1      /* Be sure ldap_init() is defined. */
48 # ifdef HAVE_LBER_H
49 #  include <lber.h>
50 # endif
51 # include <ldap.h>
52 # if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H))
53 #  include <ldap_ssl.h>
54 # endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */
55 #endif
56
57 /* These are macros in both <wincrypt.h> (in above <winldap.h>) and typedefs
58  * in BoringSSL's <openssl/x509.h>
59  */
60 #ifdef HAVE_BORINGSSL
61 # undef X509_NAME
62 # undef X509_CERT_PAIR
63 # undef X509_EXTENSIONS
64 #endif
65
66 #include "urldata.h"
67 #include <curl/curl.h>
68 #include "sendf.h"
69 #include "escape.h"
70 #include "progress.h"
71 #include "transfer.h"
72 #include "strcase.h"
73 #include "strtok.h"
74 #include "curl_ldap.h"
75 #include "curl_multibyte.h"
76 #include "curl_base64.h"
77 #include "connect.h"
78 /* The last 3 #include files should be in this order */
79 #include "curl_printf.h"
80 #include "curl_memory.h"
81 #include "memdebug.h"
82
83 #ifndef HAVE_LDAP_URL_PARSE
84
85 /* Use our own implementation. */
86
87 typedef struct {
88   char   *lud_host;
89   int     lud_port;
90 #if defined(USE_WIN32_LDAP)
91   TCHAR  *lud_dn;
92   TCHAR **lud_attrs;
93 #else
94   char   *lud_dn;
95   char  **lud_attrs;
96 #endif
97   int     lud_scope;
98 #if defined(USE_WIN32_LDAP)
99   TCHAR  *lud_filter;
100 #else
101   char   *lud_filter;
102 #endif
103   char  **lud_exts;
104   size_t    lud_attrs_dups; /* how many were dup'ed, this field is not in the
105                                "real" struct so can only be used in code
106                                without HAVE_LDAP_URL_PARSE defined */
107 } CURL_LDAPURLDesc;
108
109 #undef LDAPURLDesc
110 #define LDAPURLDesc             CURL_LDAPURLDesc
111
112 static int  _ldap_url_parse(const struct connectdata *conn,
113                             LDAPURLDesc **ludp);
114 static void _ldap_free_urldesc(LDAPURLDesc *ludp);
115
116 #undef ldap_free_urldesc
117 #define ldap_free_urldesc       _ldap_free_urldesc
118 #endif
119
120 #ifdef DEBUG_LDAP
121   #define LDAP_TRACE(x)   do { \
122                             _ldap_trace("%u: ", __LINE__); \
123                             _ldap_trace x; \
124                           } WHILE_FALSE
125
126   static void _ldap_trace(const char *fmt, ...);
127 #else
128   #define LDAP_TRACE(x)   Curl_nop_stmt
129 #endif
130
131
132 static CURLcode Curl_ldap(struct connectdata *conn, bool *done);
133
134 /*
135  * LDAP protocol handler.
136  */
137
138 const struct Curl_handler Curl_handler_ldap = {
139   "LDAP",                               /* scheme */
140   ZERO_NULL,                            /* setup_connection */
141   Curl_ldap,                            /* do_it */
142   ZERO_NULL,                            /* done */
143   ZERO_NULL,                            /* do_more */
144   ZERO_NULL,                            /* connect_it */
145   ZERO_NULL,                            /* connecting */
146   ZERO_NULL,                            /* doing */
147   ZERO_NULL,                            /* proto_getsock */
148   ZERO_NULL,                            /* doing_getsock */
149   ZERO_NULL,                            /* domore_getsock */
150   ZERO_NULL,                            /* perform_getsock */
151   ZERO_NULL,                            /* disconnect */
152   ZERO_NULL,                            /* readwrite */
153   PORT_LDAP,                            /* defport */
154   CURLPROTO_LDAP,                       /* protocol */
155   PROTOPT_NONE                          /* flags */
156 };
157
158 #ifdef HAVE_LDAP_SSL
159 /*
160  * LDAPS protocol handler.
161  */
162
163 const struct Curl_handler Curl_handler_ldaps = {
164   "LDAPS",                              /* scheme */
165   ZERO_NULL,                            /* setup_connection */
166   Curl_ldap,                            /* do_it */
167   ZERO_NULL,                            /* done */
168   ZERO_NULL,                            /* do_more */
169   ZERO_NULL,                            /* connect_it */
170   ZERO_NULL,                            /* connecting */
171   ZERO_NULL,                            /* doing */
172   ZERO_NULL,                            /* proto_getsock */
173   ZERO_NULL,                            /* doing_getsock */
174   ZERO_NULL,                            /* domore_getsock */
175   ZERO_NULL,                            /* perform_getsock */
176   ZERO_NULL,                            /* disconnect */
177   ZERO_NULL,                            /* readwrite */
178   PORT_LDAPS,                           /* defport */
179   CURLPROTO_LDAPS,                      /* protocol */
180   PROTOPT_SSL                           /* flags */
181 };
182 #endif
183
184
185 static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
186 {
187   CURLcode result = CURLE_OK;
188   int rc = 0;
189   LDAP *server = NULL;
190   LDAPURLDesc *ludp = NULL;
191   LDAPMessage *ldapmsg = NULL;
192   LDAPMessage *entryIterator;
193   int num = 0;
194   struct Curl_easy *data=conn->data;
195   int ldap_proto = LDAP_VERSION3;
196   int ldap_ssl = 0;
197   char *val_b64 = NULL;
198   size_t val_b64_sz = 0;
199   curl_off_t dlsize = 0;
200 #ifdef LDAP_OPT_NETWORK_TIMEOUT
201   struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
202 #endif
203 #if defined(USE_WIN32_LDAP)
204   TCHAR *host = NULL;
205   TCHAR *user = NULL;
206   TCHAR *passwd = NULL;
207 #else
208   char *host = NULL;
209   char *user = NULL;
210   char *passwd = NULL;
211 #endif
212
213   *done = TRUE; /* unconditionally */
214   infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
215           LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
216   infof(data, "LDAP local: %s\n", data->change.url);
217
218 #ifdef HAVE_LDAP_URL_PARSE
219   rc = ldap_url_parse(data->change.url, &ludp);
220 #else
221   rc = _ldap_url_parse(conn, &ludp);
222 #endif
223   if(rc != 0) {
224     failf(data, "LDAP local: %s", ldap_err2string(rc));
225     result = CURLE_LDAP_INVALID_URL;
226     goto quit;
227   }
228
229   /* Get the URL scheme (either ldap or ldaps) */
230   if(conn->given->flags & PROTOPT_SSL)
231     ldap_ssl = 1;
232   infof(data, "LDAP local: trying to establish %s connection\n",
233           ldap_ssl ? "encrypted" : "cleartext");
234
235 #if defined(USE_WIN32_LDAP)
236   host = Curl_convert_UTF8_to_tchar(conn->host.name);
237   if(!host) {
238     result = CURLE_OUT_OF_MEMORY;
239
240     goto quit;
241   }
242
243   if(conn->bits.user_passwd) {
244     user = Curl_convert_UTF8_to_tchar(conn->user);
245     passwd = Curl_convert_UTF8_to_tchar(conn->passwd);
246     if(!user || !passwd) {
247       result = CURLE_OUT_OF_MEMORY;
248
249       goto quit;
250     }
251   }
252 #else
253   host = conn->host.name;
254
255   if(conn->bits.user_passwd) {
256     user = conn->user;
257     passwd = conn->passwd;
258   }
259 #endif
260
261 #ifdef LDAP_OPT_NETWORK_TIMEOUT
262   ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
263 #endif
264   ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
265
266   if(ldap_ssl) {
267 #ifdef HAVE_LDAP_SSL
268 #ifdef USE_WIN32_LDAP
269     /* Win32 LDAP SDK doesn't support insecure mode without CA! */
270     server = ldap_sslinit(host, (int)conn->port, 1);
271     ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
272 #else
273     int ldap_option;
274     char *ldap_ca = conn->ssl_config.CAfile;
275 #if defined(CURL_HAS_NOVELL_LDAPSDK)
276     rc = ldapssl_client_init(NULL, NULL);
277     if(rc != LDAP_SUCCESS) {
278       failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
279       result = CURLE_SSL_CERTPROBLEM;
280       goto quit;
281     }
282     if(conn->ssl_config.verifypeer) {
283       /* Novell SDK supports DER or BASE64 files. */
284       int cert_type = LDAPSSL_CERT_FILETYPE_B64;
285       if((data->set.ssl.cert_type) &&
286          (strcasecompare(data->set.ssl.cert_type, "DER")))
287         cert_type = LDAPSSL_CERT_FILETYPE_DER;
288       if(!ldap_ca) {
289         failf(data, "LDAP local: ERROR %s CA cert not set!",
290               (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
291         result = CURLE_SSL_CERTPROBLEM;
292         goto quit;
293       }
294       infof(data, "LDAP local: using %s CA cert '%s'\n",
295               (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
296               ldap_ca);
297       rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
298       if(rc != LDAP_SUCCESS) {
299         failf(data, "LDAP local: ERROR setting %s CA cert: %s",
300                 (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
301                 ldap_err2string(rc));
302         result = CURLE_SSL_CERTPROBLEM;
303         goto quit;
304       }
305       ldap_option = LDAPSSL_VERIFY_SERVER;
306     }
307     else
308       ldap_option = LDAPSSL_VERIFY_NONE;
309     rc = ldapssl_set_verify_mode(ldap_option);
310     if(rc != LDAP_SUCCESS) {
311       failf(data, "LDAP local: ERROR setting cert verify mode: %s",
312               ldap_err2string(rc));
313       result = CURLE_SSL_CERTPROBLEM;
314       goto quit;
315     }
316     server = ldapssl_init(host, (int)conn->port, 1);
317     if(server == NULL) {
318       failf(data, "LDAP local: Cannot connect to %s:%ld",
319             conn->host.dispname, conn->port);
320       result = CURLE_COULDNT_CONNECT;
321       goto quit;
322     }
323 #elif defined(LDAP_OPT_X_TLS)
324     if(conn->ssl_config.verifypeer) {
325       /* OpenLDAP SDK supports BASE64 files. */
326       if((data->set.ssl.cert_type) &&
327          (!strcasecompare(data->set.ssl.cert_type, "PEM"))) {
328         failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!");
329         result = CURLE_SSL_CERTPROBLEM;
330         goto quit;
331       }
332       if(!ldap_ca) {
333         failf(data, "LDAP local: ERROR PEM CA cert not set!");
334         result = CURLE_SSL_CERTPROBLEM;
335         goto quit;
336       }
337       infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
338       rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
339       if(rc != LDAP_SUCCESS) {
340         failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
341                 ldap_err2string(rc));
342         result = CURLE_SSL_CERTPROBLEM;
343         goto quit;
344       }
345       ldap_option = LDAP_OPT_X_TLS_DEMAND;
346     }
347     else
348       ldap_option = LDAP_OPT_X_TLS_NEVER;
349
350     rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
351     if(rc != LDAP_SUCCESS) {
352       failf(data, "LDAP local: ERROR setting cert verify mode: %s",
353               ldap_err2string(rc));
354       result = CURLE_SSL_CERTPROBLEM;
355       goto quit;
356     }
357     server = ldap_init(host, (int)conn->port);
358     if(server == NULL) {
359       failf(data, "LDAP local: Cannot connect to %s:%ld",
360             conn->host.dispname, conn->port);
361       result = CURLE_COULDNT_CONNECT;
362       goto quit;
363     }
364     ldap_option = LDAP_OPT_X_TLS_HARD;
365     rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
366     if(rc != LDAP_SUCCESS) {
367       failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
368               ldap_err2string(rc));
369       result = CURLE_SSL_CERTPROBLEM;
370       goto quit;
371     }
372 /*
373     rc = ldap_start_tls_s(server, NULL, NULL);
374     if(rc != LDAP_SUCCESS) {
375       failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
376               ldap_err2string(rc));
377       result = CURLE_SSL_CERTPROBLEM;
378       goto quit;
379     }
380 */
381 #else
382     /* we should probably never come up to here since configure
383        should check in first place if we can support LDAP SSL/TLS */
384     failf(data, "LDAP local: SSL/TLS not supported with this version "
385             "of the OpenLDAP toolkit\n");
386     result = CURLE_SSL_CERTPROBLEM;
387     goto quit;
388 #endif
389 #endif
390 #endif /* CURL_LDAP_USE_SSL */
391   }
392   else {
393     server = ldap_init(host, (int)conn->port);
394     if(server == NULL) {
395       failf(data, "LDAP local: Cannot connect to %s:%ld",
396             conn->host.dispname, conn->port);
397       result = CURLE_COULDNT_CONNECT;
398       goto quit;
399     }
400   }
401 #ifdef USE_WIN32_LDAP
402   ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
403 #endif
404
405   rc = ldap_simple_bind_s(server, user, passwd);
406   if(!ldap_ssl && rc != 0) {
407     ldap_proto = LDAP_VERSION2;
408     ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
409     rc = ldap_simple_bind_s(server, user, passwd);
410   }
411   if(rc != 0) {
412     failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
413     result = CURLE_LDAP_CANNOT_BIND;
414     goto quit;
415   }
416
417   rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
418                      ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
419
420   if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
421     failf(data, "LDAP remote: %s", ldap_err2string(rc));
422     result = CURLE_LDAP_SEARCH_FAILED;
423     goto quit;
424   }
425
426   for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg);
427       entryIterator;
428       entryIterator = ldap_next_entry(server, entryIterator), num++) {
429     BerElement *ber = NULL;
430 #if defined(USE_WIN32_LDAP)
431     TCHAR *attribute;
432 #else
433     char  *attribute;       /*! suspicious that this isn't 'const' */
434 #endif
435     int i;
436
437     /* Get the DN and write it to the client */
438     {
439       char *name;
440       size_t name_len;
441 #if defined(USE_WIN32_LDAP)
442       TCHAR *dn = ldap_get_dn(server, entryIterator);
443       name = Curl_convert_tchar_to_UTF8(dn);
444       if(!name) {
445         ldap_memfree(dn);
446
447         result = CURLE_OUT_OF_MEMORY;
448
449         goto quit;
450       }
451 #else
452       char *dn = name = ldap_get_dn(server, entryIterator);
453 #endif
454       name_len = strlen(name);
455
456       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
457       if(result) {
458 #if defined(USE_WIN32_LDAP)
459         Curl_unicodefree(name);
460 #endif
461         ldap_memfree(dn);
462
463         goto quit;
464       }
465
466       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *) name,
467                                  name_len);
468       if(result) {
469 #if defined(USE_WIN32_LDAP)
470         Curl_unicodefree(name);
471 #endif
472         ldap_memfree(dn);
473
474         goto quit;
475       }
476
477       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
478       if(result) {
479 #if defined(USE_WIN32_LDAP)
480         Curl_unicodefree(name);
481 #endif
482         ldap_memfree(dn);
483
484         goto quit;
485       }
486
487       dlsize += name_len + 5;
488
489 #if defined(USE_WIN32_LDAP)
490       Curl_unicodefree(name);
491 #endif
492       ldap_memfree(dn);
493     }
494
495     /* Get the attributes and write them to the client */
496     for(attribute = ldap_first_attribute(server, entryIterator, &ber);
497         attribute;
498         attribute = ldap_next_attribute(server, entryIterator, ber)) {
499       BerValue **vals;
500       size_t attr_len;
501 #if defined(USE_WIN32_LDAP)
502       char *attr = Curl_convert_tchar_to_UTF8(attribute);
503       if(!attr) {
504         if(ber)
505           ber_free(ber, 0);
506
507         result = CURLE_OUT_OF_MEMORY;
508
509         goto quit;
510     }
511 #else
512       char *attr = attribute;
513 #endif
514       attr_len = strlen(attr);
515
516       vals = ldap_get_values_len(server, entryIterator, attribute);
517       if(vals != NULL) {
518         for(i = 0; (vals[i] != NULL); i++) {
519           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
520           if(result) {
521             ldap_value_free_len(vals);
522 #if defined(USE_WIN32_LDAP)
523             Curl_unicodefree(attr);
524 #endif
525             ldap_memfree(attribute);
526             if(ber)
527               ber_free(ber, 0);
528
529             goto quit;
530           }
531
532           result = Curl_client_write(conn, CLIENTWRITE_BODY,
533                                      (char *) attr, attr_len);
534           if(result) {
535             ldap_value_free_len(vals);
536 #if defined(USE_WIN32_LDAP)
537             Curl_unicodefree(attr);
538 #endif
539             ldap_memfree(attribute);
540             if(ber)
541               ber_free(ber, 0);
542
543             goto quit;
544           }
545
546           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
547           if(result) {
548             ldap_value_free_len(vals);
549 #if defined(USE_WIN32_LDAP)
550             Curl_unicodefree(attr);
551 #endif
552             ldap_memfree(attribute);
553             if(ber)
554               ber_free(ber, 0);
555
556             goto quit;
557           }
558
559           dlsize += attr_len + 3;
560
561           if((attr_len > 7) &&
562              (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) {
563             /* Binary attribute, encode to base64. */
564             result = Curl_base64_encode(data,
565                                         vals[i]->bv_val,
566                                         vals[i]->bv_len,
567                                         &val_b64,
568                                         &val_b64_sz);
569             if(result) {
570               ldap_value_free_len(vals);
571 #if defined(USE_WIN32_LDAP)
572               Curl_unicodefree(attr);
573 #endif
574               ldap_memfree(attribute);
575               if(ber)
576                 ber_free(ber, 0);
577
578               goto quit;
579             }
580
581             if(val_b64_sz > 0) {
582               result = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
583                                          val_b64_sz);
584               free(val_b64);
585               if(result) {
586                 ldap_value_free_len(vals);
587 #if defined(USE_WIN32_LDAP)
588                 Curl_unicodefree(attr);
589 #endif
590                 ldap_memfree(attribute);
591                 if(ber)
592                   ber_free(ber, 0);
593
594                 goto quit;
595               }
596
597               dlsize += val_b64_sz;
598             }
599           }
600           else {
601             result = Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
602                                        vals[i]->bv_len);
603             if(result) {
604               ldap_value_free_len(vals);
605 #if defined(USE_WIN32_LDAP)
606               Curl_unicodefree(attr);
607 #endif
608               ldap_memfree(attribute);
609               if(ber)
610                 ber_free(ber, 0);
611
612               goto quit;
613             }
614
615             dlsize += vals[i]->bv_len;
616           }
617
618           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
619           if(result) {
620             ldap_value_free_len(vals);
621 #if defined(USE_WIN32_LDAP)
622             Curl_unicodefree(attr);
623 #endif
624             ldap_memfree(attribute);
625             if(ber)
626               ber_free(ber, 0);
627
628             goto quit;
629           }
630
631           dlsize++;
632         }
633
634         /* Free memory used to store values */
635         ldap_value_free_len(vals);
636       }
637
638       /* Free the attribute as we are done with it */
639 #if defined(USE_WIN32_LDAP)
640       Curl_unicodefree(attr);
641 #endif
642       ldap_memfree(attribute);
643
644       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
645       if(result)
646         goto quit;
647       dlsize++;
648       Curl_pgrsSetDownloadCounter(data, dlsize);
649     }
650
651     if(ber)
652        ber_free(ber, 0);
653   }
654
655 quit:
656   if(ldapmsg) {
657     ldap_msgfree(ldapmsg);
658     LDAP_TRACE(("Received %d entries\n", num));
659   }
660   if(rc == LDAP_SIZELIMIT_EXCEEDED)
661     infof(data, "There are more than %d entries\n", num);
662   if(ludp)
663     ldap_free_urldesc(ludp);
664   if(server)
665     ldap_unbind_s(server);
666 #if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
667   if(ldap_ssl)
668     ldapssl_client_deinit();
669 #endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
670
671 #if defined(USE_WIN32_LDAP)
672   Curl_unicodefree(passwd);
673   Curl_unicodefree(user);
674   Curl_unicodefree(host);
675 #endif
676
677   /* no data to transfer */
678   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
679   connclose(conn, "LDAP connection always disable re-use");
680
681   return result;
682 }
683
684 #ifdef DEBUG_LDAP
685 static void _ldap_trace(const char *fmt, ...)
686 {
687   static int do_trace = -1;
688   va_list args;
689
690   if(do_trace == -1) {
691     const char *env = getenv("CURL_TRACE");
692     do_trace = (env && strtol(env, NULL, 10) > 0);
693   }
694   if(!do_trace)
695     return;
696
697   va_start(args, fmt);
698   vfprintf(stderr, fmt, args);
699   va_end(args);
700 }
701 #endif
702
703 #ifndef HAVE_LDAP_URL_PARSE
704
705 /*
706  * Return scope-value for a scope-string.
707  */
708 static int str2scope(const char *p)
709 {
710   if(strcasecompare(p, "one"))
711     return LDAP_SCOPE_ONELEVEL;
712   if(strcasecompare(p, "onetree"))
713     return LDAP_SCOPE_ONELEVEL;
714   if(strcasecompare(p, "base"))
715     return LDAP_SCOPE_BASE;
716   if(strcasecompare(p, "sub"))
717     return LDAP_SCOPE_SUBTREE;
718   if(strcasecompare(p, "subtree"))
719     return LDAP_SCOPE_SUBTREE;
720   return (-1);
721 }
722
723 /*
724  * Split 'str' into strings separated by commas.
725  * Note: out[] points into 'str'.
726  */
727 static bool split_str(char *str, char ***out, size_t *count)
728 {
729   char **res;
730   char *lasts;
731   char *s;
732   size_t  i;
733   size_t items = 1;
734
735   s = strchr(str, ',');
736   while(s) {
737     items++;
738     s = strchr(++s, ',');
739   }
740
741   res = calloc(items, sizeof(char *));
742   if(!res)
743     return FALSE;
744
745   for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items;
746       s = strtok_r(NULL, ",", &lasts), i++)
747     res[i] = s;
748
749   *out = res;
750   *count = items;
751
752   return TRUE;
753 }
754
755 /*
756  * Break apart the pieces of an LDAP URL.
757  * Syntax:
758  *   ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
759  *
760  * <hostname> already known from 'conn->host.name'.
761  * <port>     already known from 'conn->remote_port'.
762  * extract the rest from 'conn->data->state.path+1'. All fields are optional.
763  * e.g.
764  *   ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
765  * yields ludp->lud_dn = "".
766  *
767  * Defined in RFC4516 section 2.
768  */
769 static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
770 {
771   int rc = LDAP_SUCCESS;
772   char *path;
773   char *p;
774   char *q;
775   size_t i;
776
777   if(!conn->data ||
778      !conn->data->state.path ||
779      conn->data->state.path[0] != '/' ||
780      !checkprefix("LDAP", conn->data->change.url))
781     return LDAP_INVALID_SYNTAX;
782
783   ludp->lud_scope = LDAP_SCOPE_BASE;
784   ludp->lud_port  = conn->remote_port;
785   ludp->lud_host  = conn->host.name;
786
787   /* Duplicate the path */
788   p = path = strdup(conn->data->state.path + 1);
789   if(!path)
790     return LDAP_NO_MEMORY;
791
792   /* Parse the DN (Distinguished Name) */
793   q = strchr(p, '?');
794   if(q)
795     *q++ = '\0';
796
797   if(*p) {
798     char *dn = p;
799     char *unescaped;
800     CURLcode result;
801
802     LDAP_TRACE(("DN '%s'\n", dn));
803
804     /* Unescape the DN */
805     result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, FALSE);
806     if(result) {
807       rc = LDAP_NO_MEMORY;
808
809       goto quit;
810     }
811
812 #if defined(USE_WIN32_LDAP)
813     /* Convert the unescaped string to a tchar */
814     ludp->lud_dn = Curl_convert_UTF8_to_tchar(unescaped);
815
816     /* Free the unescaped string as we are done with it */
817     Curl_unicodefree(unescaped);
818
819     if(!ludp->lud_dn) {
820       rc = LDAP_NO_MEMORY;
821
822       goto quit;
823     }
824 #else
825     ludp->lud_dn = unescaped;
826 #endif
827   }
828
829   p = q;
830   if(!p)
831     goto quit;
832
833   /* Parse the attributes. skip "??" */
834   q = strchr(p, '?');
835   if(q)
836     *q++ = '\0';
837
838   if(*p) {
839     char **attributes;
840     size_t count = 0;
841
842     /* Split the string into an array of attributes */
843     if(!split_str(p, &attributes, &count)) {
844       rc = LDAP_NO_MEMORY;
845
846       goto quit;
847     }
848
849     /* Allocate our array (+1 for the NULL entry) */
850 #if defined(USE_WIN32_LDAP)
851     ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *));
852 #else
853     ludp->lud_attrs = calloc(count + 1, sizeof(char *));
854 #endif
855     if(!ludp->lud_attrs) {
856       free(attributes);
857
858       rc = LDAP_NO_MEMORY;
859
860       goto quit;
861     }
862
863     for(i = 0; i < count; i++) {
864       char *unescaped;
865       CURLcode result;
866
867       LDAP_TRACE(("attr[%d] '%s'\n", i, attributes[i]));
868
869       /* Unescape the attribute */
870       result = Curl_urldecode(conn->data, attributes[i], 0, &unescaped, NULL,
871                               FALSE);
872       if(result) {
873         free(attributes);
874
875         rc = LDAP_NO_MEMORY;
876
877         goto quit;
878       }
879
880 #if defined(USE_WIN32_LDAP)
881       /* Convert the unescaped string to a tchar */
882       ludp->lud_attrs[i] = Curl_convert_UTF8_to_tchar(unescaped);
883
884       /* Free the unescaped string as we are done with it */
885       Curl_unicodefree(unescaped);
886
887       if(!ludp->lud_attrs[i]) {
888         free(attributes);
889
890         rc = LDAP_NO_MEMORY;
891
892         goto quit;
893       }
894 #else
895       ludp->lud_attrs[i] = unescaped;
896 #endif
897
898       ludp->lud_attrs_dups++;
899     }
900
901     free(attributes);
902   }
903
904   p = q;
905   if(!p)
906     goto quit;
907
908   /* Parse the scope. skip "??" */
909   q = strchr(p, '?');
910   if(q)
911     *q++ = '\0';
912
913   if(*p) {
914     ludp->lud_scope = str2scope(p);
915     if(ludp->lud_scope == -1) {
916       rc = LDAP_INVALID_SYNTAX;
917
918       goto quit;
919     }
920     LDAP_TRACE(("scope %d\n", ludp->lud_scope));
921   }
922
923   p = q;
924   if(!p)
925     goto quit;
926
927   /* Parse the filter */
928   q = strchr(p, '?');
929   if(q)
930     *q++ = '\0';
931
932   if(*p) {
933     char *filter = p;
934     char *unescaped;
935     CURLcode result;
936
937     LDAP_TRACE(("filter '%s'\n", filter));
938
939     /* Unescape the filter */
940     result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, FALSE);
941     if(result) {
942       rc = LDAP_NO_MEMORY;
943
944       goto quit;
945     }
946
947 #if defined(USE_WIN32_LDAP)
948     /* Convert the unescaped string to a tchar */
949     ludp->lud_filter = Curl_convert_UTF8_to_tchar(unescaped);
950
951     /* Free the unescaped string as we are done with it */
952     Curl_unicodefree(unescaped);
953
954     if(!ludp->lud_filter) {
955       rc = LDAP_NO_MEMORY;
956
957       goto quit;
958     }
959 #else
960     ludp->lud_filter = unescaped;
961 #endif
962   }
963
964   p = q;
965   if(p && !*p) {
966     rc = LDAP_INVALID_SYNTAX;
967
968     goto quit;
969   }
970
971 quit:
972   free(path);
973
974   return rc;
975 }
976
977 static int _ldap_url_parse(const struct connectdata *conn,
978                            LDAPURLDesc **ludpp)
979 {
980   LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
981   int rc;
982
983   *ludpp = NULL;
984   if(!ludp)
985      return LDAP_NO_MEMORY;
986
987   rc = _ldap_url_parse2(conn, ludp);
988   if(rc != LDAP_SUCCESS) {
989     _ldap_free_urldesc(ludp);
990     ludp = NULL;
991   }
992   *ludpp = ludp;
993   return (rc);
994 }
995
996 static void _ldap_free_urldesc(LDAPURLDesc *ludp)
997 {
998   size_t i;
999
1000   if(!ludp)
1001     return;
1002
1003   free(ludp->lud_dn);
1004   free(ludp->lud_filter);
1005
1006   if(ludp->lud_attrs) {
1007     for(i = 0; i < ludp->lud_attrs_dups; i++)
1008       free(ludp->lud_attrs[i]);
1009     free(ludp->lud_attrs);
1010   }
1011
1012   free(ludp);
1013 }
1014 #endif  /* !HAVE_LDAP_URL_PARSE */
1015 #endif  /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */