Imported Upstream version 7.44.0
[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 - 2015, 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_base64.h"
48 #include "connect.h"
49 #include "curl_printf.h"
50
51 /* The last #include files should be: */
52 #include "curl_memory.h"
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_connection(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_connection,                /* 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_connection,                /* 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_connection(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   connkeep(conn, "OpenLDAP default");
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];
195   char *ptr;
196
197   (void)done;
198
199   strcpy(hosturl, "ldap");
200   ptr = hosturl+4;
201   if(conn->handler->flags & PROTOPT_SSL)
202     *ptr++ = 's';
203   snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
204     conn->host.name, conn->remote_port);
205
206   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
207   if(rc) {
208     failf(data, "LDAP local: Cannot connect to %s, %s",
209           hosturl, ldap_err2string(rc));
210     return CURLE_COULDNT_CONNECT;
211   }
212
213   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
214
215 #ifdef USE_SSL
216   if(conn->handler->flags & PROTOPT_SSL) {
217     CURLcode result;
218     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
219     if(result)
220       return result;
221   }
222 #endif
223
224   return CURLE_OK;
225 }
226
227 static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
228 {
229   ldapconninfo *li = conn->proto.generic;
230   struct SessionHandle *data = conn->data;
231   LDAPMessage *msg = NULL;
232   struct timeval tv = {0, 1}, *tvp;
233   int rc, err;
234   char *info = NULL;
235
236 #ifdef USE_SSL
237   if(conn->handler->flags & PROTOPT_SSL) {
238     /* Is the SSL handshake complete yet? */
239     if(!li->ssldone) {
240       CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
241                                                      &li->ssldone);
242       if(result || !li->ssldone)
243         return result;
244     }
245
246     /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
247     if(!li->sslinst) {
248       Sockbuf *sb;
249       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
250       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
251       li->sslinst = TRUE;
252       li->recv = conn->recv[FIRSTSOCKET];
253       li->send = conn->send[FIRSTSOCKET];
254     }
255   }
256 #endif
257
258   tvp = &tv;
259
260 retry:
261   if(!li->didbind) {
262     char *binddn;
263     struct berval passwd;
264
265     if(conn->bits.user_passwd) {
266       binddn = conn->user;
267       passwd.bv_val = conn->passwd;
268       passwd.bv_len = strlen(passwd.bv_val);
269     }
270     else {
271       binddn = NULL;
272       passwd.bv_val = NULL;
273       passwd.bv_len = 0;
274     }
275     rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
276                         NULL, NULL, &li->msgid);
277     if(rc)
278       return CURLE_LDAP_CANNOT_BIND;
279     li->didbind = TRUE;
280     if(tvp)
281       return CURLE_OK;
282   }
283
284   rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
285   if(rc < 0) {
286     failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
287     return CURLE_LDAP_CANNOT_BIND;
288   }
289   if(rc == 0) {
290     /* timed out */
291     return CURLE_OK;
292   }
293
294   rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
295   if(rc) {
296     failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
297     return CURLE_LDAP_CANNOT_BIND;
298   }
299
300   /* Try to fallback to LDAPv2? */
301   if(err == LDAP_PROTOCOL_ERROR) {
302     int proto;
303     ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
304     if(proto == LDAP_VERSION3) {
305       if(info) {
306         ldap_memfree(info);
307         info = NULL;
308       }
309       proto = LDAP_VERSION2;
310       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
311       li->didbind = FALSE;
312       goto retry;
313     }
314   }
315
316   if(err) {
317     failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
318           info ? info : "");
319     if(info)
320       ldap_memfree(info);
321     return CURLE_LOGIN_DENIED;
322   }
323
324   if(info)
325     ldap_memfree(info);
326   conn->recv[FIRSTSOCKET] = ldap_recv;
327   *done = TRUE;
328
329   return CURLE_OK;
330 }
331
332 static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
333 {
334   ldapconninfo *li = conn->proto.generic;
335   (void) dead_connection;
336
337   if(li) {
338     if(li->ld) {
339       ldap_unbind_ext(li->ld, NULL, NULL);
340       li->ld = NULL;
341     }
342     conn->proto.generic = NULL;
343     free(li);
344   }
345   return CURLE_OK;
346 }
347
348 static CURLcode ldap_do(struct connectdata *conn, bool *done)
349 {
350   ldapconninfo *li = conn->proto.generic;
351   ldapreqinfo *lr;
352   CURLcode status = CURLE_OK;
353   int rc = 0;
354   LDAPURLDesc *ludp = NULL;
355   int msgid;
356   struct SessionHandle *data=conn->data;
357
358   connkeep(conn, "OpenLDAP do");
359
360   infof(data, "LDAP local: %s\n", data->change.url);
361
362   rc = ldap_url_parse(data->change.url, &ludp);
363   if(rc != LDAP_URL_SUCCESS) {
364     const char *msg = "url parsing problem";
365     status = CURLE_URL_MALFORMAT;
366     if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
367       if(rc == LDAP_URL_ERR_MEM)
368         status = CURLE_OUT_OF_MEMORY;
369       msg = url_errs[rc];
370     }
371     failf(conn->data, "LDAP local: %s", msg);
372     return status;
373   }
374
375   rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
376                        ludp->lud_filter, ludp->lud_attrs, 0,
377                        NULL, NULL, NULL, 0, &msgid);
378   ldap_free_urldesc(ludp);
379   if(rc != LDAP_SUCCESS) {
380     failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
381     return CURLE_LDAP_SEARCH_FAILED;
382   }
383   lr = calloc(1, sizeof(ldapreqinfo));
384   if(!lr)
385     return CURLE_OUT_OF_MEMORY;
386   lr->msgid = msgid;
387   data->req.protop = lr;
388   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
389   *done = TRUE;
390   return CURLE_OK;
391 }
392
393 static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
394                           bool premature)
395 {
396   ldapreqinfo *lr = conn->data->req.protop;
397
398   (void)res;
399   (void)premature;
400
401   if(lr) {
402     /* if there was a search in progress, abandon it */
403     if(lr->msgid) {
404       ldapconninfo *li = conn->proto.generic;
405       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
406       lr->msgid = 0;
407     }
408     conn->data->req.protop = NULL;
409     free(lr);
410   }
411
412   return CURLE_OK;
413 }
414
415 static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
416                          size_t len, CURLcode *err)
417 {
418   ldapconninfo *li = conn->proto.generic;
419   struct SessionHandle *data = conn->data;
420   ldapreqinfo *lr = data->req.protop;
421   int rc, ret;
422   LDAPMessage *msg = NULL;
423   LDAPMessage *ent;
424   BerElement *ber = NULL;
425   struct timeval tv = {0, 1};
426
427   (void)len;
428   (void)buf;
429   (void)sockindex;
430
431   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg);
432   if(rc < 0) {
433     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
434     *err = CURLE_RECV_ERROR;
435     return -1;
436   }
437
438   *err = CURLE_AGAIN;
439   ret = -1;
440
441   /* timed out */
442   if(!msg)
443     return ret;
444
445   for(ent = ldap_first_message(li->ld, msg); ent;
446     ent = ldap_next_message(li->ld, ent)) {
447     struct berval bv, *bvals, **bvp = &bvals;
448     int binary = 0, msgtype;
449
450     msgtype = ldap_msgtype(ent);
451     if(msgtype == LDAP_RES_SEARCH_RESULT) {
452       int code;
453       char *info = NULL;
454       rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
455       if(rc) {
456         failf(data, "LDAP local: search ldap_parse_result %s",
457               ldap_err2string(rc));
458         *err = CURLE_LDAP_SEARCH_FAILED;
459       }
460       else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
461         failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
462               info ? info : "");
463         *err = CURLE_LDAP_SEARCH_FAILED;
464       }
465       else {
466         /* successful */
467         if(code == LDAP_SIZELIMIT_EXCEEDED)
468           infof(data, "There are more than %d entries\n", lr->nument);
469         data->req.size = data->req.bytecount;
470         *err = CURLE_OK;
471         ret = 0;
472       }
473       lr->msgid = 0;
474       ldap_memfree(info);
475       break;
476     }
477     else if(msgtype != LDAP_RES_SEARCH_ENTRY)
478       continue;
479
480     lr->nument++;
481     rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
482     if(rc < 0) {
483       /* TODO: verify that this is really how this return code should be
484          handled */
485       *err = CURLE_RECV_ERROR;
486       return -1;
487     }
488     *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
489     if(*err)
490       return -1;
491
492     *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
493                              bv.bv_len);
494     if(*err)
495       return -1;
496
497     *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
498     if(*err)
499       return -1;
500     data->req.bytecount += bv.bv_len + 5;
501
502     for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
503       rc == LDAP_SUCCESS;
504       rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
505       int i;
506
507       if(bv.bv_val == NULL) break;
508
509       if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
510         binary = 1;
511       else
512         binary = 0;
513
514       for(i=0; bvals[i].bv_val != NULL; i++) {
515         int binval = 0;
516         *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
517         if(*err)
518           return -1;
519
520         *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
521                                  bv.bv_len);
522         if(*err)
523           return -1;
524
525         *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
526         if(*err)
527           return -1;
528         data->req.bytecount += bv.bv_len + 2;
529
530         if(!binary) {
531           /* check for leading or trailing whitespace */
532           if(ISSPACE(bvals[i].bv_val[0]) ||
533               ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
534             binval = 1;
535           else {
536             /* check for unprintable characters */
537             unsigned int j;
538             for(j=0; j<bvals[i].bv_len; j++)
539               if(!ISPRINT(bvals[i].bv_val[j])) {
540                 binval = 1;
541                 break;
542               }
543           }
544         }
545         if(binary || binval) {
546           char *val_b64 = NULL;
547           size_t val_b64_sz = 0;
548           /* Binary value, encode to base64. */
549           CURLcode error = Curl_base64_encode(data,
550                                               bvals[i].bv_val,
551                                               bvals[i].bv_len,
552                                               &val_b64,
553                                               &val_b64_sz);
554           if(error) {
555             ber_memfree(bvals);
556             ber_free(ber, 0);
557             ldap_msgfree(msg);
558             *err = error;
559             return -1;
560           }
561           *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
562           if(*err)
563             return -1;
564
565           data->req.bytecount += 2;
566           if(val_b64_sz > 0) {
567             *err = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64,
568                                      val_b64_sz);
569             if(*err)
570               return -1;
571             free(val_b64);
572             data->req.bytecount += val_b64_sz;
573           }
574         }
575         else {
576           *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
577           if(*err)
578             return -1;
579
580           *err = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
581                                    bvals[i].bv_len);
582           if(*err)
583             return -1;
584
585           data->req.bytecount += bvals[i].bv_len + 1;
586         }
587         *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
588         if(*err)
589           return -1;
590
591         data->req.bytecount++;
592       }
593       ber_memfree(bvals);
594       *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
595       if(*err)
596         return -1;
597       data->req.bytecount++;
598     }
599     *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
600     if(*err)
601       return -1;
602     data->req.bytecount++;
603     ber_free(ber, 0);
604   }
605   ldap_msgfree(msg);
606   return ret;
607 }
608
609 #ifdef USE_SSL
610 static int
611 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
612 {
613   sbiod->sbiod_pvt = arg;
614   return 0;
615 }
616
617 static int
618 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
619 {
620   sbiod->sbiod_pvt = NULL;
621   return 0;
622 }
623
624 /* We don't need to do anything because libcurl does it already */
625 static int
626 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
627 {
628   (void)sbiod;
629   return 0;
630 }
631
632 static int
633 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
634 {
635   (void)arg;
636   if(opt == LBER_SB_OPT_DATA_READY) {
637     struct connectdata *conn = sbiod->sbiod_pvt;
638     return Curl_ssl_data_pending(conn, FIRSTSOCKET);
639   }
640   return 0;
641 }
642
643 static ber_slen_t
644 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
645 {
646   struct connectdata *conn = sbiod->sbiod_pvt;
647   ldapconninfo *li = conn->proto.generic;
648   ber_slen_t ret;
649   CURLcode err = CURLE_RECV_ERROR;
650
651   ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
652   if(ret < 0 && err == CURLE_AGAIN) {
653     SET_SOCKERRNO(EWOULDBLOCK);
654   }
655   return ret;
656 }
657
658 static ber_slen_t
659 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
660 {
661   struct connectdata *conn = sbiod->sbiod_pvt;
662   ldapconninfo *li = conn->proto.generic;
663   ber_slen_t ret;
664   CURLcode err = CURLE_SEND_ERROR;
665
666   ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
667   if(ret < 0 && err == CURLE_AGAIN) {
668     SET_SOCKERRNO(EWOULDBLOCK);
669   }
670   return ret;
671 }
672
673 static Sockbuf_IO ldapsb_tls =
674 {
675   ldapsb_tls_setup,
676   ldapsb_tls_remove,
677   ldapsb_tls_ctrl,
678   ldapsb_tls_read,
679   ldapsb_tls_write,
680   ldapsb_tls_close
681 };
682 #endif /* USE_SSL */
683
684 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */