1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 * SPDX-License-Identifier: curl
24 ***************************************************************************/
26 #include "curl_setup.h"
28 #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
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.
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.
44 #include <curl/curl.h>
46 #include "vtls/vtls.h"
48 #include "curl_ldap.h"
49 #include "curl_base64.h"
51 #include "curl_sasl.h"
53 /* The last 3 #include files should be in this order */
54 #include "curl_printf.h"
55 #include "curl_memory.h"
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.
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
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.
75 /* #define CURL_OPENLDAP_DEBUG */
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 */
91 extern int ldap_pvt_url_scheme2proto(const char *);
92 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
96 static CURLcode oldap_setup_connection(struct Curl_easy *data,
97 struct connectdata *conn);
98 static CURLcode oldap_do(struct Curl_easy *data, bool *done);
99 static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
100 static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
101 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
102 static CURLcode oldap_disconnect(struct Curl_easy *data,
103 struct connectdata *conn, bool dead);
105 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
106 const struct bufref *initresp);
107 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
108 const struct bufref *resp);
109 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
110 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
112 static Curl_recv oldap_recv;
115 * LDAP protocol handler.
118 const struct Curl_handler Curl_handler_ldap = {
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 */
143 * LDAPS protocol handler.
146 const 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 */
170 /* SASL parameters for the ldap protocol */
171 static 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 */
184 struct ldapconninfo {
185 struct SASL sasl; /* SASL-related parameters */
186 LDAP *ld; /* Openldap connection handle. */
187 Curl_recv *recv; /* For stacking SSL handler */
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. */
203 * This is the ONLY way to change LDAP state!
205 static void state(struct Curl_easy *data, ldapstate newstate)
207 struct ldapconninfo *ldapc = data->conn->proto.ldapc;
209 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
210 /* for debug purposes */
211 static const char * const names[] = {
223 if(ldapc->state != newstate)
224 infof(data, "LDAP %p state change from %s to %s",
225 (void *)ldapc, names[ldapc->state], names[newstate]);
228 ldapc->state = newstate;
231 /* Map some particular LDAP error codes to CURLcode values. */
232 static CURLcode oldap_map_error(int rc, CURLcode result)
236 result = CURLE_OUT_OF_MEMORY;
238 case LDAP_INVALID_CREDENTIALS:
239 result = CURLE_LOGIN_DENIED;
241 case LDAP_PROTOCOL_ERROR:
242 result = CURLE_UNSUPPORTED_PROTOCOL;
244 case LDAP_INSUFFICIENT_ACCESS:
245 result = CURLE_REMOTE_ACCESS_DENIED;
251 static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
253 CURLcode result = CURLE_OK;
254 int rc = LDAP_URL_ERR_BADURL;
255 static const char * const url_errs[] = {
259 "unrecognized scheme",
260 "unbalanced delimiter",
263 "bad or missing attributes",
264 "bad or missing scope",
265 "bad or missing filter",
266 "bad or missing extensions"
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";
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]))
280 failf(data, "LDAP local: %s", msg);
285 /* Parse the login options. */
286 static CURLcode oldap_parse_login_options(struct connectdata *conn)
288 CURLcode result = CURLE_OK;
289 struct ldapconninfo *li = conn->proto.ldapc;
290 const char *ptr = conn->options;
292 while(!result && ptr && *ptr) {
293 const char *key = ptr;
296 while(*ptr && *ptr != '=')
301 while(*ptr && *ptr != ';')
304 if(checkprefix("AUTH=", key))
305 result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
307 result = CURLE_SETOPT_OPTION_SYNTAX;
313 return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result;
316 static CURLcode oldap_setup_connection(struct Curl_easy *data,
317 struct connectdata *conn)
321 struct ldapconninfo *li;
323 /* Early URL syntax check. */
324 result = oldap_url_parse(data, &lud);
325 ldap_free_urldesc(lud);
328 li = calloc(1, sizeof(struct ldapconninfo));
330 result = CURLE_OUT_OF_MEMORY;
332 li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
333 conn->proto.ldapc = li;
334 connkeep(conn, "OpenLDAP default");
336 /* Initialize the SASL storage */
337 Curl_sasl_init(&li->sasl, data, &saslldap);
339 /* Clear the TLS upgraded flag */
340 conn->bits.tls_upgraded = FALSE;
342 result = oldap_parse_login_options(conn);
350 * Get the SASL authentication challenge from the server credential buffer.
352 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
354 struct berval *servercred = data->conn->proto.ldapc->servercred;
356 if(!servercred || !servercred->bv_val)
357 return CURLE_WEIRD_SERVER_REPLY;
358 Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
363 * Sends an initial SASL bind request to the server.
365 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
366 const struct bufref *initresp)
368 struct connectdata *conn = data->conn;
369 struct ldapconninfo *li = conn->proto.ldapc;
370 CURLcode result = CURLE_OK;
372 struct berval *pcred = &cred;
375 cred.bv_val = (char *) Curl_bufref_ptr(initresp);
376 cred.bv_len = Curl_bufref_len(initresp);
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);
386 * Sends SASL continuation.
388 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
389 const struct bufref *resp)
391 struct connectdata *conn = data->conn;
392 struct ldapconninfo *li = conn->proto.ldapc;
393 CURLcode result = CURLE_OK;
395 struct berval *pcred = &cred;
398 cred.bv_val = (char *) Curl_bufref_ptr(resp);
399 cred.bv_len = Curl_bufref_len(resp);
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);
409 * Sends SASL bind cancellation.
411 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
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,
419 if(rc != LDAP_SUCCESS)
420 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
424 /* Starts LDAP simple bind. */
425 static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
427 CURLcode result = CURLE_OK;
428 struct connectdata *conn = data->conn;
429 struct ldapconninfo *li = conn->proto.ldapc;
431 struct berval passwd;
434 passwd.bv_val = NULL;
437 if(data->state.aptr.user) {
439 passwd.bv_val = conn->passwd;
440 passwd.bv_len = strlen(passwd.bv_val);
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);
448 result = oldap_map_error(rc,
449 data->state.aptr.user?
450 CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
454 /* Query the supported SASL authentication mechanisms. */
455 static CURLcode oldap_perform_mechs(struct Curl_easy *data)
457 CURLcode result = CURLE_OK;
458 struct ldapconninfo *li = data->conn->proto.ldapc;
460 static const char * const supportedSASLMechanisms[] = {
461 "supportedSASLMechanisms",
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);
471 result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
475 /* Starts SASL bind. */
476 static CURLcode oldap_perform_sasl(struct Curl_easy *data)
478 saslprogress progress = SASL_IDLE;
479 struct ldapconninfo *li = data->conn->proto.ldapc;
480 CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
482 state(data, OLDAP_SASL);
483 if(!result && progress != SASL_INPROGRESS)
484 result = CURLE_LOGIN_DENIED;
489 static Sockbuf_IO ldapsb_tls;
491 static bool ssl_installed(struct connectdata *conn)
493 return conn->proto.ldapc->recv != NULL;
496 static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
498 CURLcode result = CURLE_OK;
499 struct connectdata *conn = data->conn;
500 struct ldapconninfo *li = conn->proto.ldapc;
503 result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
504 FIRSTSOCKET, &ssldone);
506 state(data, newstate);
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];
522 /* Send the STARTTLS request */
523 static CURLcode oldap_perform_starttls(struct Curl_easy *data)
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);
529 if(rc == LDAP_SUCCESS)
530 state(data, OLDAP_STARTTLS);
532 result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
537 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
539 struct connectdata *conn = data->conn;
540 struct ldapconninfo *li = conn->proto.ldapc;
541 static const int version = LDAP_VERSION3;
544 #ifdef CURL_OPENLDAP_DEBUG
545 static int do_trace = -1;
550 hosturl = aprintf("ldap%s://%s:%d",
551 conn->handler->flags & PROTOPT_SSL? "s": "",
552 conn->host.name, conn->remote_port);
554 return CURLE_OUT_OF_MEMORY;
556 rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
558 failf(data, "LDAP local: Cannot connect to %s, %s",
559 hosturl, ldap_err2string(rc));
561 return CURLE_COULDNT_CONNECT;
566 #ifdef CURL_OPENLDAP_DEBUG
568 const char *env = getenv("CURL_OPENLDAP_TRACE");
569 do_trace = (env && strtol(env, NULL, 10) > 0);
572 ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
575 /* Try version 3 first. */
576 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
578 /* Do not chase referrals. */
579 ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
582 if(conn->handler->flags & PROTOPT_SSL)
583 return oldap_ssl_connect(data, OLDAP_SSL);
585 if(data->set.use_ssl) {
586 CURLcode result = oldap_perform_starttls(data);
588 if(!result || data->set.use_ssl != CURLUSESSL_TRY)
593 if(li->sasl.prefmech != SASL_AUTH_NONE)
594 return oldap_perform_mechs(data);
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);
601 /* Handle the supported SASL mechanisms query response */
602 static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
603 LDAPMessage *msg, int code)
605 struct connectdata *conn = data->conn;
606 struct ldapconninfo *li = conn->proto.ldapc;
608 BerElement *ber = NULL;
609 CURLcode result = CURLE_OK;
610 struct berval bv, *bvals;
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;
618 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
620 return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
621 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
623 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
630 for(i = 0; bvals[i].bv_val; i++) {
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;
643 case LDAP_RES_SEARCH_RESULT:
645 case LDAP_SIZELIMIT_EXCEEDED:
646 infof(data, "Too many authentication mechanisms\n");
649 case LDAP_NO_RESULTS_RETURNED:
650 if(Curl_sasl_can_authenticate(&li->sasl, data))
651 result = oldap_perform_sasl(data);
653 result = CURLE_LOGIN_DENIED;
656 result = oldap_map_error(code, CURLE_LOGIN_DENIED);
666 /* Handle a SASL bind response. */
667 static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
668 LDAPMessage *msg, int code)
670 struct connectdata *conn = data->conn;
671 struct ldapconninfo *li = conn->proto.ldapc;
672 CURLcode result = CURLE_OK;
673 saslprogress progress;
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);
683 result = Curl_sasl_continue(&li->sasl, data, code, &progress);
684 if(!result && progress != SASL_INPROGRESS)
685 state(data, OLDAP_STOP);
689 ber_bvfree(li->servercred);
693 /* Handle a simple bind response. */
694 static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
697 struct connectdata *conn = data->conn;
698 struct ldapconninfo *li = conn->proto.ldapc;
699 CURLcode result = CURLE_OK;
700 struct berval *bv = NULL;
703 if(code != LDAP_SUCCESS)
704 return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
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);
713 state(data, OLDAP_STOP);
720 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
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;
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);
734 case 0: /* Timed out. */
736 case LDAP_RES_SEARCH_ENTRY:
737 case LDAP_RES_SEARCH_REFERENCE:
740 li->msgid = 0; /* Nothing to abandon upon error. */
742 failf(data, "LDAP local: connecting ldap_result %s",
743 ldap_err2string(rc));
744 return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
749 /* Get error code from message. */
750 rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
754 /* store the latest code for later retrieval */
755 data->info.httpcode = code;
758 /* If protocol version 3 is not supported, fallback to version 2. */
759 if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
761 (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
763 li->sasl.prefmech == SASL_AUTH_NONE) {
764 static const int version = LDAP_VERSION2;
766 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
768 return oldap_perform_bind(data, OLDAP_BINDV2);
772 /* Handle response message according to current state. */
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);
782 result = oldap_perform_bind(data, OLDAP_BIND);
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);
792 result = oldap_perform_bind(data, OLDAP_BIND);
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);
807 state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
815 result = oldap_state_mechs_resp(data, msg, code);
818 result = oldap_state_sasl_resp(data, msg, code);
822 result = oldap_state_bind_resp(data, msg, code);
826 result = CURLE_COULDNT_CONNECT;
832 *done = li->state == OLDAP_STOP;
834 conn->recv[FIRSTSOCKET] = oldap_recv;
836 if(result && li->msgid) {
837 ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
843 static CURLcode oldap_disconnect(struct Curl_easy *data,
844 struct connectdata *conn,
845 bool dead_connection)
847 struct ldapconninfo *li = conn->proto.ldapc;
848 (void) dead_connection;
856 if(ssl_installed(conn)) {
858 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
859 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
862 ldap_unbind_ext(li->ld, NULL, NULL);
865 Curl_sasl_cleanup(conn, li->sasl.authused);
866 conn->proto.ldapc = NULL;
872 static CURLcode oldap_do(struct Curl_easy *data, bool *done)
874 struct connectdata *conn = data->conn;
875 struct ldapconninfo *li = conn->proto.ldapc;
876 struct ldapreqinfo *lr;
882 connkeep(conn, "OpenLDAP do");
884 infof(data, "LDAP local: %s", data->state.url);
886 result = oldap_url_parse(data, &lud);
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;
897 lr = calloc(1, sizeof(struct ldapreqinfo));
899 ldap_abandon_ext(li->ld, msgid, NULL, NULL);
900 result = CURLE_OUT_OF_MEMORY;
904 data->req.p.ldap = lr;
905 Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
913 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
916 struct connectdata *conn = data->conn;
917 struct ldapreqinfo *lr = data->req.p.ldap;
923 /* if there was a search in progress, abandon it */
925 struct ldapconninfo *li = conn->proto.ldapc;
926 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
929 data->req.p.ldap = NULL;
936 static 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)
941 CURLcode result = CURLE_OK;
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] == ' ')
948 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
950 data->req.bytecount += plen;
952 if(!result && value) {
953 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
955 data->req.bytecount += len;
957 if(!result && suffix) {
958 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
960 data->req.bytecount += slen;
965 static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
966 size_t len, CURLcode *err)
968 struct connectdata *conn = data->conn;
969 struct ldapconninfo *li = conn->proto.ldapc;
970 struct ldapreqinfo *lr = data->req.p.ldap;
972 LDAPMessage *msg = NULL;
973 BerElement *ber = NULL;
974 struct timeval tv = {0, 0};
975 struct berval bv, *bvals;
977 CURLcode result = CURLE_AGAIN;
985 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
987 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
988 result = CURLE_RECV_ERROR;
993 /* error or timed out */
999 switch(ldap_msgtype(msg)) {
1000 case LDAP_RES_SEARCH_RESULT:
1002 rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1004 failf(data, "LDAP local: search ldap_parse_result %s",
1005 ldap_err2string(rc));
1006 result = CURLE_LDAP_SEARCH_FAILED;
1010 /* store the latest code for later retrieval */
1011 data->info.httpcode = code;
1014 case LDAP_SIZELIMIT_EXCEEDED:
1015 infof(data, "There are more than %d entries", lr->nument);
1018 data->req.size = data->req.bytecount;
1021 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1023 result = CURLE_LDAP_SEARCH_FAILED;
1029 case LDAP_RES_SEARCH_ENTRY:
1031 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1033 result = CURLE_RECV_ERROR;
1037 result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1042 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1044 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1051 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1058 binary = bv.bv_len > 7 &&
1059 !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1061 for(i = 0; bvals[i].bv_val != NULL; i++) {
1064 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1070 /* check for leading or trailing whitespace */
1071 if(ISBLANK(bvals[i].bv_val[0]) ||
1072 ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
1075 /* check for unprintable characters */
1077 for(j = 0; j < bvals[i].bv_len; j++)
1078 if(!ISPRINT(bvals[i].bv_val[j])) {
1084 if(binary || binval) {
1085 char *val_b64 = NULL;
1086 size_t val_b64_sz = 0;
1088 /* Binary value, encode to base64. */
1090 result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1091 &val_b64, &val_b64_sz);
1093 result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1098 result = client_write(data, STRCONST(" "),
1099 bvals[i].bv_val, bvals[i].bv_len,
1108 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1116 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1118 result = CURLE_AGAIN;
1124 return result? -1: 0;
1129 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1131 sbiod->sbiod_pvt = arg;
1136 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1138 sbiod->sbiod_pvt = NULL;
1142 /* We don't need to do anything because libcurl does it already */
1144 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1151 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, 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);
1162 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1164 struct Curl_easy *data = sbiod->sbiod_pvt;
1167 struct connectdata *conn = data->conn;
1169 struct ldapconninfo *li = conn->proto.ldapc;
1170 CURLcode err = CURLE_RECV_ERROR;
1172 ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1173 if(ret < 0 && err == CURLE_AGAIN) {
1174 SET_SOCKERRNO(EWOULDBLOCK);
1182 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1184 struct Curl_easy *data = sbiod->sbiod_pvt;
1187 struct connectdata *conn = data->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);
1200 static Sockbuf_IO ldapsb_tls =
1209 #endif /* USE_SSL */
1211 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */