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