quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ldap.c (27907B)


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