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