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