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