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