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