1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2011 - 2022, Daniel Stenberg, <[email protected]>, et al.
9 * Copyright (C) 2010, Howard Chu, <[email protected]>
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 "connect.h"
51#include "curl_sasl.h"
52#include "strcase.h"
53/* The last 3 #include files should be in this order */
54#include "curl_printf.h"
55#include "curl_memory.h"
56#include "memdebug.h"
57
58/*
59 * Uncommenting this will enable the built-in debug logging of the openldap
60 * library. The debug log level can be set using the CURL_OPENLDAP_TRACE
61 * environment variable. The debug output is written to stderr.
62 *
63 * The library supports the following debug flags:
64 * LDAP_DEBUG_NONE 0x0000
65 * LDAP_DEBUG_TRACE 0x0001
66 * LDAP_DEBUG_CONSTRUCT 0x0002
67 * LDAP_DEBUG_DESTROY 0x0004
68 * LDAP_DEBUG_PARAMETER 0x0008
69 * LDAP_DEBUG_ANY 0xffff
70 *
71 * For example, use CURL_OPENLDAP_TRACE=0 for no debug,
72 * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only,
73 * CURL_OPENLDAP_TRACE=65535 for all debug message levels.
74 */
75/* #define CURL_OPENLDAP_DEBUG */
76
77/* Machine states. */
78typedef enum {
79 OLDAP_STOP, /* Do nothing state, stops the state machine */
80 OLDAP_SSL, /* Performing SSL handshake. */
81 OLDAP_STARTTLS, /* STARTTLS request sent. */
82 OLDAP_TLS, /* Performing TLS handshake. */
83 OLDAP_MECHS, /* Get SASL authentication mechanisms. */
84 OLDAP_SASL, /* SASL binding reply. */
85 OLDAP_BIND, /* Simple bind reply. */
86 OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */
87 OLDAP_LAST /* Never used */
88} ldapstate;
89
90#ifndef _LDAP_PVT_H
91extern int ldap_pvt_url_scheme2proto(const char *);
92extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
93 LDAP **ld);
94#endif
95
96static CURLcode oldap_setup_connection(struct Curl_easy *data,
97 struct connectdata *conn);
98static CURLcode oldap_do(struct Curl_easy *data, bool *done);
99static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
100static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
101static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
102static CURLcode oldap_disconnect(struct Curl_easy *data,
103 struct connectdata *conn, bool dead);
104
105static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
106 const struct bufref *initresp);
107static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
108 const struct bufref *resp);
109static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
110static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
111
112static Curl_recv oldap_recv;
113
114/*
115 * LDAP protocol handler.
116 */
117
118const struct Curl_handler Curl_handler_ldap = {
119 "LDAP", /* scheme */
120 oldap_setup_connection, /* setup_connection */
121 oldap_do, /* do_it */
122 oldap_done, /* done */
123 ZERO_NULL, /* do_more */
124 oldap_connect, /* connect_it */
125 oldap_connecting, /* connecting */
126 ZERO_NULL, /* doing */
127 ZERO_NULL, /* proto_getsock */
128 ZERO_NULL, /* doing_getsock */
129 ZERO_NULL, /* domore_getsock */
130 ZERO_NULL, /* perform_getsock */
131 oldap_disconnect, /* disconnect */
132 ZERO_NULL, /* readwrite */
133 ZERO_NULL, /* connection_check */
134 ZERO_NULL, /* attach connection */
135 PORT_LDAP, /* defport */
136 CURLPROTO_LDAP, /* protocol */
137 CURLPROTO_LDAP, /* family */
138 PROTOPT_NONE /* flags */
139};
140
141#ifdef USE_SSL
142/*
143 * LDAPS protocol handler.
144 */
145
146const struct Curl_handler Curl_handler_ldaps = {
147 "LDAPS", /* scheme */
148 oldap_setup_connection, /* setup_connection */
149 oldap_do, /* do_it */
150 oldap_done, /* done */
151 ZERO_NULL, /* do_more */
152 oldap_connect, /* connect_it */
153 oldap_connecting, /* connecting */
154 ZERO_NULL, /* doing */
155 ZERO_NULL, /* proto_getsock */
156 ZERO_NULL, /* doing_getsock */
157 ZERO_NULL, /* domore_getsock */
158 ZERO_NULL, /* perform_getsock */
159 oldap_disconnect, /* disconnect */
160 ZERO_NULL, /* readwrite */
161 ZERO_NULL, /* connection_check */
162 ZERO_NULL, /* attach connection */
163 PORT_LDAPS, /* defport */
164 CURLPROTO_LDAPS, /* protocol */
165 CURLPROTO_LDAP, /* family */
166 PROTOPT_SSL /* flags */
167};
168#endif
169
170/* SASL parameters for the ldap protocol */
171static const struct SASLproto saslldap = {
172 "ldap", /* The service name */
173 oldap_perform_auth, /* Send authentication command */
174 oldap_continue_auth, /* Send authentication continuation */
175 oldap_cancel_auth, /* Send authentication cancellation */
176 oldap_get_message, /* Get SASL response message */
177 0, /* Maximum initial response length (no max) */
178 LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */
179 LDAP_SUCCESS, /* Code to receive upon authentication success */
180 SASL_AUTH_NONE, /* Default mechanisms */
181 0 /* Configuration flags */
182};
183
184struct ldapconninfo {
185 struct SASL sasl; /* SASL-related parameters */
186 LDAP *ld; /* Openldap connection handle. */
187 Curl_recv *recv; /* For stacking SSL handler */
188 Curl_send *send;
189 struct berval *servercred; /* SASL data from server. */
190 ldapstate state; /* Current machine state. */
191 int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
192 int msgid; /* Current message id. */
193};
194
195struct ldapreqinfo {
196 int msgid;
197 int nument;
198};
199
200/*
201 * state()
202 *
203 * This is the ONLY way to change LDAP state!
204 */
205static void state(struct Curl_easy *data, ldapstate newstate)
206{
207 struct ldapconninfo *ldapc = data->conn->proto.ldapc;
208
209#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
210 /* for debug purposes */
211 static const char * const names[] = {
212 "STOP",
213 "SSL",
214 "STARTTLS",
215 "TLS",
216 "MECHS",
217 "SASL",
218 "BIND",
219 "BINDV2",
220 /* LAST */
221 };
222
223 if(ldapc->state != newstate)
224 infof(data, "LDAP %p state change from %s to %s",
225 (void *)ldapc, names[ldapc->state], names[newstate]);
226#endif
227
228 ldapc->state = newstate;
229}
230
231/* Map some particular LDAP error codes to CURLcode values. */
232static CURLcode oldap_map_error(int rc, CURLcode result)
233{
234 switch(rc) {
235 case LDAP_NO_MEMORY:
236 result = CURLE_OUT_OF_MEMORY;
237 break;
238 case LDAP_INVALID_CREDENTIALS:
239 result = CURLE_LOGIN_DENIED;
240 break;
241 case LDAP_PROTOCOL_ERROR:
242 result = CURLE_UNSUPPORTED_PROTOCOL;
243 break;
244 case LDAP_INSUFFICIENT_ACCESS:
245 result = CURLE_REMOTE_ACCESS_DENIED;
246 break;
247 }
248 return result;
249}
250
251static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
252{
253 CURLcode result = CURLE_OK;
254 int rc = LDAP_URL_ERR_BADURL;
255 static const char * const url_errs[] = {
256 "success",
257 "out of memory",
258 "bad parameter",
259 "unrecognized scheme",
260 "unbalanced delimiter",
261 "bad URL",
262 "bad host or port",
263 "bad or missing attributes",
264 "bad or missing scope",
265 "bad or missing filter",
266 "bad or missing extensions"
267 };
268
269 *ludp = NULL;
270 if(!data->state.up.user && !data->state.up.password &&
271 !data->state.up.options)
272 rc = ldap_url_parse(data->state.url, ludp);
273 if(rc != LDAP_URL_SUCCESS) {
274 const char *msg = "url parsing problem";
275
276 result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT;
277 rc -= LDAP_URL_SUCCESS;
278 if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
279 msg = url_errs[rc];
280 failf(data, "LDAP local: %s", msg);
281 }
282 return result;
283}
284
285/* Parse the login options. */
286static CURLcode oldap_parse_login_options(struct connectdata *conn)
287{
288 CURLcode result = CURLE_OK;
289 struct ldapconninfo *li = conn->proto.ldapc;
290 const char *ptr = conn->options;
291
292 while(!result && ptr && *ptr) {
293 const char *key = ptr;
294 const char *value;
295
296 while(*ptr && *ptr != '=')
297 ptr++;
298
299 value = ptr + 1;
300
301 while(*ptr && *ptr != ';')
302 ptr++;
303
304 if(checkprefix("AUTH=", key))
305 result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
306 else
307 result = CURLE_SETOPT_OPTION_SYNTAX;
308
309 if(*ptr == ';')
310 ptr++;
311 }
312
313 return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result;
314}
315
316static CURLcode oldap_setup_connection(struct Curl_easy *data,
317 struct connectdata *conn)
318{
319 CURLcode result;
320 LDAPURLDesc *lud;
321 struct ldapconninfo *li;
322
323 /* Early URL syntax check. */
324 result = oldap_url_parse(data, &lud);
325 ldap_free_urldesc(lud);
326
327 if(!result) {
328 li = calloc(1, sizeof(struct ldapconninfo));
329 if(!li)
330 result = CURLE_OUT_OF_MEMORY;
331 else {
332 li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
333 conn->proto.ldapc = li;
334 connkeep(conn, "OpenLDAP default");
335
336 /* Initialize the SASL storage */
337 Curl_sasl_init(&li->sasl, data, &saslldap);
338
339 /* Clear the TLS upgraded flag */
340 conn->bits.tls_upgraded = FALSE;
341
342 result = oldap_parse_login_options(conn);
343 }
344 }
345
346 return result;
347}
348
349/*
350 * Get the SASL authentication challenge from the server credential buffer.
351 */
352static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
353{
354 struct berval *servercred = data->conn->proto.ldapc->servercred;
355
356 if(!servercred || !servercred->bv_val)
357 return CURLE_WEIRD_SERVER_REPLY;
358 Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
359 return CURLE_OK;
360}
361
362/*
363 * Sends an initial SASL bind request to the server.
364 */
365static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
366 const struct bufref *initresp)
367{
368 struct connectdata *conn = data->conn;
369 struct ldapconninfo *li = conn->proto.ldapc;
370 CURLcode result = CURLE_OK;
371 struct berval cred;
372 struct berval *pcred = &cred;
373 int rc;
374
375 cred.bv_val = (char *) Curl_bufref_ptr(initresp);
376 cred.bv_len = Curl_bufref_len(initresp);
377 if(!cred.bv_val)
378 pcred = NULL;
379 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
380 if(rc != LDAP_SUCCESS)
381 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
382 return result;
383}
384
385/*
386 * Sends SASL continuation.
387 */
388static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
389 const struct bufref *resp)
390{
391 struct connectdata *conn = data->conn;
392 struct ldapconninfo *li = conn->proto.ldapc;
393 CURLcode result = CURLE_OK;
394 struct berval cred;
395 struct berval *pcred = &cred;
396 int rc;
397
398 cred.bv_val = (char *) Curl_bufref_ptr(resp);
399 cred.bv_len = Curl_bufref_len(resp);
400 if(!cred.bv_val)
401 pcred = NULL;
402 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
403 if(rc != LDAP_SUCCESS)
404 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
405 return result;
406}
407
408/*
409 * Sends SASL bind cancellation.
410 */
411static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
412{
413 struct ldapconninfo *li = data->conn->proto.ldapc;
414 CURLcode result = CURLE_OK;
415 int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
416 &li->msgid);
417
418 (void)mech;
419 if(rc != LDAP_SUCCESS)
420 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
421 return result;
422}
423
424/* Starts LDAP simple bind. */
425static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
426{
427 CURLcode result = CURLE_OK;
428 struct connectdata *conn = data->conn;
429 struct ldapconninfo *li = conn->proto.ldapc;
430 char *binddn = NULL;
431 struct berval passwd;
432 int rc;
433
434 passwd.bv_val = NULL;
435 passwd.bv_len = 0;
436
437 if(data->state.aptr.user) {
438 binddn = conn->user;
439 passwd.bv_val = conn->passwd;
440 passwd.bv_len = strlen(passwd.bv_val);
441 }
442
443 rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
444 NULL, NULL, &li->msgid);
445 if(rc == LDAP_SUCCESS)
446 state(data, newstate);
447 else
448 result = oldap_map_error(rc,
449 data->state.aptr.user?
450 CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
451 return result;
452}
453
454/* Query the supported SASL authentication mechanisms. */
455static CURLcode oldap_perform_mechs(struct Curl_easy *data)
456{
457 CURLcode result = CURLE_OK;
458 struct ldapconninfo *li = data->conn->proto.ldapc;
459 int rc;
460 static const char * const supportedSASLMechanisms[] = {
461 "supportedSASLMechanisms",
462 NULL
463 };
464
465 rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
466 (char **) supportedSASLMechanisms, 0,
467 NULL, NULL, NULL, 0, &li->msgid);
468 if(rc == LDAP_SUCCESS)
469 state(data, OLDAP_MECHS);
470 else
471 result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
472 return result;
473}
474
475/* Starts SASL bind. */
476static CURLcode oldap_perform_sasl(struct Curl_easy *data)
477{
478 saslprogress progress = SASL_IDLE;
479 struct ldapconninfo *li = data->conn->proto.ldapc;
480 CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
481
482 state(data, OLDAP_SASL);
483 if(!result && progress != SASL_INPROGRESS)
484 result = CURLE_LOGIN_DENIED;
485 return result;
486}
487
488#ifdef USE_SSL
489static Sockbuf_IO ldapsb_tls;
490
491static bool ssl_installed(struct connectdata *conn)
492{
493 return conn->proto.ldapc->recv != NULL;
494}
495
496static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
497{
498 CURLcode result = CURLE_OK;
499 struct connectdata *conn = data->conn;
500 struct ldapconninfo *li = conn->proto.ldapc;
501 bool ssldone = 0;
502
503 result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
504 FIRSTSOCKET, &ssldone);
505 if(!result) {
506 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 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 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 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 state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
808 result = CURLE_OK;
809 }
810 }
811 break;
812#endif
813
814 case OLDAP_MECHS:
815 result = oldap_state_mechs_resp(data, msg, code);
816 break;
817 case OLDAP_SASL:
818 result = oldap_state_sasl_resp(data, msg, code);
819 break;
820 case OLDAP_BIND:
821 case OLDAP_BINDV2:
822 result = oldap_state_bind_resp(data, msg, code);
823 break;
824 default:
825 /* internal error */
826 result = CURLE_COULDNT_CONNECT;
827 break;
828 }
829
830 ldap_msgfree(msg);
831
832 *done = li->state == OLDAP_STOP;
833 if(*done)
834 conn->recv[FIRSTSOCKET] = oldap_recv;
835
836 if(result && li->msgid) {
837 ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
838 li->msgid = 0;
839 }
840 return result;
841}
842
843static CURLcode oldap_disconnect(struct Curl_easy *data,
844 struct connectdata *conn,
845 bool dead_connection)
846{
847 struct ldapconninfo *li = conn->proto.ldapc;
848 (void) dead_connection;
849#ifndef USE_SSL
850 (void)data;
851#endif
852
853 if(li) {
854 if(li->ld) {
855#ifdef USE_SSL
856 if(ssl_installed(conn)) {
857 Sockbuf *sb;
858 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
859 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
860 }
861#endif
862 ldap_unbind_ext(li->ld, NULL, NULL);
863 li->ld = NULL;
864 }
865 Curl_sasl_cleanup(conn, li->sasl.authused);
866 conn->proto.ldapc = NULL;
867 free(li);
868 }
869 return CURLE_OK;
870}
871
872static CURLcode oldap_do(struct Curl_easy *data, bool *done)
873{
874 struct connectdata *conn = data->conn;
875 struct ldapconninfo *li = conn->proto.ldapc;
876 struct ldapreqinfo *lr;
877 CURLcode result;
878 int rc;
879 LDAPURLDesc *lud;
880 int msgid;
881
882 connkeep(conn, "OpenLDAP do");
883
884 infof(data, "LDAP local: %s", data->state.url);
885
886 result = oldap_url_parse(data, &lud);
887 if(!result) {
888 rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
889 lud->lud_filter, lud->lud_attrs, 0,
890 NULL, NULL, NULL, 0, &msgid);
891 ldap_free_urldesc(lud);
892 if(rc != LDAP_SUCCESS) {
893 failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
894 result = CURLE_LDAP_SEARCH_FAILED;
895 }
896 else {
897 lr = calloc(1, sizeof(struct ldapreqinfo));
898 if(!lr) {
899 ldap_abandon_ext(li->ld, msgid, NULL, NULL);
900 result = CURLE_OUT_OF_MEMORY;
901 }
902 else {
903 lr->msgid = msgid;
904 data->req.p.ldap = lr;
905 Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
906 *done = TRUE;
907 }
908 }
909 }
910 return result;
911}
912
913static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
914 bool premature)
915{
916 struct connectdata *conn = data->conn;
917 struct ldapreqinfo *lr = data->req.p.ldap;
918
919 (void)res;
920 (void)premature;
921
922 if(lr) {
923 /* if there was a search in progress, abandon it */
924 if(lr->msgid) {
925 struct ldapconninfo *li = conn->proto.ldapc;
926 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
927 lr->msgid = 0;
928 }
929 data->req.p.ldap = NULL;
930 free(lr);
931 }
932
933 return CURLE_OK;
934}
935
936static CURLcode client_write(struct Curl_easy *data,
937 const char *prefix, size_t plen,
938 const char *value, size_t len,
939 const char *suffix, size_t slen)
940{
941 CURLcode result = CURLE_OK;
942
943 if(prefix) {
944 /* If we have a zero-length value and the prefix ends with a space
945 separator, drop the latter. */
946 if(!len && plen && prefix[plen - 1] == ' ')
947 plen--;
948 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
949 if(!result)
950 data->req.bytecount += plen;
951 }
952 if(!result && value) {
953 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
954 if(!result)
955 data->req.bytecount += len;
956 }
957 if(!result && suffix) {
958 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
959 if(!result)
960 data->req.bytecount += slen;
961 }
962 return result;
963}
964
965static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
966 size_t len, CURLcode *err)
967{
968 struct connectdata *conn = data->conn;
969 struct ldapconninfo *li = conn->proto.ldapc;
970 struct ldapreqinfo *lr = data->req.p.ldap;
971 int rc;
972 LDAPMessage *msg = NULL;
973 BerElement *ber = NULL;
974 struct timeval tv = {0, 0};
975 struct berval bv, *bvals;
976 int binary = 0;
977 CURLcode result = CURLE_AGAIN;
978 int code;
979 char *info = NULL;
980
981 (void)len;
982 (void)buf;
983 (void)sockindex;
984
985 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
986 if(rc < 0) {
987 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
988 result = CURLE_RECV_ERROR;
989 }
990
991 *err = result;
992
993 /* error or timed out */
994 if(!msg)
995 return -1;
996
997 result = CURLE_OK;
998
999 switch(ldap_msgtype(msg)) {
1000 case LDAP_RES_SEARCH_RESULT:
1001 lr->msgid = 0;
1002 rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1003 if(rc) {
1004 failf(data, "LDAP local: search ldap_parse_result %s",
1005 ldap_err2string(rc));
1006 result = CURLE_LDAP_SEARCH_FAILED;
1007 break;
1008 }
1009
1010 /* store the latest code for later retrieval */
1011 data->info.httpcode = code;
1012
1013 switch(code) {
1014 case LDAP_SIZELIMIT_EXCEEDED:
1015 infof(data, "There are more than %d entries", lr->nument);
1016 /* FALLTHROUGH */
1017 case LDAP_SUCCESS:
1018 data->req.size = data->req.bytecount;
1019 break;
1020 default:
1021 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1022 info ? info : "");
1023 result = CURLE_LDAP_SEARCH_FAILED;
1024 break;
1025 }
1026 if(info)
1027 ldap_memfree(info);
1028 break;
1029 case LDAP_RES_SEARCH_ENTRY:
1030 lr->nument++;
1031 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1032 if(rc < 0) {
1033 result = CURLE_RECV_ERROR;
1034 break;
1035 }
1036
1037 result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1038 STRCONST("\n"));
1039 if(result)
1040 break;
1041
1042 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1043 rc == LDAP_SUCCESS;
1044 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1045 int i;
1046
1047 if(!bv.bv_val)
1048 break;
1049
1050 if(!bvals) {
1051 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1052 STRCONST(":\n"));
1053 if(result)
1054 break;
1055 continue;
1056 }
1057
1058 binary = bv.bv_len > 7 &&
1059 !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1060
1061 for(i = 0; bvals[i].bv_val != NULL; i++) {
1062 int binval = 0;
1063
1064 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1065 STRCONST(":"));
1066 if(result)
1067 break;
1068
1069 if(!binary) {
1070 /* check for leading or trailing whitespace */
1071 if(ISSPACE(bvals[i].bv_val[0]) ||
1072 ISSPACE(bvals[i].bv_val[bvals[i].bv_len - 1]))
1073 binval = 1;
1074 else {
1075 /* check for unprintable characters */
1076 unsigned int j;
1077 for(j = 0; j < bvals[i].bv_len; j++)
1078 if(!ISPRINT(bvals[i].bv_val[j])) {
1079 binval = 1;
1080 break;
1081 }
1082 }
1083 }
1084 if(binary || binval) {
1085 char *val_b64 = NULL;
1086 size_t val_b64_sz = 0;
1087
1088 /* Binary value, encode to base64. */
1089 if(bvals[i].bv_len)
1090 result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1091 &val_b64, &val_b64_sz);
1092 if(!result)
1093 result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1094 STRCONST("\n"));
1095 free(val_b64);
1096 }
1097 else
1098 result = client_write(data, STRCONST(" "),
1099 bvals[i].bv_val, bvals[i].bv_len,
1100 STRCONST("\n"));
1101 if(result)
1102 break;
1103 }
1104
1105 ber_memfree(bvals);
1106 bvals = NULL;
1107 if(!result)
1108 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1109 if(result)
1110 break;
1111 }
1112
1113 ber_free(ber, 0);
1114
1115 if(!result)
1116 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1117 if(!result)
1118 result = CURLE_AGAIN;
1119 break;
1120 }
1121
1122 ldap_msgfree(msg);
1123 *err = result;
1124 return result? -1: 0;
1125}
1126
1127#ifdef USE_SSL
1128static int
1129ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1130{
1131 sbiod->sbiod_pvt = arg;
1132 return 0;
1133}
1134
1135static int
1136ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1137{
1138 sbiod->sbiod_pvt = NULL;
1139 return 0;
1140}
1141
1142/* We don't need to do anything because libcurl does it already */
1143static int
1144ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1145{
1146 (void)sbiod;
1147 return 0;
1148}
1149
1150static int
1151ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
1152{
1153 (void)arg;
1154 if(opt == LBER_SB_OPT_DATA_READY) {
1155 struct Curl_easy *data = sbiod->sbiod_pvt;
1156 return Curl_ssl_data_pending(data->conn, FIRSTSOCKET);
1157 }
1158 return 0;
1159}
1160
1161static ber_slen_t
1162ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1163{
1164 struct Curl_easy *data = sbiod->sbiod_pvt;
1165 ber_slen_t ret = 0;
1166 if(data) {
1167 struct connectdata *conn = data->conn;
1168 if(conn) {
1169 struct ldapconninfo *li = conn->proto.ldapc;
1170 CURLcode err = CURLE_RECV_ERROR;
1171
1172 ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1173 if(ret < 0 && err == CURLE_AGAIN) {
1174 SET_SOCKERRNO(EWOULDBLOCK);
1175 }
1176 }
1177 }
1178 return ret;
1179}
1180
1181static ber_slen_t
1182ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1183{
1184 struct Curl_easy *data = sbiod->sbiod_pvt;
1185 ber_slen_t ret = 0;
1186 if(data) {
1187 struct connectdata *conn = data->conn;
1188 if(conn) {
1189 struct ldapconninfo *li = conn->proto.ldapc;
1190 CURLcode err = CURLE_SEND_ERROR;
1191 ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
1192 if(ret < 0 && err == CURLE_AGAIN) {
1193 SET_SOCKERRNO(EWOULDBLOCK);
1194 }
1195 }
1196 }
1197 return ret;
1198}
1199
1200static Sockbuf_IO ldapsb_tls =
1201{
1202 ldapsb_tls_setup,
1203 ldapsb_tls_remove,
1204 ldapsb_tls_ctrl,
1205 ldapsb_tls_read,
1206 ldapsb_tls_write,
1207 ldapsb_tls_close
1208};
1209#endif /* USE_SSL */
1210
1211#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1212