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