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