1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) Howard Chu, <hyc@openldap.org>
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 https://curl.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 * SPDX-License-Identifier: curl
23 *
24 ***************************************************************************/
25
26#include "curl_setup.h"
27
28#if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
29
30/*
31 * Notice that USE_OPENLDAP is only a source code selection switch. When
32 * libcurl is built with USE_OPENLDAP defined the libcurl source code that
33 * gets compiled is the code from openldap.c, otherwise the code that gets
34 * compiled is the code from ldap.c.
35 *
36 * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
37 * might be required for compilation and runtime. In order to use ancient
38 * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
39 */
40
41#include <ldap.h>
42
43#include "urldata.h"
44#include <curl/curl.h>
45#include "sendf.h"
46#include "vtls/vtls.h"
47#include "transfer.h"
48#include "curl_ldap.h"
49#include "curl_base64.h"
50#include "cfilters.h"
51#include "connect.h"
52#include "curl_sasl.h"
53#include "strcase.h"
54/* The last 3 #include files should be in this order */
55#include "curl_printf.h"
56#include "curl_memory.h"
57#include "memdebug.h"
58
59/*
60 * Uncommenting this will enable the built-in debug logging of the openldap
61 * library. The debug log level can be set using the CURL_OPENLDAP_TRACE
62 * environment variable. The debug output is written to stderr.
63 *
64 * The library supports the following debug flags:
65 * LDAP_DEBUG_NONE 0x0000
66 * LDAP_DEBUG_TRACE 0x0001
67 * LDAP_DEBUG_CONSTRUCT 0x0002
68 * LDAP_DEBUG_DESTROY 0x0004
69 * LDAP_DEBUG_PARAMETER 0x0008
70 * LDAP_DEBUG_ANY 0xffff
71 *
72 * For example, use CURL_OPENLDAP_TRACE=0 for no debug,
73 * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only,
74 * CURL_OPENLDAP_TRACE=65535 for all debug message levels.
75 */
76/* #define CURL_OPENLDAP_DEBUG */
77
78/* Machine states. */
79typedef enum {
80 OLDAP_STOP, /* Do nothing state, stops the state machine */
81 OLDAP_SSL, /* Performing SSL handshake. */
82 OLDAP_STARTTLS, /* STARTTLS request sent. */
83 OLDAP_TLS, /* Performing TLS handshake. */
84 OLDAP_MECHS, /* Get SASL authentication mechanisms. */
85 OLDAP_SASL, /* SASL binding reply. */
86 OLDAP_BIND, /* Simple bind reply. */
87 OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */
88 OLDAP_LAST /* Never used */
89} ldapstate;
90
91#ifndef _LDAP_PVT_H
92extern int ldap_pvt_url_scheme2proto(const char *);
93extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
94 LDAP **ld);
95#endif
96
97static CURLcode oldap_setup_connection(struct Curl_easy *data,
98 struct connectdata *conn);
99static CURLcode oldap_do(struct Curl_easy *data, bool *done);
100static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
101static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
102static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
103static CURLcode oldap_disconnect(struct Curl_easy *data,
104 struct connectdata *conn, bool dead);
105
106static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
107 const struct bufref *initresp);
108static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
109 const struct bufref *resp);
110static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
111static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
112
113static Curl_recv oldap_recv;
114
115/*
116 * LDAP protocol handler.
117 */
118
119const struct Curl_handler Curl_handler_ldap = {
120 "LDAP", /* scheme */
121 oldap_setup_connection, /* setup_connection */
122 oldap_do, /* do_it */
123 oldap_done, /* done */
124 ZERO_NULL, /* do_more */
125 oldap_connect, /* connect_it */
126 oldap_connecting, /* connecting */
127 ZERO_NULL, /* doing */
128 ZERO_NULL, /* proto_getsock */
129 ZERO_NULL, /* doing_getsock */
130 ZERO_NULL, /* domore_getsock */
131 ZERO_NULL, /* perform_getsock */
132 oldap_disconnect, /* disconnect */
133 ZERO_NULL, /* readwrite */
134 ZERO_NULL, /* connection_check */
135 ZERO_NULL, /* attach connection */
136 PORT_LDAP, /* defport */
137 CURLPROTO_LDAP, /* protocol */
138 CURLPROTO_LDAP, /* family */
139 PROTOPT_NONE /* flags */
140};
141
142#ifdef USE_SSL
143/*
144 * LDAPS protocol handler.
145 */
146
147const struct Curl_handler Curl_handler_ldaps = {
148 "LDAPS", /* scheme */
149 oldap_setup_connection, /* setup_connection */
150 oldap_do, /* do_it */
151 oldap_done, /* done */
152 ZERO_NULL, /* do_more */
153 oldap_connect, /* connect_it */
154 oldap_connecting, /* connecting */
155 ZERO_NULL, /* doing */
156 ZERO_NULL, /* proto_getsock */
157 ZERO_NULL, /* doing_getsock */
158 ZERO_NULL, /* domore_getsock */
159 ZERO_NULL, /* perform_getsock */
160 oldap_disconnect, /* disconnect */
161 ZERO_NULL, /* readwrite */
162 ZERO_NULL, /* connection_check */
163 ZERO_NULL, /* attach connection */
164 PORT_LDAPS, /* defport */
165 CURLPROTO_LDAPS, /* protocol */
166 CURLPROTO_LDAP, /* family */
167 PROTOPT_SSL /* flags */
168};
169#endif
170
171/* SASL parameters for the ldap protocol */
172static const struct SASLproto saslldap = {
173 "ldap", /* The service name */
174 oldap_perform_auth, /* Send authentication command */
175 oldap_continue_auth, /* Send authentication continuation */
176 oldap_cancel_auth, /* Send authentication cancellation */
177 oldap_get_message, /* Get SASL response message */
178 0, /* Maximum initial response length (no max) */
179 LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */
180 LDAP_SUCCESS, /* Code to receive upon authentication success */
181 SASL_AUTH_NONE, /* Default mechanisms */
182 0 /* Configuration flags */
183};
184
185struct ldapconninfo {
186 struct SASL sasl; /* SASL-related parameters */
187 LDAP *ld; /* Openldap connection handle. */
188 Curl_recv *recv; /* For stacking SSL handler */
189 Curl_send *send;
190 struct berval *servercred; /* SASL data from server. */
191 ldapstate state; /* Current machine state. */
192 int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
193 int msgid; /* Current message id. */
194};
195
196struct ldapreqinfo {
197 int msgid;
198 int nument;
199};
200
201/*
202 * oldap_state()
203 *
204 * This is the ONLY way to change LDAP state!
205 */
206static void oldap_state(struct Curl_easy *data, ldapstate newstate)
207{
208 struct ldapconninfo *ldapc = data->conn->proto.ldapc;
209
210#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
211 /* for debug purposes */
212 static const char * const names[] = {
213 "STOP",
214 "SSL",
215 "STARTTLS",
216 "TLS",
217 "MECHS",
218 "SASL",
219 "BIND",
220 "BINDV2",
221 /* LAST */
222 };
223
224 if(ldapc->state != newstate)
225 infof(data, "LDAP %p state change from %s to %s",
226 (void *)ldapc, names[ldapc->state], names[newstate]);
227#endif
228
229 ldapc->state = newstate;
230}
231
232/* Map some particular LDAP error codes to CURLcode values. */
233static CURLcode oldap_map_error(int rc, CURLcode result)
234{
235 switch(rc) {
236 case LDAP_NO_MEMORY:
237 result = CURLE_OUT_OF_MEMORY;
238 break;
239 case LDAP_INVALID_CREDENTIALS:
240 result = CURLE_LOGIN_DENIED;
241 break;
242 case LDAP_PROTOCOL_ERROR:
243 result = CURLE_UNSUPPORTED_PROTOCOL;
244 break;
245 case LDAP_INSUFFICIENT_ACCESS:
246 result = CURLE_REMOTE_ACCESS_DENIED;
247 break;
248 }
249 return result;
250}
251
252static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
253{
254 CURLcode result = CURLE_OK;
255 int rc = LDAP_URL_ERR_BADURL;
256 static const char * const url_errs[] = {
257 "success",
258 "out of memory",
259 "bad parameter",
260 "unrecognized scheme",
261 "unbalanced delimiter",
262 "bad URL",
263 "bad host or port",
264 "bad or missing attributes",
265 "bad or missing scope",
266 "bad or missing filter",
267 "bad or missing extensions"
268 };
269
270 *ludp = NULL;
271 if(!data->state.up.user && !data->state.up.password &&
272 !data->state.up.options)
273 rc = ldap_url_parse(data->state.url, ludp);
274 if(rc != LDAP_URL_SUCCESS) {
275 const char *msg = "url parsing problem";
276
277 result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT;
278 rc -= LDAP_URL_SUCCESS;
279 if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
280 msg = url_errs[rc];
281 failf(data, "LDAP local: %s", msg);
282 }
283 return result;
284}
285
286/* Parse the login options. */
287static CURLcode oldap_parse_login_options(struct connectdata *conn)
288{
289 CURLcode result = CURLE_OK;
290 struct ldapconninfo *li = conn->proto.ldapc;
291 const char *ptr = conn->options;
292
293 while(!result && ptr && *ptr) {
294 const char *key = ptr;
295 const char *value;
296
297 while(*ptr && *ptr != '=')
298 ptr++;
299
300 value = ptr + 1;
301
302 while(*ptr && *ptr != ';')
303 ptr++;
304
305 if(checkprefix("AUTH=", key))
306 result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
307 else
308 result = CURLE_SETOPT_OPTION_SYNTAX;
309
310 if(*ptr == ';')
311 ptr++;
312 }
313
314 return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result;
315}
316
317static CURLcode oldap_setup_connection(struct Curl_easy *data,
318 struct connectdata *conn)
319{
320 CURLcode result;
321 LDAPURLDesc *lud;
322 struct ldapconninfo *li;
323
324 /* Early URL syntax check. */
325 result = oldap_url_parse(data, &lud);
326 ldap_free_urldesc(lud);
327
328 if(!result) {
329 li = calloc(1, sizeof(struct ldapconninfo));
330 if(!li)
331 result = CURLE_OUT_OF_MEMORY;
332 else {
333 li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
334 conn->proto.ldapc = li;
335 connkeep(conn, "OpenLDAP default");
336
337 /* Initialize the SASL storage */
338 Curl_sasl_init(&li->sasl, data, &saslldap);
339
340 /* Clear the TLS upgraded flag */
341 conn->bits.tls_upgraded = FALSE;
342
343 result = oldap_parse_login_options(conn);
344 }
345 }
346
347 return result;
348}
349
350/*
351 * Get the SASL authentication challenge from the server credential buffer.
352 */
353static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
354{
355 struct berval *servercred = data->conn->proto.ldapc->servercred;
356
357 if(!servercred || !servercred->bv_val)
358 return CURLE_WEIRD_SERVER_REPLY;
359 Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
360 return CURLE_OK;
361}
362
363/*
364 * Sends an initial SASL bind request to the server.
365 */
366static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
367 const struct bufref *initresp)
368{
369 struct connectdata *conn = data->conn;
370 struct ldapconninfo *li = conn->proto.ldapc;
371 CURLcode result = CURLE_OK;
372 struct berval cred;
373 struct berval *pcred = &cred;
374 int rc;
375
376 cred.bv_val = (char *) Curl_bufref_ptr(initresp);
377 cred.bv_len = Curl_bufref_len(initresp);
378 if(!cred.bv_val)
379 pcred = NULL;
380 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
381 if(rc != LDAP_SUCCESS)
382 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
383 return result;
384}
385
386/*
387 * Sends SASL continuation.
388 */
389static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
390 const struct bufref *resp)
391{
392 struct connectdata *conn = data->conn;
393 struct ldapconninfo *li = conn->proto.ldapc;
394 CURLcode result = CURLE_OK;
395 struct berval cred;
396 struct berval *pcred = &cred;
397 int rc;
398
399 cred.bv_val = (char *) Curl_bufref_ptr(resp);
400 cred.bv_len = Curl_bufref_len(resp);
401 if(!cred.bv_val)
402 pcred = NULL;
403 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
404 if(rc != LDAP_SUCCESS)
405 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
406 return result;
407}
408
409/*
410 * Sends SASL bind cancellation.
411 */
412static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
413{
414 struct ldapconninfo *li = data->conn->proto.ldapc;
415 CURLcode result = CURLE_OK;
416 int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
417 &li->msgid);
418
419 (void)mech;
420 if(rc != LDAP_SUCCESS)
421 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
422 return result;
423}
424
425/* Starts LDAP simple bind. */
426static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
427{
428 CURLcode result = CURLE_OK;
429 struct connectdata *conn = data->conn;
430 struct ldapconninfo *li = conn->proto.ldapc;
431 char *binddn = NULL;
432 struct berval passwd;
433 int rc;
434
435 passwd.bv_val = NULL;
436 passwd.bv_len = 0;
437
438 if(data->state.aptr.user) {
439 binddn = conn->user;
440 passwd.bv_val = conn->passwd;
441 passwd.bv_len = strlen(passwd.bv_val);
442 }
443
444 rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
445 NULL, NULL, &li->msgid);
446 if(rc == LDAP_SUCCESS)
447 oldap_state(data, newstate);
448 else
449 result = oldap_map_error(rc,
450 data->state.aptr.user?
451 CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
452 return result;
453}
454
455/* Query the supported SASL authentication mechanisms. */
456static CURLcode oldap_perform_mechs(struct Curl_easy *data)
457{
458 CURLcode result = CURLE_OK;
459 struct ldapconninfo *li = data->conn->proto.ldapc;
460 int rc;
461 static const char * const supportedSASLMechanisms[] = {
462 "supportedSASLMechanisms",
463 NULL
464 };
465
466 rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
467 (char **) supportedSASLMechanisms, 0,
468 NULL, NULL, NULL, 0, &li->msgid);
469 if(rc == LDAP_SUCCESS)
470 oldap_state(data, OLDAP_MECHS);
471 else
472 result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
473 return result;
474}
475
476/* Starts SASL bind. */
477static CURLcode oldap_perform_sasl(struct Curl_easy *data)
478{
479 saslprogress progress = SASL_IDLE;
480 struct ldapconninfo *li = data->conn->proto.ldapc;
481 CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
482
483 oldap_state(data, OLDAP_SASL);
484 if(!result && progress != SASL_INPROGRESS)
485 result = CURLE_LOGIN_DENIED;
486 return result;
487}
488
489#ifdef USE_SSL
490static Sockbuf_IO ldapsb_tls;
491
492static bool ssl_installed(struct connectdata *conn)
493{
494 return conn->proto.ldapc->recv != NULL;
495}
496
497static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
498{
499 CURLcode result = CURLE_OK;
500 struct connectdata *conn = data->conn;
501 struct ldapconninfo *li = conn->proto.ldapc;
502 bool ssldone = 0;
503
504 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
505 if(!result) {
506 oldap_state(data, newstate);
507
508 if(ssldone) {
509 Sockbuf *sb;
510
511 /* Install the libcurl SSL handlers into the sockbuf. */
512 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
513 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
514 li->recv = conn->recv[FIRSTSOCKET];
515 li->send = conn->send[FIRSTSOCKET];
516 }
517 }
518
519 return result;
520}
521
522/* Send the STARTTLS request */
523static CURLcode oldap_perform_starttls(struct Curl_easy *data)
524{
525 CURLcode result = CURLE_OK;
526 struct ldapconninfo *li = data->conn->proto.ldapc;
527 int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
528
529 if(rc == LDAP_SUCCESS)
530 oldap_state(data, OLDAP_STARTTLS);
531 else
532 result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
533 return result;
534}
535#endif
536
537static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
538{
539 struct connectdata *conn = data->conn;
540 struct ldapconninfo *li = conn->proto.ldapc;
541 static const int version = LDAP_VERSION3;
542 int rc;
543 char *hosturl;
544#ifdef CURL_OPENLDAP_DEBUG
545 static int do_trace = -1;
546#endif
547
548 (void)done;
549
550 hosturl = aprintf("ldap%s://%s:%d",
551 conn->handler->flags & PROTOPT_SSL? "s": "",
552 conn->host.name, conn->remote_port);
553 if(!hosturl)
554 return CURLE_OUT_OF_MEMORY;
555
556 rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
557 if(rc) {
558 failf(data, "LDAP local: Cannot connect to %s, %s",
559 hosturl, ldap_err2string(rc));
560 free(hosturl);
561 return CURLE_COULDNT_CONNECT;
562 }
563
564 free(hosturl);
565
566#ifdef CURL_OPENLDAP_DEBUG
567 if(do_trace < 0) {
568 const char *env = getenv("CURL_OPENLDAP_TRACE");
569 do_trace = (env && strtol(env, NULL, 10) > 0);
570 }
571 if(do_trace)
572 ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
573#endif
574
575 /* Try version 3 first. */
576 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
577
578 /* Do not chase referrals. */
579 ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
580
581#ifdef USE_SSL
582 if(conn->handler->flags & PROTOPT_SSL)
583 return oldap_ssl_connect(data, OLDAP_SSL);
584
585 if(data->set.use_ssl) {
586 CURLcode result = oldap_perform_starttls(data);
587
588 if(!result || data->set.use_ssl != CURLUSESSL_TRY)
589 return result;
590 }
591#endif
592
593 if(li->sasl.prefmech != SASL_AUTH_NONE)
594 return oldap_perform_mechs(data);
595
596 /* Force bind even if anonymous bind is not needed in protocol version 3
597 to detect missing version 3 support. */
598 return oldap_perform_bind(data, OLDAP_BIND);
599}
600
601/* Handle the supported SASL mechanisms query response */
602static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
603 LDAPMessage *msg, int code)
604{
605 struct connectdata *conn = data->conn;
606 struct ldapconninfo *li = conn->proto.ldapc;
607 int rc;
608 BerElement *ber = NULL;
609 CURLcode result = CURLE_OK;
610 struct berval bv, *bvals;
611
612 switch(ldap_msgtype(msg)) {
613 case LDAP_RES_SEARCH_ENTRY:
614 /* Got a list of supported SASL mechanisms. */
615 if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
616 return CURLE_LOGIN_DENIED;
617
618 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
619 if(rc < 0)
620 return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
621 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
622 rc == LDAP_SUCCESS;
623 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
624 int i;
625
626 if(!bv.bv_val)
627 break;
628
629 if(bvals) {
630 for(i = 0; bvals[i].bv_val; i++) {
631 size_t llen;
632 unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val,
633 bvals[i].bv_len, &llen);
634 if(bvals[i].bv_len == llen)
635 li->sasl.authmechs |= mech;
636 }
637 ber_memfree(bvals);
638 }
639 }
640 ber_free(ber, 0);
641 break;
642
643 case LDAP_RES_SEARCH_RESULT:
644 switch(code) {
645 case LDAP_SIZELIMIT_EXCEEDED:
646 infof(data, "Too many authentication mechanisms\n");
647 /* FALLTHROUGH */
648 case LDAP_SUCCESS:
649 case LDAP_NO_RESULTS_RETURNED:
650 if(Curl_sasl_can_authenticate(&li->sasl, data))
651 result = oldap_perform_sasl(data);
652 else
653 result = CURLE_LOGIN_DENIED;
654 break;
655 default:
656 result = oldap_map_error(code, CURLE_LOGIN_DENIED);
657 break;
658 }
659 break;
660 default:
661 break;
662 }
663 return result;
664}
665
666/* Handle a SASL bind response. */
667static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
668 LDAPMessage *msg, int code)
669{
670 struct connectdata *conn = data->conn;
671 struct ldapconninfo *li = conn->proto.ldapc;
672 CURLcode result = CURLE_OK;
673 saslprogress progress;
674 int rc;
675
676 li->servercred = NULL;
677 rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
678 if(rc != LDAP_SUCCESS) {
679 failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc));
680 result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
681 }
682 else {
683 result = Curl_sasl_continue(&li->sasl, data, code, &progress);
684 if(!result && progress != SASL_INPROGRESS)
685 oldap_state(data, OLDAP_STOP);
686 }
687
688 if(li->servercred)
689 ber_bvfree(li->servercred);
690 return result;
691}
692
693/* Handle a simple bind response. */
694static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
695 int code)
696{
697 struct connectdata *conn = data->conn;
698 struct ldapconninfo *li = conn->proto.ldapc;
699 CURLcode result = CURLE_OK;
700 struct berval *bv = NULL;
701 int rc;
702
703 if(code != LDAP_SUCCESS)
704 return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
705
706 rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
707 if(rc != LDAP_SUCCESS) {
708 failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
709 ldap_err2string(rc));
710 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
711 }
712 else
713 oldap_state(data, OLDAP_STOP);
714
715 if(bv)
716 ber_bvfree(bv);
717 return result;
718}
719
720static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
721{
722 CURLcode result = CURLE_OK;
723 struct connectdata *conn = data->conn;
724 struct ldapconninfo *li = conn->proto.ldapc;
725 LDAPMessage *msg = NULL;
726 struct timeval tv = {0, 0};
727 int code = LDAP_SUCCESS;
728 int rc;
729
730 if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
731 /* Get response to last command. */
732 rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
733 switch(rc) {
734 case 0: /* Timed out. */
735 return CURLE_OK;
736 case LDAP_RES_SEARCH_ENTRY:
737 case LDAP_RES_SEARCH_REFERENCE:
738 break;
739 default:
740 li->msgid = 0; /* Nothing to abandon upon error. */
741 if(rc < 0) {
742 failf(data, "LDAP local: connecting ldap_result %s",
743 ldap_err2string(rc));
744 return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
745 }
746 break;
747 }
748
749 /* Get error code from message. */
750 rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
751 if(rc)
752 code = rc;
753 else {
754 /* store the latest code for later retrieval */
755 data->info.httpcode = code;
756 }
757
758 /* If protocol version 3 is not supported, fallback to version 2. */
759 if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
760#ifdef USE_SSL
761 (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
762#endif
763 li->sasl.prefmech == SASL_AUTH_NONE) {
764 static const int version = LDAP_VERSION2;
765
766 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
767 ldap_msgfree(msg);
768 return oldap_perform_bind(data, OLDAP_BINDV2);
769 }
770 }
771
772 /* Handle response message according to current state. */
773 switch(li->state) {
774
775#ifdef USE_SSL
776 case OLDAP_SSL:
777 result = oldap_ssl_connect(data, OLDAP_SSL);
778 if(!result && ssl_installed(conn)) {
779 if(li->sasl.prefmech != SASL_AUTH_NONE)
780 result = oldap_perform_mechs(data);
781 else
782 result = oldap_perform_bind(data, OLDAP_BIND);
783 }
784 break;
785 case OLDAP_STARTTLS:
786 if(code != LDAP_SUCCESS) {
787 if(data->set.use_ssl != CURLUSESSL_TRY)
788 result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
789 else if(li->sasl.prefmech != SASL_AUTH_NONE)
790 result = oldap_perform_mechs(data);
791 else
792 result = oldap_perform_bind(data, OLDAP_BIND);
793 break;
794 }
795 /* FALLTHROUGH */
796 case OLDAP_TLS:
797 result = oldap_ssl_connect(data, OLDAP_TLS);
798 if(result && data->set.use_ssl != CURLUSESSL_TRY)
799 result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
800 else if(ssl_installed(conn)) {
801 conn->bits.tls_upgraded = TRUE;
802 if(li->sasl.prefmech != SASL_AUTH_NONE)
803 result = oldap_perform_mechs(data);
804 else if(data->state.aptr.user)
805 result = oldap_perform_bind(data, OLDAP_BIND);
806 else {
807 /* Version 3 supported: no bind required */
808 oldap_state(data, OLDAP_STOP);
809 result = CURLE_OK;
810 }
811 }
812 break;
813#endif
814
815 case OLDAP_MECHS:
816 result = oldap_state_mechs_resp(data, msg, code);
817 break;
818 case OLDAP_SASL:
819 result = oldap_state_sasl_resp(data, msg, code);
820 break;
821 case OLDAP_BIND:
822 case OLDAP_BINDV2:
823 result = oldap_state_bind_resp(data, msg, code);
824 break;
825 default:
826 /* internal error */
827 result = CURLE_COULDNT_CONNECT;
828 break;
829 }
830
831 ldap_msgfree(msg);
832
833 *done = li->state == OLDAP_STOP;
834 if(*done)
835 conn->recv[FIRSTSOCKET] = oldap_recv;
836
837 if(result && li->msgid) {
838 ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
839 li->msgid = 0;
840 }
841 return result;
842}
843
844static CURLcode oldap_disconnect(struct Curl_easy *data,
845 struct connectdata *conn,
846 bool dead_connection)
847{
848 struct ldapconninfo *li = conn->proto.ldapc;
849 (void) dead_connection;
850#ifndef USE_SSL
851 (void)data;
852#endif
853
854 if(li) {
855 if(li->ld) {
856#ifdef USE_SSL
857 if(ssl_installed(conn)) {
858 Sockbuf *sb;
859 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
860 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
861 }
862#endif
863 ldap_unbind_ext(li->ld, NULL, NULL);
864 li->ld = NULL;
865 }
866 Curl_sasl_cleanup(conn, li->sasl.authused);
867 conn->proto.ldapc = NULL;
868 free(li);
869 }
870 return CURLE_OK;
871}
872
873static CURLcode oldap_do(struct Curl_easy *data, bool *done)
874{
875 struct connectdata *conn = data->conn;
876 struct ldapconninfo *li = conn->proto.ldapc;
877 struct ldapreqinfo *lr;
878 CURLcode result;
879 int rc;
880 LDAPURLDesc *lud;
881 int msgid;
882
883 connkeep(conn, "OpenLDAP do");
884
885 infof(data, "LDAP local: %s", data->state.url);
886
887 result = oldap_url_parse(data, &lud);
888 if(!result) {
889 rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
890 lud->lud_filter, lud->lud_attrs, 0,
891 NULL, NULL, NULL, 0, &msgid);
892 ldap_free_urldesc(lud);
893 if(rc != LDAP_SUCCESS) {
894 failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
895 result = CURLE_LDAP_SEARCH_FAILED;
896 }
897 else {
898 lr = calloc(1, sizeof(struct ldapreqinfo));
899 if(!lr) {
900 ldap_abandon_ext(li->ld, msgid, NULL, NULL);
901 result = CURLE_OUT_OF_MEMORY;
902 }
903 else {
904 lr->msgid = msgid;
905 data->req.p.ldap = lr;
906 Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
907 *done = TRUE;
908 }
909 }
910 }
911 return result;
912}
913
914static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
915 bool premature)
916{
917 struct connectdata *conn = data->conn;
918 struct ldapreqinfo *lr = data->req.p.ldap;
919
920 (void)res;
921 (void)premature;
922
923 if(lr) {
924 /* if there was a search in progress, abandon it */
925 if(lr->msgid) {
926 struct ldapconninfo *li = conn->proto.ldapc;
927 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
928 lr->msgid = 0;
929 }
930 data->req.p.ldap = NULL;
931 free(lr);
932 }
933
934 return CURLE_OK;
935}
936
937static CURLcode client_write(struct Curl_easy *data,
938 const char *prefix, size_t plen,
939 const char *value, size_t len,
940 const char *suffix, size_t slen)
941{
942 CURLcode result = CURLE_OK;
943
944 if(prefix) {
945 /* If we have a zero-length value and the prefix ends with a space
946 separator, drop the latter. */
947 if(!len && plen && prefix[plen - 1] == ' ')
948 plen--;
949 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
950 if(!result)
951 data->req.bytecount += plen;
952 }
953 if(!result && value) {
954 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
955 if(!result)
956 data->req.bytecount += len;
957 }
958 if(!result && suffix) {
959 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
960 if(!result)
961 data->req.bytecount += slen;
962 }
963 return result;
964}
965
966static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
967 size_t len, CURLcode *err)
968{
969 struct connectdata *conn = data->conn;
970 struct ldapconninfo *li = conn->proto.ldapc;
971 struct ldapreqinfo *lr = data->req.p.ldap;
972 int rc;
973 LDAPMessage *msg = NULL;
974 BerElement *ber = NULL;
975 struct timeval tv = {0, 0};
976 struct berval bv, *bvals;
977 int binary = 0;
978 CURLcode result = CURLE_AGAIN;
979 int code;
980 char *info = NULL;
981
982 (void)len;
983 (void)buf;
984 (void)sockindex;
985
986 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
987 if(rc < 0) {
988 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
989 result = CURLE_RECV_ERROR;
990 }
991
992 *err = result;
993
994 /* error or timed out */
995 if(!msg)
996 return -1;
997
998 result = CURLE_OK;
999
1000 switch(ldap_msgtype(msg)) {
1001 case LDAP_RES_SEARCH_RESULT:
1002 lr->msgid = 0;
1003 rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1004 if(rc) {
1005 failf(data, "LDAP local: search ldap_parse_result %s",
1006 ldap_err2string(rc));
1007 result = CURLE_LDAP_SEARCH_FAILED;
1008 break;
1009 }
1010
1011 /* store the latest code for later retrieval */
1012 data->info.httpcode = code;
1013
1014 switch(code) {
1015 case LDAP_SIZELIMIT_EXCEEDED:
1016 infof(data, "There are more than %d entries", lr->nument);
1017 /* FALLTHROUGH */
1018 case LDAP_SUCCESS:
1019 data->req.size = data->req.bytecount;
1020 break;
1021 default:
1022 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1023 info ? info : "");
1024 result = CURLE_LDAP_SEARCH_FAILED;
1025 break;
1026 }
1027 if(info)
1028 ldap_memfree(info);
1029 break;
1030 case LDAP_RES_SEARCH_ENTRY:
1031 lr->nument++;
1032 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1033 if(rc < 0) {
1034 result = CURLE_RECV_ERROR;
1035 break;
1036 }
1037
1038 result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1039 STRCONST("\n"));
1040 if(result)
1041 break;
1042
1043 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1044 rc == LDAP_SUCCESS;
1045 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1046 int i;
1047
1048 if(!bv.bv_val)
1049 break;
1050
1051 if(!bvals) {
1052 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1053 STRCONST(":\n"));
1054 if(result)
1055 break;
1056 continue;
1057 }
1058
1059 binary = bv.bv_len > 7 &&
1060 !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1061
1062 for(i = 0; bvals[i].bv_val != NULL; i++) {
1063 int binval = 0;
1064
1065 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1066 STRCONST(":"));
1067 if(result)
1068 break;
1069
1070 if(!binary) {
1071 /* check for leading or trailing whitespace */
1072 if(ISBLANK(bvals[i].bv_val[0]) ||
1073 ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
1074 binval = 1;
1075 else {
1076 /* check for unprintable characters */
1077 unsigned int j;
1078 for(j = 0; j < bvals[i].bv_len; j++)
1079 if(!ISPRINT(bvals[i].bv_val[j])) {
1080 binval = 1;
1081 break;
1082 }
1083 }
1084 }
1085 if(binary || binval) {
1086 char *val_b64 = NULL;
1087 size_t val_b64_sz = 0;
1088
1089 /* Binary value, encode to base64. */
1090 if(bvals[i].bv_len)
1091 result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1092 &val_b64, &val_b64_sz);
1093 if(!result)
1094 result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1095 STRCONST("\n"));
1096 free(val_b64);
1097 }
1098 else
1099 result = client_write(data, STRCONST(" "),
1100 bvals[i].bv_val, bvals[i].bv_len,
1101 STRCONST("\n"));
1102 if(result)
1103 break;
1104 }
1105
1106 ber_memfree(bvals);
1107 bvals = NULL;
1108 if(!result)
1109 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1110 if(result)
1111 break;
1112 }
1113
1114 ber_free(ber, 0);
1115
1116 if(!result)
1117 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1118 if(!result)
1119 result = CURLE_AGAIN;
1120 break;
1121 }
1122
1123 ldap_msgfree(msg);
1124 *err = result;
1125 return result? -1: 0;
1126}
1127
1128#ifdef USE_SSL
1129static int
1130ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1131{
1132 sbiod->sbiod_pvt = arg;
1133 return 0;
1134}
1135
1136static int
1137ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1138{
1139 sbiod->sbiod_pvt = NULL;
1140 return 0;
1141}
1142
1143/* We don't need to do anything because libcurl does it already */
1144static int
1145ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1146{
1147 (void)sbiod;
1148 return 0;
1149}
1150
1151static int
1152ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
1153{
1154 (void)arg;
1155 if(opt == LBER_SB_OPT_DATA_READY) {
1156 struct Curl_easy *data = sbiod->sbiod_pvt;
1157 return Curl_conn_data_pending(data, FIRSTSOCKET);
1158 }
1159 return 0;
1160}
1161
1162static ber_slen_t
1163ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1164{
1165 struct Curl_easy *data = sbiod->sbiod_pvt;
1166 ber_slen_t ret = 0;
1167 if(data) {
1168 struct connectdata *conn = data->conn;
1169 if(conn) {
1170 struct ldapconninfo *li = conn->proto.ldapc;
1171 CURLcode err = CURLE_RECV_ERROR;
1172
1173 ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1174 if(ret < 0 && err == CURLE_AGAIN) {
1175 SET_SOCKERRNO(EWOULDBLOCK);
1176 }
1177 }
1178 }
1179 return ret;
1180}
1181
1182static ber_slen_t
1183ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1184{
1185 struct Curl_easy *data = sbiod->sbiod_pvt;
1186 ber_slen_t ret = 0;
1187 if(data) {
1188 struct connectdata *conn = data->conn;
1189 if(conn) {
1190 struct ldapconninfo *li = conn->proto.ldapc;
1191 CURLcode err = CURLE_SEND_ERROR;
1192 ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
1193 if(ret < 0 && err == CURLE_AGAIN) {
1194 SET_SOCKERRNO(EWOULDBLOCK);
1195 }
1196 }
1197 }
1198 return ret;
1199}
1200
1201static Sockbuf_IO ldapsb_tls =
1202{
1203 ldapsb_tls_setup,
1204 ldapsb_tls_remove,
1205 ldapsb_tls_ctrl,
1206 ldapsb_tls_read,
1207 ldapsb_tls_write,
1208 ldapsb_tls_close
1209};
1210#endif /* USE_SSL */
1211
1212#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1213