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