Revert "Update to 7.40.1"
[platform/upstream/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(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,                           /* 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,                           /* 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(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], *ptr;
196   (void)done;
197
198   strcpy(hosturl, "ldap");
199   ptr = hosturl+4;
200   if(conn->handler->flags & PROTOPT_SSL)
201     *ptr++ = 's';
202   snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
203     conn->host.name, conn->remote_port);
204
205   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
206   if(rc) {
207     failf(data, "LDAP local: Cannot connect to %s, %s",
208           hosturl, ldap_err2string(rc));
209     return CURLE_COULDNT_CONNECT;
210   }
211
212   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
213
214 #ifdef USE_SSL
215   if(conn->handler->flags & PROTOPT_SSL) {
216     CURLcode res;
217     res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
218     if(res)
219       return res;
220   }
221 #endif
222
223   return CURLE_OK;
224 }
225
226 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
227 {
228   ldapconninfo *li = conn->proto.generic;
229   struct SessionHandle *data=conn->data;
230   LDAPMessage *result = NULL;
231   struct timeval tv = {0,1}, *tvp;
232   int rc, err;
233   char *info = NULL;
234
235 #ifdef USE_SSL
236   if(conn->handler->flags & PROTOPT_SSL) {
237     /* Is the SSL handshake complete yet? */
238     if(!li->ssldone) {
239       CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
240                                                   &li->ssldone);
241       if(res || !li->ssldone)
242         return res;
243     }
244     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
245     if(!li->sslinst) {
246       Sockbuf *sb;
247       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
248       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
249       li->sslinst = TRUE;
250       li->recv = conn->recv[FIRSTSOCKET];
251       li->send = conn->send[FIRSTSOCKET];
252     }
253   }
254 #endif
255
256   tvp = &tv;
257
258 retry:
259   if(!li->didbind) {
260     char *binddn;
261     struct berval passwd;
262
263     if(conn->bits.user_passwd) {
264       binddn = conn->user;
265       passwd.bv_val = conn->passwd;
266       passwd.bv_len = strlen(passwd.bv_val);
267     }
268     else {
269       binddn = NULL;
270       passwd.bv_val = NULL;
271       passwd.bv_len = 0;
272     }
273     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
274                         NULL, NULL, &li->msgid);
275     if(rc)
276       return CURLE_LDAP_CANNOT_BIND;
277     li->didbind = TRUE;
278     if(tvp)
279       return CURLE_OK;
280   }
281
282   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result);
283   if(rc < 0) {
284     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
285     return CURLE_LDAP_CANNOT_BIND;
286   }
287   if(rc == 0) {
288     /* timed out */
289     return CURLE_OK;
290   }
291   rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1);
292   if(rc) {
293     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
294     return CURLE_LDAP_CANNOT_BIND;
295   }
296   /* Try to fallback to LDAPv2? */
297   if(err == LDAP_PROTOCOL_ERROR) {
298     int proto;
299     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
300     if(proto == LDAP_VERSION3) {
301       if(info) {
302         ldap_memfree(info);
303         info = NULL;
304       }
305       proto = LDAP_VERSION2;
306       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
307       li->didbind = FALSE;
308       goto retry;
309     }
310   }
311
312   if(err) {
313     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
314           info ? info : "");
315     if(info)
316       ldap_memfree(info);
317     return CURLE_LOGIN_DENIED;
318   }
319
320   if(info)
321     ldap_memfree(info);
322   conn->recv[FIRSTSOCKET] = ldap_recv;
323   *done = TRUE;
324   return CURLE_OK;
325 }
326
327 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
328 {
329   ldapconninfo *li = conn->proto.generic;
330   (void) dead_connection;
331
332   if(li) {
333     if(li->ld) {
334       ldap_unbind_ext(li->ld, NULL, NULL);
335       li->ld = NULL;
336     }
337     conn->proto.generic = NULL;
338     free(li);
339   }
340   return CURLE_OK;
341 }
342
343 static CURLcode ldap_do(struct connectdata *conn, bool *done)
344 {
345   ldapconninfo *li = conn->proto.generic;
346   ldapreqinfo *lr;
347   CURLcode status = CURLE_OK;
348   int rc = 0;
349   LDAPURLDesc *ludp = NULL;
350   int msgid;
351   struct SessionHandle *data=conn->data;
352
353   connkeep(conn, "OpenLDAP do");
354
355   infof(data, "LDAP local: %s\n", data->change.url);
356
357   rc = ldap_url_parse(data->change.url, &ludp);
358   if(rc != LDAP_URL_SUCCESS) {
359     const char *msg = "url parsing problem";
360     status = CURLE_URL_MALFORMAT;
361     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
362       if(rc == LDAP_URL_ERR_MEM)
363         status = CURLE_OUT_OF_MEMORY;
364       msg = url_errs[rc];
365     }
366     failf(conn->data, "LDAP local: %s", msg);
367     return status;
368   }
369
370   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
371                        ludp->lud_filter, ludp->lud_attrs, 0,
372                        NULL, NULL, NULL, 0, &msgid);
373   ldap_free_urldesc(ludp);
374   if(rc != LDAP_SUCCESS) {
375     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
376     return CURLE_LDAP_SEARCH_FAILED;
377   }
378   lr = calloc(1,sizeof(ldapreqinfo));
379   if(!lr)
380     return CURLE_OUT_OF_MEMORY;
381   lr->msgid = msgid;
382   data->req.protop = lr;
383   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
384   *done = TRUE;
385   return CURLE_OK;
386 }
387
388 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
389                           bool premature)
390 {
391   ldapreqinfo *lr = conn->data->req.protop;
392   (void)res;
393   (void)premature;
394
395   if(lr) {
396     /* if there was a search in progress, abandon it */
397     if(lr->msgid) {
398       ldapconninfo *li = conn->proto.generic;
399       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
400       lr->msgid = 0;
401     }
402     conn->data->req.protop = NULL;
403     free(lr);
404   }
405   return CURLE_OK;
406 }
407
408 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
409                          size_t len, CURLcode *err)
410 {
411   ldapconninfo *li = conn->proto.generic;
412   struct SessionHandle *data=conn->data;
413   ldapreqinfo *lr = data->req.protop;
414   int rc, ret;
415   LDAPMessage *result = NULL;
416   LDAPMessage *ent;
417   BerElement *ber = NULL;
418   struct timeval tv = {0,1};
419   (void)len;
420   (void)buf;
421   (void)sockindex;
422
423   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
424   if(rc < 0) {
425     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
426     *err = CURLE_RECV_ERROR;
427     return -1;
428   }
429
430   *err = CURLE_AGAIN;
431   ret = -1;
432
433   /* timed out */
434   if(result == NULL)
435     return ret;
436
437   for(ent = ldap_first_message(li->ld, result); ent;
438     ent = ldap_next_message(li->ld, ent)) {
439     struct berval bv, *bvals, **bvp = &bvals;
440     int binary = 0, msgtype;
441
442     msgtype = ldap_msgtype(ent);
443     if(msgtype == LDAP_RES_SEARCH_RESULT) {
444       int code;
445       char *info = NULL;
446       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
447       if(rc) {
448         failf(data, "LDAP local: search ldap_parse_result %s",
449               ldap_err2string(rc));
450         *err = CURLE_LDAP_SEARCH_FAILED;
451       }
452       else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
453         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
454               info ? info : "");
455         *err = CURLE_LDAP_SEARCH_FAILED;
456       }
457       else {
458         /* successful */
459         if(code == LDAP_SIZELIMIT_EXCEEDED)
460           infof(data, "There are more than %d entries\n", lr->nument);
461         data->req.size = data->req.bytecount;
462         *err = CURLE_OK;
463         ret = 0;
464       }
465       lr->msgid = 0;
466       ldap_memfree(info);
467       break;
468     }
469     else if(msgtype != LDAP_RES_SEARCH_ENTRY)
470       continue;
471
472     lr->nument++;
473     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
474     if(rc < 0) {
475       /* TODO: verify that this is really how this return code should be
476          handled */
477       *err = CURLE_RECV_ERROR;
478       return -1;
479     }
480     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
481     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
482     Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
483     data->req.bytecount += bv.bv_len + 5;
484
485     for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
486       rc == LDAP_SUCCESS;
487       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
488       int i;
489
490       if(bv.bv_val == NULL) break;
491
492       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
493         binary = 1;
494       else
495         binary = 0;
496
497       for(i=0; bvals[i].bv_val != NULL; i++) {
498         int binval = 0;
499         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
500         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
501                           bv.bv_len);
502         Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
503         data->req.bytecount += bv.bv_len + 2;
504
505         if(!binary) {
506           /* check for leading or trailing whitespace */
507           if(ISSPACE(bvals[i].bv_val[0]) ||
508               ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
509             binval = 1;
510           else {
511             /* check for unprintable characters */
512             unsigned int j;
513             for(j=0; j<bvals[i].bv_len; j++)
514               if(!ISPRINT(bvals[i].bv_val[j])) {
515                 binval = 1;
516                 break;
517               }
518           }
519         }
520         if(binary || binval) {
521           char *val_b64 = NULL;
522           size_t val_b64_sz = 0;
523           /* Binary value, encode to base64. */
524           CURLcode error = Curl_base64_encode(data,
525                                               bvals[i].bv_val,
526                                               bvals[i].bv_len,
527                                               &val_b64,
528                                               &val_b64_sz);
529           if(error) {
530             ber_memfree(bvals);
531             ber_free(ber, 0);
532             ldap_msgfree(result);
533             *err = error;
534             return -1;
535           }
536           Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
537           data->req.bytecount += 2;
538           if(val_b64_sz > 0) {
539             Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
540             free(val_b64);
541             data->req.bytecount += val_b64_sz;
542           }
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 */