openldap header inclusions fix
[platform/upstream/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);
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   /* TODO:
169    * - provide option to choose SASL Binds instead of Simple
170    */
171   return CURLE_OK;
172 }
173
174 #ifdef USE_SSL
175 static Sockbuf_IO ldapsb_tls;
176 #endif
177
178 static CURLcode ldap_connect(struct connectdata *conn, bool *done)
179 {
180   ldapconninfo *li = conn->proto.generic;
181   struct SessionHandle *data=conn->data;
182   int rc, proto = LDAP_VERSION3;
183   char hosturl[1024], *ptr;
184
185   strcpy(hosturl, "ldap");
186   ptr = hosturl+4;
187   if (conn->protocol & PROT_SSL)
188     *ptr++ = 's';
189   snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
190     conn->host.name, conn->port);
191
192   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
193   if (rc) {
194     failf(data, "LDAP local: Cannot connect to %s, %s",
195           hosturl, ldap_err2string(rc));
196     return CURLE_COULDNT_CONNECT;
197   }
198
199   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
200
201 #ifdef USE_SSL
202   if (conn->protocol & PROT_SSL) {
203     CURLcode res;
204     if (data->state.used_interface == Curl_if_easy) {
205       res = Curl_ssl_connect(conn, FIRSTSOCKET);
206       if (res)
207         return res;
208       li->ssldone = TRUE;
209     } else {
210       res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
211       if (res)
212         return res;
213     }
214   }
215 #endif
216
217   if (data->state.used_interface == Curl_if_easy)
218     return ldap_connecting(conn, done);
219
220   return CURLE_OK;
221 }
222
223 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
224 {
225   ldapconninfo *li = conn->proto.generic;
226   struct SessionHandle *data=conn->data;
227   LDAPMessage *result = NULL;
228   struct timeval tv = {0,1}, *tvp;
229   int rc, err;
230   char *info = NULL;
231
232 #ifdef USE_SSL
233   if (conn->protocol & PROT_SSL) {
234     /* Is the SSL handshake complete yet? */
235     if (!li->ssldone) {
236       CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
237       if (res || !li->ssldone)
238         return res;
239     }
240     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
241     if (!li->sslinst) {
242       Sockbuf *sb;
243       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
244       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
245       li->sslinst = TRUE;
246       li->recv = conn->recv[FIRSTSOCKET];
247       li->send = conn->send[FIRSTSOCKET];
248     }
249   }
250 #endif
251
252   if (data->state.used_interface == Curl_if_easy)
253     tvp = NULL;    /* let ldap_result block indefinitely */
254   else
255     tvp = &tv;
256
257 retry:
258   if (!li->didbind) {
259     char *binddn;
260     struct berval passwd;
261
262     if (conn->bits.user_passwd) {
263       binddn = conn->user;
264       passwd.bv_val = conn->passwd;
265       passwd.bv_len = strlen(passwd.bv_val);
266     } else {
267       binddn = NULL;
268       passwd.bv_val = NULL;
269       passwd.bv_len = 0;
270     }
271     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
272                         NULL, NULL, &li->msgid);
273     if (rc)
274       return CURLE_LDAP_CANNOT_BIND;
275     li->didbind = TRUE;
276     if (tvp)
277       return CURLE_OK;
278   }
279
280   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result);
281   if (rc < 0) {
282     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
283     return CURLE_LDAP_CANNOT_BIND;
284   }
285   if (rc == 0) {
286     /* timed out */
287     return CURLE_OK;
288   }
289   rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1);
290   if (rc) {
291     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
292     return CURLE_LDAP_CANNOT_BIND;
293   }
294   /* Try to fallback to LDAPv2? */
295   if (err == LDAP_PROTOCOL_ERROR) {
296     int proto;
297     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
298     if (proto == LDAP_VERSION3) {
299       ldap_memfree(info);
300       proto = LDAP_VERSION2;
301       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
302       li->didbind = FALSE;
303       goto retry;
304     }
305   }
306
307   if (err) {
308     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
309           info ? info : "");
310     return CURLE_LOGIN_DENIED;
311   }
312   conn->recv[FIRSTSOCKET] = ldap_recv;
313   *done = TRUE;
314   return CURLE_OK;
315 }
316
317 static CURLcode ldap_disconnect(struct connectdata *conn)
318 {
319   ldapconninfo *li = conn->proto.generic;
320
321   if (li) {
322     if (li->ld) {
323       ldap_unbind_ext(li->ld, NULL, NULL);
324       li->ld = NULL;
325     }
326     conn->proto.generic = NULL;
327     free(li);
328   }
329   return CURLE_OK;
330 }
331
332 static CURLcode ldap_do(struct connectdata *conn, bool *done)
333 {
334   ldapconninfo *li = conn->proto.generic;
335   ldapreqinfo *lr;
336   CURLcode status = CURLE_OK;
337   int rc = 0;
338   LDAPURLDesc *ludp = NULL;
339   int msgid;
340   struct SessionHandle *data=conn->data;
341
342   conn->bits.close = FALSE;
343
344   infof(data, "LDAP local: %s\n", data->change.url);
345
346   rc = ldap_url_parse(data->change.url, &ludp);
347   if (rc != LDAP_URL_SUCCESS) {
348     const char *msg = "url parsing problem";
349     status = CURLE_URL_MALFORMAT;
350     if (rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
351       if (rc == LDAP_URL_ERR_MEM)
352         status = CURLE_OUT_OF_MEMORY;
353       msg = url_errs[rc];
354     }
355     failf(conn->data, "LDAP local: %s", msg);
356     return status;
357   }
358
359   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
360                        ludp->lud_filter, ludp->lud_attrs, 0,
361                        NULL, NULL, NULL, 0, &msgid);
362   ldap_free_urldesc(ludp);
363   if (rc != LDAP_SUCCESS) {
364     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
365     return CURLE_LDAP_SEARCH_FAILED;
366   }
367   lr = calloc(1,sizeof(ldapreqinfo));
368   lr->msgid = msgid;
369   data->state.proto.generic = lr;
370   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
371   *done = TRUE;
372   return CURLE_OK;
373 }
374
375 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
376                           bool premature)
377 {
378   ldapreqinfo *lr = conn->data->state.proto.generic;
379   (void)res;
380   (void)premature;
381
382   if (lr) {
383     /* if there was a search in progress, abandon it */
384     if (lr->msgid) {
385       ldapconninfo *li = conn->proto.generic;
386       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
387       lr->msgid = 0;
388     }
389     conn->data->state.proto.generic = NULL;
390     free(lr);
391   }
392   return CURLE_OK;
393 }
394
395 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
396                          size_t len, CURLcode *err)
397 {
398   ldapconninfo *li = conn->proto.generic;
399   struct SessionHandle *data=conn->data;
400   ldapreqinfo *lr = data->state.proto.generic;
401   int rc, ret;
402   LDAPMessage *result = NULL;
403   LDAPMessage *ent;
404   BerElement *ber = NULL;
405   struct timeval tv = {0,1};
406   (void)len;
407   (void)buf;
408   (void)sockindex;
409
410   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
411   if (rc < 0) {
412     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
413     *err = CURLE_RECV_ERROR;
414     return -1;
415   }
416
417   *err = CURLE_AGAIN;
418   ret = -1;
419
420   /* timed out */
421   if (result == NULL)
422     return ret;
423
424   for (ent = ldap_first_message(li->ld, result); ent;
425     ent = ldap_next_message(li->ld, ent)) {
426     struct berval bv, *bvals, **bvp = &bvals;
427     int binary = 0, msgtype;
428
429     msgtype = ldap_msgtype(ent);
430     if (msgtype == LDAP_RES_SEARCH_RESULT) {
431       int code;
432       char *info = NULL;
433       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
434       if (rc) {
435         failf(data, "LDAP local: search ldap_parse_result %s", ldap_err2string(rc));
436         *err = CURLE_LDAP_SEARCH_FAILED;
437       } else if (code && code != LDAP_SIZELIMIT_EXCEEDED) {
438         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
439           info ? info : "");
440         *err = CURLE_LDAP_SEARCH_FAILED;
441       } else {
442         /* successful */
443         if (code == LDAP_SIZELIMIT_EXCEEDED)
444           infof(data, "There are more than %d entries\n", lr->nument);
445         data->req.size = data->req.bytecount;
446         *err = CURLE_OK;
447         ret = 0;
448       }
449       lr->msgid = 0;
450       ldap_memfree(info);
451       break;
452     } else if (msgtype != LDAP_RES_SEARCH_ENTRY) {
453       continue;
454     }
455
456     lr->nument++;
457     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
458     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
459     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
460     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
461     data->req.bytecount += bv.bv_len + 5;
462
463     for (rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
464       rc == LDAP_SUCCESS;
465       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
466       int i;
467
468       if (bv.bv_val == NULL) break;
469
470       if (bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
471         binary = 1;
472
473       for (i=0; bvals[i].bv_val != NULL; i++) {
474         int binval = 0;
475         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
476         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
477         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
478         data->req.bytecount += bv.bv_len + 2;
479
480         if (!binary) {
481           /* check for leading or trailing whitespace */
482           if (isspace(bvals[i].bv_val[0]) ||
483               isspace(bvals[i].bv_val[bvals[i].bv_len-1])) {
484             binval = 1;
485           } else {
486             /* check for unprintable characters */
487             unsigned int j;
488             for (j=0; j<bvals[i].bv_len; j++)
489               if (!isprint(bvals[i].bv_val[j])) {
490                 binval = 1;
491                 break;
492               }
493           }
494         }
495         if (binary || binval) {
496           char *val_b64;
497           /* Binary value, encode to base64. */
498           size_t val_b64_sz = Curl_base64_encode(data,
499                                             bvals[i].bv_val,
500                                             bvals[i].bv_len,
501                                             &val_b64);
502           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
503           data->req.bytecount += 2;
504           if(val_b64_sz > 0) {
505             Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
506             free(val_b64);
507             data->req.bytecount += val_b64_sz;
508           }
509         } else {
510           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
511           Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
512                             bvals[i].bv_len);
513           data->req.bytecount += bvals[i].bv_len + 1;
514         }
515         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
516         data->req.bytecount++;
517       }
518       ber_memfree(bvals);
519       Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
520       data->req.bytecount++;
521     }
522     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
523     data->req.bytecount++;
524     ber_free(ber, 0);
525   }
526   ldap_msgfree(result);
527   return ret;
528 }
529
530 #ifdef USE_SSL
531 static int
532 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
533 {
534   sbiod->sbiod_pvt = arg;
535   return 0;
536 }
537
538 static int
539 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
540 {
541   sbiod->sbiod_pvt = NULL;
542   return 0;
543 }
544
545 /* We don't need to do anything because libcurl does it already */
546 static int
547 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
548 {
549   (void)sbiod;
550   return 0;
551 }
552
553 static int
554 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
555 {
556   (void)arg;
557   if (opt == LBER_SB_OPT_DATA_READY) {
558     struct connectdata *conn = sbiod->sbiod_pvt;
559     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
560   }
561   return 0;
562 }
563
564 static ber_slen_t
565 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
566 {
567   struct connectdata *conn = sbiod->sbiod_pvt;
568   ldapconninfo *li = conn->proto.generic;
569   ber_slen_t ret;
570   CURLcode err = CURLE_RECV_ERROR;
571
572   ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
573   if (ret < 0 && err == CURLE_AGAIN) {
574     SET_SOCKERRNO(EWOULDBLOCK);
575   }
576   return ret;
577 }
578
579 static ber_slen_t
580 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
581 {
582   struct connectdata *conn = sbiod->sbiod_pvt;
583   ldapconninfo *li = conn->proto.generic;
584   ber_slen_t ret;
585   CURLcode err = CURLE_SEND_ERROR;
586
587   ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
588   if (ret < 0 && err == CURLE_AGAIN) {
589     SET_SOCKERRNO(EWOULDBLOCK);
590   }
591   return ret;
592 }
593
594 static Sockbuf_IO ldapsb_tls =
595 {
596   ldapsb_tls_setup,
597   ldapsb_tls_remove,
598   ldapsb_tls_ctrl,
599   ldapsb_tls_read,
600   ldapsb_tls_write,
601   ldapsb_tls_close
602 };
603 #endif /* USE_SSL */
604
605 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */