diff options
author | dartraiden <wowemuh@gmail.com> | 2022-06-10 01:53:35 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2022-06-10 02:24:49 +0300 |
commit | 39de82f0732a81d4dc96197bab4ca585a52c353a (patch) | |
tree | 8755c2bc4e9779166f30484f2d94c97c87e910a5 /libs/libcurl/src/openldap.c | |
parent | 3c5f696829216c7a113f71ff9099178c11817aca (diff) |
libcurl: update to 7.83.1
Diffstat (limited to 'libs/libcurl/src/openldap.c')
-rw-r--r-- | libs/libcurl/src/openldap.c | 373 |
1 files changed, 338 insertions, 35 deletions
diff --git a/libs/libcurl/src/openldap.c b/libs/libcurl/src/openldap.c index 0ffb6a36a2..4e92567706 100644 --- a/libs/libcurl/src/openldap.c +++ b/libs/libcurl/src/openldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2010, Howard Chu, <hyc@openldap.org> * * This software is licensed as described in the file COPYING, which @@ -46,6 +46,8 @@ #include "curl_ldap.h" #include "curl_base64.h" #include "connect.h" +#include "curl_sasl.h" +#include "strcase.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -76,6 +78,8 @@ typedef enum { OLDAP_SSL, /* Performing SSL handshake. */ OLDAP_STARTTLS, /* STARTTLS request sent. */ OLDAP_TLS, /* Performing TLS handshake. */ + OLDAP_MECHS, /* Get SASL authentication mechanisms. */ + OLDAP_SASL, /* SASL binding reply. */ OLDAP_BIND, /* Simple bind reply. */ OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ OLDAP_LAST /* Never used */ @@ -96,6 +100,13 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); static CURLcode oldap_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); + static Curl_recv oldap_recv; /* @@ -154,10 +165,26 @@ const struct Curl_handler Curl_handler_ldaps = { }; #endif +/* SASL parameters for the ldap protocol */ +static const struct SASLproto saslldap = { + "ldap", /* The service name */ + oldap_perform_auth, /* Send authentication command */ + oldap_continue_auth, /* Send authentication continuation */ + oldap_cancel_auth, /* Send authentication cancellation */ + oldap_get_message, /* Get SASL response message */ + 0, /* Maximum initial response length (no max) */ + LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ + LDAP_SUCCESS, /* Code to receive upon authentication success */ + SASL_AUTH_NONE, /* Default mechanisms */ + 0 /* Configuration flags */ +}; + struct ldapconninfo { + struct SASL sasl; /* SASL-related parameters */ LDAP *ld; /* Openldap connection handle. */ Curl_recv *recv; /* For stacking SSL handler */ Curl_send *send; + struct berval *servercred; /* SASL data from server. */ ldapstate state; /* Current machine state. */ int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ int msgid; /* Current message id. */ @@ -184,6 +211,8 @@ static void state(struct Curl_easy *data, ldapstate newstate) "SSL", "STARTTLS", "TLS", + "MECHS", + "SASL", "BIND", "BINDV2", /* LAST */ @@ -251,6 +280,37 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) return result; } +/* Parse the login options. */ +static CURLcode oldap_parse_login_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = conn->proto.ldapc; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(checkprefix("AUTH=", key)) + result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value); + else + result = CURLE_SETOPT_OPTION_SYNTAX; + + if(*ptr == ';') + ptr++; + } + + return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result; +} + static CURLcode oldap_setup_connection(struct Curl_easy *data, struct connectdata *conn) { @@ -271,14 +331,94 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data, conn->proto.ldapc = li; connkeep(conn, "OpenLDAP default"); + /* Initialize the SASL storage */ + Curl_sasl_init(&li->sasl, data, &saslldap); + /* Clear the TLS upgraded flag */ conn->bits.tls_upgraded = FALSE; + + result = oldap_parse_login_options(conn); } } return result; } +/* + * Get the SASL authentication challenge from the server credential buffer. + */ +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out) +{ + struct berval *servercred = data->conn->proto.ldapc->servercred; + + if(!servercred || !servercred->bv_val) + return CURLE_WEIRD_SERVER_REPLY; + Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL); + return CURLE_OK; +} + +/* + * Sends an initial SASL bind request to the server. + */ +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(initresp); + cred.bv_len = Curl_bufref_len(initresp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL continuation. + */ +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(resp); + cred.bv_len = Curl_bufref_len(resp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL bind cancellation. + */ +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = CURLE_OK; + int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL, + &li->msgid); + + (void)mech; + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + /* Starts LDAP simple bind. */ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) { @@ -292,7 +432,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(conn->bits.user_passwd) { + if(data->state.aptr.user) { binddn = conn->user; passwd.bv_val = conn->passwd; passwd.bv_len = strlen(passwd.bv_val); @@ -304,11 +444,45 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) state(data, newstate); else result = oldap_map_error(rc, - conn->bits.user_passwd? + data->state.aptr.user? CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND); return result; } +/* Query the supported SASL authentication mechanisms. */ +static CURLcode oldap_perform_mechs(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = data->conn->proto.ldapc; + int rc; + static const char * const supportedSASLMechanisms[] = { + "supportedSASLMechanisms", + NULL + }; + + rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", + (char **) supportedSASLMechanisms, 0, + NULL, NULL, NULL, 0, &li->msgid); + if(rc == LDAP_SUCCESS) + state(data, OLDAP_MECHS); + else + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + return result; +} + +/* Starts SASL bind. */ +static CURLcode oldap_perform_sasl(struct Curl_easy *data) +{ + saslprogress progress = SASL_IDLE; + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress); + + state(data, OLDAP_SASL); + if(!result && progress != SASL_INPROGRESS) + result = CURLE_LOGIN_DENIED; + return result; +} + #ifdef USE_SSL static Sockbuf_IO ldapsb_tls; @@ -414,11 +588,106 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) } #endif + if(li->sasl.prefmech != SASL_AUTH_NONE) + return oldap_perform_mechs(data); + /* 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); } +/* Handle the supported SASL mechanisms query response */ +static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + int rc; + BerElement *ber = NULL; + CURLcode result = CURLE_OK; + struct berval bv, *bvals; + + switch(ldap_msgtype(msg)) { + case LDAP_RES_SEARCH_ENTRY: + /* Got a list of supported SASL mechanisms. */ + if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED) + return CURLE_LOGIN_DENIED; + + rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); + if(rc < 0) + return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING); + for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { + int i; + + if(!bv.bv_val) + break; + + if(bvals) { + for(i = 0; bvals[i].bv_val; i++) { + size_t llen; + unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val, + bvals[i].bv_len, &llen); + if(bvals[i].bv_len == llen) + li->sasl.authmechs |= mech; + } + ber_memfree(bvals); + } + } + ber_free(ber, 0); + break; + + case LDAP_RES_SEARCH_RESULT: + switch(code) { + case LDAP_SIZELIMIT_EXCEEDED: + infof(data, "Too many authentication mechanisms\n"); + /* FALLTHROUGH */ + case LDAP_SUCCESS: + case LDAP_NO_RESULTS_RETURNED: + if(Curl_sasl_can_authenticate(&li->sasl, data)) + result = oldap_perform_sasl(data); + else + result = CURLE_LOGIN_DENIED; + break; + default: + result = oldap_map_error(code, CURLE_LOGIN_DENIED); + break; + } + break; + default: + break; + } + return result; +} + +/* Handle a SASL bind response. */ +static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + saslprogress progress; + int rc; + + li->servercred = NULL; + rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc)); + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + } + else { + result = Curl_sasl_continue(&li->sasl, data, code, &progress); + if(!result && progress != SASL_INPROGRESS) + state(data, OLDAP_STOP); + } + + if(li->servercred) + ber_bvfree(li->servercred); + return result; +} + /* Handle a simple bind response. */ static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, int code) @@ -459,12 +728,20 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) { /* 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); + switch(rc) { + case 0: /* Timed out. */ + return CURLE_OK; + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + break; + default: + li->msgid = 0; /* Nothing to abandon upon error. */ + if(rc < 0) { + failf(data, "LDAP local: connecting ldap_result %s", + ldap_err2string(rc)); + return oldap_map_error(rc, CURLE_COULDNT_CONNECT); + } + break; } /* Get error code from message. */ @@ -477,11 +754,11 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) } /* If protocol version 3 is not supported, fallback to version 2. */ - if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 + if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 && #ifdef USE_SSL - && (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) + (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) && #endif - ) { + li->sasl.prefmech == SASL_AUTH_NONE) { static const int version = LDAP_VERSION2; ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); @@ -496,13 +773,19 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) #ifdef USE_SSL case OLDAP_SSL: result = oldap_ssl_connect(data, OLDAP_SSL); - if(!result && ssl_installed(conn)) - result = oldap_perform_bind(data, OLDAP_BIND); + if(!result && ssl_installed(conn)) { + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else + result = oldap_perform_bind(data, OLDAP_BIND); + } break; case OLDAP_STARTTLS: if(code != LDAP_SUCCESS) { if(data->set.use_ssl != CURLUSESSL_TRY) result = oldap_map_error(code, CURLE_USE_SSL_FAILED); + else if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); else result = oldap_perform_bind(data, OLDAP_BIND); break; @@ -514,7 +797,9 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) result = oldap_map_error(code, CURLE_USE_SSL_FAILED); else if(ssl_installed(conn)) { conn->bits.tls_upgraded = TRUE; - if(conn->bits.user_passwd) + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else if(data->state.aptr.user) result = oldap_perform_bind(data, OLDAP_BIND); else { state(data, OLDAP_STOP); /* Version 3 supported: no bind required */ @@ -524,6 +809,12 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) break; #endif + case OLDAP_MECHS: + result = oldap_state_mechs_resp(data, msg, code); + break; + case OLDAP_SASL: + result = oldap_state_sasl_resp(data, msg, code); + break; case OLDAP_BIND: case OLDAP_BINDV2: result = oldap_state_bind_resp(data, msg, code); @@ -540,6 +831,10 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) if(*done) conn->recv[FIRSTSOCKET] = oldap_recv; + if(result && li->msgid) { + ldap_abandon_ext(li->ld, li->msgid, NULL, NULL); + li->msgid = 0; + } return result; } @@ -549,6 +844,9 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, { struct ldapconninfo *li = conn->proto.ldapc; (void) dead_connection; +#ifndef USE_SSL + (void)data; +#endif if(li) { if(li->ld) { @@ -562,6 +860,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, ldap_unbind_ext(li->ld, NULL, NULL); li->ld = NULL; } + Curl_sasl_cleanup(conn, li->sasl.authused); conn->proto.ldapc = NULL; free(li); } @@ -632,21 +931,21 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, return CURLE_OK; } -static CURLcode client_write(struct Curl_easy *data, const char *prefix, - const char *value, size_t len, const char *suffix) +static CURLcode client_write(struct Curl_easy *data, + const char *prefix, size_t plen, + const char *value, size_t len, + const char *suffix, size_t slen) { CURLcode result = CURLE_OK; - size_t l; if(prefix) { - l = strlen(prefix); /* If we have a zero-length value and the prefix ends with a space separator, drop the latter. */ - if(!len && l && prefix[l - 1] == ' ') - l--; - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, l); + if(!len && plen && prefix[plen - 1] == ' ') + plen--; + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); if(!result) - data->req.bytecount += l; + data->req.bytecount += plen; } if(!result && value) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); @@ -654,10 +953,9 @@ static CURLcode client_write(struct Curl_easy *data, const char *prefix, data->req.bytecount += len; } if(!result && suffix) { - l = strlen(suffix); - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, l); + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); if(!result) - data->req.bytecount += l; + data->req.bytecount += slen; } return result; } @@ -734,7 +1032,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; } - result = client_write(data, "DN: ", bv.bv_val, bv.bv_len, "\n"); + result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, + STRCONST("\n")); if(result) break; @@ -747,7 +1046,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; if(!bvals) { - result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":\n"); + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":\n")); if(result) break; continue; @@ -759,7 +1059,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, for(i = 0; bvals[i].bv_val != NULL; i++) { int binval = 0; - result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":"); + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":")); if(result) break; @@ -784,15 +1085,17 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, /* Binary value, encode to base64. */ if(bvals[i].bv_len) - result = Curl_base64_encode(data, bvals[i].bv_val, bvals[i].bv_len, + result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len, &val_b64, &val_b64_sz); if(!result) - result = client_write(data, ": ", val_b64, val_b64_sz, "\n"); + result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, + STRCONST("\n")); free(val_b64); } else - result = client_write(data, " ", - bvals[i].bv_val, bvals[i].bv_len, "\n"); + result = client_write(data, STRCONST(" "), + bvals[i].bv_val, bvals[i].bv_len, + STRCONST("\n")); if(result) break; } @@ -800,7 +1103,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, ber_memfree(bvals); bvals = NULL; if(!result) - result = client_write(data, "\n", NULL, 0, NULL); + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); if(result) break; } @@ -808,7 +1111,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, ber_free(ber, 0); if(!result) - result = client_write(data, "\n", NULL, 0, NULL); + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); if(!result) result = CURLE_AGAIN; break; |