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