• 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 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 };
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   struct 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(struct ldapconninfo));
194   if(!li)
195     return CURLE_OUT_OF_MEMORY;
196   li->proto = proto;
197   conn->proto.ldapc = li;
198   connkeep(conn, "OpenLDAP default");
199   return CURLE_OK;
200 }
201 
202 #ifdef USE_SSL
203 static Sockbuf_IO ldapsb_tls;
204 #endif
205 
ldap_connect(struct connectdata * conn,bool * done)206 static CURLcode ldap_connect(struct connectdata *conn, bool *done)
207 {
208   struct ldapconninfo *li = conn->proto.ldapc;
209   struct Curl_easy *data = conn->data;
210   int rc, proto = LDAP_VERSION3;
211   char hosturl[1024];
212   char *ptr;
213 
214   (void)done;
215 
216   strcpy(hosturl, "ldap");
217   ptr = hosturl + 4;
218   if(conn->handler->flags & PROTOPT_SSL)
219     *ptr++ = 's';
220   msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
221             conn->host.name, conn->remote_port);
222 
223 #ifdef CURL_OPENLDAP_DEBUG
224   static int do_trace = 0;
225   const char *env = getenv("CURL_OPENLDAP_TRACE");
226   do_trace = (env && strtol(env, NULL, 10) > 0);
227   if(do_trace) {
228     ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
229   }
230 #endif
231 
232   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
233   if(rc) {
234     failf(data, "LDAP local: Cannot connect to %s, %s",
235           hosturl, ldap_err2string(rc));
236     return CURLE_COULDNT_CONNECT;
237   }
238 
239   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
240 
241 #ifdef USE_SSL
242   if(conn->handler->flags & PROTOPT_SSL) {
243     CURLcode result;
244     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
245     if(result)
246       return result;
247   }
248 #endif
249 
250   return CURLE_OK;
251 }
252 
ldap_connecting(struct connectdata * conn,bool * done)253 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
254 {
255   struct ldapconninfo *li = conn->proto.ldapc;
256   struct Curl_easy *data = conn->data;
257   LDAPMessage *msg = NULL;
258   struct timeval tv = {0, 1}, *tvp;
259   int rc, err;
260   char *info = NULL;
261 
262 #ifdef USE_SSL
263   if(conn->handler->flags & PROTOPT_SSL) {
264     /* Is the SSL handshake complete yet? */
265     if(!li->ssldone) {
266       CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
267                                                      &li->ssldone);
268       if(result || !li->ssldone)
269         return result;
270     }
271 
272     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
273     if(!li->sslinst) {
274       Sockbuf *sb;
275       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
276       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
277       li->sslinst = TRUE;
278       li->recv = conn->recv[FIRSTSOCKET];
279       li->send = conn->send[FIRSTSOCKET];
280     }
281   }
282 #endif
283 
284   tvp = &tv;
285 
286   retry:
287   if(!li->didbind) {
288     char *binddn;
289     struct berval passwd;
290 
291     if(conn->bits.user_passwd) {
292       binddn = conn->user;
293       passwd.bv_val = conn->passwd;
294       passwd.bv_len = strlen(passwd.bv_val);
295     }
296     else {
297       binddn = NULL;
298       passwd.bv_val = NULL;
299       passwd.bv_len = 0;
300     }
301     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
302                         NULL, NULL, &li->msgid);
303     if(rc)
304       return CURLE_LDAP_CANNOT_BIND;
305     li->didbind = TRUE;
306     if(tvp)
307       return CURLE_OK;
308   }
309 
310   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
311   if(rc < 0) {
312     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
313     return CURLE_LDAP_CANNOT_BIND;
314   }
315   if(rc == 0) {
316     /* timed out */
317     return CURLE_OK;
318   }
319 
320   rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
321   if(rc) {
322     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
323     return CURLE_LDAP_CANNOT_BIND;
324   }
325 
326   /* Try to fallback to LDAPv2? */
327   if(err == LDAP_PROTOCOL_ERROR) {
328     int proto;
329     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
330     if(proto == LDAP_VERSION3) {
331       if(info) {
332         ldap_memfree(info);
333         info = NULL;
334       }
335       proto = LDAP_VERSION2;
336       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
337       li->didbind = FALSE;
338       goto retry;
339     }
340   }
341 
342   if(err) {
343     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
344           info ? info : "");
345     if(info)
346       ldap_memfree(info);
347     return CURLE_LOGIN_DENIED;
348   }
349 
350   if(info)
351     ldap_memfree(info);
352   conn->recv[FIRSTSOCKET] = ldap_recv;
353   *done = TRUE;
354 
355   return CURLE_OK;
356 }
357 
ldap_disconnect(struct connectdata * conn,bool dead_connection)358 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
359 {
360   struct ldapconninfo *li = conn->proto.ldapc;
361   (void) dead_connection;
362 
363   if(li) {
364     if(li->ld) {
365       ldap_unbind_ext(li->ld, NULL, NULL);
366       li->ld = NULL;
367     }
368     conn->proto.ldapc = NULL;
369     free(li);
370   }
371   return CURLE_OK;
372 }
373 
ldap_do(struct connectdata * conn,bool * done)374 static CURLcode ldap_do(struct connectdata *conn, bool *done)
375 {
376   struct ldapconninfo *li = conn->proto.ldapc;
377   ldapreqinfo *lr;
378   CURLcode status = CURLE_OK;
379   int rc = 0;
380   LDAPURLDesc *ludp = NULL;
381   int msgid;
382   struct Curl_easy *data = conn->data;
383 
384   connkeep(conn, "OpenLDAP do");
385 
386   infof(data, "LDAP local: %s\n", data->change.url);
387 
388   rc = ldap_url_parse(data->change.url, &ludp);
389   if(rc != LDAP_URL_SUCCESS) {
390     const char *msg = "url parsing problem";
391     status = CURLE_URL_MALFORMAT;
392     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
393       if(rc == LDAP_URL_ERR_MEM)
394         status = CURLE_OUT_OF_MEMORY;
395       msg = url_errs[rc];
396     }
397     failf(conn->data, "LDAP local: %s", msg);
398     return status;
399   }
400 
401   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
402                        ludp->lud_filter, ludp->lud_attrs, 0,
403                        NULL, NULL, NULL, 0, &msgid);
404   ldap_free_urldesc(ludp);
405   if(rc != LDAP_SUCCESS) {
406     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
407     return CURLE_LDAP_SEARCH_FAILED;
408   }
409   lr = calloc(1, sizeof(ldapreqinfo));
410   if(!lr)
411     return CURLE_OUT_OF_MEMORY;
412   lr->msgid = msgid;
413   data->req.protop = lr;
414   Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
415   *done = TRUE;
416   return CURLE_OK;
417 }
418 
ldap_done(struct connectdata * conn,CURLcode res,bool premature)419 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
420                           bool premature)
421 {
422   ldapreqinfo *lr = conn->data->req.protop;
423 
424   (void)res;
425   (void)premature;
426 
427   if(lr) {
428     /* if there was a search in progress, abandon it */
429     if(lr->msgid) {
430       struct ldapconninfo *li = conn->proto.ldapc;
431       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
432       lr->msgid = 0;
433     }
434     conn->data->req.protop = NULL;
435     free(lr);
436   }
437 
438   return CURLE_OK;
439 }
440 
ldap_recv(struct connectdata * conn,int sockindex,char * buf,size_t len,CURLcode * err)441 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
442                          size_t len, CURLcode *err)
443 {
444   struct ldapconninfo *li = conn->proto.ldapc;
445   struct Curl_easy *data = conn->data;
446   ldapreqinfo *lr = data->req.protop;
447   int rc, ret;
448   LDAPMessage *msg = NULL;
449   LDAPMessage *ent;
450   BerElement *ber = NULL;
451   struct timeval tv = {0, 1};
452 
453   (void)len;
454   (void)buf;
455   (void)sockindex;
456 
457   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg);
458   if(rc < 0) {
459     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
460     *err = CURLE_RECV_ERROR;
461     return -1;
462   }
463 
464   *err = CURLE_AGAIN;
465   ret = -1;
466 
467   /* timed out */
468   if(!msg)
469     return ret;
470 
471   for(ent = ldap_first_message(li->ld, msg); ent;
472       ent = ldap_next_message(li->ld, ent)) {
473     struct berval bv, *bvals;
474     int binary = 0, msgtype;
475     CURLcode writeerr;
476 
477     msgtype = ldap_msgtype(ent);
478     if(msgtype == LDAP_RES_SEARCH_RESULT) {
479       int code;
480       char *info = NULL;
481       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
482       if(rc) {
483         failf(data, "LDAP local: search ldap_parse_result %s",
484               ldap_err2string(rc));
485         *err = CURLE_LDAP_SEARCH_FAILED;
486       }
487       else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
488         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
489               info ? info : "");
490         *err = CURLE_LDAP_SEARCH_FAILED;
491       }
492       else {
493         /* successful */
494         if(code == LDAP_SIZELIMIT_EXCEEDED)
495           infof(data, "There are more than %d entries\n", lr->nument);
496         data->req.size = data->req.bytecount;
497         *err = CURLE_OK;
498         ret = 0;
499       }
500       lr->msgid = 0;
501       ldap_memfree(info);
502       break;
503     }
504     else if(msgtype != LDAP_RES_SEARCH_ENTRY)
505       continue;
506 
507     lr->nument++;
508     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
509     if(rc < 0) {
510       *err = CURLE_RECV_ERROR;
511       return -1;
512     }
513     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
514     if(writeerr) {
515       *err = writeerr;
516       return -1;
517     }
518 
519     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
520                                  bv.bv_len);
521     if(writeerr) {
522       *err = writeerr;
523       return -1;
524     }
525 
526     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
527     if(writeerr) {
528       *err = writeerr;
529       return -1;
530     }
531     data->req.bytecount += bv.bv_len + 5;
532 
533     for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals);
534         rc == LDAP_SUCCESS;
535         rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals)) {
536       int i;
537 
538       if(bv.bv_val == NULL)
539         break;
540 
541       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
542         binary = 1;
543       else
544         binary = 0;
545 
546       if(bvals == NULL) {
547         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
548         if(writeerr) {
549           *err = writeerr;
550           return -1;
551         }
552         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
553                                      bv.bv_len);
554         if(writeerr) {
555           *err = writeerr;
556           return -1;
557         }
558         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":\n", 2);
559         if(writeerr) {
560           *err = writeerr;
561           return -1;
562         }
563         data->req.bytecount += bv.bv_len + 3;
564         continue;
565       }
566 
567       for(i = 0; bvals[i].bv_val != NULL; i++) {
568         int binval = 0;
569         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
570         if(writeerr) {
571           *err = writeerr;
572           return -1;
573         }
574 
575         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
576                                      bv.bv_len);
577         if(writeerr) {
578           *err = writeerr;
579           return -1;
580         }
581 
582         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
583         if(writeerr) {
584           *err = writeerr;
585           return -1;
586         }
587         data->req.bytecount += bv.bv_len + 2;
588 
589         if(!binary) {
590           /* check for leading or trailing whitespace */
591           if(ISSPACE(bvals[i].bv_val[0]) ||
592              ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
593             binval = 1;
594           else {
595             /* check for unprintable characters */
596             unsigned int j;
597             for(j = 0; j<bvals[i].bv_len; j++)
598               if(!ISPRINT(bvals[i].bv_val[j])) {
599                 binval = 1;
600                 break;
601               }
602           }
603         }
604         if(binary || binval) {
605           char *val_b64 = NULL;
606           size_t val_b64_sz = 0;
607           /* Binary value, encode to base64. */
608           CURLcode error = Curl_base64_encode(data,
609                                               bvals[i].bv_val,
610                                               bvals[i].bv_len,
611                                               &val_b64,
612                                               &val_b64_sz);
613           if(error) {
614             ber_memfree(bvals);
615             ber_free(ber, 0);
616             ldap_msgfree(msg);
617             *err = error;
618             return -1;
619           }
620           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY,
621                                        (char *)": ", 2);
622           if(writeerr) {
623             *err = writeerr;
624             return -1;
625           }
626 
627           data->req.bytecount += 2;
628           if(val_b64_sz > 0) {
629             writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
630                                          val_b64_sz);
631             if(writeerr) {
632               *err = writeerr;
633               return -1;
634             }
635             free(val_b64);
636             data->req.bytecount += val_b64_sz;
637           }
638         }
639         else {
640           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
641           if(writeerr) {
642             *err = writeerr;
643             return -1;
644           }
645 
646           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
647                                        bvals[i].bv_len);
648           if(writeerr) {
649             *err = writeerr;
650             return -1;
651           }
652 
653           data->req.bytecount += bvals[i].bv_len + 1;
654         }
655         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
656         if(writeerr) {
657           *err = writeerr;
658           return -1;
659         }
660 
661         data->req.bytecount++;
662       }
663       ber_memfree(bvals);
664       writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
665       if(writeerr) {
666         *err = writeerr;
667         return -1;
668       }
669       data->req.bytecount++;
670     }
671     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
672     if(writeerr) {
673       *err = writeerr;
674       return -1;
675     }
676     data->req.bytecount++;
677     ber_free(ber, 0);
678   }
679   ldap_msgfree(msg);
680   return ret;
681 }
682 
683 #ifdef USE_SSL
684 static int
ldapsb_tls_setup(Sockbuf_IO_Desc * sbiod,void * arg)685 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
686 {
687   sbiod->sbiod_pvt = arg;
688   return 0;
689 }
690 
691 static int
ldapsb_tls_remove(Sockbuf_IO_Desc * sbiod)692 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
693 {
694   sbiod->sbiod_pvt = NULL;
695   return 0;
696 }
697 
698 /* We don't need to do anything because libcurl does it already */
699 static int
ldapsb_tls_close(Sockbuf_IO_Desc * sbiod)700 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
701 {
702   (void)sbiod;
703   return 0;
704 }
705 
706 static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)707 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
708 {
709   (void)arg;
710   if(opt == LBER_SB_OPT_DATA_READY) {
711     struct connectdata *conn = sbiod->sbiod_pvt;
712     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
713   }
714   return 0;
715 }
716 
717 static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)718 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
719 {
720   struct connectdata *conn = sbiod->sbiod_pvt;
721   struct ldapconninfo *li = conn->proto.ldapc;
722   ber_slen_t ret;
723   CURLcode err = CURLE_RECV_ERROR;
724 
725   ret = (li->recv)(conn, FIRSTSOCKET, buf, len, &err);
726   if(ret < 0 && err == CURLE_AGAIN) {
727     SET_SOCKERRNO(EWOULDBLOCK);
728   }
729   return ret;
730 }
731 
732 static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)733 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
734 {
735   struct connectdata *conn = sbiod->sbiod_pvt;
736   struct ldapconninfo *li = conn->proto.ldapc;
737   ber_slen_t ret;
738   CURLcode err = CURLE_SEND_ERROR;
739 
740   ret = (li->send)(conn, FIRSTSOCKET, buf, len, &err);
741   if(ret < 0 && err == CURLE_AGAIN) {
742     SET_SOCKERRNO(EWOULDBLOCK);
743   }
744   return ret;
745 }
746 
747 static Sockbuf_IO ldapsb_tls =
748 {
749   ldapsb_tls_setup,
750   ldapsb_tls_remove,
751   ldapsb_tls_ctrl,
752   ldapsb_tls_read,
753   ldapsb_tls_write,
754   ldapsb_tls_close
755 };
756 #endif /* USE_SSL */
757 
758 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
759