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