openldap.c revision e6cd738ed3716c02557fb3a47515244e949ade39
1/***************************************************************************
2 *                      _   _ ____  _
3 *  Project         ___| | | |  _ \| |
4 *                 / __| | | | |_) | |
5 *                | (__| |_| |  _ <| |___
6 *                 \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
9 * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at 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
56extern int ldap_pvt_url_scheme2proto(const char *);
57extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
58                        LDAP **ld);
59#endif
60
61static CURLcode ldap_setup_connection(struct connectdata *conn);
62static CURLcode ldap_do(struct connectdata *conn, bool *done);
63static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
64static CURLcode ldap_connect(struct connectdata *conn, bool *done);
65static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
66static CURLcode ldap_disconnect(struct connectdata *conn, bool dead);
67
68static Curl_recv ldap_recv;
69
70/*
71 * LDAP protocol handler.
72 */
73
74const 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
99const 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
120static 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
134typedef 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
145typedef struct ldapreqinfo {
146  int msgid;
147  int nument;
148} ldapreqinfo;
149
150static 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
186static Sockbuf_IO ldapsb_tls;
187#endif
188
189static 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
227static 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
260retry:
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
332static 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
348static 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
393static 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
415static 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
610static int
611ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
612{
613  sbiod->sbiod_pvt = arg;
614  return 0;
615}
616
617static int
618ldapsb_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 */
625static int
626ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
627{
628  (void)sbiod;
629  return 0;
630}
631
632static int
633ldapsb_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
643static ber_slen_t
644ldapsb_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
658static ber_slen_t
659ldapsb_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
673static 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