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