Git init
[external/curl.git] / lib / openldap.c
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                 \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "setup.h"
24
25 #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
26
27 /*
28  * Notice that USE_OPENLDAP is only a source code selection switch. When
29  * libcurl is built with USE_OPENLDAP defined the libcurl source code that
30  * gets compiled is the code from openldap.c, otherwise the code that gets
31  * compiled is the code from ldap.c.
32  *
33  * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
34  * might be required for compilation and runtime. In order to use ancient
35  * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
36  */
37
38 #include <ldap.h>
39
40 #include "urldata.h"
41 #include <curl/curl.h>
42 #include "sendf.h"
43 #include "sslgen.h"
44 #include "transfer.h"
45 #include "curl_ldap.h"
46 #include "curl_memory.h"
47 #include "curl_base64.h"
48
49 #define _MPRINTF_REPLACE /* use our functions only */
50 #include <curl/mprintf.h>
51
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, LDAP **ld);
57 #endif
58
59 static CURLcode ldap_setup(struct connectdata *conn);
60 static CURLcode ldap_do(struct connectdata *conn, bool *done);
61 static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
62 static CURLcode ldap_connect(struct connectdata *conn, bool *done);
63 static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
64 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection);
65
66 static Curl_recv ldap_recv;
67
68 /*
69  * LDAP protocol handler.
70  */
71
72 const struct Curl_handler Curl_handler_ldap = {
73   "LDAP",                               /* scheme */
74   ldap_setup,                           /* setup_connection */
75   ldap_do,                              /* do_it */
76   ldap_done,                            /* done */
77   ZERO_NULL,                            /* do_more */
78   ldap_connect,                         /* connect_it */
79   ldap_connecting,                      /* connecting */
80   ZERO_NULL,                            /* doing */
81   ZERO_NULL,                            /* proto_getsock */
82   ZERO_NULL,                            /* doing_getsock */
83   ZERO_NULL,                            /* perform_getsock */
84   ldap_disconnect,                      /* disconnect */
85   PORT_LDAP,                            /* defport */
86   PROT_LDAP                             /* protocol */
87 };
88
89 #ifdef USE_SSL
90 /*
91  * LDAPS protocol handler.
92  */
93
94 const struct Curl_handler Curl_handler_ldaps = {
95   "LDAPS",                              /* scheme */
96   ldap_setup,                           /* setup_connection */
97   ldap_do,                              /* do_it */
98   ldap_done,                            /* done */
99   ZERO_NULL,                            /* do_more */
100   ldap_connect,                         /* connect_it */
101   ldap_connecting,                      /* connecting */
102   ZERO_NULL,                            /* doing */
103   ZERO_NULL,                            /* proto_getsock */
104   ZERO_NULL,                            /* doing_getsock */
105   ZERO_NULL,                            /* perform_getsock */
106   ldap_disconnect,                      /* disconnect */
107   PORT_LDAPS,                           /* defport */
108   PROT_LDAP | PROT_SSL                  /* protocol */
109 };
110 #endif
111
112 static const char *url_errs[] = {
113   "success",
114   "out of memory",
115   "bad parameter",
116   "unrecognized scheme",
117   "unbalanced delimiter",
118   "bad URL",
119   "bad host or port",
120   "bad or missing attributes",
121   "bad or missing scope",
122   "bad or missing filter",
123   "bad or missing extensions"
124 };
125
126 typedef struct ldapconninfo {
127   LDAP *ld;
128   Curl_recv *recv;  /* for stacking SSL handler */
129   Curl_send *send;
130   int proto;
131   int msgid;
132   bool ssldone;
133   bool sslinst;
134   bool didbind;
135 } ldapconninfo;
136
137 typedef struct ldapreqinfo {
138   int msgid;
139   int nument;
140 } ldapreqinfo;
141
142 static CURLcode ldap_setup(struct connectdata *conn)
143 {
144   ldapconninfo *li;
145   LDAPURLDesc *lud;
146   struct SessionHandle *data=conn->data;
147   int rc, proto;
148   CURLcode status;
149
150   rc = ldap_url_parse(data->change.url, &lud);
151   if (rc != LDAP_URL_SUCCESS) {
152     const char *msg = "url parsing problem";
153     status = CURLE_URL_MALFORMAT;
154     if (rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
155       if (rc == LDAP_URL_ERR_MEM)
156         status = CURLE_OUT_OF_MEMORY;
157       msg = url_errs[rc];
158     }
159     failf(conn->data, "LDAP local: %s", msg);
160     return status;
161   }
162   proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
163   ldap_free_urldesc(lud);
164
165   li = calloc(1, sizeof(ldapconninfo));
166   li->proto = proto;
167   conn->proto.generic = li;
168   conn->bits.close = FALSE;
169   /* TODO:
170    * - provide option to choose SASL Binds instead of Simple
171    */
172   return CURLE_OK;
173 }
174
175 #ifdef USE_SSL
176 static Sockbuf_IO ldapsb_tls;
177 #endif
178
179 static CURLcode ldap_connect(struct connectdata *conn, bool *done)
180 {
181   ldapconninfo *li = conn->proto.generic;
182   struct SessionHandle *data=conn->data;
183   int rc, proto = LDAP_VERSION3;
184   char hosturl[1024], *ptr;
185
186   strcpy(hosturl, "ldap");
187   ptr = hosturl+4;
188   if (conn->protocol & PROT_SSL)
189     *ptr++ = 's';
190   snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
191     conn->host.name, conn->remote_port);
192
193   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
194   if (rc) {
195     failf(data, "LDAP local: Cannot connect to %s, %s",
196           hosturl, ldap_err2string(rc));
197     return CURLE_COULDNT_CONNECT;
198   }
199
200   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
201
202 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
203   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
204     /* for LDAP over HTTP proxy */
205     struct HTTP http_proxy;
206     ldapconninfo *li_save;
207     CURLcode result;
208
209     /* BLOCKING */
210     /* We want "seamless" LDAP operations through HTTP proxy tunnel */
211
212     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
213      * conn->proto.http; we want LDAP through HTTP and we have to change the
214      * member temporarily for connecting to the HTTP proxy. After
215      * Curl_proxyCONNECT we have to set back the member to the original struct
216      * LDAP pointer
217      */
218     li_save = data->state.proto.generic;
219     memset(&http_proxy, 0, sizeof(http_proxy));
220     data->state.proto.http = &http_proxy;
221     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
222                                conn->host.name, conn->remote_port);
223
224     data->state.proto.generic = li_save;
225
226     if(CURLE_OK != result)
227       return result;
228   }
229 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
230
231 #ifdef USE_SSL
232   if (conn->protocol & PROT_SSL) {
233     CURLcode res;
234     if (data->state.used_interface == Curl_if_easy) {
235       res = Curl_ssl_connect(conn, FIRSTSOCKET);
236       if (res)
237         return res;
238       li->ssldone = TRUE;
239     } else {
240       res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
241       if (res)
242         return res;
243     }
244   }
245 #endif
246
247   if (data->state.used_interface == Curl_if_easy)
248     return ldap_connecting(conn, done);
249
250   return CURLE_OK;
251 }
252
253 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
254 {
255   ldapconninfo *li = conn->proto.generic;
256   struct SessionHandle *data=conn->data;
257   LDAPMessage *result = NULL;
258   struct timeval tv = {0,1}, *tvp;
259   int rc, err;
260   char *info = NULL;
261
262 #ifdef USE_SSL
263   if (conn->protocol & PROT_SSL) {
264     /* Is the SSL handshake complete yet? */
265     if (!li->ssldone) {
266       CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
267       if (res || !li->ssldone)
268         return res;
269     }
270     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
271     if (!li->sslinst) {
272       Sockbuf *sb;
273       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
274       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
275       li->sslinst = TRUE;
276       li->recv = conn->recv[FIRSTSOCKET];
277       li->send = conn->send[FIRSTSOCKET];
278     }
279   }
280 #endif
281
282   if (data->state.used_interface == Curl_if_easy)
283     tvp = NULL;    /* let ldap_result block indefinitely */
284   else
285     tvp = &tv;
286
287 retry:
288   if (!li->didbind) {
289     char *binddn;
290     struct berval passwd;
291
292     if (conn->bits.user_passwd) {
293       binddn = conn->user;
294       passwd.bv_val = conn->passwd;
295       passwd.bv_len = strlen(passwd.bv_val);
296     } else {
297       binddn = NULL;
298       passwd.bv_val = NULL;
299       passwd.bv_len = 0;
300     }
301     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
302                         NULL, NULL, &li->msgid);
303     if (rc)
304       return CURLE_LDAP_CANNOT_BIND;
305     li->didbind = TRUE;
306     if (tvp)
307       return CURLE_OK;
308   }
309
310   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result);
311   if (rc < 0) {
312     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
313     return CURLE_LDAP_CANNOT_BIND;
314   }
315   if (rc == 0) {
316     /* timed out */
317     return CURLE_OK;
318   }
319   rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1);
320   if (rc) {
321     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
322     return CURLE_LDAP_CANNOT_BIND;
323   }
324   /* Try to fallback to LDAPv2? */
325   if (err == LDAP_PROTOCOL_ERROR) {
326     int proto;
327     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
328     if (proto == LDAP_VERSION3) {
329       ldap_memfree(info);
330       proto = LDAP_VERSION2;
331       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
332       li->didbind = FALSE;
333       goto retry;
334     }
335   }
336
337   if (err) {
338     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
339           info ? info : "");
340     return CURLE_LOGIN_DENIED;
341   }
342   conn->recv[FIRSTSOCKET] = ldap_recv;
343   *done = TRUE;
344   return CURLE_OK;
345 }
346
347 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
348 {
349   ldapconninfo *li = conn->proto.generic;
350   (void) dead_connection;
351
352   if (li) {
353     if (li->ld) {
354       ldap_unbind_ext(li->ld, NULL, NULL);
355       li->ld = NULL;
356     }
357     conn->proto.generic = NULL;
358     free(li);
359   }
360   return CURLE_OK;
361 }
362
363 static CURLcode ldap_do(struct connectdata *conn, bool *done)
364 {
365   ldapconninfo *li = conn->proto.generic;
366   ldapreqinfo *lr;
367   CURLcode status = CURLE_OK;
368   int rc = 0;
369   LDAPURLDesc *ludp = NULL;
370   int msgid;
371   struct SessionHandle *data=conn->data;
372
373   conn->bits.close = FALSE;
374
375   infof(data, "LDAP local: %s\n", data->change.url);
376
377   rc = ldap_url_parse(data->change.url, &ludp);
378   if (rc != LDAP_URL_SUCCESS) {
379     const char *msg = "url parsing problem";
380     status = CURLE_URL_MALFORMAT;
381     if (rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
382       if (rc == LDAP_URL_ERR_MEM)
383         status = CURLE_OUT_OF_MEMORY;
384       msg = url_errs[rc];
385     }
386     failf(conn->data, "LDAP local: %s", msg);
387     return status;
388   }
389
390   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
391                        ludp->lud_filter, ludp->lud_attrs, 0,
392                        NULL, NULL, NULL, 0, &msgid);
393   ldap_free_urldesc(ludp);
394   if (rc != LDAP_SUCCESS) {
395     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
396     return CURLE_LDAP_SEARCH_FAILED;
397   }
398   lr = calloc(1,sizeof(ldapreqinfo));
399   lr->msgid = msgid;
400   data->state.proto.generic = lr;
401   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
402   *done = TRUE;
403   return CURLE_OK;
404 }
405
406 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
407                           bool premature)
408 {
409   ldapreqinfo *lr = conn->data->state.proto.generic;
410   (void)res;
411   (void)premature;
412
413   if (lr) {
414     /* if there was a search in progress, abandon it */
415     if (lr->msgid) {
416       ldapconninfo *li = conn->proto.generic;
417       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
418       lr->msgid = 0;
419     }
420     conn->data->state.proto.generic = NULL;
421     free(lr);
422   }
423   return CURLE_OK;
424 }
425
426 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
427                          size_t len, CURLcode *err)
428 {
429   ldapconninfo *li = conn->proto.generic;
430   struct SessionHandle *data=conn->data;
431   ldapreqinfo *lr = data->state.proto.generic;
432   int rc, ret;
433   LDAPMessage *result = NULL;
434   LDAPMessage *ent;
435   BerElement *ber = NULL;
436   struct timeval tv = {0,1};
437   (void)len;
438   (void)buf;
439   (void)sockindex;
440
441   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
442   if (rc < 0) {
443     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
444     *err = CURLE_RECV_ERROR;
445     return -1;
446   }
447
448   *err = CURLE_AGAIN;
449   ret = -1;
450
451   /* timed out */
452   if (result == NULL)
453     return ret;
454
455   for (ent = ldap_first_message(li->ld, result); ent;
456     ent = ldap_next_message(li->ld, ent)) {
457     struct berval bv, *bvals, **bvp = &bvals;
458     int binary = 0, msgtype;
459
460     msgtype = ldap_msgtype(ent);
461     if (msgtype == LDAP_RES_SEARCH_RESULT) {
462       int code;
463       char *info = NULL;
464       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
465       if (rc) {
466         failf(data, "LDAP local: search ldap_parse_result %s", ldap_err2string(rc));
467         *err = CURLE_LDAP_SEARCH_FAILED;
468       } else if (code && code != LDAP_SIZELIMIT_EXCEEDED) {
469         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
470           info ? info : "");
471         *err = CURLE_LDAP_SEARCH_FAILED;
472       } else {
473         /* successful */
474         if (code == LDAP_SIZELIMIT_EXCEEDED)
475           infof(data, "There are more than %d entries\n", lr->nument);
476         data->req.size = data->req.bytecount;
477         *err = CURLE_OK;
478         ret = 0;
479       }
480       lr->msgid = 0;
481       ldap_memfree(info);
482       break;
483     } else if (msgtype != LDAP_RES_SEARCH_ENTRY) {
484       continue;
485     }
486
487     lr->nument++;
488     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
489     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
490     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
491     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
492     data->req.bytecount += bv.bv_len + 5;
493
494     for (rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
495       rc == LDAP_SUCCESS;
496       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
497       int i;
498
499       if (bv.bv_val == NULL) break;
500
501       if (bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
502         binary = 1;
503       else
504         binary = 0;
505
506       for (i=0; bvals[i].bv_val != NULL; i++) {
507         int binval = 0;
508         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
509         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
510         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
511         data->req.bytecount += bv.bv_len + 2;
512
513         if (!binary) {
514           /* check for leading or trailing whitespace */
515           if (ISSPACE(bvals[i].bv_val[0]) ||
516               ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1])) {
517             binval = 1;
518           } else {
519             /* check for unprintable characters */
520             unsigned int j;
521             for (j=0; j<bvals[i].bv_len; j++)
522               if (!ISPRINT(bvals[i].bv_val[j])) {
523                 binval = 1;
524                 break;
525               }
526           }
527         }
528         if (binary || binval) {
529           char *val_b64;
530           /* Binary value, encode to base64. */
531           size_t val_b64_sz = Curl_base64_encode(data,
532                                             bvals[i].bv_val,
533                                             bvals[i].bv_len,
534                                             &val_b64);
535           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
536           data->req.bytecount += 2;
537           if(val_b64_sz > 0) {
538             Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
539             free(val_b64);
540             data->req.bytecount += val_b64_sz;
541           }
542         } else {
543           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
544           Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
545                             bvals[i].bv_len);
546           data->req.bytecount += bvals[i].bv_len + 1;
547         }
548         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
549         data->req.bytecount++;
550       }
551       ber_memfree(bvals);
552       Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
553       data->req.bytecount++;
554     }
555     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
556     data->req.bytecount++;
557     ber_free(ber, 0);
558   }
559   ldap_msgfree(result);
560   return ret;
561 }
562
563 #ifdef USE_SSL
564 static int
565 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
566 {
567   sbiod->sbiod_pvt = arg;
568   return 0;
569 }
570
571 static int
572 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
573 {
574   sbiod->sbiod_pvt = NULL;
575   return 0;
576 }
577
578 /* We don't need to do anything because libcurl does it already */
579 static int
580 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
581 {
582   (void)sbiod;
583   return 0;
584 }
585
586 static int
587 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
588 {
589   (void)arg;
590   if (opt == LBER_SB_OPT_DATA_READY) {
591     struct connectdata *conn = sbiod->sbiod_pvt;
592     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
593   }
594   return 0;
595 }
596
597 static ber_slen_t
598 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
599 {
600   struct connectdata *conn = sbiod->sbiod_pvt;
601   ldapconninfo *li = conn->proto.generic;
602   ber_slen_t ret;
603   CURLcode err = CURLE_RECV_ERROR;
604
605   ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
606   if (ret < 0 && err == CURLE_AGAIN) {
607     SET_SOCKERRNO(EWOULDBLOCK);
608   }
609   return ret;
610 }
611
612 static ber_slen_t
613 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
614 {
615   struct connectdata *conn = sbiod->sbiod_pvt;
616   ldapconninfo *li = conn->proto.generic;
617   ber_slen_t ret;
618   CURLcode err = CURLE_SEND_ERROR;
619
620   ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
621   if (ret < 0 && err == CURLE_AGAIN) {
622     SET_SOCKERRNO(EWOULDBLOCK);
623   }
624   return ret;
625 }
626
627 static Sockbuf_IO ldapsb_tls =
628 {
629   ldapsb_tls_setup,
630   ldapsb_tls_remove,
631   ldapsb_tls_ctrl,
632   ldapsb_tls_read,
633   ldapsb_tls_write,
634   ldapsb_tls_close
635 };
636 #endif /* USE_SSL */
637
638 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */