Imported Upstream version 7.50.2
[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 - 2016, 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 https://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_base64.h"
48 #include "connect.h"
49 /* The last 3 #include files should be in this order */
50 #include "curl_printf.h"
51 #include "curl_memory.h"
52 #include "memdebug.h"
53
54 #ifndef _LDAP_PVT_H
55 extern int ldap_pvt_url_scheme2proto(const char *);
56 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
57                         LDAP **ld);
58 #endif
59
60 static CURLcode ldap_setup_connection(struct connectdata *conn);
61 static CURLcode ldap_do(struct connectdata *conn, bool *done);
62 static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
63 static CURLcode ldap_connect(struct connectdata *conn, bool *done);
64 static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
65 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead);
66
67 static Curl_recv ldap_recv;
68
69 /*
70  * LDAP protocol handler.
71  */
72
73 const struct Curl_handler Curl_handler_ldap = {
74   "LDAP",                               /* scheme */
75   ldap_setup_connection,                /* setup_connection */
76   ldap_do,                              /* do_it */
77   ldap_done,                            /* done */
78   ZERO_NULL,                            /* do_more */
79   ldap_connect,                         /* connect_it */
80   ldap_connecting,                      /* connecting */
81   ZERO_NULL,                            /* doing */
82   ZERO_NULL,                            /* proto_getsock */
83   ZERO_NULL,                            /* doing_getsock */
84   ZERO_NULL,                            /* domore_getsock */
85   ZERO_NULL,                            /* perform_getsock */
86   ldap_disconnect,                      /* disconnect */
87   ZERO_NULL,                            /* readwrite */
88   PORT_LDAP,                            /* defport */
89   CURLPROTO_LDAP,                       /* protocol */
90   PROTOPT_NONE                          /* flags */
91 };
92
93 #ifdef USE_SSL
94 /*
95  * LDAPS protocol handler.
96  */
97
98 const struct Curl_handler Curl_handler_ldaps = {
99   "LDAPS",                              /* scheme */
100   ldap_setup_connection,                /* setup_connection */
101   ldap_do,                              /* do_it */
102   ldap_done,                            /* done */
103   ZERO_NULL,                            /* do_more */
104   ldap_connect,                         /* connect_it */
105   ldap_connecting,                      /* connecting */
106   ZERO_NULL,                            /* doing */
107   ZERO_NULL,                            /* proto_getsock */
108   ZERO_NULL,                            /* doing_getsock */
109   ZERO_NULL,                            /* domore_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_connection(struct connectdata *conn)
150 {
151   ldapconninfo *li;
152   LDAPURLDesc *lud;
153   struct Curl_easy *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   if(!li)
174     return CURLE_OUT_OF_MEMORY;
175   li->proto = proto;
176   conn->proto.generic = li;
177   connkeep(conn, "OpenLDAP default");
178   /* TODO:
179    * - provide option to choose SASL Binds instead of Simple
180    */
181   return CURLE_OK;
182 }
183
184 #ifdef USE_SSL
185 static Sockbuf_IO ldapsb_tls;
186 #endif
187
188 static CURLcode ldap_connect(struct connectdata *conn, bool *done)
189 {
190   ldapconninfo *li = conn->proto.generic;
191   struct Curl_easy *data = conn->data;
192   int rc, proto = LDAP_VERSION3;
193   char hosturl[1024];
194   char *ptr;
195
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 result;
217     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
218     if(result)
219       return result;
220   }
221 #endif
222
223   return CURLE_OK;
224 }
225
226 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
227 {
228   ldapconninfo *li = conn->proto.generic;
229   struct Curl_easy *data = conn->data;
230   LDAPMessage *msg = 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 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
240                                                      &li->ssldone);
241       if(result || !li->ssldone)
242         return result;
243     }
244
245     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
246     if(!li->sslinst) {
247       Sockbuf *sb;
248       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
249       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
250       li->sslinst = TRUE;
251       li->recv = conn->recv[FIRSTSOCKET];
252       li->send = conn->send[FIRSTSOCKET];
253     }
254   }
255 #endif
256
257   tvp = &tv;
258
259 retry:
260   if(!li->didbind) {
261     char *binddn;
262     struct berval passwd;
263
264     if(conn->bits.user_passwd) {
265       binddn = conn->user;
266       passwd.bv_val = conn->passwd;
267       passwd.bv_len = strlen(passwd.bv_val);
268     }
269     else {
270       binddn = NULL;
271       passwd.bv_val = NULL;
272       passwd.bv_len = 0;
273     }
274     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
275                         NULL, NULL, &li->msgid);
276     if(rc)
277       return CURLE_LDAP_CANNOT_BIND;
278     li->didbind = TRUE;
279     if(tvp)
280       return CURLE_OK;
281   }
282
283   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
284   if(rc < 0) {
285     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
286     return CURLE_LDAP_CANNOT_BIND;
287   }
288   if(rc == 0) {
289     /* timed out */
290     return CURLE_OK;
291   }
292
293   rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
294   if(rc) {
295     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
296     return CURLE_LDAP_CANNOT_BIND;
297   }
298
299   /* Try to fallback to LDAPv2? */
300   if(err == LDAP_PROTOCOL_ERROR) {
301     int proto;
302     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
303     if(proto == LDAP_VERSION3) {
304       if(info) {
305         ldap_memfree(info);
306         info = NULL;
307       }
308       proto = LDAP_VERSION2;
309       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
310       li->didbind = FALSE;
311       goto retry;
312     }
313   }
314
315   if(err) {
316     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
317           info ? info : "");
318     if(info)
319       ldap_memfree(info);
320     return CURLE_LOGIN_DENIED;
321   }
322
323   if(info)
324     ldap_memfree(info);
325   conn->recv[FIRSTSOCKET] = ldap_recv;
326   *done = TRUE;
327
328   return CURLE_OK;
329 }
330
331 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
332 {
333   ldapconninfo *li = conn->proto.generic;
334   (void) dead_connection;
335
336   if(li) {
337     if(li->ld) {
338       ldap_unbind_ext(li->ld, NULL, NULL);
339       li->ld = NULL;
340     }
341     conn->proto.generic = NULL;
342     free(li);
343   }
344   return CURLE_OK;
345 }
346
347 static CURLcode ldap_do(struct connectdata *conn, bool *done)
348 {
349   ldapconninfo *li = conn->proto.generic;
350   ldapreqinfo *lr;
351   CURLcode status = CURLE_OK;
352   int rc = 0;
353   LDAPURLDesc *ludp = NULL;
354   int msgid;
355   struct Curl_easy *data=conn->data;
356
357   connkeep(conn, "OpenLDAP do");
358
359   infof(data, "LDAP local: %s\n", data->change.url);
360
361   rc = ldap_url_parse(data->change.url, &ludp);
362   if(rc != LDAP_URL_SUCCESS) {
363     const char *msg = "url parsing problem";
364     status = CURLE_URL_MALFORMAT;
365     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
366       if(rc == LDAP_URL_ERR_MEM)
367         status = CURLE_OUT_OF_MEMORY;
368       msg = url_errs[rc];
369     }
370     failf(conn->data, "LDAP local: %s", msg);
371     return status;
372   }
373
374   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
375                        ludp->lud_filter, ludp->lud_attrs, 0,
376                        NULL, NULL, NULL, 0, &msgid);
377   ldap_free_urldesc(ludp);
378   if(rc != LDAP_SUCCESS) {
379     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
380     return CURLE_LDAP_SEARCH_FAILED;
381   }
382   lr = calloc(1, sizeof(ldapreqinfo));
383   if(!lr)
384     return CURLE_OUT_OF_MEMORY;
385   lr->msgid = msgid;
386   data->req.protop = lr;
387   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
388   *done = TRUE;
389   return CURLE_OK;
390 }
391
392 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
393                           bool premature)
394 {
395   ldapreqinfo *lr = conn->data->req.protop;
396
397   (void)res;
398   (void)premature;
399
400   if(lr) {
401     /* if there was a search in progress, abandon it */
402     if(lr->msgid) {
403       ldapconninfo *li = conn->proto.generic;
404       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
405       lr->msgid = 0;
406     }
407     conn->data->req.protop = NULL;
408     free(lr);
409   }
410
411   return CURLE_OK;
412 }
413
414 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
415                          size_t len, CURLcode *err)
416 {
417   ldapconninfo *li = conn->proto.generic;
418   struct Curl_easy *data = conn->data;
419   ldapreqinfo *lr = data->req.protop;
420   int rc, ret;
421   LDAPMessage *msg = NULL;
422   LDAPMessage *ent;
423   BerElement *ber = NULL;
424   struct timeval tv = {0, 1};
425
426   (void)len;
427   (void)buf;
428   (void)sockindex;
429
430   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg);
431   if(rc < 0) {
432     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
433     *err = CURLE_RECV_ERROR;
434     return -1;
435   }
436
437   *err = CURLE_AGAIN;
438   ret = -1;
439
440   /* timed out */
441   if(!msg)
442     return ret;
443
444   for(ent = ldap_first_message(li->ld, msg); ent;
445     ent = ldap_next_message(li->ld, ent)) {
446     struct berval bv, *bvals, **bvp = &bvals;
447     int binary = 0, msgtype;
448     CURLcode writeerr;
449
450     msgtype = ldap_msgtype(ent);
451     if(msgtype == LDAP_RES_SEARCH_RESULT) {
452       int code;
453       char *info = NULL;
454       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
455       if(rc) {
456         failf(data, "LDAP local: search ldap_parse_result %s",
457               ldap_err2string(rc));
458         *err = CURLE_LDAP_SEARCH_FAILED;
459       }
460       else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
461         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
462               info ? info : "");
463         *err = CURLE_LDAP_SEARCH_FAILED;
464       }
465       else {
466         /* successful */
467         if(code == LDAP_SIZELIMIT_EXCEEDED)
468           infof(data, "There are more than %d entries\n", lr->nument);
469         data->req.size = data->req.bytecount;
470         *err = CURLE_OK;
471         ret = 0;
472       }
473       lr->msgid = 0;
474       ldap_memfree(info);
475       break;
476     }
477     else if(msgtype != LDAP_RES_SEARCH_ENTRY)
478       continue;
479
480     lr->nument++;
481     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
482     if(rc < 0) {
483       /* TODO: verify that this is really how this return code should be
484          handled */
485       *err = CURLE_RECV_ERROR;
486       return -1;
487     }
488     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
489     if(writeerr) {
490       *err = writeerr;
491       return -1;
492     }
493
494     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
495                                  bv.bv_len);
496     if(writeerr) {
497       *err = writeerr;
498       return -1;
499     }
500
501     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
502     if(writeerr) {
503       *err = writeerr;
504       return -1;
505     }
506     data->req.bytecount += bv.bv_len + 5;
507
508     for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
509       rc == LDAP_SUCCESS;
510       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
511       int i;
512
513       if(bv.bv_val == NULL) break;
514
515       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
516         binary = 1;
517       else
518         binary = 0;
519
520       for(i=0; bvals[i].bv_val != NULL; i++) {
521         int binval = 0;
522         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
523         if(writeerr) {
524           *err = writeerr;
525           return -1;
526         }
527
528        writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
529                                     bv.bv_len);
530        if(writeerr) {
531          *err = writeerr;
532          return -1;
533        }
534
535         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
536        if(writeerr) {
537          *err = writeerr;
538          return -1;
539        }
540         data->req.bytecount += bv.bv_len + 2;
541
542         if(!binary) {
543           /* check for leading or trailing whitespace */
544           if(ISSPACE(bvals[i].bv_val[0]) ||
545               ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
546             binval = 1;
547           else {
548             /* check for unprintable characters */
549             unsigned int j;
550             for(j=0; j<bvals[i].bv_len; j++)
551               if(!ISPRINT(bvals[i].bv_val[j])) {
552                 binval = 1;
553                 break;
554               }
555           }
556         }
557         if(binary || binval) {
558           char *val_b64 = NULL;
559           size_t val_b64_sz = 0;
560           /* Binary value, encode to base64. */
561           CURLcode error = Curl_base64_encode(data,
562                                               bvals[i].bv_val,
563                                               bvals[i].bv_len,
564                                               &val_b64,
565                                               &val_b64_sz);
566           if(error) {
567             ber_memfree(bvals);
568             ber_free(ber, 0);
569             ldap_msgfree(msg);
570             *err = error;
571             return -1;
572           }
573           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY,
574                                        (char *)": ", 2);
575           if(writeerr) {
576             *err = writeerr;
577             return -1;
578           }
579
580           data->req.bytecount += 2;
581           if(val_b64_sz > 0) {
582             writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
583                                      val_b64_sz);
584             if(writeerr) {
585               *err = writeerr;
586               return -1;
587             }
588             free(val_b64);
589             data->req.bytecount += val_b64_sz;
590           }
591         }
592         else {
593           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
594           if(writeerr) {
595             *err = writeerr;
596             return -1;
597           }
598
599           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
600                                        bvals[i].bv_len);
601           if(writeerr) {
602             *err = writeerr;
603             return -1;
604           }
605
606           data->req.bytecount += bvals[i].bv_len + 1;
607         }
608         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
609         if(writeerr) {
610           *err = writeerr;
611           return -1;
612         }
613
614         data->req.bytecount++;
615       }
616       ber_memfree(bvals);
617       writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
618       if(writeerr) {
619         *err = writeerr;
620         return -1;
621       }
622       data->req.bytecount++;
623     }
624     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
625     if(writeerr) {
626       *err = writeerr;
627       return -1;
628     }
629     data->req.bytecount++;
630     ber_free(ber, 0);
631   }
632   ldap_msgfree(msg);
633   return ret;
634 }
635
636 #ifdef USE_SSL
637 static int
638 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
639 {
640   sbiod->sbiod_pvt = arg;
641   return 0;
642 }
643
644 static int
645 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
646 {
647   sbiod->sbiod_pvt = NULL;
648   return 0;
649 }
650
651 /* We don't need to do anything because libcurl does it already */
652 static int
653 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
654 {
655   (void)sbiod;
656   return 0;
657 }
658
659 static int
660 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
661 {
662   (void)arg;
663   if(opt == LBER_SB_OPT_DATA_READY) {
664     struct connectdata *conn = sbiod->sbiod_pvt;
665     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
666   }
667   return 0;
668 }
669
670 static ber_slen_t
671 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
672 {
673   struct connectdata *conn = sbiod->sbiod_pvt;
674   ldapconninfo *li = conn->proto.generic;
675   ber_slen_t ret;
676   CURLcode err = CURLE_RECV_ERROR;
677
678   ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
679   if(ret < 0 && err == CURLE_AGAIN) {
680     SET_SOCKERRNO(EWOULDBLOCK);
681   }
682   return ret;
683 }
684
685 static ber_slen_t
686 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
687 {
688   struct connectdata *conn = sbiod->sbiod_pvt;
689   ldapconninfo *li = conn->proto.generic;
690   ber_slen_t ret;
691   CURLcode err = CURLE_SEND_ERROR;
692
693   ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
694   if(ret < 0 && err == CURLE_AGAIN) {
695     SET_SOCKERRNO(EWOULDBLOCK);
696   }
697   return ret;
698 }
699
700 static Sockbuf_IO ldapsb_tls =
701 {
702   ldapsb_tls_setup,
703   ldapsb_tls_remove,
704   ldapsb_tls_ctrl,
705   ldapsb_tls_read,
706   ldapsb_tls_write,
707   ldapsb_tls_close
708 };
709 #endif /* USE_SSL */
710
711 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */