Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                      _   _ ____  _
      3  *  Project         ___| | | |  _ \| |
      4  *                 / __| | | | |_) | |
      5  *                | (__| |_| |  _ <| |___
      6  *                 \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, 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 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 */
   1088