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