Imported Upstream version 7.59.0
[platform/upstream/curl.git] / lib / ldap.c
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                 \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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   ZERO_NULL,                            /* connection_check */
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   ZERO_NULL,                            /* connection_check */
180   PORT_LDAPS,                           /* defport */
181   CURLPROTO_LDAPS,                      /* protocol */
182   PROTOPT_SSL                           /* flags */
183 };
184 #endif
185
186 #if defined(USE_WIN32_LDAP)
187
188 #if defined(USE_WINDOWS_SSPI)
189 static int ldap_win_bind_auth(LDAP *server, const char *user,
190                               const char *passwd, unsigned long authflags)
191 {
192   ULONG method = 0;
193   SEC_WINNT_AUTH_IDENTITY cred;
194   int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
195
196   memset(&cred, 0, sizeof(cred));
197
198 #if defined(USE_SPNEGO)
199   if(authflags & CURLAUTH_NEGOTIATE) {
200     method = LDAP_AUTH_NEGOTIATE;
201   }
202   else
203 #endif
204 #if defined(USE_NTLM)
205   if(authflags & CURLAUTH_NTLM) {
206     method = LDAP_AUTH_NTLM;
207   }
208   else
209 #endif
210 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
211   if(authflags & CURLAUTH_DIGEST) {
212     method = LDAP_AUTH_DIGEST;
213   }
214   else
215 #endif
216   {
217     /* required anyway if one of upper preprocessor definitions enabled */
218   }
219
220   if(method && user && passwd) {
221     rc = Curl_create_sspi_identity(user, passwd, &cred);
222     if(!rc) {
223       rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method);
224       Curl_sspi_free_identity(&cred);
225     }
226   }
227   else {
228     /* proceed with current user credentials */
229     method = LDAP_AUTH_NEGOTIATE;
230     rc = ldap_bind_s(server, NULL, NULL, method);
231   }
232   return rc;
233 }
234 #endif /* #if defined(USE_WINDOWS_SSPI) */
235
236 static int ldap_win_bind(struct connectdata *conn, LDAP *server,
237                          const char *user, const char *passwd)
238 {
239   int rc = LDAP_INVALID_CREDENTIALS;
240
241   PTCHAR inuser = NULL;
242   PTCHAR inpass = NULL;
243
244   if(user && passwd && (conn->data->set.httpauth & CURLAUTH_BASIC)) {
245     inuser = Curl_convert_UTF8_to_tchar((char *) user);
246     inpass = Curl_convert_UTF8_to_tchar((char *) passwd);
247
248     rc = ldap_simple_bind_s(server, inuser, inpass);
249
250     Curl_unicodefree(inuser);
251     Curl_unicodefree(inpass);
252   }
253 #if defined(USE_WINDOWS_SSPI)
254   else {
255     rc = ldap_win_bind_auth(server, user, passwd, conn->data->set.httpauth);
256   }
257 #endif
258
259   return rc;
260 }
261 #endif /* #if defined(USE_WIN32_LDAP) */
262
263 static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
264 {
265   CURLcode result = CURLE_OK;
266   int rc = 0;
267   LDAP *server = NULL;
268   LDAPURLDesc *ludp = NULL;
269   LDAPMessage *ldapmsg = NULL;
270   LDAPMessage *entryIterator;
271   int num = 0;
272   struct Curl_easy *data = conn->data;
273   int ldap_proto = LDAP_VERSION3;
274   int ldap_ssl = 0;
275   char *val_b64 = NULL;
276   size_t val_b64_sz = 0;
277   curl_off_t dlsize = 0;
278 #ifdef LDAP_OPT_NETWORK_TIMEOUT
279   struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
280 #endif
281 #if defined(USE_WIN32_LDAP)
282   TCHAR *host = NULL;
283 #else
284   char *host = NULL;
285 #endif
286   char *user = NULL;
287   char *passwd = NULL;
288
289   *done = TRUE; /* unconditionally */
290   infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
291           LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
292   infof(data, "LDAP local: %s\n", data->change.url);
293
294 #ifdef HAVE_LDAP_URL_PARSE
295   rc = ldap_url_parse(data->change.url, &ludp);
296 #else
297   rc = _ldap_url_parse(conn, &ludp);
298 #endif
299   if(rc != 0) {
300     failf(data, "LDAP local: %s", ldap_err2string(rc));
301     result = CURLE_LDAP_INVALID_URL;
302     goto quit;
303   }
304
305   /* Get the URL scheme (either ldap or ldaps) */
306   if(conn->given->flags & PROTOPT_SSL)
307     ldap_ssl = 1;
308   infof(data, "LDAP local: trying to establish %s connection\n",
309           ldap_ssl ? "encrypted" : "cleartext");
310
311 #if defined(USE_WIN32_LDAP)
312   host = Curl_convert_UTF8_to_tchar(conn->host.name);
313   if(!host) {
314     result = CURLE_OUT_OF_MEMORY;
315
316     goto quit;
317   }
318 #else
319   host = conn->host.name;
320 #endif
321
322   if(conn->bits.user_passwd) {
323     user = conn->user;
324     passwd = conn->passwd;
325   }
326
327 #ifdef LDAP_OPT_NETWORK_TIMEOUT
328   ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
329 #endif
330   ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
331
332   if(ldap_ssl) {
333 #ifdef HAVE_LDAP_SSL
334 #ifdef USE_WIN32_LDAP
335     /* Win32 LDAP SDK doesn't support insecure mode without CA! */
336     server = ldap_sslinit(host, (int)conn->port, 1);
337     ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
338 #else
339     int ldap_option;
340     char *ldap_ca = conn->ssl_config.CAfile;
341 #if defined(CURL_HAS_NOVELL_LDAPSDK)
342     rc = ldapssl_client_init(NULL, NULL);
343     if(rc != LDAP_SUCCESS) {
344       failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
345       result = CURLE_SSL_CERTPROBLEM;
346       goto quit;
347     }
348     if(conn->ssl_config.verifypeer) {
349       /* Novell SDK supports DER or BASE64 files. */
350       int cert_type = LDAPSSL_CERT_FILETYPE_B64;
351       if((data->set.ssl.cert_type) &&
352          (strcasecompare(data->set.ssl.cert_type, "DER")))
353         cert_type = LDAPSSL_CERT_FILETYPE_DER;
354       if(!ldap_ca) {
355         failf(data, "LDAP local: ERROR %s CA cert not set!",
356               (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
357         result = CURLE_SSL_CERTPROBLEM;
358         goto quit;
359       }
360       infof(data, "LDAP local: using %s CA cert '%s'\n",
361               (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
362               ldap_ca);
363       rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
364       if(rc != LDAP_SUCCESS) {
365         failf(data, "LDAP local: ERROR setting %s CA cert: %s",
366                 (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
367                 ldap_err2string(rc));
368         result = CURLE_SSL_CERTPROBLEM;
369         goto quit;
370       }
371       ldap_option = LDAPSSL_VERIFY_SERVER;
372     }
373     else
374       ldap_option = LDAPSSL_VERIFY_NONE;
375     rc = ldapssl_set_verify_mode(ldap_option);
376     if(rc != LDAP_SUCCESS) {
377       failf(data, "LDAP local: ERROR setting cert verify mode: %s",
378               ldap_err2string(rc));
379       result = CURLE_SSL_CERTPROBLEM;
380       goto quit;
381     }
382     server = ldapssl_init(host, (int)conn->port, 1);
383     if(server == NULL) {
384       failf(data, "LDAP local: Cannot connect to %s:%ld",
385             conn->host.dispname, conn->port);
386       result = CURLE_COULDNT_CONNECT;
387       goto quit;
388     }
389 #elif defined(LDAP_OPT_X_TLS)
390     if(conn->ssl_config.verifypeer) {
391       /* OpenLDAP SDK supports BASE64 files. */
392       if((data->set.ssl.cert_type) &&
393          (!strcasecompare(data->set.ssl.cert_type, "PEM"))) {
394         failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!");
395         result = CURLE_SSL_CERTPROBLEM;
396         goto quit;
397       }
398       if(!ldap_ca) {
399         failf(data, "LDAP local: ERROR PEM CA cert not set!");
400         result = CURLE_SSL_CERTPROBLEM;
401         goto quit;
402       }
403       infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
404       rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
405       if(rc != LDAP_SUCCESS) {
406         failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
407                 ldap_err2string(rc));
408         result = CURLE_SSL_CERTPROBLEM;
409         goto quit;
410       }
411       ldap_option = LDAP_OPT_X_TLS_DEMAND;
412     }
413     else
414       ldap_option = LDAP_OPT_X_TLS_NEVER;
415
416     rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
417     if(rc != LDAP_SUCCESS) {
418       failf(data, "LDAP local: ERROR setting cert verify mode: %s",
419               ldap_err2string(rc));
420       result = CURLE_SSL_CERTPROBLEM;
421       goto quit;
422     }
423     server = ldap_init(host, (int)conn->port);
424     if(server == NULL) {
425       failf(data, "LDAP local: Cannot connect to %s:%ld",
426             conn->host.dispname, conn->port);
427       result = CURLE_COULDNT_CONNECT;
428       goto quit;
429     }
430     ldap_option = LDAP_OPT_X_TLS_HARD;
431     rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
432     if(rc != LDAP_SUCCESS) {
433       failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
434               ldap_err2string(rc));
435       result = CURLE_SSL_CERTPROBLEM;
436       goto quit;
437     }
438 /*
439     rc = ldap_start_tls_s(server, NULL, NULL);
440     if(rc != LDAP_SUCCESS) {
441       failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
442               ldap_err2string(rc));
443       result = CURLE_SSL_CERTPROBLEM;
444       goto quit;
445     }
446 */
447 #else
448     /* we should probably never come up to here since configure
449        should check in first place if we can support LDAP SSL/TLS */
450     failf(data, "LDAP local: SSL/TLS not supported with this version "
451             "of the OpenLDAP toolkit\n");
452     result = CURLE_SSL_CERTPROBLEM;
453     goto quit;
454 #endif
455 #endif
456 #endif /* CURL_LDAP_USE_SSL */
457   }
458   else {
459     server = ldap_init(host, (int)conn->port);
460     if(server == NULL) {
461       failf(data, "LDAP local: Cannot connect to %s:%ld",
462             conn->host.dispname, conn->port);
463       result = CURLE_COULDNT_CONNECT;
464       goto quit;
465     }
466   }
467 #ifdef USE_WIN32_LDAP
468   ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
469 #endif
470
471 #ifdef USE_WIN32_LDAP
472   rc = ldap_win_bind(conn, server, user, passwd);
473 #else
474   rc = ldap_simple_bind_s(server, user, passwd);
475 #endif
476   if(!ldap_ssl && rc != 0) {
477     ldap_proto = LDAP_VERSION2;
478     ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
479 #ifdef USE_WIN32_LDAP
480     rc = ldap_win_bind(conn, server, user, passwd);
481 #else
482     rc = ldap_simple_bind_s(server, user, passwd);
483 #endif
484   }
485   if(rc != 0) {
486     failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
487     result = CURLE_LDAP_CANNOT_BIND;
488     goto quit;
489   }
490
491   rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
492                      ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
493
494   if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
495     failf(data, "LDAP remote: %s", ldap_err2string(rc));
496     result = CURLE_LDAP_SEARCH_FAILED;
497     goto quit;
498   }
499
500   for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg);
501       entryIterator;
502       entryIterator = ldap_next_entry(server, entryIterator), num++) {
503     BerElement *ber = NULL;
504 #if defined(USE_WIN32_LDAP)
505     TCHAR *attribute;
506 #else
507     char  *attribute;       /*! suspicious that this isn't 'const' */
508 #endif
509     int i;
510
511     /* Get the DN and write it to the client */
512     {
513       char *name;
514       size_t name_len;
515 #if defined(USE_WIN32_LDAP)
516       TCHAR *dn = ldap_get_dn(server, entryIterator);
517       name = Curl_convert_tchar_to_UTF8(dn);
518       if(!name) {
519         ldap_memfree(dn);
520
521         result = CURLE_OUT_OF_MEMORY;
522
523         goto quit;
524       }
525 #else
526       char *dn = name = ldap_get_dn(server, entryIterator);
527 #endif
528       name_len = strlen(name);
529
530       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
531       if(result) {
532 #if defined(USE_WIN32_LDAP)
533         Curl_unicodefree(name);
534 #endif
535         ldap_memfree(dn);
536
537         goto quit;
538       }
539
540       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *) name,
541                                  name_len);
542       if(result) {
543 #if defined(USE_WIN32_LDAP)
544         Curl_unicodefree(name);
545 #endif
546         ldap_memfree(dn);
547
548         goto quit;
549       }
550
551       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
552       if(result) {
553 #if defined(USE_WIN32_LDAP)
554         Curl_unicodefree(name);
555 #endif
556         ldap_memfree(dn);
557
558         goto quit;
559       }
560
561       dlsize += name_len + 5;
562
563 #if defined(USE_WIN32_LDAP)
564       Curl_unicodefree(name);
565 #endif
566       ldap_memfree(dn);
567     }
568
569     /* Get the attributes and write them to the client */
570     for(attribute = ldap_first_attribute(server, entryIterator, &ber);
571         attribute;
572         attribute = ldap_next_attribute(server, entryIterator, ber)) {
573       BerValue **vals;
574       size_t attr_len;
575 #if defined(USE_WIN32_LDAP)
576       char *attr = Curl_convert_tchar_to_UTF8(attribute);
577       if(!attr) {
578         if(ber)
579           ber_free(ber, 0);
580
581         result = CURLE_OUT_OF_MEMORY;
582
583         goto quit;
584     }
585 #else
586       char *attr = attribute;
587 #endif
588       attr_len = strlen(attr);
589
590       vals = ldap_get_values_len(server, entryIterator, attribute);
591       if(vals != NULL) {
592         for(i = 0; (vals[i] != NULL); i++) {
593           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
594           if(result) {
595             ldap_value_free_len(vals);
596 #if defined(USE_WIN32_LDAP)
597             Curl_unicodefree(attr);
598 #endif
599             ldap_memfree(attribute);
600             if(ber)
601               ber_free(ber, 0);
602
603             goto quit;
604           }
605
606           result = Curl_client_write(conn, CLIENTWRITE_BODY,
607                                      (char *) attr, attr_len);
608           if(result) {
609             ldap_value_free_len(vals);
610 #if defined(USE_WIN32_LDAP)
611             Curl_unicodefree(attr);
612 #endif
613             ldap_memfree(attribute);
614             if(ber)
615               ber_free(ber, 0);
616
617             goto quit;
618           }
619
620           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
621           if(result) {
622             ldap_value_free_len(vals);
623 #if defined(USE_WIN32_LDAP)
624             Curl_unicodefree(attr);
625 #endif
626             ldap_memfree(attribute);
627             if(ber)
628               ber_free(ber, 0);
629
630             goto quit;
631           }
632
633           dlsize += attr_len + 3;
634
635           if((attr_len > 7) &&
636              (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) {
637             /* Binary attribute, encode to base64. */
638             result = Curl_base64_encode(data,
639                                         vals[i]->bv_val,
640                                         vals[i]->bv_len,
641                                         &val_b64,
642                                         &val_b64_sz);
643             if(result) {
644               ldap_value_free_len(vals);
645 #if defined(USE_WIN32_LDAP)
646               Curl_unicodefree(attr);
647 #endif
648               ldap_memfree(attribute);
649               if(ber)
650                 ber_free(ber, 0);
651
652               goto quit;
653             }
654
655             if(val_b64_sz > 0) {
656               result = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
657                                          val_b64_sz);
658               free(val_b64);
659               if(result) {
660                 ldap_value_free_len(vals);
661 #if defined(USE_WIN32_LDAP)
662                 Curl_unicodefree(attr);
663 #endif
664                 ldap_memfree(attribute);
665                 if(ber)
666                   ber_free(ber, 0);
667
668                 goto quit;
669               }
670
671               dlsize += val_b64_sz;
672             }
673           }
674           else {
675             result = Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
676                                        vals[i]->bv_len);
677             if(result) {
678               ldap_value_free_len(vals);
679 #if defined(USE_WIN32_LDAP)
680               Curl_unicodefree(attr);
681 #endif
682               ldap_memfree(attribute);
683               if(ber)
684                 ber_free(ber, 0);
685
686               goto quit;
687             }
688
689             dlsize += vals[i]->bv_len;
690           }
691
692           result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
693           if(result) {
694             ldap_value_free_len(vals);
695 #if defined(USE_WIN32_LDAP)
696             Curl_unicodefree(attr);
697 #endif
698             ldap_memfree(attribute);
699             if(ber)
700               ber_free(ber, 0);
701
702             goto quit;
703           }
704
705           dlsize++;
706         }
707
708         /* Free memory used to store values */
709         ldap_value_free_len(vals);
710       }
711
712       /* Free the attribute as we are done with it */
713 #if defined(USE_WIN32_LDAP)
714       Curl_unicodefree(attr);
715 #endif
716       ldap_memfree(attribute);
717
718       result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
719       if(result)
720         goto quit;
721       dlsize++;
722       Curl_pgrsSetDownloadCounter(data, dlsize);
723     }
724
725     if(ber)
726        ber_free(ber, 0);
727   }
728
729 quit:
730   if(ldapmsg) {
731     ldap_msgfree(ldapmsg);
732     LDAP_TRACE(("Received %d entries\n", num));
733   }
734   if(rc == LDAP_SIZELIMIT_EXCEEDED)
735     infof(data, "There are more than %d entries\n", num);
736   if(ludp)
737     ldap_free_urldesc(ludp);
738   if(server)
739     ldap_unbind_s(server);
740 #if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
741   if(ldap_ssl)
742     ldapssl_client_deinit();
743 #endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
744
745 #if defined(USE_WIN32_LDAP)
746   Curl_unicodefree(host);
747 #endif
748
749   /* no data to transfer */
750   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
751   connclose(conn, "LDAP connection always disable re-use");
752
753   return result;
754 }
755
756 #ifdef DEBUG_LDAP
757 static void _ldap_trace(const char *fmt, ...)
758 {
759   static int do_trace = -1;
760   va_list args;
761
762   if(do_trace == -1) {
763     const char *env = getenv("CURL_TRACE");
764     do_trace = (env && strtol(env, NULL, 10) > 0);
765   }
766   if(!do_trace)
767     return;
768
769   va_start(args, fmt);
770   vfprintf(stderr, fmt, args);
771   va_end(args);
772 }
773 #endif
774
775 #ifndef HAVE_LDAP_URL_PARSE
776
777 /*
778  * Return scope-value for a scope-string.
779  */
780 static int str2scope(const char *p)
781 {
782   if(strcasecompare(p, "one"))
783     return LDAP_SCOPE_ONELEVEL;
784   if(strcasecompare(p, "onetree"))
785     return LDAP_SCOPE_ONELEVEL;
786   if(strcasecompare(p, "base"))
787     return LDAP_SCOPE_BASE;
788   if(strcasecompare(p, "sub"))
789     return LDAP_SCOPE_SUBTREE;
790   if(strcasecompare(p, "subtree"))
791     return LDAP_SCOPE_SUBTREE;
792   return (-1);
793 }
794
795 /*
796  * Split 'str' into strings separated by commas.
797  * Note: out[] points into 'str'.
798  */
799 static bool split_str(char *str, char ***out, size_t *count)
800 {
801   char **res;
802   char *lasts;
803   char *s;
804   size_t  i;
805   size_t items = 1;
806
807   s = strchr(str, ',');
808   while(s) {
809     items++;
810     s = strchr(++s, ',');
811   }
812
813   res = calloc(items, sizeof(char *));
814   if(!res)
815     return FALSE;
816
817   for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items;
818       s = strtok_r(NULL, ",", &lasts), i++)
819     res[i] = s;
820
821   *out = res;
822   *count = items;
823
824   return TRUE;
825 }
826
827 /*
828  * Break apart the pieces of an LDAP URL.
829  * Syntax:
830  *   ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
831  *
832  * <hostname> already known from 'conn->host.name'.
833  * <port>     already known from 'conn->remote_port'.
834  * extract the rest from 'conn->data->state.path+1'. All fields are optional.
835  * e.g.
836  *   ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
837  * yields ludp->lud_dn = "".
838  *
839  * Defined in RFC4516 section 2.
840  */
841 static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
842 {
843   int rc = LDAP_SUCCESS;
844   char *path;
845   char *p;
846   char *q;
847   size_t i;
848
849   if(!conn->data ||
850      !conn->data->state.path ||
851      conn->data->state.path[0] != '/' ||
852      !checkprefix("LDAP", conn->data->change.url))
853     return LDAP_INVALID_SYNTAX;
854
855   ludp->lud_scope = LDAP_SCOPE_BASE;
856   ludp->lud_port  = conn->remote_port;
857   ludp->lud_host  = conn->host.name;
858
859   /* Duplicate the path */
860   p = path = strdup(conn->data->state.path + 1);
861   if(!path)
862     return LDAP_NO_MEMORY;
863
864   /* Parse the DN (Distinguished Name) */
865   q = strchr(p, '?');
866   if(q)
867     *q++ = '\0';
868
869   if(*p) {
870     char *dn = p;
871     char *unescaped;
872     CURLcode result;
873
874     LDAP_TRACE(("DN '%s'\n", dn));
875
876     /* Unescape the DN */
877     result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, FALSE);
878     if(result) {
879       rc = LDAP_NO_MEMORY;
880
881       goto quit;
882     }
883
884 #if defined(USE_WIN32_LDAP)
885     /* Convert the unescaped string to a tchar */
886     ludp->lud_dn = Curl_convert_UTF8_to_tchar(unescaped);
887
888     /* Free the unescaped string as we are done with it */
889     Curl_unicodefree(unescaped);
890
891     if(!ludp->lud_dn) {
892       rc = LDAP_NO_MEMORY;
893
894       goto quit;
895     }
896 #else
897     ludp->lud_dn = unescaped;
898 #endif
899   }
900
901   p = q;
902   if(!p)
903     goto quit;
904
905   /* Parse the attributes. skip "??" */
906   q = strchr(p, '?');
907   if(q)
908     *q++ = '\0';
909
910   if(*p) {
911     char **attributes;
912     size_t count = 0;
913
914     /* Split the string into an array of attributes */
915     if(!split_str(p, &attributes, &count)) {
916       rc = LDAP_NO_MEMORY;
917
918       goto quit;
919     }
920
921     /* Allocate our array (+1 for the NULL entry) */
922 #if defined(USE_WIN32_LDAP)
923     ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *));
924 #else
925     ludp->lud_attrs = calloc(count + 1, sizeof(char *));
926 #endif
927     if(!ludp->lud_attrs) {
928       free(attributes);
929
930       rc = LDAP_NO_MEMORY;
931
932       goto quit;
933     }
934
935     for(i = 0; i < count; i++) {
936       char *unescaped;
937       CURLcode result;
938
939       LDAP_TRACE(("attr[%d] '%s'\n", i, attributes[i]));
940
941       /* Unescape the attribute */
942       result = Curl_urldecode(conn->data, attributes[i], 0, &unescaped, NULL,
943                               FALSE);
944       if(result) {
945         free(attributes);
946
947         rc = LDAP_NO_MEMORY;
948
949         goto quit;
950       }
951
952 #if defined(USE_WIN32_LDAP)
953       /* Convert the unescaped string to a tchar */
954       ludp->lud_attrs[i] = Curl_convert_UTF8_to_tchar(unescaped);
955
956       /* Free the unescaped string as we are done with it */
957       Curl_unicodefree(unescaped);
958
959       if(!ludp->lud_attrs[i]) {
960         free(attributes);
961
962         rc = LDAP_NO_MEMORY;
963
964         goto quit;
965       }
966 #else
967       ludp->lud_attrs[i] = unescaped;
968 #endif
969
970       ludp->lud_attrs_dups++;
971     }
972
973     free(attributes);
974   }
975
976   p = q;
977   if(!p)
978     goto quit;
979
980   /* Parse the scope. skip "??" */
981   q = strchr(p, '?');
982   if(q)
983     *q++ = '\0';
984
985   if(*p) {
986     ludp->lud_scope = str2scope(p);
987     if(ludp->lud_scope == -1) {
988       rc = LDAP_INVALID_SYNTAX;
989
990       goto quit;
991     }
992     LDAP_TRACE(("scope %d\n", ludp->lud_scope));
993   }
994
995   p = q;
996   if(!p)
997     goto quit;
998
999   /* Parse the filter */
1000   q = strchr(p, '?');
1001   if(q)
1002     *q++ = '\0';
1003
1004   if(*p) {
1005     char *filter = p;
1006     char *unescaped;
1007     CURLcode result;
1008
1009     LDAP_TRACE(("filter '%s'\n", filter));
1010
1011     /* Unescape the filter */
1012     result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, FALSE);
1013     if(result) {
1014       rc = LDAP_NO_MEMORY;
1015
1016       goto quit;
1017     }
1018
1019 #if defined(USE_WIN32_LDAP)
1020     /* Convert the unescaped string to a tchar */
1021     ludp->lud_filter = Curl_convert_UTF8_to_tchar(unescaped);
1022
1023     /* Free the unescaped string as we are done with it */
1024     Curl_unicodefree(unescaped);
1025
1026     if(!ludp->lud_filter) {
1027       rc = LDAP_NO_MEMORY;
1028
1029       goto quit;
1030     }
1031 #else
1032     ludp->lud_filter = unescaped;
1033 #endif
1034   }
1035
1036   p = q;
1037   if(p && !*p) {
1038     rc = LDAP_INVALID_SYNTAX;
1039
1040     goto quit;
1041   }
1042
1043 quit:
1044   free(path);
1045
1046   return rc;
1047 }
1048
1049 static int _ldap_url_parse(const struct connectdata *conn,
1050                            LDAPURLDesc **ludpp)
1051 {
1052   LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
1053   int rc;
1054
1055   *ludpp = NULL;
1056   if(!ludp)
1057      return LDAP_NO_MEMORY;
1058
1059   rc = _ldap_url_parse2(conn, ludp);
1060   if(rc != LDAP_SUCCESS) {
1061     _ldap_free_urldesc(ludp);
1062     ludp = NULL;
1063   }
1064   *ludpp = ludp;
1065   return (rc);
1066 }
1067
1068 static void _ldap_free_urldesc(LDAPURLDesc *ludp)
1069 {
1070   size_t i;
1071
1072   if(!ludp)
1073     return;
1074
1075   free(ludp->lud_dn);
1076   free(ludp->lud_filter);
1077
1078   if(ludp->lud_attrs) {
1079     for(i = 0; i < ludp->lud_attrs_dups; i++)
1080       free(ludp->lud_attrs[i]);
1081     free(ludp->lud_attrs);
1082   }
1083
1084   free(ludp);
1085 }
1086 #endif  /* !HAVE_LDAP_URL_PARSE */
1087 #endif  /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */