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 */