1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2010, Howard Chu, <hyc (at) openldap.org> 9 * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel (at) haxx.se>, et al. 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 http://curl.haxx.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 ***************************************************************************/ 23 24 #include "curl_setup.h" 25 26 #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP) 27 28 /* 29 * Notice that USE_OPENLDAP is only a source code selection switch. When 30 * libcurl is built with USE_OPENLDAP defined the libcurl source code that 31 * gets compiled is the code from openldap.c, otherwise the code that gets 32 * compiled is the code from ldap.c. 33 * 34 * When USE_OPENLDAP is defined a recent version of the OpenLDAP library 35 * might be required for compilation and runtime. In order to use ancient 36 * OpenLDAP library versions, USE_OPENLDAP shall not be defined. 37 */ 38 39 #include <ldap.h> 40 41 #include "urldata.h" 42 #include <curl/curl.h> 43 #include "sendf.h" 44 #include "vtls/vtls.h" 45 #include "transfer.h" 46 #include "curl_ldap.h" 47 #include "curl_base64.h" 48 #include "connect.h" 49 #include "curl_printf.h" 50 51 /* The last #include files should be: */ 52 #include "curl_memory.h" 53 #include "memdebug.h" 54 55 #ifndef _LDAP_PVT_H 56 extern int ldap_pvt_url_scheme2proto(const char *); 57 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, 58 LDAP **ld); 59 #endif 60 61 static CURLcode ldap_setup_connection(struct connectdata *conn); 62 static CURLcode ldap_do(struct connectdata *conn, bool *done); 63 static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool); 64 static CURLcode ldap_connect(struct connectdata *conn, bool *done); 65 static CURLcode ldap_connecting(struct connectdata *conn, bool *done); 66 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead); 67 68 static Curl_recv ldap_recv; 69 70 /* 71 * LDAP protocol handler. 72 */ 73 74 const struct Curl_handler Curl_handler_ldap = { 75 "LDAP", /* scheme */ 76 ldap_setup_connection, /* setup_connection */ 77 ldap_do, /* do_it */ 78 ldap_done, /* done */ 79 ZERO_NULL, /* do_more */ 80 ldap_connect, /* connect_it */ 81 ldap_connecting, /* connecting */ 82 ZERO_NULL, /* doing */ 83 ZERO_NULL, /* proto_getsock */ 84 ZERO_NULL, /* doing_getsock */ 85 ZERO_NULL, /* domore_getsock */ 86 ZERO_NULL, /* perform_getsock */ 87 ldap_disconnect, /* disconnect */ 88 ZERO_NULL, /* readwrite */ 89 PORT_LDAP, /* defport */ 90 CURLPROTO_LDAP, /* protocol */ 91 PROTOPT_NONE /* flags */ 92 }; 93 94 #ifdef USE_SSL 95 /* 96 * LDAPS protocol handler. 97 */ 98 99 const struct Curl_handler Curl_handler_ldaps = { 100 "LDAPS", /* scheme */ 101 ldap_setup_connection, /* setup_connection */ 102 ldap_do, /* do_it */ 103 ldap_done, /* done */ 104 ZERO_NULL, /* do_more */ 105 ldap_connect, /* connect_it */ 106 ldap_connecting, /* connecting */ 107 ZERO_NULL, /* doing */ 108 ZERO_NULL, /* proto_getsock */ 109 ZERO_NULL, /* doing_getsock */ 110 ZERO_NULL, /* domore_getsock */ 111 ZERO_NULL, /* perform_getsock */ 112 ldap_disconnect, /* disconnect */ 113 ZERO_NULL, /* readwrite */ 114 PORT_LDAPS, /* defport */ 115 CURLPROTO_LDAP, /* protocol */ 116 PROTOPT_SSL /* flags */ 117 }; 118 #endif 119 120 static const char *url_errs[] = { 121 "success", 122 "out of memory", 123 "bad parameter", 124 "unrecognized scheme", 125 "unbalanced delimiter", 126 "bad URL", 127 "bad host or port", 128 "bad or missing attributes", 129 "bad or missing scope", 130 "bad or missing filter", 131 "bad or missing extensions" 132 }; 133 134 typedef struct ldapconninfo { 135 LDAP *ld; 136 Curl_recv *recv; /* for stacking SSL handler */ 137 Curl_send *send; 138 int proto; 139 int msgid; 140 bool ssldone; 141 bool sslinst; 142 bool didbind; 143 } ldapconninfo; 144 145 typedef struct ldapreqinfo { 146 int msgid; 147 int nument; 148 } ldapreqinfo; 149 150 static CURLcode ldap_setup_connection(struct connectdata *conn) 151 { 152 ldapconninfo *li; 153 LDAPURLDesc *lud; 154 struct SessionHandle *data=conn->data; 155 int rc, proto; 156 CURLcode status; 157 158 rc = ldap_url_parse(data->change.url, &lud); 159 if(rc != LDAP_URL_SUCCESS) { 160 const char *msg = "url parsing problem"; 161 status = CURLE_URL_MALFORMAT; 162 if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) { 163 if(rc == LDAP_URL_ERR_MEM) 164 status = CURLE_OUT_OF_MEMORY; 165 msg = url_errs[rc]; 166 } 167 failf(conn->data, "LDAP local: %s", msg); 168 return status; 169 } 170 proto = ldap_pvt_url_scheme2proto(lud->lud_scheme); 171 ldap_free_urldesc(lud); 172 173 li = calloc(1, sizeof(ldapconninfo)); 174 if(!li) 175 return CURLE_OUT_OF_MEMORY; 176 li->proto = proto; 177 conn->proto.generic = li; 178 connkeep(conn, "OpenLDAP default"); 179 /* TODO: 180 * - provide option to choose SASL Binds instead of Simple 181 */ 182 return CURLE_OK; 183 } 184 185 #ifdef USE_SSL 186 static Sockbuf_IO ldapsb_tls; 187 #endif 188 189 static CURLcode ldap_connect(struct connectdata *conn, bool *done) 190 { 191 ldapconninfo *li = conn->proto.generic; 192 struct SessionHandle *data = conn->data; 193 int rc, proto = LDAP_VERSION3; 194 char hosturl[1024]; 195 char *ptr; 196 197 (void)done; 198 199 strcpy(hosturl, "ldap"); 200 ptr = hosturl+4; 201 if(conn->handler->flags & PROTOPT_SSL) 202 *ptr++ = 's'; 203 snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d", 204 conn->host.name, conn->remote_port); 205 206 rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); 207 if(rc) { 208 failf(data, "LDAP local: Cannot connect to %s, %s", 209 hosturl, ldap_err2string(rc)); 210 return CURLE_COULDNT_CONNECT; 211 } 212 213 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); 214 215 #ifdef USE_SSL 216 if(conn->handler->flags & PROTOPT_SSL) { 217 CURLcode result; 218 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone); 219 if(result) 220 return result; 221 } 222 #endif 223 224 return CURLE_OK; 225 } 226 227 static CURLcode ldap_connecting(struct connectdata *conn, bool *done) 228 { 229 ldapconninfo *li = conn->proto.generic; 230 struct SessionHandle *data = conn->data; 231 LDAPMessage *msg = NULL; 232 struct timeval tv = {0, 1}, *tvp; 233 int rc, err; 234 char *info = NULL; 235 236 #ifdef USE_SSL 237 if(conn->handler->flags & PROTOPT_SSL) { 238 /* Is the SSL handshake complete yet? */ 239 if(!li->ssldone) { 240 CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, 241 &li->ssldone); 242 if(result || !li->ssldone) 243 return result; 244 } 245 246 /* Have we installed the libcurl SSL handlers into the sockbuf yet? */ 247 if(!li->sslinst) { 248 Sockbuf *sb; 249 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); 250 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn); 251 li->sslinst = TRUE; 252 li->recv = conn->recv[FIRSTSOCKET]; 253 li->send = conn->send[FIRSTSOCKET]; 254 } 255 } 256 #endif 257 258 tvp = &tv; 259 260 retry: 261 if(!li->didbind) { 262 char *binddn; 263 struct berval passwd; 264 265 if(conn->bits.user_passwd) { 266 binddn = conn->user; 267 passwd.bv_val = conn->passwd; 268 passwd.bv_len = strlen(passwd.bv_val); 269 } 270 else { 271 binddn = NULL; 272 passwd.bv_val = NULL; 273 passwd.bv_len = 0; 274 } 275 rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, 276 NULL, NULL, &li->msgid); 277 if(rc) 278 return CURLE_LDAP_CANNOT_BIND; 279 li->didbind = TRUE; 280 if(tvp) 281 return CURLE_OK; 282 } 283 284 rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg); 285 if(rc < 0) { 286 failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc)); 287 return CURLE_LDAP_CANNOT_BIND; 288 } 289 if(rc == 0) { 290 /* timed out */ 291 return CURLE_OK; 292 } 293 294 rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1); 295 if(rc) { 296 failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc)); 297 return CURLE_LDAP_CANNOT_BIND; 298 } 299 300 /* Try to fallback to LDAPv2? */ 301 if(err == LDAP_PROTOCOL_ERROR) { 302 int proto; 303 ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); 304 if(proto == LDAP_VERSION3) { 305 if(info) { 306 ldap_memfree(info); 307 info = NULL; 308 } 309 proto = LDAP_VERSION2; 310 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); 311 li->didbind = FALSE; 312 goto retry; 313 } 314 } 315 316 if(err) { 317 failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc), 318 info ? info : ""); 319 if(info) 320 ldap_memfree(info); 321 return CURLE_LOGIN_DENIED; 322 } 323 324 if(info) 325 ldap_memfree(info); 326 conn->recv[FIRSTSOCKET] = ldap_recv; 327 *done = TRUE; 328 329 return CURLE_OK; 330 } 331 332 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection) 333 { 334 ldapconninfo *li = conn->proto.generic; 335 (void) dead_connection; 336 337 if(li) { 338 if(li->ld) { 339 ldap_unbind_ext(li->ld, NULL, NULL); 340 li->ld = NULL; 341 } 342 conn->proto.generic = NULL; 343 free(li); 344 } 345 return CURLE_OK; 346 } 347 348 static CURLcode ldap_do(struct connectdata *conn, bool *done) 349 { 350 ldapconninfo *li = conn->proto.generic; 351 ldapreqinfo *lr; 352 CURLcode status = CURLE_OK; 353 int rc = 0; 354 LDAPURLDesc *ludp = NULL; 355 int msgid; 356 struct SessionHandle *data=conn->data; 357 358 connkeep(conn, "OpenLDAP do"); 359 360 infof(data, "LDAP local: %s\n", data->change.url); 361 362 rc = ldap_url_parse(data->change.url, &ludp); 363 if(rc != LDAP_URL_SUCCESS) { 364 const char *msg = "url parsing problem"; 365 status = CURLE_URL_MALFORMAT; 366 if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) { 367 if(rc == LDAP_URL_ERR_MEM) 368 status = CURLE_OUT_OF_MEMORY; 369 msg = url_errs[rc]; 370 } 371 failf(conn->data, "LDAP local: %s", msg); 372 return status; 373 } 374 375 rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope, 376 ludp->lud_filter, ludp->lud_attrs, 0, 377 NULL, NULL, NULL, 0, &msgid); 378 ldap_free_urldesc(ludp); 379 if(rc != LDAP_SUCCESS) { 380 failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); 381 return CURLE_LDAP_SEARCH_FAILED; 382 } 383 lr = calloc(1, sizeof(ldapreqinfo)); 384 if(!lr) 385 return CURLE_OUT_OF_MEMORY; 386 lr->msgid = msgid; 387 data->req.protop = lr; 388 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); 389 *done = TRUE; 390 return CURLE_OK; 391 } 392 393 static CURLcode ldap_done(struct connectdata *conn, CURLcode res, 394 bool premature) 395 { 396 ldapreqinfo *lr = conn->data->req.protop; 397 398 (void)res; 399 (void)premature; 400 401 if(lr) { 402 /* if there was a search in progress, abandon it */ 403 if(lr->msgid) { 404 ldapconninfo *li = conn->proto.generic; 405 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); 406 lr->msgid = 0; 407 } 408 conn->data->req.protop = NULL; 409 free(lr); 410 } 411 412 return CURLE_OK; 413 } 414 415 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, 416 size_t len, CURLcode *err) 417 { 418 ldapconninfo *li = conn->proto.generic; 419 struct SessionHandle *data = conn->data; 420 ldapreqinfo *lr = data->req.protop; 421 int rc, ret; 422 LDAPMessage *msg = NULL; 423 LDAPMessage *ent; 424 BerElement *ber = NULL; 425 struct timeval tv = {0, 1}; 426 427 (void)len; 428 (void)buf; 429 (void)sockindex; 430 431 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg); 432 if(rc < 0) { 433 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); 434 *err = CURLE_RECV_ERROR; 435 return -1; 436 } 437 438 *err = CURLE_AGAIN; 439 ret = -1; 440 441 /* timed out */ 442 if(!msg) 443 return ret; 444 445 for(ent = ldap_first_message(li->ld, msg); ent; 446 ent = ldap_next_message(li->ld, ent)) { 447 struct berval bv, *bvals, **bvp = &bvals; 448 int binary = 0, msgtype; 449 450 msgtype = ldap_msgtype(ent); 451 if(msgtype == LDAP_RES_SEARCH_RESULT) { 452 int code; 453 char *info = NULL; 454 rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0); 455 if(rc) { 456 failf(data, "LDAP local: search ldap_parse_result %s", 457 ldap_err2string(rc)); 458 *err = CURLE_LDAP_SEARCH_FAILED; 459 } 460 else if(code && code != LDAP_SIZELIMIT_EXCEEDED) { 461 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc), 462 info ? info : ""); 463 *err = CURLE_LDAP_SEARCH_FAILED; 464 } 465 else { 466 /* successful */ 467 if(code == LDAP_SIZELIMIT_EXCEEDED) 468 infof(data, "There are more than %d entries\n", lr->nument); 469 data->req.size = data->req.bytecount; 470 *err = CURLE_OK; 471 ret = 0; 472 } 473 lr->msgid = 0; 474 ldap_memfree(info); 475 break; 476 } 477 else if(msgtype != LDAP_RES_SEARCH_ENTRY) 478 continue; 479 480 lr->nument++; 481 rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv); 482 if(rc < 0) { 483 /* TODO: verify that this is really how this return code should be 484 handled */ 485 *err = CURLE_RECV_ERROR; 486 return -1; 487 } 488 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); 489 if(*err) 490 return -1; 491 492 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, 493 bv.bv_len); 494 if(*err) 495 return -1; 496 497 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); 498 if(*err) 499 return -1; 500 data->req.bytecount += bv.bv_len + 5; 501 502 for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp); 503 rc == LDAP_SUCCESS; 504 rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) { 505 int i; 506 507 if(bv.bv_val == NULL) break; 508 509 if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7)) 510 binary = 1; 511 else 512 binary = 0; 513 514 for(i=0; bvals[i].bv_val != NULL; i++) { 515 int binval = 0; 516 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); 517 if(*err) 518 return -1; 519 520 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, 521 bv.bv_len); 522 if(*err) 523 return -1; 524 525 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1); 526 if(*err) 527 return -1; 528 data->req.bytecount += bv.bv_len + 2; 529 530 if(!binary) { 531 /* check for leading or trailing whitespace */ 532 if(ISSPACE(bvals[i].bv_val[0]) || 533 ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1])) 534 binval = 1; 535 else { 536 /* check for unprintable characters */ 537 unsigned int j; 538 for(j=0; j<bvals[i].bv_len; j++) 539 if(!ISPRINT(bvals[i].bv_val[j])) { 540 binval = 1; 541 break; 542 } 543 } 544 } 545 if(binary || binval) { 546 char *val_b64 = NULL; 547 size_t val_b64_sz = 0; 548 /* Binary value, encode to base64. */ 549 CURLcode error = Curl_base64_encode(data, 550 bvals[i].bv_val, 551 bvals[i].bv_len, 552 &val_b64, 553 &val_b64_sz); 554 if(error) { 555 ber_memfree(bvals); 556 ber_free(ber, 0); 557 ldap_msgfree(msg); 558 *err = error; 559 return -1; 560 } 561 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); 562 if(*err) 563 return -1; 564 565 data->req.bytecount += 2; 566 if(val_b64_sz > 0) { 567 *err = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, 568 val_b64_sz); 569 if(*err) 570 return -1; 571 free(val_b64); 572 data->req.bytecount += val_b64_sz; 573 } 574 } 575 else { 576 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1); 577 if(*err) 578 return -1; 579 580 *err = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val, 581 bvals[i].bv_len); 582 if(*err) 583 return -1; 584 585 data->req.bytecount += bvals[i].bv_len + 1; 586 } 587 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); 588 if(*err) 589 return -1; 590 591 data->req.bytecount++; 592 } 593 ber_memfree(bvals); 594 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); 595 if(*err) 596 return -1; 597 data->req.bytecount++; 598 } 599 *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); 600 if(*err) 601 return -1; 602 data->req.bytecount++; 603 ber_free(ber, 0); 604 } 605 ldap_msgfree(msg); 606 return ret; 607 } 608 609 #ifdef USE_SSL 610 static int 611 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) 612 { 613 sbiod->sbiod_pvt = arg; 614 return 0; 615 } 616 617 static int 618 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) 619 { 620 sbiod->sbiod_pvt = NULL; 621 return 0; 622 } 623 624 /* We don't need to do anything because libcurl does it already */ 625 static int 626 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) 627 { 628 (void)sbiod; 629 return 0; 630 } 631 632 static int 633 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) 634 { 635 (void)arg; 636 if(opt == LBER_SB_OPT_DATA_READY) { 637 struct connectdata *conn = sbiod->sbiod_pvt; 638 return Curl_ssl_data_pending(conn, FIRSTSOCKET); 639 } 640 return 0; 641 } 642 643 static ber_slen_t 644 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 645 { 646 struct connectdata *conn = sbiod->sbiod_pvt; 647 ldapconninfo *li = conn->proto.generic; 648 ber_slen_t ret; 649 CURLcode err = CURLE_RECV_ERROR; 650 651 ret = li->recv(conn, FIRSTSOCKET, buf, len, &err); 652 if(ret < 0 && err == CURLE_AGAIN) { 653 SET_SOCKERRNO(EWOULDBLOCK); 654 } 655 return ret; 656 } 657 658 static ber_slen_t 659 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 660 { 661 struct connectdata *conn = sbiod->sbiod_pvt; 662 ldapconninfo *li = conn->proto.generic; 663 ber_slen_t ret; 664 CURLcode err = CURLE_SEND_ERROR; 665 666 ret = li->send(conn, FIRSTSOCKET, buf, len, &err); 667 if(ret < 0 && err == CURLE_AGAIN) { 668 SET_SOCKERRNO(EWOULDBLOCK); 669 } 670 return ret; 671 } 672 673 static Sockbuf_IO ldapsb_tls = 674 { 675 ldapsb_tls_setup, 676 ldapsb_tls_remove, 677 ldapsb_tls_ctrl, 678 ldapsb_tls_read, 679 ldapsb_tls_write, 680 ldapsb_tls_close 681 }; 682 #endif /* USE_SSL */ 683 684 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ 685