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