• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                 \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
9  * Copyright (C) 2011 - 2019, 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 /*
55  * Uncommenting this will enable the built-in debug logging of the openldap
56  * library. The debug log level can be set using the CURL_OPENLDAP_TRACE
57  * environment variable. The debug output is written to stderr.
58  *
59  * The library supports the following debug flags:
60  * LDAP_DEBUG_NONE         0x0000
61  * LDAP_DEBUG_TRACE        0x0001
62  * LDAP_DEBUG_CONSTRUCT    0x0002
63  * LDAP_DEBUG_DESTROY      0x0004
64  * LDAP_DEBUG_PARAMETER    0x0008
65  * LDAP_DEBUG_ANY          0xffff
66  *
67  * For example, use CURL_OPENLDAP_TRACE=0 for no debug,
68  * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only,
69  * CURL_OPENLDAP_TRACE=65535 for all debug message levels.
70  */
71 /* #define CURL_OPENLDAP_DEBUG */
72 
73 #ifndef _LDAP_PVT_H
74 extern int ldap_pvt_url_scheme2proto(const char *);
75 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
76                         LDAP **ld);
77 #endif
78 
79 static CURLcode ldap_setup_connection(struct connectdata *conn);
80 static CURLcode ldap_do(struct connectdata *conn, bool *done);
81 static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
82 static CURLcode ldap_connect(struct connectdata *conn, bool *done);
83 static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
84 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead);
85 
86 static Curl_recv ldap_recv;
87 
88 /*
89  * LDAP protocol handler.
90  */
91 
92 const struct Curl_handler Curl_handler_ldap = {
93   "LDAP",                               /* scheme */
94   ldap_setup_connection,                /* setup_connection */
95   ldap_do,                              /* do_it */
96   ldap_done,                            /* done */
97   ZERO_NULL,                            /* do_more */
98   ldap_connect,                         /* connect_it */
99   ldap_connecting,                      /* connecting */
100   ZERO_NULL,                            /* doing */
101   ZERO_NULL,                            /* proto_getsock */
102   ZERO_NULL,                            /* doing_getsock */
103   ZERO_NULL,                            /* domore_getsock */
104   ZERO_NULL,                            /* perform_getsock */
105   ldap_disconnect,                      /* disconnect */
106   ZERO_NULL,                            /* readwrite */
107   ZERO_NULL,                            /* connection_check */
108   PORT_LDAP,                            /* defport */
109   CURLPROTO_LDAP,                       /* protocol */
110   PROTOPT_NONE                          /* flags */
111 };
112 
113 #ifdef USE_SSL
114 /*
115  * LDAPS protocol handler.
116  */
117 
118 const struct Curl_handler Curl_handler_ldaps = {
119   "LDAPS",                              /* scheme */
120   ldap_setup_connection,                /* setup_connection */
121   ldap_do,                              /* do_it */
122   ldap_done,                            /* done */
123   ZERO_NULL,                            /* do_more */
124   ldap_connect,                         /* connect_it */
125   ldap_connecting,                      /* connecting */
126   ZERO_NULL,                            /* doing */
127   ZERO_NULL,                            /* proto_getsock */
128   ZERO_NULL,                            /* doing_getsock */
129   ZERO_NULL,                            /* domore_getsock */
130   ZERO_NULL,                            /* perform_getsock */
131   ldap_disconnect,                      /* disconnect */
132   ZERO_NULL,                            /* readwrite */
133   ZERO_NULL,                            /* connection_check */
134   PORT_LDAPS,                           /* defport */
135   CURLPROTO_LDAP,                       /* protocol */
136   PROTOPT_SSL                           /* flags */
137 };
138 #endif
139 
140 static const char *url_errs[] = {
141   "success",
142   "out of memory",
143   "bad parameter",
144   "unrecognized scheme",
145   "unbalanced delimiter",
146   "bad URL",
147   "bad host or port",
148   "bad or missing attributes",
149   "bad or missing scope",
150   "bad or missing filter",
151   "bad or missing extensions"
152 };
153 
154 typedef struct ldapconninfo {
155   LDAP *ld;
156   Curl_recv *recv;  /* for stacking SSL handler */
157   Curl_send *send;
158   int proto;
159   int msgid;
160   bool ssldone;
161   bool sslinst;
162   bool didbind;
163 } ldapconninfo;
164 
165 typedef struct ldapreqinfo {
166   int msgid;
167   int nument;
168 } ldapreqinfo;
169 
ldap_setup_connection(struct connectdata * conn)170 static CURLcode ldap_setup_connection(struct connectdata *conn)
171 {
172   ldapconninfo *li;
173   LDAPURLDesc *lud;
174   struct Curl_easy *data = conn->data;
175   int rc, proto;
176   CURLcode status;
177 
178   rc = ldap_url_parse(data->change.url, &lud);
179   if(rc != LDAP_URL_SUCCESS) {
180     const char *msg = "url parsing problem";
181     status = CURLE_URL_MALFORMAT;
182     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
183       if(rc == LDAP_URL_ERR_MEM)
184         status = CURLE_OUT_OF_MEMORY;
185       msg = url_errs[rc];
186     }
187     failf(conn->data, "LDAP local: %s", msg);
188     return status;
189   }
190   proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
191   ldap_free_urldesc(lud);
192 
193   li = calloc(1, sizeof(ldapconninfo));
194   if(!li)
195     return CURLE_OUT_OF_MEMORY;
196   li->proto = proto;
197   conn->proto.generic = li;
198   connkeep(conn, "OpenLDAP default");
199   /* TODO:
200    * - provide option to choose SASL Binds instead of Simple
201    */
202   return CURLE_OK;
203 }
204 
205 #ifdef USE_SSL
206 static Sockbuf_IO ldapsb_tls;
207 #endif
208 
ldap_connect(struct connectdata * conn,bool * done)209 static CURLcode ldap_connect(struct connectdata *conn, bool *done)
210 {
211   ldapconninfo *li = conn->proto.generic;
212   struct Curl_easy *data = conn->data;
213   int rc, proto = LDAP_VERSION3;
214   char hosturl[1024];
215   char *ptr;
216 
217   (void)done;
218 
219   strcpy(hosturl, "ldap");
220   ptr = hosturl + 4;
221   if(conn->handler->flags & PROTOPT_SSL)
222     *ptr++ = 's';
223   msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
224             conn->host.name, conn->remote_port);
225 
226 #ifdef CURL_OPENLDAP_DEBUG
227   static int do_trace = 0;
228   const char *env = getenv("CURL_OPENLDAP_TRACE");
229   do_trace = (env && strtol(env, NULL, 10) > 0);
230   if(do_trace) {
231     ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
232   }
233 #endif
234 
235   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
236   if(rc) {
237     failf(data, "LDAP local: Cannot connect to %s, %s",
238           hosturl, ldap_err2string(rc));
239     return CURLE_COULDNT_CONNECT;
240   }
241 
242   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
243 
244 #ifdef USE_SSL
245   if(conn->handler->flags & PROTOPT_SSL) {
246     CURLcode result;
247     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
248     if(result)
249       return result;
250   }
251 #endif
252 
253   return CURLE_OK;
254 }
255 
ldap_connecting(struct connectdata * conn,bool * done)256 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
257 {
258   ldapconninfo *li = conn->proto.generic;
259   struct Curl_easy *data = conn->data;
260   LDAPMessage *msg = NULL;
261   struct timeval tv = {0, 1}, *tvp;
262   int rc, err;
263   char *info = NULL;
264 
265 #ifdef USE_SSL
266   if(conn->handler->flags & PROTOPT_SSL) {
267     /* Is the SSL handshake complete yet? */
268     if(!li->ssldone) {
269       CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
270                                                      &li->ssldone);
271       if(result || !li->ssldone)
272         return result;
273     }
274 
275     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
276     if(!li->sslinst) {
277       Sockbuf *sb;
278       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
279       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
280       li->sslinst = TRUE;
281       li->recv = conn->recv[FIRSTSOCKET];
282       li->send = conn->send[FIRSTSOCKET];
283     }
284   }
285 #endif
286 
287   tvp = &tv;
288 
289   retry:
290   if(!li->didbind) {
291     char *binddn;
292     struct berval passwd;
293 
294     if(conn->bits.user_passwd) {
295       binddn = conn->user;
296       passwd.bv_val = conn->passwd;
297       passwd.bv_len = strlen(passwd.bv_val);
298     }
299     else {
300       binddn = NULL;
301       passwd.bv_val = NULL;
302       passwd.bv_len = 0;
303     }
304     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
305                         NULL, NULL, &li->msgid);
306     if(rc)
307       return CURLE_LDAP_CANNOT_BIND;
308     li->didbind = TRUE;
309     if(tvp)
310       return CURLE_OK;
311   }
312 
313   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
314   if(rc < 0) {
315     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
316     return CURLE_LDAP_CANNOT_BIND;
317   }
318   if(rc == 0) {
319     /* timed out */
320     return CURLE_OK;
321   }
322 
323   rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
324   if(rc) {
325     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
326     return CURLE_LDAP_CANNOT_BIND;
327   }
328 
329   /* Try to fallback to LDAPv2? */
330   if(err == LDAP_PROTOCOL_ERROR) {
331     int proto;
332     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
333     if(proto == LDAP_VERSION3) {
334       if(info) {
335         ldap_memfree(info);
336         info = NULL;
337       }
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     if(info)
349       ldap_memfree(info);
350     return CURLE_LOGIN_DENIED;
351   }
352 
353   if(info)
354     ldap_memfree(info);
355   conn->recv[FIRSTSOCKET] = ldap_recv;
356   *done = TRUE;
357 
358   return CURLE_OK;
359 }
360 
ldap_disconnect(struct connectdata * conn,bool dead_connection)361 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
362 {
363   ldapconninfo *li = conn->proto.generic;
364   (void) dead_connection;
365 
366   if(li) {
367     if(li->ld) {
368       ldap_unbind_ext(li->ld, NULL, NULL);
369       li->ld = NULL;
370     }
371     conn->proto.generic = NULL;
372     free(li);
373   }
374   return CURLE_OK;
375 }
376 
ldap_do(struct connectdata * conn,bool * done)377 static CURLcode ldap_do(struct connectdata *conn, bool *done)
378 {
379   ldapconninfo *li = conn->proto.generic;
380   ldapreqinfo *lr;
381   CURLcode status = CURLE_OK;
382   int rc = 0;
383   LDAPURLDesc *ludp = NULL;
384   int msgid;
385   struct Curl_easy *data = conn->data;
386 
387   connkeep(conn, "OpenLDAP do");
388 
389   infof(data, "LDAP local: %s\n", data->change.url);
390 
391   rc = ldap_url_parse(data->change.url, &ludp);
392   if(rc != LDAP_URL_SUCCESS) {
393     const char *msg = "url parsing problem";
394     status = CURLE_URL_MALFORMAT;
395     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
396       if(rc == LDAP_URL_ERR_MEM)
397         status = CURLE_OUT_OF_MEMORY;
398       msg = url_errs[rc];
399     }
400     failf(conn->data, "LDAP local: %s", msg);
401     return status;
402   }
403 
404   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
405                        ludp->lud_filter, ludp->lud_attrs, 0,
406                        NULL, NULL, NULL, 0, &msgid);
407   ldap_free_urldesc(ludp);
408   if(rc != LDAP_SUCCESS) {
409     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
410     return CURLE_LDAP_SEARCH_FAILED;
411   }
412   lr = calloc(1, sizeof(ldapreqinfo));
413   if(!lr)
414     return CURLE_OUT_OF_MEMORY;
415   lr->msgid = msgid;
416   data->req.protop = lr;
417   Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
418   *done = TRUE;
419   return CURLE_OK;
420 }
421 
ldap_done(struct connectdata * conn,CURLcode res,bool premature)422 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
423                           bool premature)
424 {
425   ldapreqinfo *lr = conn->data->req.protop;
426 
427   (void)res;
428   (void)premature;
429 
430   if(lr) {
431     /* if there was a search in progress, abandon it */
432     if(lr->msgid) {
433       ldapconninfo *li = conn->proto.generic;
434       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
435       lr->msgid = 0;
436     }
437     conn->data->req.protop = NULL;
438     free(lr);
439   }
440 
441   return CURLE_OK;
442 }
443 
ldap_recv(struct connectdata * conn,int sockindex,char * buf,size_t len,CURLcode * err)444 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
445                          size_t len, CURLcode *err)
446 {
447   ldapconninfo *li = conn->proto.generic;
448   struct Curl_easy *data = conn->data;
449   ldapreqinfo *lr = data->req.protop;
450   int rc, ret;
451   LDAPMessage *msg = NULL;
452   LDAPMessage *ent;
453   BerElement *ber = NULL;
454   struct timeval tv = {0, 1};
455 
456   (void)len;
457   (void)buf;
458   (void)sockindex;
459 
460   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg);
461   if(rc < 0) {
462     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
463     *err = CURLE_RECV_ERROR;
464     return -1;
465   }
466 
467   *err = CURLE_AGAIN;
468   ret = -1;
469 
470   /* timed out */
471   if(!msg)
472     return ret;
473 
474   for(ent = ldap_first_message(li->ld, msg); ent;
475       ent = ldap_next_message(li->ld, ent)) {
476     struct berval bv, *bvals;
477     int binary = 0, msgtype;
478     CURLcode writeerr;
479 
480     msgtype = ldap_msgtype(ent);
481     if(msgtype == LDAP_RES_SEARCH_RESULT) {
482       int code;
483       char *info = NULL;
484       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
485       if(rc) {
486         failf(data, "LDAP local: search ldap_parse_result %s",
487               ldap_err2string(rc));
488         *err = CURLE_LDAP_SEARCH_FAILED;
489       }
490       else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
491         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
492               info ? info : "");
493         *err = CURLE_LDAP_SEARCH_FAILED;
494       }
495       else {
496         /* successful */
497         if(code == LDAP_SIZELIMIT_EXCEEDED)
498           infof(data, "There are more than %d entries\n", lr->nument);
499         data->req.size = data->req.bytecount;
500         *err = CURLE_OK;
501         ret = 0;
502       }
503       lr->msgid = 0;
504       ldap_memfree(info);
505       break;
506     }
507     else if(msgtype != LDAP_RES_SEARCH_ENTRY)
508       continue;
509 
510     lr->nument++;
511     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
512     if(rc < 0) {
513       /* TODO: verify that this is really how this return code should be
514          handled */
515       *err = CURLE_RECV_ERROR;
516       return -1;
517     }
518     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
519     if(writeerr) {
520       *err = writeerr;
521       return -1;
522     }
523 
524     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
525                                  bv.bv_len);
526     if(writeerr) {
527       *err = writeerr;
528       return -1;
529     }
530 
531     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
532     if(writeerr) {
533       *err = writeerr;
534       return -1;
535     }
536     data->req.bytecount += bv.bv_len + 5;
537 
538     for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals);
539         rc == LDAP_SUCCESS;
540         rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals)) {
541       int i;
542 
543       if(bv.bv_val == NULL)
544         break;
545 
546       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
547         binary = 1;
548       else
549         binary = 0;
550 
551       if(bvals == NULL) {
552         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
553         if(writeerr) {
554           *err = writeerr;
555           return -1;
556         }
557         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
558                                      bv.bv_len);
559         if(writeerr) {
560           *err = writeerr;
561           return -1;
562         }
563         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":\n", 2);
564         if(writeerr) {
565           *err = writeerr;
566           return -1;
567         }
568         data->req.bytecount += bv.bv_len + 3;
569         continue;
570       }
571 
572       for(i = 0; bvals[i].bv_val != NULL; i++) {
573         int binval = 0;
574         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
575         if(writeerr) {
576           *err = writeerr;
577           return -1;
578         }
579 
580         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
581                                      bv.bv_len);
582         if(writeerr) {
583           *err = writeerr;
584           return -1;
585         }
586 
587         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
588         if(writeerr) {
589           *err = writeerr;
590           return -1;
591         }
592         data->req.bytecount += bv.bv_len + 2;
593 
594         if(!binary) {
595           /* check for leading or trailing whitespace */
596           if(ISSPACE(bvals[i].bv_val[0]) ||
597              ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
598             binval = 1;
599           else {
600             /* check for unprintable characters */
601             unsigned int j;
602             for(j = 0; j<bvals[i].bv_len; j++)
603               if(!ISPRINT(bvals[i].bv_val[j])) {
604                 binval = 1;
605                 break;
606               }
607           }
608         }
609         if(binary || binval) {
610           char *val_b64 = NULL;
611           size_t val_b64_sz = 0;
612           /* Binary value, encode to base64. */
613           CURLcode error = Curl_base64_encode(data,
614                                               bvals[i].bv_val,
615                                               bvals[i].bv_len,
616                                               &val_b64,
617                                               &val_b64_sz);
618           if(error) {
619             ber_memfree(bvals);
620             ber_free(ber, 0);
621             ldap_msgfree(msg);
622             *err = error;
623             return -1;
624           }
625           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY,
626                                        (char *)": ", 2);
627           if(writeerr) {
628             *err = writeerr;
629             return -1;
630           }
631 
632           data->req.bytecount += 2;
633           if(val_b64_sz > 0) {
634             writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
635                                          val_b64_sz);
636             if(writeerr) {
637               *err = writeerr;
638               return -1;
639             }
640             free(val_b64);
641             data->req.bytecount += val_b64_sz;
642           }
643         }
644         else {
645           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
646           if(writeerr) {
647             *err = writeerr;
648             return -1;
649           }
650 
651           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
652                                        bvals[i].bv_len);
653           if(writeerr) {
654             *err = writeerr;
655             return -1;
656           }
657 
658           data->req.bytecount += bvals[i].bv_len + 1;
659         }
660         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
661         if(writeerr) {
662           *err = writeerr;
663           return -1;
664         }
665 
666         data->req.bytecount++;
667       }
668       ber_memfree(bvals);
669       writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
670       if(writeerr) {
671         *err = writeerr;
672         return -1;
673       }
674       data->req.bytecount++;
675     }
676     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
677     if(writeerr) {
678       *err = writeerr;
679       return -1;
680     }
681     data->req.bytecount++;
682     ber_free(ber, 0);
683   }
684   ldap_msgfree(msg);
685   return ret;
686 }
687 
688 #ifdef USE_SSL
689 static int
ldapsb_tls_setup(Sockbuf_IO_Desc * sbiod,void * arg)690 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
691 {
692   sbiod->sbiod_pvt = arg;
693   return 0;
694 }
695 
696 static int
ldapsb_tls_remove(Sockbuf_IO_Desc * sbiod)697 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
698 {
699   sbiod->sbiod_pvt = NULL;
700   return 0;
701 }
702 
703 /* We don't need to do anything because libcurl does it already */
704 static int
ldapsb_tls_close(Sockbuf_IO_Desc * sbiod)705 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
706 {
707   (void)sbiod;
708   return 0;
709 }
710 
711 static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)712 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
713 {
714   (void)arg;
715   if(opt == LBER_SB_OPT_DATA_READY) {
716     struct connectdata *conn = sbiod->sbiod_pvt;
717     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
718   }
719   return 0;
720 }
721 
722 static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)723 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
724 {
725   struct connectdata *conn = sbiod->sbiod_pvt;
726   ldapconninfo *li = conn->proto.generic;
727   ber_slen_t ret;
728   CURLcode err = CURLE_RECV_ERROR;
729 
730   ret = (li->recv)(conn, FIRSTSOCKET, buf, len, &err);
731   if(ret < 0 && err == CURLE_AGAIN) {
732     SET_SOCKERRNO(EWOULDBLOCK);
733   }
734   return ret;
735 }
736 
737 static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)738 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
739 {
740   struct connectdata *conn = sbiod->sbiod_pvt;
741   ldapconninfo *li = conn->proto.generic;
742   ber_slen_t ret;
743   CURLcode err = CURLE_SEND_ERROR;
744 
745   ret = (li->send)(conn, FIRSTSOCKET, buf, len, &err);
746   if(ret < 0 && err == CURLE_AGAIN) {
747     SET_SOCKERRNO(EWOULDBLOCK);
748   }
749   return ret;
750 }
751 
752 static Sockbuf_IO ldapsb_tls =
753 {
754   ldapsb_tls_setup,
755   ldapsb_tls_remove,
756   ldapsb_tls_ctrl,
757   ldapsb_tls_read,
758   ldapsb_tls_write,
759   ldapsb_tls_close
760 };
761 #endif /* USE_SSL */
762 
763 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
764