Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                      _   _ ____  _
      3  *  Project         ___| | | |  _ \| |
      4  *                 / __| | | | |_) | |
      5  *                | (__| |_| |  _ <| |___
      6  *                 \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2010, 2017, Howard Chu, <hyc (at) openldap.org>
      9  * Copyright (C) 2011 - 2016, Daniel Stenberg, <daniel (at) 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 
    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 
    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   snprintf(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 
    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 
    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 
    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(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
    418   *done = TRUE;
    419   return CURLE_OK;
    420 }
    421 
    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 
    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, **bvp = &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, bvp);
    539       rc == LDAP_SUCCESS;
    540       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
    541       int i;
    542 
    543       if(bv.bv_val == NULL) break;
    544 
    545       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
    546         binary = 1;
    547       else
    548         binary = 0;
    549 
    550       for(i = 0; bvals[i].bv_val != NULL; i++) {
    551         int binval = 0;
    552         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
    553         if(writeerr) {
    554           *err = writeerr;
    555           return -1;
    556         }
    557 
    558        writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
    559                                     bv.bv_len);
    560        if(writeerr) {
    561          *err = writeerr;
    562          return -1;
    563        }
    564 
    565         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
    566        if(writeerr) {
    567          *err = writeerr;
    568          return -1;
    569        }
    570         data->req.bytecount += bv.bv_len + 2;
    571 
    572         if(!binary) {
    573           /* check for leading or trailing whitespace */
    574           if(ISSPACE(bvals[i].bv_val[0]) ||
    575               ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
    576             binval = 1;
    577           else {
    578             /* check for unprintable characters */
    579             unsigned int j;
    580             for(j = 0; j<bvals[i].bv_len; j++)
    581               if(!ISPRINT(bvals[i].bv_val[j])) {
    582                 binval = 1;
    583                 break;
    584               }
    585           }
    586         }
    587         if(binary || binval) {
    588           char *val_b64 = NULL;
    589           size_t val_b64_sz = 0;
    590           /* Binary value, encode to base64. */
    591           CURLcode error = Curl_base64_encode(data,
    592                                               bvals[i].bv_val,
    593                                               bvals[i].bv_len,
    594                                               &val_b64,
    595                                               &val_b64_sz);
    596           if(error) {
    597             ber_memfree(bvals);
    598             ber_free(ber, 0);
    599             ldap_msgfree(msg);
    600             *err = error;
    601             return -1;
    602           }
    603           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY,
    604                                        (char *)": ", 2);
    605           if(writeerr) {
    606             *err = writeerr;
    607             return -1;
    608           }
    609 
    610           data->req.bytecount += 2;
    611           if(val_b64_sz > 0) {
    612             writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
    613                                      val_b64_sz);
    614             if(writeerr) {
    615               *err = writeerr;
    616               return -1;
    617             }
    618             free(val_b64);
    619             data->req.bytecount += val_b64_sz;
    620           }
    621         }
    622         else {
    623           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
    624           if(writeerr) {
    625             *err = writeerr;
    626             return -1;
    627           }
    628 
    629           writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
    630                                        bvals[i].bv_len);
    631           if(writeerr) {
    632             *err = writeerr;
    633             return -1;
    634           }
    635 
    636           data->req.bytecount += bvals[i].bv_len + 1;
    637         }
    638         writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
    639         if(writeerr) {
    640           *err = writeerr;
    641           return -1;
    642         }
    643 
    644         data->req.bytecount++;
    645       }
    646       ber_memfree(bvals);
    647       writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
    648       if(writeerr) {
    649         *err = writeerr;
    650         return -1;
    651       }
    652       data->req.bytecount++;
    653     }
    654     writeerr = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
    655     if(writeerr) {
    656       *err = writeerr;
    657       return -1;
    658     }
    659     data->req.bytecount++;
    660     ber_free(ber, 0);
    661   }
    662   ldap_msgfree(msg);
    663   return ret;
    664 }
    665 
    666 #ifdef USE_SSL
    667 static int
    668 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
    669 {
    670   sbiod->sbiod_pvt = arg;
    671   return 0;
    672 }
    673 
    674 static int
    675 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
    676 {
    677   sbiod->sbiod_pvt = NULL;
    678   return 0;
    679 }
    680 
    681 /* We don't need to do anything because libcurl does it already */
    682 static int
    683 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
    684 {
    685   (void)sbiod;
    686   return 0;
    687 }
    688 
    689 static int
    690 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
    691 {
    692   (void)arg;
    693   if(opt == LBER_SB_OPT_DATA_READY) {
    694     struct connectdata *conn = sbiod->sbiod_pvt;
    695     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
    696   }
    697   return 0;
    698 }
    699 
    700 static ber_slen_t
    701 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
    702 {
    703   struct connectdata *conn = sbiod->sbiod_pvt;
    704   ldapconninfo *li = conn->proto.generic;
    705   ber_slen_t ret;
    706   CURLcode err = CURLE_RECV_ERROR;
    707 
    708   ret = (li->recv)(conn, FIRSTSOCKET, buf, len, &err);
    709   if(ret < 0 && err == CURLE_AGAIN) {
    710     SET_SOCKERRNO(EWOULDBLOCK);
    711   }
    712   return ret;
    713 }
    714 
    715 static ber_slen_t
    716 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
    717 {
    718   struct connectdata *conn = sbiod->sbiod_pvt;
    719   ldapconninfo *li = conn->proto.generic;
    720   ber_slen_t ret;
    721   CURLcode err = CURLE_SEND_ERROR;
    722 
    723   ret = (li->send)(conn, FIRSTSOCKET, buf, len, &err);
    724   if(ret < 0 && err == CURLE_AGAIN) {
    725     SET_SOCKERRNO(EWOULDBLOCK);
    726   }
    727   return ret;
    728 }
    729 
    730 static Sockbuf_IO ldapsb_tls =
    731 {
    732   ldapsb_tls_setup,
    733   ldapsb_tls_remove,
    734   ldapsb_tls_ctrl,
    735   ldapsb_tls_read,
    736   ldapsb_tls_write,
    737   ldapsb_tls_close
    738 };
    739 #endif /* USE_SSL */
    740 
    741 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
    742