4d5db4a516605ec4e79d89c148d3512a2093edf7
[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   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->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)
348 {
349   ldapconninfo *li = conn->proto.generic;
350
351   if (li) {
352     if (li->ld) {
353       ldap_unbind_ext(li->ld, NULL, NULL);
354       li->ld = NULL;
355     }
356     conn->proto.generic = NULL;
357     free(li);
358   }
359   return CURLE_OK;
360 }
361
362 static CURLcode ldap_do(struct connectdata *conn, bool *done)
363 {
364   ldapconninfo *li = conn->proto.generic;
365   ldapreqinfo *lr;
366   CURLcode status = CURLE_OK;
367   int rc = 0;
368   LDAPURLDesc *ludp = NULL;
369   int msgid;
370   struct SessionHandle *data=conn->data;
371
372   conn->bits.close = FALSE;
373
374   infof(data, "LDAP local: %s\n", data->change.url);
375
376   rc = ldap_url_parse(data->change.url, &ludp);
377   if (rc != LDAP_URL_SUCCESS) {
378     const char *msg = "url parsing problem";
379     status = CURLE_URL_MALFORMAT;
380     if (rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
381       if (rc == LDAP_URL_ERR_MEM)
382         status = CURLE_OUT_OF_MEMORY;
383       msg = url_errs[rc];
384     }
385     failf(conn->data, "LDAP local: %s", msg);
386     return status;
387   }
388
389   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
390                        ludp->lud_filter, ludp->lud_attrs, 0,
391                        NULL, NULL, NULL, 0, &msgid);
392   ldap_free_urldesc(ludp);
393   if (rc != LDAP_SUCCESS) {
394     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
395     return CURLE_LDAP_SEARCH_FAILED;
396   }
397   lr = calloc(1,sizeof(ldapreqinfo));
398   lr->msgid = msgid;
399   data->state.proto.generic = lr;
400   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
401   *done = TRUE;
402   return CURLE_OK;
403 }
404
405 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
406                           bool premature)
407 {
408   ldapreqinfo *lr = conn->data->state.proto.generic;
409   (void)res;
410   (void)premature;
411
412   if (lr) {
413     /* if there was a search in progress, abandon it */
414     if (lr->msgid) {
415       ldapconninfo *li = conn->proto.generic;
416       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
417       lr->msgid = 0;
418     }
419     conn->data->state.proto.generic = NULL;
420     free(lr);
421   }
422   return CURLE_OK;
423 }
424
425 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
426                          size_t len, CURLcode *err)
427 {
428   ldapconninfo *li = conn->proto.generic;
429   struct SessionHandle *data=conn->data;
430   ldapreqinfo *lr = data->state.proto.generic;
431   int rc, ret;
432   LDAPMessage *result = NULL;
433   LDAPMessage *ent;
434   BerElement *ber = NULL;
435   struct timeval tv = {0,1};
436   (void)len;
437   (void)buf;
438   (void)sockindex;
439
440   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
441   if (rc < 0) {
442     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
443     *err = CURLE_RECV_ERROR;
444     return -1;
445   }
446
447   *err = CURLE_AGAIN;
448   ret = -1;
449
450   /* timed out */
451   if (result == NULL)
452     return ret;
453
454   for (ent = ldap_first_message(li->ld, result); ent;
455     ent = ldap_next_message(li->ld, ent)) {
456     struct berval bv, *bvals, **bvp = &bvals;
457     int binary = 0, msgtype;
458
459     msgtype = ldap_msgtype(ent);
460     if (msgtype == LDAP_RES_SEARCH_RESULT) {
461       int code;
462       char *info = NULL;
463       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
464       if (rc) {
465         failf(data, "LDAP local: search ldap_parse_result %s", ldap_err2string(rc));
466         *err = CURLE_LDAP_SEARCH_FAILED;
467       } else if (code && code != LDAP_SIZELIMIT_EXCEEDED) {
468         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
469           info ? info : "");
470         *err = CURLE_LDAP_SEARCH_FAILED;
471       } else {
472         /* successful */
473         if (code == LDAP_SIZELIMIT_EXCEEDED)
474           infof(data, "There are more than %d entries\n", lr->nument);
475         data->req.size = data->req.bytecount;
476         *err = CURLE_OK;
477         ret = 0;
478       }
479       lr->msgid = 0;
480       ldap_memfree(info);
481       break;
482     } else if (msgtype != LDAP_RES_SEARCH_ENTRY) {
483       continue;
484     }
485
486     lr->nument++;
487     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
488     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
489     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
490     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
491     data->req.bytecount += bv.bv_len + 5;
492
493     for (rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
494       rc == LDAP_SUCCESS;
495       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
496       int i;
497
498       if (bv.bv_val == NULL) break;
499
500       if (bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
501         binary = 1;
502
503       for (i=0; bvals[i].bv_val != NULL; i++) {
504         int binval = 0;
505         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
506         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
507         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
508         data->req.bytecount += bv.bv_len + 2;
509
510         if (!binary) {
511           /* check for leading or trailing whitespace */
512           if (isspace(bvals[i].bv_val[0]) ||
513               isspace(bvals[i].bv_val[bvals[i].bv_len-1])) {
514             binval = 1;
515           } else {
516             /* check for unprintable characters */
517             unsigned int j;
518             for (j=0; j<bvals[i].bv_len; j++)
519               if (!isprint(bvals[i].bv_val[j])) {
520                 binval = 1;
521                 break;
522               }
523           }
524         }
525         if (binary || binval) {
526           char *val_b64;
527           /* Binary value, encode to base64. */
528           size_t val_b64_sz = Curl_base64_encode(data,
529                                             bvals[i].bv_val,
530                                             bvals[i].bv_len,
531                                             &val_b64);
532           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
533           data->req.bytecount += 2;
534           if(val_b64_sz > 0) {
535             Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
536             free(val_b64);
537             data->req.bytecount += val_b64_sz;
538           }
539         } else {
540           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
541           Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
542                             bvals[i].bv_len);
543           data->req.bytecount += bvals[i].bv_len + 1;
544         }
545         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
546         data->req.bytecount++;
547       }
548       ber_memfree(bvals);
549       Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
550       data->req.bytecount++;
551     }
552     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
553     data->req.bytecount++;
554     ber_free(ber, 0);
555   }
556   ldap_msgfree(result);
557   return ret;
558 }
559
560 #ifdef USE_SSL
561 static int
562 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
563 {
564   sbiod->sbiod_pvt = arg;
565   return 0;
566 }
567
568 static int
569 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
570 {
571   sbiod->sbiod_pvt = NULL;
572   return 0;
573 }
574
575 /* We don't need to do anything because libcurl does it already */
576 static int
577 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
578 {
579   (void)sbiod;
580   return 0;
581 }
582
583 static int
584 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
585 {
586   (void)arg;
587   if (opt == LBER_SB_OPT_DATA_READY) {
588     struct connectdata *conn = sbiod->sbiod_pvt;
589     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
590   }
591   return 0;
592 }
593
594 static ber_slen_t
595 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
596 {
597   struct connectdata *conn = sbiod->sbiod_pvt;
598   ldapconninfo *li = conn->proto.generic;
599   ber_slen_t ret;
600   CURLcode err = CURLE_RECV_ERROR;
601
602   ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
603   if (ret < 0 && err == CURLE_AGAIN) {
604     SET_SOCKERRNO(EWOULDBLOCK);
605   }
606   return ret;
607 }
608
609 static ber_slen_t
610 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
611 {
612   struct connectdata *conn = sbiod->sbiod_pvt;
613   ldapconninfo *li = conn->proto.generic;
614   ber_slen_t ret;
615   CURLcode err = CURLE_SEND_ERROR;
616
617   ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
618   if (ret < 0 && err == CURLE_AGAIN) {
619     SET_SOCKERRNO(EWOULDBLOCK);
620   }
621   return ret;
622 }
623
624 static Sockbuf_IO ldapsb_tls =
625 {
626   ldapsb_tls_setup,
627   ldapsb_tls_remove,
628   ldapsb_tls_ctrl,
629   ldapsb_tls_read,
630   ldapsb_tls_write,
631   ldapsb_tls_close
632 };
633 #endif /* USE_SSL */
634
635 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */