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