Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                      _   _ ____  _
      3  *  Project         ___| | | |  _ \| |
      4  *                 / __| | | | |_) | |
      5  *                | (__| |_| |  _ <| |___
      6  *                 \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2010, Howard Chu, <hyc (at) openldap.org>
      9  * Copyright (C) 2011 - 2019, 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   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 
    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(data, FIRSTSOCKET, -1, FALSE, -1);
    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;
    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
    690 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
    691 {
    692   sbiod->sbiod_pvt = arg;
    693   return 0;
    694 }
    695 
    696 static int
    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
    705 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
    706 {
    707   (void)sbiod;
    708   return 0;
    709 }
    710 
    711 static int
    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
    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
    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