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
88typedef 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
113static int  _ldap_url_parse (const struct connectdata *conn,
114                             LDAPURLDesc **ludp);
115static 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
133static CURLcode Curl_ldap(struct connectdata *conn, bool *done);
134
135/*
136 * LDAP protocol handler.
137 */
138
139const 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
164const 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
186static 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
656quit:
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
686static 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 */
709static 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 */
728static 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 */
770static 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
968quit:
969  free(path);
970
971  return rc;
972}
973
974static 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
993static 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 */
1013