openldap.c (37746B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * Copyright (C) Howard Chu, <hyc@openldap.org> 10 * 11 * This software is licensed as described in the file COPYING, which 12 * you should have received as part of this distribution. The terms 13 * are also available at https://curl.se/docs/copyright.html. 14 * 15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 16 * copies of the Software, and permit persons to whom the Software is 17 * furnished to do so, under the terms of the COPYING file. 18 * 19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20 * KIND, either express or implied. 21 * 22 * SPDX-License-Identifier: curl 23 * 24 ***************************************************************************/ 25 26 #include "curl_setup.h" 27 28 #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP) 29 30 /* 31 * Notice that USE_OPENLDAP is only a source code selection switch. When 32 * libcurl is built with USE_OPENLDAP defined the libcurl source code that 33 * gets compiled is the code from openldap.c, otherwise the code that gets 34 * compiled is the code from ldap.c. 35 * 36 * When USE_OPENLDAP is defined a recent version of the OpenLDAP library 37 * might be required for compilation and runtime. In order to use ancient 38 * OpenLDAP library versions, USE_OPENLDAP shall not be defined. 39 */ 40 41 #include <ldap.h> 42 43 #include "urldata.h" 44 #include "url.h" 45 #include <curl/curl.h> 46 #include "sendf.h" 47 #include "vtls/vtls.h" 48 #include "transfer.h" 49 #include "curl_ldap.h" 50 #include "curlx/base64.h" 51 #include "cfilters.h" 52 #include "connect.h" 53 #include "curl_sasl.h" 54 #include "strcase.h" 55 /* The last 3 #include files should be in this order */ 56 #include "curl_printf.h" 57 #include "curl_memory.h" 58 #include "memdebug.h" 59 60 /* 61 * Uncommenting this will enable the built-in debug logging of the openldap 62 * library. The debug log level can be set using the CURL_OPENLDAP_TRACE 63 * environment variable. The debug output is written to stderr. 64 * 65 * The library supports the following debug flags: 66 * LDAP_DEBUG_NONE 0x0000 67 * LDAP_DEBUG_TRACE 0x0001 68 * LDAP_DEBUG_CONSTRUCT 0x0002 69 * LDAP_DEBUG_DESTROY 0x0004 70 * LDAP_DEBUG_PARAMETER 0x0008 71 * LDAP_DEBUG_ANY 0xffff 72 * 73 * For example, use CURL_OPENLDAP_TRACE=0 for no debug, 74 * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only, 75 * CURL_OPENLDAP_TRACE=65535 for all debug message levels. 76 */ 77 /* #define CURL_OPENLDAP_DEBUG */ 78 79 /* Machine states. */ 80 typedef enum { 81 OLDAP_STOP, /* Do nothing state, stops the state machine */ 82 OLDAP_SSL, /* Performing SSL handshake. */ 83 OLDAP_STARTTLS, /* STARTTLS request sent. */ 84 OLDAP_TLS, /* Performing TLS handshake. */ 85 OLDAP_MECHS, /* Get SASL authentication mechanisms. */ 86 OLDAP_SASL, /* SASL binding reply. */ 87 OLDAP_BIND, /* Simple bind reply. */ 88 OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ 89 OLDAP_LAST /* Never used */ 90 } ldapstate; 91 92 #ifndef _LDAP_PVT_H 93 extern int ldap_pvt_url_scheme2proto(const char *); 94 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, 95 LDAP **ld); 96 #endif 97 98 static CURLcode oldap_setup_connection(struct Curl_easy *data, 99 struct connectdata *conn); 100 static CURLcode oldap_do(struct Curl_easy *data, bool *done); 101 static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool); 102 static CURLcode oldap_connect(struct Curl_easy *data, bool *done); 103 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); 104 static CURLcode oldap_disconnect(struct Curl_easy *data, 105 struct connectdata *conn, bool dead); 106 107 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, 108 const struct bufref *initresp); 109 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, 110 const struct bufref *resp); 111 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); 112 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); 113 114 static Curl_recv oldap_recv; 115 116 /* 117 * LDAP protocol handler. 118 */ 119 120 const struct Curl_handler Curl_handler_ldap = { 121 "ldap", /* scheme */ 122 oldap_setup_connection, /* setup_connection */ 123 oldap_do, /* do_it */ 124 oldap_done, /* done */ 125 ZERO_NULL, /* do_more */ 126 oldap_connect, /* connect_it */ 127 oldap_connecting, /* connecting */ 128 ZERO_NULL, /* doing */ 129 ZERO_NULL, /* proto_getsock */ 130 ZERO_NULL, /* doing_getsock */ 131 ZERO_NULL, /* domore_getsock */ 132 ZERO_NULL, /* perform_getsock */ 133 oldap_disconnect, /* disconnect */ 134 ZERO_NULL, /* write_resp */ 135 ZERO_NULL, /* write_resp_hd */ 136 ZERO_NULL, /* connection_check */ 137 ZERO_NULL, /* attach connection */ 138 ZERO_NULL, /* follow */ 139 PORT_LDAP, /* defport */ 140 CURLPROTO_LDAP, /* protocol */ 141 CURLPROTO_LDAP, /* family */ 142 PROTOPT_NONE /* flags */ 143 }; 144 145 #ifdef USE_SSL 146 /* 147 * LDAPS protocol handler. 148 */ 149 150 const struct Curl_handler Curl_handler_ldaps = { 151 "ldaps", /* scheme */ 152 oldap_setup_connection, /* setup_connection */ 153 oldap_do, /* do_it */ 154 oldap_done, /* done */ 155 ZERO_NULL, /* do_more */ 156 oldap_connect, /* connect_it */ 157 oldap_connecting, /* connecting */ 158 ZERO_NULL, /* doing */ 159 ZERO_NULL, /* proto_getsock */ 160 ZERO_NULL, /* doing_getsock */ 161 ZERO_NULL, /* domore_getsock */ 162 ZERO_NULL, /* perform_getsock */ 163 oldap_disconnect, /* disconnect */ 164 ZERO_NULL, /* write_resp */ 165 ZERO_NULL, /* write_resp_hd */ 166 ZERO_NULL, /* connection_check */ 167 ZERO_NULL, /* attach connection */ 168 ZERO_NULL, /* follow */ 169 PORT_LDAPS, /* defport */ 170 CURLPROTO_LDAPS, /* protocol */ 171 CURLPROTO_LDAP, /* family */ 172 PROTOPT_SSL /* flags */ 173 }; 174 #endif 175 176 /* SASL parameters for the ldap protocol */ 177 static const struct SASLproto saslldap = { 178 "ldap", /* The service name */ 179 oldap_perform_auth, /* Send authentication command */ 180 oldap_continue_auth, /* Send authentication continuation */ 181 oldap_cancel_auth, /* Send authentication cancellation */ 182 oldap_get_message, /* Get SASL response message */ 183 0, /* Maximum initial response length (no max) */ 184 LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ 185 LDAP_SUCCESS, /* Code to receive upon authentication success */ 186 SASL_AUTH_NONE, /* Default mechanisms */ 187 0 /* Configuration flags */ 188 }; 189 190 struct ldapconninfo { 191 struct SASL sasl; /* SASL-related parameters */ 192 LDAP *ld; /* Openldap connection handle. */ 193 Curl_recv *recv; /* For stacking SSL handler */ 194 Curl_send *send; 195 struct berval *servercred; /* SASL data from server. */ 196 ldapstate state; /* Current machine state. */ 197 int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ 198 int msgid; /* Current message id. */ 199 }; 200 201 struct ldapreqinfo { 202 int msgid; 203 int nument; 204 }; 205 206 /* meta key for storing ldapconninfo at easy handle */ 207 #define CURL_META_LDAP_EASY "meta:proto:ldap:easy" 208 /* meta key for storing ldapconninfo at connection */ 209 #define CURL_META_LDAP_CONN "meta:proto:ldap:conn" 210 211 212 /* 213 * oldap_state() 214 * 215 * This is the ONLY way to change LDAP state! 216 */ 217 static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li, 218 ldapstate newstate) 219 { 220 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 221 /* for debug purposes */ 222 static const char * const names[] = { 223 "STOP", 224 "SSL", 225 "STARTTLS", 226 "TLS", 227 "MECHS", 228 "SASL", 229 "BIND", 230 "BINDV2", 231 /* LAST */ 232 }; 233 234 if(li->state != newstate) 235 infof(data, "LDAP %p state change from %s to %s", 236 (void *)li, names[li->state], names[newstate]); 237 #endif 238 (void)data; 239 li->state = newstate; 240 } 241 242 /* Map some particular LDAP error codes to CURLcode values. */ 243 static CURLcode oldap_map_error(int rc, CURLcode result) 244 { 245 switch(rc) { 246 case LDAP_NO_MEMORY: 247 return CURLE_OUT_OF_MEMORY; 248 case LDAP_INVALID_CREDENTIALS: 249 return CURLE_LOGIN_DENIED; 250 case LDAP_PROTOCOL_ERROR: 251 return CURLE_UNSUPPORTED_PROTOCOL; 252 case LDAP_INSUFFICIENT_ACCESS: 253 return CURLE_REMOTE_ACCESS_DENIED; 254 } 255 return result; 256 } 257 258 static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) 259 { 260 CURLcode result = CURLE_OK; 261 int rc = LDAP_URL_ERR_BADURL; 262 static const char * const url_errs[] = { 263 "success", 264 "out of memory", 265 "bad parameter", 266 "unrecognized scheme", 267 "unbalanced delimiter", 268 "bad URL", 269 "bad host or port", 270 "bad or missing attributes", 271 "bad or missing scope", 272 "bad or missing filter", 273 "bad or missing extensions" 274 }; 275 276 *ludp = NULL; 277 if(!data->state.up.user && !data->state.up.password && 278 !data->state.up.options) 279 rc = ldap_url_parse(data->state.url, ludp); 280 if(rc != LDAP_URL_SUCCESS) { 281 const char *msg = "url parsing problem"; 282 283 result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY : 284 CURLE_URL_MALFORMAT; 285 rc -= LDAP_URL_SUCCESS; 286 if((size_t) rc < CURL_ARRAYSIZE(url_errs)) 287 msg = url_errs[rc]; 288 failf(data, "LDAP local: %s", msg); 289 } 290 return result; 291 } 292 293 /* Parse the login options. */ 294 static CURLcode oldap_parse_login_options(struct connectdata *conn) 295 { 296 CURLcode result = CURLE_OK; 297 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 298 const char *ptr = conn->options; 299 300 DEBUGASSERT(li); 301 if(!li) 302 return CURLE_FAILED_INIT; 303 304 while(!result && ptr && *ptr) { 305 const char *key = ptr; 306 const char *value; 307 308 while(*ptr && *ptr != '=') 309 ptr++; 310 311 value = ptr + 1; 312 313 while(*ptr && *ptr != ';') 314 ptr++; 315 316 if(checkprefix("AUTH=", key)) 317 result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value); 318 else 319 result = CURLE_SETOPT_OPTION_SYNTAX; 320 321 if(*ptr == ';') 322 ptr++; 323 } 324 325 return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result; 326 } 327 328 static CURLcode oldap_setup_connection(struct Curl_easy *data, 329 struct connectdata *conn) 330 { 331 CURLcode result; 332 LDAPURLDesc *lud; 333 (void)conn; 334 335 /* Early URL syntax check. */ 336 result = oldap_url_parse(data, &lud); 337 ldap_free_urldesc(lud); 338 339 return result; 340 } 341 342 /* 343 * Get the SASL authentication challenge from the server credential buffer. 344 */ 345 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out) 346 { 347 struct ldapconninfo *li = 348 Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN); 349 struct berval *servercred = li ? li->servercred : NULL; 350 DEBUGASSERT(li); 351 if(!li) 352 return CURLE_FAILED_INIT; 353 354 if(!servercred || !servercred->bv_val) 355 return CURLE_WEIRD_SERVER_REPLY; 356 Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL); 357 return CURLE_OK; 358 } 359 360 /* 361 * Sends an initial SASL bind request to the server. 362 */ 363 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, 364 const struct bufref *initresp) 365 { 366 struct connectdata *conn = data->conn; 367 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 368 struct berval cred; 369 struct berval *pcred = &cred; 370 int rc; 371 372 DEBUGASSERT(li); 373 if(!li) 374 return CURLE_FAILED_INIT; 375 cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(initresp)); 376 cred.bv_len = Curl_bufref_len(initresp); 377 if(!cred.bv_val) 378 pcred = NULL; 379 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); 380 if(rc != LDAP_SUCCESS) 381 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); 382 return CURLE_OK; 383 } 384 385 /* 386 * Sends SASL continuation. 387 */ 388 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, 389 const struct bufref *resp) 390 { 391 struct connectdata *conn = data->conn; 392 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 393 struct berval cred; 394 struct berval *pcred = &cred; 395 int rc; 396 397 if(!li) 398 return CURLE_FAILED_INIT; 399 cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(resp)); 400 cred.bv_len = Curl_bufref_len(resp); 401 if(!cred.bv_val) 402 pcred = NULL; 403 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); 404 if(rc != LDAP_SUCCESS) 405 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); 406 return CURLE_OK; 407 } 408 409 /* 410 * Sends SASL bind cancellation. 411 */ 412 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech) 413 { 414 struct ldapconninfo *li = 415 Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN); 416 int rc; 417 418 (void)mech; 419 if(!li) 420 return CURLE_FAILED_INIT; 421 rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL, 422 &li->msgid); 423 if(rc != LDAP_SUCCESS) 424 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); 425 return CURLE_OK; 426 } 427 428 /* Starts LDAP simple bind. */ 429 static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) 430 { 431 struct connectdata *conn = data->conn; 432 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 433 char *binddn = NULL; 434 struct berval passwd; 435 int rc; 436 437 if(!li) 438 return CURLE_FAILED_INIT; 439 passwd.bv_val = NULL; 440 passwd.bv_len = 0; 441 442 if(data->state.aptr.user) { 443 binddn = conn->user; 444 passwd.bv_val = conn->passwd; 445 passwd.bv_len = strlen(passwd.bv_val); 446 } 447 448 rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, 449 NULL, NULL, &li->msgid); 450 if(rc != LDAP_SUCCESS) 451 return oldap_map_error(rc, 452 data->state.aptr.user ? 453 CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND); 454 oldap_state(data, li, newstate); 455 return CURLE_OK; 456 } 457 458 /* Query the supported SASL authentication mechanisms. */ 459 static CURLcode oldap_perform_mechs(struct Curl_easy *data) 460 { 461 struct ldapconninfo *li = 462 Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN); 463 int rc; 464 static const char * const supportedSASLMechanisms[] = { 465 "supportedSASLMechanisms", 466 NULL 467 }; 468 469 if(!li) 470 return CURLE_FAILED_INIT; 471 rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", 472 (char **)CURL_UNCONST(supportedSASLMechanisms), 0, 473 NULL, NULL, NULL, 0, &li->msgid); 474 if(rc != LDAP_SUCCESS) 475 return oldap_map_error(rc, CURLE_LOGIN_DENIED); 476 oldap_state(data, li, OLDAP_MECHS); 477 return CURLE_OK; 478 } 479 480 /* Starts SASL bind. */ 481 static CURLcode oldap_perform_sasl(struct Curl_easy *data) 482 { 483 struct ldapconninfo *li = 484 Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN); 485 saslprogress progress = SASL_IDLE; 486 CURLcode result; 487 488 if(!li) 489 return CURLE_FAILED_INIT; 490 result = Curl_sasl_start(&li->sasl, data, TRUE, &progress); 491 492 oldap_state(data, li, OLDAP_SASL); 493 if(!result && progress != SASL_INPROGRESS) 494 result = Curl_sasl_is_blocked(&li->sasl, data); 495 return result; 496 } 497 498 #ifdef USE_SSL 499 static Sockbuf_IO ldapsb_tls; 500 501 static bool ssl_installed(struct connectdata *conn) 502 { 503 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 504 return li && li->recv != NULL; 505 } 506 507 static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) 508 { 509 struct connectdata *conn = data->conn; 510 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 511 bool ssldone = FALSE; 512 CURLcode result; 513 514 if(!li) 515 return CURLE_FAILED_INIT; 516 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); 517 if(!result) { 518 oldap_state(data, li, newstate); 519 520 if(ssldone) { 521 Sockbuf *sb; 522 523 /* Install the libcurl SSL handlers into the sockbuf. */ 524 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); 525 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); 526 li->recv = conn->recv[FIRSTSOCKET]; 527 li->send = conn->send[FIRSTSOCKET]; 528 } 529 } 530 531 return result; 532 } 533 534 /* Send the STARTTLS request */ 535 static CURLcode oldap_perform_starttls(struct Curl_easy *data) 536 { 537 struct ldapconninfo *li = 538 Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN); 539 int rc; 540 541 if(!li) 542 return CURLE_FAILED_INIT; 543 rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid); 544 if(rc != LDAP_SUCCESS) 545 return oldap_map_error(rc, CURLE_USE_SSL_FAILED); 546 oldap_state(data, li, OLDAP_STARTTLS); 547 return CURLE_OK; 548 } 549 #endif 550 551 static void oldap_easy_dtor(void *key, size_t klen, void *entry) 552 { 553 struct ldapreqinfo *lr = entry; 554 (void)key; 555 (void)klen; 556 free(lr); 557 } 558 559 static void oldap_conn_dtor(void *key, size_t klen, void *entry) 560 { 561 struct ldapconninfo *li = entry; 562 (void)key; 563 (void)klen; 564 if(li->ld) { 565 ldap_unbind_ext(li->ld, NULL, NULL); 566 li->ld = NULL; 567 } 568 free(li); 569 } 570 571 static CURLcode oldap_connect(struct Curl_easy *data, bool *done) 572 { 573 struct connectdata *conn = data->conn; 574 struct ldapconninfo *li; 575 static const int version = LDAP_VERSION3; 576 char *hosturl = NULL; 577 CURLcode result; 578 int rc; 579 #ifdef CURL_OPENLDAP_DEBUG 580 static int do_trace = -1; 581 #endif 582 583 (void)done; 584 585 li = calloc(1, sizeof(struct ldapconninfo)); 586 if(!li) { 587 result = CURLE_OUT_OF_MEMORY; 588 goto out; 589 } 590 591 result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor); 592 if(result) 593 goto out; 594 595 li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); 596 597 /* Initialize the SASL storage */ 598 Curl_sasl_init(&li->sasl, data, &saslldap); 599 600 result = oldap_parse_login_options(conn); 601 if(result) 602 goto out; 603 604 hosturl = aprintf("%s://%s%s%s:%d", 605 conn->handler->scheme, 606 conn->bits.ipv6_ip ? "[" : "", 607 conn->host.name, 608 conn->bits.ipv6_ip ? "]" : "", 609 conn->remote_port); 610 if(!hosturl) { 611 result = CURLE_OUT_OF_MEMORY; 612 goto out; 613 } 614 615 rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); 616 if(rc) { 617 failf(data, "LDAP local: Cannot connect to %s, %s", 618 hosturl, ldap_err2string(rc)); 619 result = CURLE_COULDNT_CONNECT; 620 goto out; 621 } 622 623 #ifdef CURL_OPENLDAP_DEBUG 624 if(do_trace < 0) { 625 const char *env = getenv("CURL_OPENLDAP_TRACE"); 626 curl_off_t e = 0; 627 if(!curlx_str_number(&env, &e, INT_MAX)) 628 do_trace = e > 0; 629 } 630 if(do_trace) 631 ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); 632 #endif 633 634 /* Try version 3 first. */ 635 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); 636 637 /* Do not chase referrals. */ 638 ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 639 640 #ifdef USE_SSL 641 if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { 642 result = oldap_ssl_connect(data, OLDAP_SSL); 643 goto out; 644 } 645 646 if(data->set.use_ssl) { 647 result = oldap_perform_starttls(data); 648 if(!result || data->set.use_ssl != CURLUSESSL_TRY) 649 goto out; 650 } 651 #endif 652 653 if(li->sasl.prefmech != SASL_AUTH_NONE) { 654 result = oldap_perform_mechs(data); 655 goto out; 656 } 657 658 /* Force bind even if anonymous bind is not needed in protocol version 3 659 to detect missing version 3 support. */ 660 result = oldap_perform_bind(data, OLDAP_BIND); 661 662 out: 663 free(hosturl); 664 return result; 665 } 666 667 /* Handle the supported SASL mechanisms query response */ 668 static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, 669 LDAPMessage *msg, int code) 670 { 671 struct connectdata *conn = data->conn; 672 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 673 int rc; 674 BerElement *ber = NULL; 675 CURLcode result = CURLE_OK; 676 struct berval bv, *bvals; 677 678 if(!li) 679 return CURLE_FAILED_INIT; 680 switch(ldap_msgtype(msg)) { 681 case LDAP_RES_SEARCH_ENTRY: 682 /* Got a list of supported SASL mechanisms. */ 683 if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED) 684 return CURLE_LOGIN_DENIED; 685 686 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); 687 if(rc < 0) 688 return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING); 689 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); 690 rc == LDAP_SUCCESS; 691 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { 692 int i; 693 694 if(!bv.bv_val) 695 break; 696 697 if(bvals) { 698 for(i = 0; bvals[i].bv_val; i++) { 699 size_t llen; 700 unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val, 701 bvals[i].bv_len, &llen); 702 if(bvals[i].bv_len == llen) 703 li->sasl.authmechs |= mech; 704 } 705 ber_memfree(bvals); 706 } 707 } 708 ber_free(ber, 0); 709 break; 710 711 case LDAP_RES_SEARCH_RESULT: 712 switch(code) { 713 case LDAP_SIZELIMIT_EXCEEDED: 714 infof(data, "Too many authentication mechanisms\n"); 715 FALLTHROUGH(); 716 case LDAP_SUCCESS: 717 case LDAP_NO_RESULTS_RETURNED: 718 if(Curl_sasl_can_authenticate(&li->sasl, data)) 719 result = oldap_perform_sasl(data); 720 else 721 result = CURLE_LOGIN_DENIED; 722 break; 723 default: 724 result = oldap_map_error(code, CURLE_LOGIN_DENIED); 725 break; 726 } 727 break; 728 default: 729 break; 730 } 731 return result; 732 } 733 734 /* Handle a SASL bind response. */ 735 static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, 736 LDAPMessage *msg, int code) 737 { 738 struct connectdata *conn = data->conn; 739 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 740 CURLcode result = CURLE_OK; 741 saslprogress progress; 742 int rc; 743 744 if(!li) 745 return CURLE_FAILED_INIT; 746 li->servercred = NULL; 747 rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0); 748 if(rc != LDAP_SUCCESS) { 749 failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc)); 750 result = oldap_map_error(rc, CURLE_LOGIN_DENIED); 751 } 752 else { 753 result = Curl_sasl_continue(&li->sasl, data, code, &progress); 754 if(!result && progress != SASL_INPROGRESS) 755 oldap_state(data, li, OLDAP_STOP); 756 } 757 758 if(li->servercred) 759 ber_bvfree(li->servercred); 760 return result; 761 } 762 763 /* Handle a simple bind response. */ 764 static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, 765 int code) 766 { 767 struct connectdata *conn = data->conn; 768 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 769 CURLcode result = CURLE_OK; 770 struct berval *bv = NULL; 771 int rc; 772 773 if(!li) 774 return CURLE_FAILED_INIT; 775 776 if(code != LDAP_SUCCESS) 777 return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND); 778 779 rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0); 780 if(rc != LDAP_SUCCESS) { 781 failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s", 782 ldap_err2string(rc)); 783 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); 784 } 785 else 786 oldap_state(data, li, OLDAP_STOP); 787 788 if(bv) 789 ber_bvfree(bv); 790 return result; 791 } 792 793 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) 794 { 795 CURLcode result = CURLE_OK; 796 struct connectdata *conn = data->conn; 797 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 798 LDAPMessage *msg = NULL; 799 struct timeval tv = {0, 0}; 800 int code = LDAP_SUCCESS; 801 int rc; 802 803 if(!li) 804 return CURLE_FAILED_INIT; 805 806 if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) { 807 /* Get response to last command. */ 808 rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg); 809 switch(rc) { 810 case 0: /* Timed out. */ 811 return CURLE_OK; 812 case LDAP_RES_SEARCH_ENTRY: 813 case LDAP_RES_SEARCH_REFERENCE: 814 break; 815 default: 816 li->msgid = 0; /* Nothing to abandon upon error. */ 817 if(rc < 0) { 818 failf(data, "LDAP local: connecting ldap_result %s", 819 ldap_err2string(rc)); 820 return oldap_map_error(rc, CURLE_COULDNT_CONNECT); 821 } 822 break; 823 } 824 825 /* Get error code from message. */ 826 rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0); 827 if(rc) 828 code = rc; 829 else { 830 /* store the latest code for later retrieval */ 831 data->info.httpcode = code; 832 } 833 834 /* If protocol version 3 is not supported, fallback to version 2. */ 835 if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 && 836 #ifdef USE_SSL 837 (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) && 838 #endif 839 li->sasl.prefmech == SASL_AUTH_NONE) { 840 static const int version = LDAP_VERSION2; 841 842 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); 843 ldap_msgfree(msg); 844 return oldap_perform_bind(data, OLDAP_BINDV2); 845 } 846 } 847 848 /* Handle response message according to current state. */ 849 switch(li->state) { 850 851 #ifdef USE_SSL 852 case OLDAP_SSL: 853 result = oldap_ssl_connect(data, OLDAP_SSL); 854 if(!result && ssl_installed(conn)) { 855 if(li->sasl.prefmech != SASL_AUTH_NONE) 856 result = oldap_perform_mechs(data); 857 else 858 result = oldap_perform_bind(data, OLDAP_BIND); 859 } 860 break; 861 case OLDAP_STARTTLS: 862 if(code != LDAP_SUCCESS) { 863 if(data->set.use_ssl != CURLUSESSL_TRY) 864 result = oldap_map_error(code, CURLE_USE_SSL_FAILED); 865 else if(li->sasl.prefmech != SASL_AUTH_NONE) 866 result = oldap_perform_mechs(data); 867 else 868 result = oldap_perform_bind(data, OLDAP_BIND); 869 break; 870 } 871 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); 872 if(result) 873 break; 874 FALLTHROUGH(); 875 case OLDAP_TLS: 876 result = oldap_ssl_connect(data, OLDAP_TLS); 877 if(result) 878 result = oldap_map_error(code, CURLE_USE_SSL_FAILED); 879 else if(ssl_installed(conn)) { 880 if(li->sasl.prefmech != SASL_AUTH_NONE) 881 result = oldap_perform_mechs(data); 882 else if(data->state.aptr.user) 883 result = oldap_perform_bind(data, OLDAP_BIND); 884 else { 885 /* Version 3 supported: no bind required */ 886 oldap_state(data, li, OLDAP_STOP); 887 result = CURLE_OK; 888 } 889 } 890 break; 891 #endif 892 893 case OLDAP_MECHS: 894 result = oldap_state_mechs_resp(data, msg, code); 895 break; 896 case OLDAP_SASL: 897 result = oldap_state_sasl_resp(data, msg, code); 898 break; 899 case OLDAP_BIND: 900 case OLDAP_BINDV2: 901 result = oldap_state_bind_resp(data, msg, code); 902 break; 903 default: 904 /* internal error */ 905 result = CURLE_COULDNT_CONNECT; 906 break; 907 } 908 909 ldap_msgfree(msg); 910 911 *done = li->state == OLDAP_STOP; 912 if(*done) 913 conn->recv[FIRSTSOCKET] = oldap_recv; 914 915 if(result && li->msgid) { 916 ldap_abandon_ext(li->ld, li->msgid, NULL, NULL); 917 li->msgid = 0; 918 } 919 return result; 920 } 921 922 static CURLcode oldap_disconnect(struct Curl_easy *data, 923 struct connectdata *conn, 924 bool dead_connection) 925 { 926 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 927 (void) dead_connection; 928 #ifndef USE_SSL 929 (void)data; 930 #endif 931 932 if(li) { 933 if(li->ld) { 934 #ifdef USE_SSL 935 if(ssl_installed(conn)) { 936 Sockbuf *sb; 937 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); 938 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); 939 } 940 #endif 941 ldap_unbind_ext(li->ld, NULL, NULL); 942 li->ld = NULL; 943 } 944 } 945 return CURLE_OK; 946 } 947 948 static CURLcode oldap_do(struct Curl_easy *data, bool *done) 949 { 950 struct connectdata *conn = data->conn; 951 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 952 struct ldapreqinfo *lr; 953 CURLcode result; 954 int rc; 955 LDAPURLDesc *lud; 956 int msgid; 957 958 if(!li) 959 return CURLE_FAILED_INIT; 960 connkeep(conn, "OpenLDAP do"); 961 962 infof(data, "LDAP local: %s", data->state.url); 963 964 result = oldap_url_parse(data, &lud); 965 if(result) 966 goto out; 967 968 #ifdef USE_SSL 969 if(ssl_installed(conn)) { 970 Sockbuf *sb; 971 /* re-install the libcurl SSL handlers into the sockbuf. */ 972 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); 973 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); 974 } 975 #endif 976 977 rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, 978 lud->lud_filter, lud->lud_attrs, 0, 979 NULL, NULL, NULL, 0, &msgid); 980 ldap_free_urldesc(lud); 981 if(rc != LDAP_SUCCESS) { 982 failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); 983 result = CURLE_LDAP_SEARCH_FAILED; 984 goto out; 985 } 986 987 lr = calloc(1, sizeof(struct ldapreqinfo)); 988 if(!lr || 989 Curl_meta_set(data, CURL_META_LDAP_EASY, lr, oldap_easy_dtor)) { 990 ldap_abandon_ext(li->ld, msgid, NULL, NULL); 991 result = CURLE_OUT_OF_MEMORY; 992 goto out; 993 } 994 995 lr->msgid = msgid; 996 Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); 997 *done = TRUE; 998 999 out: 1000 return result; 1001 } 1002 1003 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, 1004 bool premature) 1005 { 1006 struct connectdata *conn = data->conn; 1007 struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY); 1008 1009 (void)res; 1010 (void)premature; 1011 1012 if(lr) { 1013 /* if there was a search in progress, abandon it */ 1014 if(lr->msgid) { 1015 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 1016 if(li && li->ld) { 1017 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); 1018 } 1019 lr->msgid = 0; 1020 } 1021 Curl_meta_remove(data, CURL_META_LDAP_EASY); 1022 } 1023 1024 return CURLE_OK; 1025 } 1026 1027 static CURLcode client_write(struct Curl_easy *data, 1028 const char *prefix, size_t plen, 1029 const char *value, size_t len, 1030 const char *suffix, size_t slen) 1031 { 1032 CURLcode result = CURLE_OK; 1033 1034 if(prefix) { 1035 /* If we have a zero-length value and the prefix ends with a space 1036 separator, drop the latter. */ 1037 if(!len && plen && prefix[plen - 1] == ' ') 1038 plen--; 1039 result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen); 1040 } 1041 if(!result && value) { 1042 result = Curl_client_write(data, CLIENTWRITE_BODY, value, len); 1043 } 1044 if(!result && suffix) { 1045 result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen); 1046 } 1047 return result; 1048 } 1049 1050 static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, 1051 size_t len, size_t *pnread) 1052 { 1053 struct connectdata *conn = data->conn; 1054 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 1055 struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY); 1056 int rc; 1057 LDAPMessage *msg = NULL; 1058 BerElement *ber = NULL; 1059 struct timeval tv = {0, 0}; 1060 struct berval bv, *bvals; 1061 bool binary = FALSE; 1062 CURLcode result = CURLE_AGAIN; 1063 int code; 1064 char *info = NULL; 1065 1066 (void)len; 1067 (void)buf; 1068 (void)sockindex; 1069 *pnread = 0; 1070 if(!li || !lr) 1071 return CURLE_FAILED_INIT; 1072 1073 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg); 1074 if(rc < 0) { 1075 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); 1076 result = CURLE_RECV_ERROR; 1077 } 1078 1079 /* error or timed out */ 1080 if(!msg) 1081 return result; 1082 1083 result = CURLE_OK; 1084 1085 switch(ldap_msgtype(msg)) { 1086 case LDAP_RES_SEARCH_RESULT: 1087 lr->msgid = 0; 1088 rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0); 1089 if(rc) { 1090 failf(data, "LDAP local: search ldap_parse_result %s", 1091 ldap_err2string(rc)); 1092 result = CURLE_LDAP_SEARCH_FAILED; 1093 break; 1094 } 1095 1096 /* store the latest code for later retrieval */ 1097 data->info.httpcode = code; 1098 1099 switch(code) { 1100 case LDAP_SIZELIMIT_EXCEEDED: 1101 infof(data, "There are more than %d entries", lr->nument); 1102 FALLTHROUGH(); 1103 case LDAP_SUCCESS: 1104 data->req.size = data->req.bytecount; 1105 break; 1106 default: 1107 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code), 1108 info ? info : ""); 1109 result = CURLE_LDAP_SEARCH_FAILED; 1110 break; 1111 } 1112 if(info) 1113 ldap_memfree(info); 1114 break; 1115 case LDAP_RES_SEARCH_ENTRY: 1116 lr->nument++; 1117 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); 1118 if(rc < 0) { 1119 result = CURLE_RECV_ERROR; 1120 break; 1121 } 1122 1123 result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, 1124 STRCONST("\n")); 1125 if(result) 1126 break; 1127 1128 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); 1129 rc == LDAP_SUCCESS; 1130 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { 1131 int i; 1132 1133 if(!bv.bv_val) 1134 break; 1135 1136 if(!bvals) { 1137 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, 1138 STRCONST(":\n")); 1139 if(result) 1140 break; 1141 continue; 1142 } 1143 1144 binary = bv.bv_len > 7 && 1145 !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7); 1146 1147 for(i = 0; bvals[i].bv_val != NULL; i++) { 1148 bool binval = FALSE; 1149 1150 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, 1151 STRCONST(":")); 1152 if(result) 1153 break; 1154 1155 if(!binary) { 1156 /* check for leading or trailing whitespace */ 1157 if(ISBLANK(bvals[i].bv_val[0]) || 1158 ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])) 1159 binval = TRUE; 1160 else { 1161 /* check for unprintable characters */ 1162 unsigned int j; 1163 for(j = 0; j < bvals[i].bv_len; j++) 1164 if(!ISPRINT(bvals[i].bv_val[j])) { 1165 binval = TRUE; 1166 break; 1167 } 1168 } 1169 } 1170 if(binary || binval) { 1171 char *val_b64 = NULL; 1172 size_t val_b64_sz = 0; 1173 1174 /* Binary value, encode to base64. */ 1175 if(bvals[i].bv_len) 1176 result = curlx_base64_encode(bvals[i].bv_val, bvals[i].bv_len, 1177 &val_b64, &val_b64_sz); 1178 if(!result) 1179 result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, 1180 STRCONST("\n")); 1181 free(val_b64); 1182 } 1183 else 1184 result = client_write(data, STRCONST(" "), 1185 bvals[i].bv_val, bvals[i].bv_len, 1186 STRCONST("\n")); 1187 if(result) 1188 break; 1189 } 1190 1191 ber_memfree(bvals); 1192 bvals = NULL; 1193 if(!result) 1194 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); 1195 if(result) 1196 break; 1197 } 1198 1199 ber_free(ber, 0); 1200 1201 if(!result) 1202 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); 1203 if(!result) 1204 result = CURLE_AGAIN; 1205 break; 1206 } 1207 1208 ldap_msgfree(msg); 1209 return result; 1210 } 1211 1212 #ifdef USE_SSL 1213 static int 1214 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) 1215 { 1216 sbiod->sbiod_pvt = arg; 1217 return 0; 1218 } 1219 1220 static int 1221 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) 1222 { 1223 sbiod->sbiod_pvt = NULL; 1224 return 0; 1225 } 1226 1227 /* We do not need to do anything because libcurl does it already */ 1228 static int 1229 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) 1230 { 1231 (void)sbiod; 1232 return 0; 1233 } 1234 1235 static int 1236 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) 1237 { 1238 (void)arg; 1239 if(opt == LBER_SB_OPT_DATA_READY) { 1240 struct Curl_easy *data = sbiod->sbiod_pvt; 1241 return Curl_conn_data_pending(data, FIRSTSOCKET); 1242 } 1243 return 0; 1244 } 1245 1246 static ber_slen_t 1247 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1248 { 1249 struct Curl_easy *data = sbiod->sbiod_pvt; 1250 ber_slen_t ret = 0; 1251 if(data) { 1252 struct connectdata *conn = data->conn; 1253 if(conn) { 1254 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 1255 CURLcode err = CURLE_RECV_ERROR; 1256 size_t nread; 1257 1258 if(!li) { 1259 SET_SOCKERRNO(SOCKEINVAL); 1260 return -1; 1261 } 1262 err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); 1263 if(err == CURLE_AGAIN) { 1264 SET_SOCKERRNO(SOCKEWOULDBLOCK); 1265 } 1266 ret = err ? -1 : (ber_slen_t)nread; 1267 } 1268 } 1269 return ret; 1270 } 1271 static ber_slen_t 1272 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1273 { 1274 struct Curl_easy *data = sbiod->sbiod_pvt; 1275 ber_slen_t ret = 0; 1276 if(data) { 1277 struct connectdata *conn = data->conn; 1278 if(conn) { 1279 struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); 1280 CURLcode err = CURLE_SEND_ERROR; 1281 size_t nwritten; 1282 1283 if(!li) { 1284 SET_SOCKERRNO(SOCKEINVAL); 1285 return -1; 1286 } 1287 err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); 1288 if(err == CURLE_AGAIN) { 1289 SET_SOCKERRNO(SOCKEWOULDBLOCK); 1290 } 1291 ret = err ? -1 : (ber_slen_t)nwritten; 1292 } 1293 } 1294 return ret; 1295 } 1296 1297 static Sockbuf_IO ldapsb_tls = 1298 { 1299 ldapsb_tls_setup, 1300 ldapsb_tls_remove, 1301 ldapsb_tls_ctrl, 1302 ldapsb_tls_read, 1303 ldapsb_tls_write, 1304 ldapsb_tls_close 1305 }; 1306 #endif /* USE_SSL */ 1307 1308 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */