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