summaryrefslogtreecommitdiff
path: root/lib/openldap.c
diff options
context:
space:
mode:
authorPatrick Monnerat <patrick@monnerat.net>2021-11-25 15:03:56 +0100
committerDaniel Stenberg <daniel@haxx.se>2021-11-26 14:32:12 +0100
commitac89965f996161f75403ddb3a2ad6d0e8d146a4c (patch)
treea7b1bbdc7715b85156680076c3357cdf9f10f481 /lib/openldap.c
parent76d6e719d9545cdb95fb4473cc3899a1612cb996 (diff)
downloadcurl-ac89965f996161f75403ddb3a2ad6d0e8d146a4c.tar.gz
openldap: handle connect phase with a state machine
Closes #8054
Diffstat (limited to 'lib/openldap.c')
-rw-r--r--lib/openldap.c324
1 files changed, 213 insertions, 111 deletions
diff --git a/lib/openldap.c b/lib/openldap.c
index fb5e743c2..ba632d85b 100644
--- a/lib/openldap.c
+++ b/lib/openldap.c
@@ -70,6 +70,15 @@
*/
/* #define CURL_OPENLDAP_DEBUG */
+/* Machine states. */
+typedef enum {
+ OLDAP_STOP, /* Do nothing state, stops the state machine */
+ OLDAP_SSL, /* Performing SSL handshake. */
+ OLDAP_BIND, /* Simple bind reply. */
+ OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */
+ OLDAP_LAST /* Never used */
+} ldapstate;
+
#ifndef _LDAP_PVT_H
extern int ldap_pvt_url_scheme2proto(const char *);
extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
@@ -158,14 +167,12 @@ static const char *url_errs[] = {
};
struct ldapconninfo {
- LDAP *ld;
- Curl_recv *recv; /* for stacking SSL handler */
+ LDAP *ld; /* Openldap connection handle. */
+ Curl_recv *recv; /* For stacking SSL handler */
Curl_send *send;
- int proto;
- int msgid;
- bool ssldone;
- bool sslinst;
- bool didbind;
+ ldapstate state; /* Current machine state. */
+ int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
+ int msgid; /* Current message id. */
};
struct ldapreqinfo {
@@ -173,6 +180,53 @@ struct ldapreqinfo {
int nument;
};
+/*
+ * state()
+ *
+ * This is the ONLY way to change LDAP state!
+ */
+static void state(struct Curl_easy *data, ldapstate newstate)
+{
+ struct ldapconninfo *ldapc = data->conn->proto.ldapc;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "STOP",
+ "SSL",
+ "BIND",
+ "BINDV2",
+ /* LAST */
+ };
+
+ if(ldapc->state != newstate)
+ infof(data, "LDAP %p state change from %s to %s",
+ (void *)ldapc, names[ldapc->state], names[newstate]);
+#endif
+
+ ldapc->state = newstate;
+}
+
+/* Map some particular LDAP error codes to CURLcode values. */
+static CURLcode oldap_map_error(int rc, CURLcode result)
+{
+ switch(rc) {
+ case LDAP_NO_MEMORY:
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ case LDAP_INVALID_CREDENTIALS:
+ result = CURLE_LOGIN_DENIED;
+ break;
+ case LDAP_PROTOCOL_ERROR:
+ result = CURLE_UNSUPPORTED_PROTOCOL;
+ break;
+ case LDAP_INSUFFICIENT_ACCESS:
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ break;
+ }
+ return result;
+}
+
static CURLcode oldap_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
@@ -205,8 +259,69 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data,
return CURLE_OK;
}
+/* Starts LDAP simple bind. */
+static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ char *binddn = NULL;
+ struct berval passwd;
+ int rc;
+
+ passwd.bv_val = NULL;
+ passwd.bv_len = 0;
+
+ if(conn->bits.user_passwd) {
+ binddn = conn->user;
+ passwd.bv_val = conn->passwd;
+ passwd.bv_len = strlen(passwd.bv_val);
+ }
+
+ rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &li->msgid);
+ if(rc == LDAP_SUCCESS)
+ state(data, newstate);
+ else
+ result = oldap_map_error(rc,
+ conn->bits.user_passwd?
+ CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
+ return result;
+}
+
#ifdef USE_SSL
static Sockbuf_IO ldapsb_tls;
+
+static bool ssl_installed(struct connectdata *conn)
+{
+ return conn->proto.ldapc->recv != NULL;
+}
+
+static CURLcode oldap_ssl_connect(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ bool ssldone = 0;
+
+ result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+ FIRSTSOCKET, &ssldone);
+ if(!result) {
+ state(data, OLDAP_SSL);
+
+ if(ssldone) {
+ Sockbuf *sb;
+
+ /* Install the libcurl SSL handlers into the sockbuf. */
+ ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+ ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+ li->recv = conn->recv[FIRSTSOCKET];
+ li->send = conn->send[FIRSTSOCKET];
+ }
+ }
+
+ return result;
+}
#endif
static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
@@ -216,6 +331,9 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
int rc, proto = LDAP_VERSION3;
char hosturl[1024];
char *ptr;
+#ifdef CURL_OPENLDAP_DEBUG
+ static int do_trace = -1;
+#endif
(void)done;
@@ -226,15 +344,6 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
conn->host.name, conn->remote_port);
-#ifdef CURL_OPENLDAP_DEBUG
- static int do_trace = 0;
- const char *env = getenv("CURL_OPENLDAP_TRACE");
- do_trace = (env && strtol(env, NULL, 10) > 0);
- if(do_trace) {
- ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
- }
-#endif
-
rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
if(rc) {
failf(data, "LDAP local: Cannot connect to %s, %s",
@@ -242,125 +351,118 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
return CURLE_COULDNT_CONNECT;
}
+#ifdef CURL_OPENLDAP_DEBUG
+ if(do_trace < 0) {
+ const char *env = getenv("CURL_OPENLDAP_TRACE");
+ do_trace = (env && strtol(env, NULL, 10) > 0);
+ }
+ if(do_trace)
+ ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
+#endif
+
ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
#ifdef USE_SSL
- if(conn->handler->flags & PROTOPT_SSL) {
- CURLcode result;
- result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
- FIRSTSOCKET, &li->ssldone);
- if(result)
- return result;
- }
+ if(conn->handler->flags & PROTOPT_SSL)
+ return oldap_ssl_connect(data);
#endif
- return CURLE_OK;
+ /* Force bind even if anonymous bind is not needed in protocol version 3
+ to detect missing version 3 support. */
+ return oldap_perform_bind(data, OLDAP_BIND);
}
-static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
+/* Handle a simple bind response. */
+static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
+ int code)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = conn->proto.ldapc;
- LDAPMessage *msg = NULL;
- struct timeval tv = {0, 1}, *tvp;
- int rc, err;
- char *info = NULL;
+ CURLcode result = CURLE_OK;
+ struct berval *bv = NULL;
+ int rc;
-#ifdef USE_SSL
- if(conn->handler->flags & PROTOPT_SSL) {
- /* Is the SSL handshake complete yet? */
- if(!li->ssldone) {
- CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
- FIRSTSOCKET,
- &li->ssldone);
- if(result || !li->ssldone)
- return result;
- }
+ if(code != LDAP_SUCCESS)
+ return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
- /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
- if(!li->sslinst) {
- Sockbuf *sb;
- ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
- ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
- li->sslinst = TRUE;
- li->recv = conn->recv[FIRSTSOCKET];
- li->send = conn->send[FIRSTSOCKET];
- }
+ rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
+ ldap_err2string(rc));
+ result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
}
-#endif
+ else
+ state(data, OLDAP_STOP);
- tvp = &tv;
-
- retry:
- if(!li->didbind) {
- char *binddn;
- struct berval passwd;
+ if(bv)
+ ber_bvfree(bv);
+ return result;
+}
- if(conn->bits.user_passwd) {
- binddn = conn->user;
- passwd.bv_val = conn->passwd;
- passwd.bv_len = strlen(passwd.bv_val);
- }
- else {
- binddn = NULL;
- passwd.bv_val = NULL;
- passwd.bv_len = 0;
+static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ LDAPMessage *msg = NULL;
+ struct timeval tv = {0, 0};
+ int code = LDAP_SUCCESS;
+ int rc;
+
+ if(li->state != OLDAP_SSL) {
+ /* Get response to last command. */
+ rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
+ if(!rc)
+ return CURLE_OK; /* Timed out. */
+ if(rc < 0) {
+ failf(data, "LDAP local: connecting ldap_result %s",
+ ldap_err2string(rc));
+ return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
}
- rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
- NULL, NULL, &li->msgid);
- if(rc)
- return CURLE_LDAP_CANNOT_BIND;
- li->didbind = TRUE;
- if(tvp)
- return CURLE_OK;
- }
- rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
- if(rc < 0) {
- failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
- return CURLE_LDAP_CANNOT_BIND;
- }
- if(rc == 0) {
- /* timed out */
- return CURLE_OK;
- }
+ /* Get error code from message. */
+ rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
+ if(rc)
+ code = rc;
- rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
- if(rc) {
- failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
- return CURLE_LDAP_CANNOT_BIND;
- }
+ /* If protocol version 3 is not supported, fallback to version 2. */
+ if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2) {
+ static const int version = LDAP_VERSION2;
- /* Try to fallback to LDAPv2? */
- if(err == LDAP_PROTOCOL_ERROR) {
- int proto;
- ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
- if(proto == LDAP_VERSION3) {
- if(info) {
- ldap_memfree(info);
- info = NULL;
- }
- proto = LDAP_VERSION2;
- ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
- li->didbind = FALSE;
- goto retry;
+ ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ ldap_msgfree(msg);
+ return oldap_perform_bind(data, OLDAP_BINDV2);
}
}
- if(err) {
- failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
- info ? info : "");
- if(info)
- ldap_memfree(info);
- return CURLE_LOGIN_DENIED;
+ /* Handle response message according to current state. */
+ switch(li->state) {
+
+#ifdef USE_SSL
+ case OLDAP_SSL:
+ result = oldap_ssl_connect(data);
+ if(!result && ssl_installed(conn))
+ result = oldap_perform_bind(data, OLDAP_BIND);
+ break;
+#endif
+
+ case OLDAP_BIND:
+ case OLDAP_BINDV2:
+ result = oldap_state_bind_resp(data, msg, code);
+ break;
+ default:
+ /* internal error */
+ result = CURLE_COULDNT_CONNECT;
+ break;
}
- if(info)
- ldap_memfree(info);
- conn->recv[FIRSTSOCKET] = oldap_recv;
- *done = TRUE;
+ ldap_msgfree(msg);
- return CURLE_OK;
+ *done = li->state == OLDAP_STOP;
+ if(*done)
+ conn->recv[FIRSTSOCKET] = oldap_recv;
+
+ return result;
}
static CURLcode oldap_disconnect(struct Curl_easy *data,
@@ -373,7 +475,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
if(li) {
if(li->ld) {
#ifdef USE_SSL
- if(conn->ssl[FIRSTSOCKET].use) {
+ if(ssl_installed(conn)) {
Sockbuf *sb;
ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);