diff --git a/docs/Makefile.am b/docs/Makefile.am index 01c223ccb3..fdc4511b5c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -53,6 +53,7 @@ INTERNALDOCS = \ internals/CLIENT-WRITERS.md \ internals/CODE_STYLE.md \ internals/CONNECTION-FILTERS.md \ + internals/CREDENTIALS.md \ internals/CURLX.md \ internals/DYNBUF.md \ internals/HASH.md \ diff --git a/docs/internals/CREDENTIALS.md b/docs/internals/CREDENTIALS.md new file mode 100644 index 0000000000..95c12cb47c --- /dev/null +++ b/docs/internals/CREDENTIALS.md @@ -0,0 +1,75 @@ + + +# curl `creds` + +Authorization credentials are kept in `struct Curl_creds`. This contains: + +* `user`: the username, maybe the empty string +* `passwd`: the password, maybe the empty string +* `sasl_authzid`: the SASL `authz` value, maybe the empty string +* `oauth_bearer`: the OAUTH bearer token, maybe the empty string +* `source`: where the credentials from +* `refcount`: a reference counter to link/unlink `creds` + +A `creds` with all values empty is equivalent to NULL, e.g. no `creds` +instance. With reference counting, `creds` can be linked in several places. + +Two `creds` are the same if all values are equal apart from `source` +and `refcount`. The comparison of strings is done via `Curl_timestrcmp()` +to prevent side channel attacks. + +## `creds` locations + +Credentials are kept in three places: + +* `data->state.creds`: the credentials to use for the transfer in talking + to the `origin` (see PEERS) +* `conn->creds`: the credentials tied to a connection (more below) +* `conn->*_proxy.creds`: credentials used to talk to the `conn->*_proxy.peer` + +### `data->state.creds` + +This `creds` instance is created when the transfer starts looking for a +suitable connection. For an `easy_perform()` this may happen several times +if, for example, http redirects are followed. + +When an `easy_perform()` starts, the transfer's `data->state.initial_origin` +peer is cleared. When creating the connection, `conn->origin` is calculated +(e.g. who the request talks to). If `data->state.initial_origin` is not +set, the first `conn->origin` is linked there. Now `libcurl` knows where +the transfer initially talked to on all possible subsequent requests. + +Credential information from `CURLOPT_*` settings is only applicable for the +initial origin. Any followup request going to another origin must not +use it. Therefore `data->state.creds` is *only* created from `CURLOPT_*` +when current origin and initial origin match. + +Without credentials from `CURLOPT_*`, the URL is inspected for user and +password and `netrc` is consulted as well (when built in). + +### `conn->creds` + +Once `data->state.creds` is known, the connection credentials are +determined. For protocols that tie authorization to everything send +on a connection (protocols without flag `PROTOPT_CREDSPERREQUEST`), +`conn->creds` is linked to `data->state.creds`. Only connections +carrying the same credentials may be reused. + +Protocol with flag `PROTOPT_CREDSPERREQUEST` leave `conn->creds` empty, +as connections for such protocols may be reused with different +credentials. + +That being said, there are authentication schemes like `NTLM` and +`NEGOTIATE` that tie credentials to a connection. Those do set `conn->creds` +once they start to operate, preventing connection reuse from then on +for transfers with different credentials. + +### `conn->*_proxy.creds` + +Those are set during connection setup from the `CURLOPT_*` values. They +do not require any "initial origin" handling as the origin of a proxy +does not change for a transfer. diff --git a/lib/Makefile.inc b/lib/Makefile.inc index f1b0ef8f0d..2c7259af0d 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -161,6 +161,7 @@ LIB_CFILES = \ connect.c \ content_encoding.c \ cookie.c \ + creds.c \ cshutdn.c \ curl_addrinfo.c \ curl_endian.c \ @@ -293,6 +294,7 @@ LIB_HFILES = \ connect.h \ content_encoding.h \ cookie.h \ + creds.h \ curl_addrinfo.h \ curl_ctype.h \ curl_endian.h \ diff --git a/lib/connect.c b/lib/connect.c index c36a7e0381..e0f93d3c46 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -387,8 +387,7 @@ connect_sub_chain: result = Curl_cf_socks_proxy_insert_after( cf, data, dest, cf->conn->ip_version, cf->conn->socks_proxy.proxytype, - cf->conn->socks_proxy.user, - cf->conn->socks_proxy.passwd); + cf->conn->socks_proxy.creds); CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", dest->hostname, dest->port, result); diff --git a/lib/creds.c b/lib/creds.c new file mode 100644 index 0000000000..fe8693a97e --- /dev/null +++ b/lib/creds.c @@ -0,0 +1,183 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include /* for offsetof() */ + +#include "creds.h" +#include "curl_trc.h" +#include "strcase.h" +#include "urldata.h" + + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *sasl_authzid, + const char *oauth_bearer, + uint8_t source, + struct Curl_creds **pcreds) +{ + struct Curl_creds *creds = NULL; + size_t ulen = user ? strlen(user) : 0; + size_t plen = passwd ? strlen(passwd) : 0; + size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; + size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; + char *s, *buf; + CURLcode result = CURLE_OK; + + Curl_creds_unlink(pcreds); + + /* Everything empty/NULL, this is the NULL credential */ + if(!ulen && !plen && !salen && !olen) + goto out; + + if((ulen > CURL_MAX_INPUT_LENGTH) || + (plen > CURL_MAX_INPUT_LENGTH) || + (salen > CURL_MAX_INPUT_LENGTH) || + (olen > CURL_MAX_INPUT_LENGTH)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* NUL terminator for user already part of struct */ + creds = curlx_calloc(1, sizeof(*creds) + + ulen + plen + 1 + salen + 1 + olen + 1); + if(!creds) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + creds->refcount = 1; + creds->source = source; + /* Some compilers try to be too smart about our dynamic struct size */ + buf = ((char *)creds) + offsetof(struct Curl_creds, buf); + creds->user = s = buf; + if(ulen) + memcpy(s, CURL_UNCONST(user), ulen + 1); + creds->passwd = s = buf + ulen + 1; + if(plen) + memcpy(s, CURL_UNCONST(passwd), plen + 1); + creds->sasl_authzid = s = buf + ulen + 1 + plen + 1; + if(salen) + memcpy(s, CURL_UNCONST(sasl_authzid), salen + 1); + creds->oauth_bearer = s = buf + ulen + 1 + plen + 1 + salen + 1; + if(olen) + memcpy(s, CURL_UNCONST(oauth_bearer), olen + 1); + +out: + if(!result) + *pcreds = creds; + else + Curl_creds_unlink(&creds); + return result; +} + +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out) +{ + struct Curl_creds *creds_out = NULL; + CURLcode result; + + if(!user || !user[0]) + user = Curl_creds_user(creds_in); + if(!passwd || !passwd[0]) + passwd = Curl_creds_passwd(creds_in); + result = Curl_creds_create(user, passwd, + Curl_creds_sasl_authzid(creds_in), + Curl_creds_oauth_bearer(creds_in), + source, &creds_out); + Curl_creds_link(pcreds_out, creds_out); + Curl_creds_unlink(&creds_out); + return result; +} + +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src) +{ + if(*pdest != src) { + Curl_creds_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_creds_unlink(struct Curl_creds **pcreds) +{ + if(*pcreds) { + struct Curl_creds *creds = *pcreds; + + DEBUGASSERT(creds->refcount); + *pcreds = NULL; + if(creds->refcount) + creds->refcount--; + if(!creds->refcount) { + curlx_free(creds); + } + } +} + +bool Curl_creds_same_user(struct Curl_creds *creds, const char *user) +{ + return creds && !Curl_timestrcmp(creds->user, user); +} + +bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd) +{ + return creds && !Curl_timestrcmp(creds->passwd, passwd); +} + +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return (c1 == c2) || + (c1 && c2 && + !Curl_timestrcmp(c1->user, c2->user) && + !Curl_timestrcmp(c1->passwd, c2->passwd) && + !Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) && + !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer)); +} + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg) +{ + if(creds) { + CURL_TRC_M(data, "%s: user=%s, passwd=%s, " + "sasl_authzid=%s, oauth_bearer=%s, source=%d", + msg, + Curl_creds_user(creds), + Curl_creds_has_passwd(creds) ? "***" : "", + Curl_creds_sasl_authzid(creds), + Curl_creds_oauth_bearer(creds), + creds->source); + } + else + CURL_TRC_M(data, "%s: -", msg); +} + +#endif diff --git a/lib/creds.h b/lib/creds.h new file mode 100644 index 0000000000..2eb5998cc8 --- /dev/null +++ b/lib/creds.h @@ -0,0 +1,86 @@ +#ifndef HEADER_CURL_CREDS_H +#define HEADER_CURL_CREDS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; + +#define CREDS_NONE 0 /* used for default username/passwd */ +#define CREDS_URL 1 /* username/passwd from URL */ +#define CREDS_OPTION 2 /* username/passwd set with a CURLOPT_ */ +#define CREDS_NETRC 3 /* username/passwd found in netrc */ + +struct Curl_creds { + const char *user; /* non-NULL, maybe empty string */ + const char *passwd; /* non-NULL, maybe empty string */ + const char *sasl_authzid; /* non-NULL, maybe empty string */ + const char *oauth_bearer; /* non-NULL, maybe empty string */ + uint32_t refcount; + uint8_t source; /* CREDS_* value */ + char buf[1]; +}; + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *sasl_authzid, + const char *oauth_bearer, + uint8_t source, + struct Curl_creds **pcreds); + +/* Create credentials by overriding `user` and/or `passwd` in `creds_in` */ +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out); + +/* Unlink any creds in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src); + +/* Drop a reference, creds may be passed as NULL */ +void Curl_creds_unlink(struct Curl_creds **pcreds); + +/* TRUE if both creds are NULL or have same username and password. */ +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); +bool Curl_creds_same_user(struct Curl_creds *creds, const char *user); +bool Curl_creds_same_passwd(struct Curl_creds *creds, const char *passwd); + + +/* Provides properties for creds or, if creds is NULL, the empty string */ +#define Curl_creds_has_user(c) ((c) && (c)->user[0]) +#define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) +#define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) +#define Curl_creds_user(c) ((c)? (c)->user : "") +#define Curl_creds_passwd(c) ((c)? (c)->passwd : "") +#define Curl_creds_sasl_authzid(c) ((c)? (c)->sasl_authzid : "") +#define Curl_creds_oauth_bearer(c) ((c)? (c)->oauth_bearer : "") + + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg); +#endif + +#endif /* HEADER_CURL_CREDS_H */ diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 00eff8e6a9..6c955446fe 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -276,7 +276,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) { /* Have credentials been provided? */ - if(data->conn->user[0]) + if(data->conn->creds) return TRUE; /* EXTERNAL can authenticate without a username and/or password */ @@ -299,13 +299,15 @@ struct sasl_ctx { static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) { - if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { + if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && + !Curl_creds_has_passwd(sctx->conn->creds)) { sctx->mech = SASL_MECH_STRING_EXTERNAL; sctx->state1 = SASL_EXTERNAL; sctx->sasl->authused = SASL_MECH_EXTERNAL; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_external_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_external_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -316,7 +318,7 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) { if((sctx->enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && - Curl_auth_user_contains_domain(sctx->conn->user)) { + Curl_auth_user_contains_domain(sctx->conn->creds)) { const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : sctx->sasl->params->service; @@ -330,8 +332,7 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn); sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_gssapi_user_message(data, sctx->conn->creds, service, sctx->conn->origin->hostname, (bool)sctx->sasl->mutual_auth, @@ -375,8 +376,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) Curl_bufref_init(&nullmsg); sctx->state1 = SASL_GSASL; sctx->state2 = SASL_GSASL; - sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user, - sctx->conn->passwd, gsasl); + sctx->result = Curl_auth_gsasl_start(data, sctx->conn->creds, gsasl); if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); return TRUE; @@ -427,9 +427,7 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE); sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_ntlm_type1_message(data, sctx->conn->creds, service, hostname, ntlm, &sctx->resp); } @@ -441,11 +439,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + if(Curl_creds_has_oauth_bearer(data->state.creds) && + (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { const char *hostname; int port; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -457,9 +452,8 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_oauth_bearer_message(sctx->conn->user, - hostname, port, - oauth_bearer, &sctx->resp); + Curl_auth_create_oauth_bearer_message(sctx->conn->creds, + hostname, port, &sctx->resp); return TRUE; } return FALSE; @@ -467,19 +461,15 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { + if(Curl_creds_has_oauth_bearer(sctx->conn->creds) && + (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { sctx->mech = SASL_MECH_STRING_XOAUTH2; sctx->state1 = SASL_OAUTH2; sctx->sasl->authused = SASL_MECH_XOAUTH2; if(sctx->sasl->force_ir || data->set.sasl_ir) - sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user, - oauth_bearer, - &sctx->resp); + sctx->result = Curl_auth_create_xoauth_bearer_message( + sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -494,9 +484,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_plain_message(sctx->conn->sasl_authzid, - sctx->conn->user, sctx->conn->passwd, - &sctx->resp); + Curl_auth_create_plain_message(sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -511,7 +499,8 @@ static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) sctx->sasl->authused = SASL_MECH_LOGIN; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_login_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_login_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -606,7 +595,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, data->set.str[STRING_SERVICE_NAME] : sasl->params->service; #endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -634,18 +622,17 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, &resp); + result = Curl_auth_create_plain_message(conn->creds, &resp); break; case SASL_LOGIN: - Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(Curl_creds_user(conn->creds), &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - Curl_auth_create_login_message(conn->passwd, &resp); + Curl_auth_create_login_message(Curl_creds_passwd(conn->creds), &resp); break; case SASL_EXTERNAL: - Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(Curl_creds_user(conn->creds), &resp); break; #ifdef USE_GSASL case SASL_GSASL: @@ -663,15 +650,15 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, - conn->passwd, &resp); + result = Curl_auth_create_cram_md5_message(&serverdata, conn->creds, + &resp); break; case SASL_DIGESTMD5: result = get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_create_digest_md5_message(data, &serverdata, - conn->user, conn->passwd, - service, &resp); + conn->creds, service, + &resp); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) newstate = SASL_DIGESTMD5_RESP; break; @@ -685,8 +672,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, /* Create the type-1 message */ struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, + Curl_auth_create_ntlm_type1_message(data, conn->creds, service, hostname, ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; @@ -700,9 +686,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!result) result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); if(!result) - result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, ntlm, - &resp); + result = Curl_auth_create_ntlm_type3_message(data, conn->creds, + ntlm, &resp); break; } #endif @@ -711,7 +696,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_GSSAPI: { struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, + Curl_auth_create_gssapi_user_message(data, conn->creds, service, conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); @@ -727,7 +712,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, else if(sasl->mutual_auth) { /* Decode the user token challenge and create the optional response message */ - result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, + result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, (bool)sasl->mutual_auth, &serverdata, @@ -736,10 +721,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else /* Decode the security challenge and create the response message */ - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; case SASL_GSSAPI_NO_DATA: @@ -750,10 +734,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!krb5) result = CURLE_OUT_OF_MEMORY; else - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; #endif @@ -761,18 +744,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_OAUTH2: /* Create the authorization message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { - result = Curl_auth_create_oauth_bearer_message(conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->creds, hostname, port, - oauth_bearer, &resp); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ newstate = SASL_OAUTH2_RESP; } else - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, + result = Curl_auth_create_xoauth_bearer_message(conn->creds, &resp); break; @@ -862,7 +843,7 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, else { if(param_missing) infof(data, "SASL: %s is missing %s", mname, param_missing); - if(!data->conn->user[0]) + if(!Curl_creds_has_user(data->conn->creds)) infof(data, "SASL: %s is missing username", mname); } } @@ -904,7 +885,8 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) "auth mechanisms"); else { infof(data, "SASL: no auth mechanism offered could be selected"); - if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0]) + if((enabledmechs & SASL_MECH_EXTERNAL) && + Curl_creds_has_passwd(data->conn->creds)) infof(data, "SASL: auth EXTERNAL not chosen with password"); sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs, CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL); @@ -919,10 +901,10 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs, CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL); sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); } #endif /* CURLVERBOSE */ diff --git a/lib/ftp.c b/lib/ftp.c index 3f55f68d82..68c1a46ca5 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -743,7 +743,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) { ftpc->ftp_trying_alternative = FALSE; ftp_state(data, ftpc, FTP_USER); @@ -2941,7 +2941,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ - result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd); + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", + Curl_creds_passwd(data->conn->creds)); if(!result) ftp_state(data, ftpc, FTP_PASS); } diff --git a/lib/http.c b/lib/http.c index edca1dc1ea..9cb8b17b34 100644 --- a/lib/http.c +++ b/lib/http.c @@ -250,14 +250,14 @@ char *Curl_copy_header_value(const char *header) * * Returns CURLcode. */ -static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) +static CURLcode http_output_basic(struct Curl_easy *data, + struct connectdata *conn, bool proxy) { size_t size = 0; char *authorization = NULL; char **p_hd; - const char *user; - const char *pwd; CURLcode result; + struct Curl_creds *creds = NULL; char *out; /* credentials are unique per transfer for HTTP, do not use the ones for the @@ -265,19 +265,23 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY p_hd = &data->req.hd_proxy_auth; - user = data->state.aptr.proxyuser; - pwd = data->state.aptr.proxypasswd; + creds = conn->http_proxy.creds; #else + (void)conn; return CURLE_NOT_BUILT_IN; #endif } else { p_hd = &data->req.hd_auth; - user = data->state.aptr.user; - pwd = data->state.aptr.passwd; + creds = data->state.creds; } - out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + if(!creds) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + + out = curl_maprintf("%s:%s", creds->user, creds->passwd); if(!out) return CURLE_OUT_OF_MEMORY; @@ -320,10 +324,11 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; + DEBUGASSERT(Curl_creds_has_oauth_bearer(data->state.creds)); userp = &data->req.hd_auth; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); + Curl_creds_oauth_bearer(data->state.creds)); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -527,10 +532,10 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) * Either we are not authenticating, or we are supposed to be authenticating * something else. This is an error. */ - if((httpcode == 401) && !data->state.aptr.user) + if((httpcode == 401) && !data->state.creds) return TRUE; #ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + if((httpcode == 407) && !data->conn->http_proxy.creds) return TRUE; #endif @@ -551,7 +556,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) CURLcode result = CURLE_OK; unsigned long authmask = ~0UL; - if(!data->set.str[STRING_BEARER]) + if(!Curl_creds_has_oauth_bearer(data->state.creds)) authmask &= (unsigned long)~CURLAUTH_BEARER; if(100 <= data->req.httpcode && data->req.httpcode <= 199) @@ -561,7 +566,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authproblem) return data->set.http_fail_on_error ? CURLE_HTTP_RETURNED_ERROR : CURLE_OK; - if((data->state.aptr.user || data->set.str[STRING_BEARER]) && + if(data->state.creds && ((data->req.httpcode == 401) || (data->req.authneg && data->req.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost, authmask); @@ -578,7 +583,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) } } #ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_user_passwd && + if(conn->http_proxy.creds && ((data->req.httpcode == 407) || (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, @@ -694,14 +699,14 @@ static CURLcode output_auth_headers(struct Curl_easy *data, /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && + (proxy && conn->http_proxy.creds && !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && + (!proxy && data->state.creds && !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; - result = http_output_basic(data, proxy); + result = http_output_basic(data, conn, proxy); if(result) return result; } @@ -714,8 +719,7 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if(!proxy && data->set.str[STRING_BEARER] && - Curl_auth_allowed_to_host(data) && + if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); @@ -737,15 +741,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data, data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, - proxy ? (data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : "") : - (data->state.aptr.user ? - data->state.aptr.user : "")); + proxy ? (conn->http_proxy.creds ? + conn->http_proxy.creds->user : "") : + (data->state.creds ? + data->state.creds->user : "")); #else (void)proxy; infof(data, "Server auth using %s with user '%s'", - auth, data->state.aptr.user ? - data->state.aptr.user : ""); + auth, data->state.creds ? + data->state.creds->user : ""); #endif authstatus->multipass = !authstatus->done; } @@ -780,14 +784,13 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, if( #ifndef CURL_DISABLE_PROXY - (!conn->bits.httpproxy || !conn->bits.proxy_user_passwd) && + (!conn->bits.httpproxy || !conn->http_proxy.creds) && #endif - !data->state.aptr.user && #ifdef USE_SPNEGO !(authhost->want & CURLAUTH_NEGOTIATE) && !(authproxy->want & CURLAUTH_NEGOTIATE) && #endif - !data->set.str[STRING_BEARER]) { + !data->state.creds) { /* no authentication with no user or password */ authhost->done = TRUE; authproxy->done = TRUE; @@ -832,13 +835,9 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, with it */ authproxy->done = TRUE; - /* To prevent the user+password to get sent to other than the original host - due to a location-follow */ - if(Curl_auth_allowed_to_host(data) -#ifndef CURL_DISABLE_NETRC - || conn->bits.netrc -#endif - ) + /* Either we have credentials for the origin we talk to or + performing authentication is allowed here */ + if(data->state.creds || Curl_auth_allowed_to_host(data)) result = output_auth_headers(data, conn, authhost, request, path_and_query, FALSE); else @@ -1227,8 +1226,6 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return CURLE_OUT_OF_MEMORY; } else { - bool same_origin; - CURLcode result; CURLU *u = curl_url(); if(!u) return CURLE_OUT_OF_MEMORY; @@ -1242,29 +1239,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return Curl_uc_to_curlcode(uc); } - same_origin = Curl_url_same_origin(u, data->state.uh); - curl_url_cleanup(u); - #ifndef CURL_DISABLE_DIGEST_AUTH - if(!same_origin) - Curl_auth_digest_cleanup(&data->state.digest); + { + bool same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); + } +#else + curl_url_cleanup(u); #endif - - if((!same_origin && !data->set.allow_auth_to_other_hosts) || - !data->set.str[STRING_USERNAME]) { - result = Curl_reset_userpwd(data); - if(result) { - curlx_free(follow_url); - return result; - } - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); - } - result = Curl_reset_proxypwd(data); - if(result) { - curlx_free(follow_url); - return result; - } } DEBUGASSERT(follow_url); @@ -2005,9 +1989,6 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr; - if(!data->state.this_is_a_follow) - Curl_peer_link(&data->state.first_origin, conn->origin); - curlx_safefree(aptr->host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); @@ -2015,7 +1996,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || - Curl_peer_equal(data->state.first_origin, conn->origin))) { + Curl_peer_equal(data->state.initial_origin, conn->origin))) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2138,6 +2119,19 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } + else if(data->state.creds && (data->state.creds->source != CREDS_URL)) { + /* credentials not from the URL need to be set */ + uc = curl_url_set(h, CURLUPART_USER, + data->state.creds->user, CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(h, CURLUPART_PASSWORD, + data->state.creds->passwd, CURLU_URLENCODE); + if(uc) { + curl_url_cleanup(h); + return Curl_uc_to_curlcode(uc); + } + } + /* Extract the URL to use in the request. */ uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); if(uc) { diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 5761acae5f..f77f7a088c 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -848,7 +848,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) char *request_type = NULL; char *credential_scope = NULL; char *str_to_sign = NULL; - const char *user = data->state.aptr.user ? data->state.aptr.user : ""; + const char *user = Curl_creds_user(data->state.creds); + const char *passwd = Curl_creds_passwd(data->state.creds); char *secret = NULL; unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; @@ -1068,8 +1069,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) str_to_sign); secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + curlx_str(&provider0), passwd); if(!secret) goto fail; /* make provider0 part done uppercase */ diff --git a/lib/http_digest.c b/lib/http_digest.c index e87fb362ed..d20e165623 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -77,8 +77,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, char **allocuserpwd; /* Point to the name and password for this */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; /* Point to the correct struct with this */ struct digestdata *digest; @@ -90,28 +89,19 @@ CURLcode Curl_output_digest(struct Curl_easy *data, #else digest = &data->state.proxydigest; allocuserpwd = &data->req.hd_proxy_auth; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + creds = data->conn->http_proxy.creds; authp = &data->state.authproxy; #endif } else { digest = &data->state.digest; allocuserpwd = &data->req.hd_auth; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + creds = data->state.creds; authp = &data->state.authhost; } curlx_safefree(*allocuserpwd); - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI have_chlg = !!digest->input_token; #else @@ -123,8 +113,8 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - result = Curl_auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, + result = Curl_auth_create_digest_http_message(data, creds, request, + uripath, digest, &response, &len); if(result) return result; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index b037bb2ec9..d987b8b9d1 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -40,8 +40,10 @@ static void http_auth_nego_reset(struct connectdata *conn, { if(proxy) conn->proxy_negotiate_state = GSS_AUTHNONE; - else + else { conn->http_negotiate_state = GSS_AUTHNONE; + Curl_creds_unlink(&conn->creds); + } if(neg_ctx) Curl_auth_cleanup_spnego(neg_ctx); } @@ -53,8 +55,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, size_t len; /* Point to the username, password, service and host */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; const char *service; const char *host; @@ -64,8 +65,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = conn->http_proxy.user; - passwdp = conn->http_proxy.passwd; + creds = conn->http_proxy.creds; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.peer->hostname; @@ -75,8 +75,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif } else { - userp = conn->user; - passwdp = conn->passwd; + creds = data->state.creds; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->origin->hostname; @@ -87,13 +86,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(!neg_ctx) return CURLE_OUT_OF_MEMORY; - /* Not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - /* Obtain the input token, if any */ header += strlen("Negotiate"); curlx_str_passblanks(&header); @@ -135,7 +127,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, + result = Curl_auth_decode_spnego_message(data, creds, service, host, header, neg_ctx); #ifdef GSS_C_CHANNEL_BOUND_FLAG @@ -145,6 +137,16 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(result) http_auth_nego_reset(conn, neg_ctx, proxy); + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_creds_link(&conn->creds, creds); + } + return result; } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 0240251a5f..1a02a0fd86 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -122,9 +122,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) server, which is for a plain host or for an HTTP proxy */ char **allocuserpwd; - /* point to the username, password, service and host */ - const char *userp; - const char *passwdp; + /* point to credentials, service and host */ + struct Curl_creds *creds = NULL; const char *service = NULL; const char *hostname = NULL; @@ -140,8 +139,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY allocuserpwd = &data->req.hd_proxy_auth; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + creds = conn->http_proxy.creds; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; hostname = conn->http_proxy.peer->hostname; @@ -153,26 +151,19 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) } else { allocuserpwd = &data->req.hd_auth; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + creds = data->state.creds; service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; hostname = conn->origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; } + ntlm = Curl_auth_ntlm_get(conn, proxy); if(!ntlm) return CURLE_OUT_OF_MEMORY; authp->done = FALSE; - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ @@ -195,8 +186,16 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) switch(*state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ - /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_creds_link(&conn->creds, creds); + } + result = Curl_auth_create_ntlm_type1_message(data, creds, service, hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); @@ -215,8 +214,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ - result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, - ntlm, &ntlmmsg); + result = Curl_auth_create_ntlm_type3_message(data, creds, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); diff --git a/lib/imap.c b/lib/imap.c index 5ef2a2cb21..7c73255e96 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -597,15 +597,15 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { imap_state(data, imapc, IMAP_STOP); return result; } /* Make sure the username and password are in the correct atom format */ - user = imap_atom(conn->user, FALSE); - passwd = imap_atom(conn->passwd, FALSE); + user = imap_atom(Curl_creds_user(conn->creds), FALSE); + passwd = imap_atom(Curl_creds_passwd(conn->creds), FALSE); /* Send the LOGIN command */ result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "", @@ -712,7 +712,6 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, /* Calculate the SASL login details */ result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, &progress); - if(!result) { if(progress == SASL_INPROGRESS) imap_state(data, imapc, IMAP_AUTHENTICATE); diff --git a/lib/ldap.c b/lib/ldap.c index 3705754476..f476da4ea0 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -252,8 +252,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *host = NULL; #endif - char *user = NULL; - char *passwd = NULL; + const char *user = Curl_creds_has_user(data->state.creds) ? + data->state.creds->user : NULL; + const char *passwd = Curl_creds_has_passwd(data->state.creds) ? + data->state.creds->passwd : NULL; struct ip_quadruple ipquad; bool is_ipv6; BerElement *ber = NULL; @@ -295,11 +297,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) host = conn->origin->hostname; #endif - if(data->state.aptr.user) { - user = conn->user; - passwd = conn->passwd; - } - #ifdef USE_WIN32_LDAP if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); diff --git a/lib/mqtt.c b/lib/mqtt.c index d28a25bb50..1137909454 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -280,11 +280,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data) char *packet = NULL; /* extracting username from request */ - const char *username = data->state.aptr.user ? data->state.aptr.user : ""; - const size_t ulen = strlen(username); - /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : ""; - const size_t plen = strlen(passwd); + struct Curl_creds *creds = data->state.creds; + const size_t ulen = creds ? strlen(creds->user) : 0; + const size_t plen = creds ? strlen(creds->passwd) : 0; const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 + /* The plus 2s below are for the MSB and LSB describing the length of the string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ @@ -326,7 +324,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) if(ulen) { start_pwd += 2; - rc = add_user(username, ulen, + rc = add_user(creds->user, ulen, (unsigned char *)packet, start_user, remain_pos); if(rc) { failf(data, "Username too long: [%zu]", ulen); @@ -337,7 +335,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) /* if passwd was provided, add it to the packet */ if(plen) { - rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); + rc = add_passwd(creds->passwd, plen, packet, start_pwd, remain_pos); if(rc) { failf(data, "Password too long: [%zu]", plen); result = CURLE_WEIRD_SERVER_REPLY; @@ -351,8 +349,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) end: if(packet) curlx_free(packet); - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); + Curl_creds_unlink(&data->state.creds); return result; } diff --git a/lib/netrc.c b/lib/netrc.c index 72d8feee7d..76fd5541ce 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -36,6 +36,7 @@ #endif #include "netrc.h" +#include "creds.h" #include "strcase.h" #include "curl_get_line.h" #include "curlx/fopen.h" @@ -108,6 +109,7 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) /* bundled parser state to keep function signatures compact */ struct netrc_state { + struct Curl_creds *existing; char *login; char *password; enum host_lookup_state state; @@ -116,7 +118,6 @@ struct netrc_state { unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ bool our_login; bool done; - bool specific_login; }; /* @@ -250,8 +251,7 @@ static void netrc_new_machine(struct netrc_state *ns) ns->found = 0; ns->our_login = FALSE; curlx_safefree(ns->password); - if(!ns->specific_login) - curlx_safefree(ns->login); + curlx_safefree(ns->login); } /* @@ -263,8 +263,8 @@ static void netrc_new_machine(struct netrc_state *ns) static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) { if(ns->keyword == LOGIN) { - if(ns->specific_login) - ns->our_login = !Curl_timestrcmp(ns->login, tok); + if(Curl_creds_has_user(ns->existing)) + ns->our_login = !Curl_timestrcmp(ns->existing->user, tok); else { ns->our_login = TRUE; curlx_free(ns->login); @@ -289,15 +289,15 @@ static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) ns->keyword = PASSWORD; else if(curl_strequal("machine", tok)) { /* a new machine here */ + bool specific_login = Curl_creds_has_user(ns->existing); - if(ns->found & FOUND_PASSWORD && + if((ns->found & FOUND_PASSWORD) && /* a password was provided for this host */ - - ((!ns->specific_login || ns->our_login) || - /* either there was no specific login to search for, or this - is the specific one we wanted */ - (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { - /* or we look for a specific login, but that was not specified */ + (!specific_login || ns->our_login || + /* and found a login that is suitable + (either matched specific one or simply present) */ + (specific_login && !(ns->found & FOUND_LOGIN)))) { + /* or we look for a specific login, but no login was not specified */ ns->done = TRUE; return NETRC_OK; @@ -361,38 +361,48 @@ static NETRCcode netrc_handle_token(struct netrc_state *ns, * resources on error. */ static NETRCcode netrc_finalize(struct netrc_state *ns, - char **loginp, - char **passwordp, - struct store_netrc *store) + struct store_netrc *store, + struct Curl_creds **pcreds) { NETRCcode retcode = ns->retcode; if(!retcode) { if(!ns->password && ns->our_login) { /* success without a password, set a blank one */ ns->password = curlx_strdup(""); - if(!ns->password) + if(!ns->password) { retcode = NETRC_OUT_OF_MEMORY; + goto out; + } } - else if(!ns->login && !ns->password) + else if(!ns->login && !ns->password) { /* a default with no credentials */ retcode = NETRC_NO_MATCH; + goto out; + } } - if(!retcode) { - /* success */ - if(!ns->specific_login) - *loginp = ns->login; - /* netrc_finalize() can return a password even when specific_login is set + if(!retcode) { + /* success + netrc_finalize() can return a password even when specific_login is set but our_login is false (e.g., host matched but the requested login never matched). See test 685. */ - *passwordp = ns->password; + const char *login = Curl_creds_has_user(ns->existing) ? + ns->existing->user : ns->login; + /* success without a password, set a blank one */ + const char *passwd = ns->password ? ns->password : ""; + + if(Curl_creds_create(login, passwd, NULL, NULL, CREDS_NETRC, pcreds)) { + retcode = NETRC_OUT_OF_MEMORY; + goto out; + } } - else { + +out: + curlx_free(ns->login); + curlx_free(ns->password); + if(retcode) { curlx_dyn_free(&store->filebuf); store->loaded = FALSE; - if(!ns->specific_login) - curlx_free(ns->login); - curlx_free(ns->password); } return retcode; } @@ -402,21 +412,20 @@ static NETRCcode netrc_finalize(struct netrc_state *ns, */ static NETRCcode parsenetrc(struct store_netrc *store, const char *host, - char **loginp, - char **passwordp, - const char *netrcfile) + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds) { const char *netrcbuffer; struct dynbuf token; struct dynbuf *filebuf = &store->filebuf; struct netrc_state ns; + DEBUGASSERT(!existing || !Curl_creds_has_passwd(existing)); memset(&ns, 0, sizeof(ns)); ns.retcode = NETRC_NO_MATCH; - ns.login = *loginp; - ns.specific_login = !!ns.login; + ns.existing = existing; - DEBUGASSERT(!*passwordp); curlx_dyn_init(&token, MAX_NETRC_TOKEN); if(!store->loaded) { @@ -466,7 +475,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, out: curlx_dyn_free(&token); - return netrc_finalize(&ns, loginp, passwordp, store); + return netrc_finalize(&ns, store, pcreds); } const char *Curl_netrc_strerror(NETRCcode ret) @@ -493,12 +502,14 @@ const char *Curl_netrc_strerror(NETRCcode ret) * in. */ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile) + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; char *filealloc = NULL; + Curl_creds_unlink(pcreds); if(!netrcfile) { char *home = NULL; char *homea = NULL; @@ -543,10 +554,11 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { curlx_free(homea); - return NETRC_OUT_OF_MEMORY; + retcode = NETRC_OUT_OF_MEMORY; + goto out; } } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, existing, filealloc, pcreds); curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -556,14 +568,17 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, curlx_free(homea); return NETRC_OUT_OF_MEMORY; } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, existing, filealloc, pcreds); curlx_free(filealloc); } #endif curlx_free(homea); } else - retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); + retcode = parsenetrc(store, host, existing, netrcfile, pcreds); +out: + if(retcode) + Curl_creds_unlink(pcreds); return retcode; } diff --git a/lib/netrc.h b/lib/netrc.h index 90318c2bd6..92dd4d47c9 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -29,6 +29,8 @@ #include "curlx/dynbuf.h" +struct Curl_creds; + struct store_netrc { struct dynbuf filebuf; char *filename; @@ -49,8 +51,9 @@ void Curl_netrc_init(struct store_netrc *store); void Curl_netrc_cleanup(struct store_netrc *store); NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile); + struct Curl_creds *existing, + const char *netrcfile, + struct Curl_creds **pcreds); /* Assume: (*passwordp)[0]=0, host[0] != 0. * If (*loginp)[0] = 0, search for login and password within a machine * section in the netrc. diff --git a/lib/openldap.c b/lib/openldap.c index 30e4bcc752..1ed72c1ea8 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -345,9 +345,9 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(data->state.aptr.user) { - binddn = conn->user; - passwd.bv_val = conn->passwd; + if(data->state.creds) { + binddn = Curl_creds_user(conn->creds); + passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds)); passwd.bv_len = strlen(passwd.bv_val); } @@ -355,7 +355,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) NULL, NULL, &li->msgid); if(rc != LDAP_SUCCESS) return oldap_map_error(rc, - data->state.aptr.user ? + data->state.creds ? CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND); oldap_state(data, li, newstate); return CURLE_OK; @@ -911,7 +911,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) else if(ssl_installed(conn)) { if(li->sasl.prefmech != SASL_AUTH_NONE) result = oldap_perform_mechs(data); - else if(data->state.aptr.user) + else if(data->state.creds) result = oldap_perform_bind(data, OLDAP_BIND); else { /* Version 3 supported: no bind required */ diff --git a/lib/pop3.c b/lib/pop3.c index 317c04bbe3..b7bbd765b9 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -527,7 +527,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -535,7 +535,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Send the USER command */ result = Curl_pp_sendf(data, &pop3c->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) pop3_state(data, POP3_USER); @@ -564,7 +564,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -578,8 +578,8 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp, curlx_uztoui(strlen(pop3c->apoptimestamp))); - Curl_MD5_update(ctxt, (const unsigned char *)conn->passwd, - curlx_uztoui(strlen(conn->passwd))); + Curl_MD5_update(ctxt, (const unsigned char *)Curl_creds_passwd(conn->creds), + curlx_uztoui(strlen(Curl_creds_passwd(conn->creds)))); /* Finalise the digest */ Curl_MD5_final(ctxt, digest); @@ -588,7 +588,8 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]); - result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); + result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", + Curl_creds_user(conn->creds), secret); if(!result) pop3_state(data, POP3_APOP); @@ -1038,7 +1039,8 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, } else /* Send the PASS command */ - result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", conn->passwd); + result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", + Curl_creds_passwd(conn->creds)); if(!result) pop3_state(data, POP3_PASS); diff --git a/lib/rtsp.c b/lib/rtsp.c index b39b8a740b..60c09cbb4b 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -301,12 +301,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; - /* Setup the first_* fields to allow auth details get sent - to this origin */ - - if(!data->state.first_origin) - Curl_peer_link(&data->state.first_origin, conn->origin); - /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. diff --git a/lib/smb.c b/lib/smb.c index 8a75b2dbae..a660f053eb 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -61,7 +61,7 @@ enum smb_conn_state { /* SMB connection data, kept at connection */ struct smb_conn { enum smb_conn_state state; - char *user; + const char *user; char *domain; char *share; unsigned char challenge[8]; @@ -468,13 +468,14 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN); char *slash; + const char *user = Curl_creds_user(conn->creds); (void)done; if(!smbc) return CURLE_FAILED_INIT; /* Check we have a username and password to authenticate with */ - if(!data->state.aptr.user) + if(!Curl_creds_has_user(data->state.creds)) return CURLE_LOGIN_DENIED; /* Initialize the connection state */ @@ -487,19 +488,19 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* Parse the username, domain, and password */ - slash = strchr(conn->user, '/'); + slash = strchr(user, '/'); if(!slash) - slash = strchr(conn->user, '\\'); + slash = strchr(user, '\\'); if(slash) { smbc->user = slash + 1; - smbc->domain = curlx_strdup(conn->user); + smbc->domain = curlx_strdup(user); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; - smbc->domain[slash - conn->user] = 0; + smbc->domain[slash - user] = 0; } else { - smbc->user = conn->user; + smbc->user = user; smbc->domain = curlx_strdup(conn->origin->hostname); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; @@ -670,6 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) unsigned char nt_hash[21]; unsigned char nt[24]; size_t byte_count; + const char *passwd = Curl_creds_passwd(conn->creds); if(!smbc || !req) return CURLE_FAILED_INIT; @@ -680,9 +682,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data) if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); + Curl_ntlm_core_mk_lm_hash(passwd, lm_hash); Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); - Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); + Curl_ntlm_core_mk_nt_hash(passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); diff --git a/lib/socks.c b/lib/socks.c index ed60063ab5..2d8a4f3ab6 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -99,8 +99,7 @@ struct socks_ctx { enum socks_state_t state; struct bufq iobuf; struct Curl_peer *dest; - const char *user; - const char *passwd; + struct Curl_creds *creds; CURLproxycode presult; uint32_t resolv_id; uint8_t ip_version; @@ -287,8 +286,8 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, CURLcode result; size_t nwritten; - if(sx->user) { - size_t plen = strlen(sx->user); + if(sx->creds) { + size_t plen = strlen(sx->creds->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -297,7 +296,7 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -603,7 +602,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->user = NULL; + Curl_creds_unlink(&sx->creds); req[0] = 5; /* version */ nauths = 1; @@ -614,7 +613,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->user) { + if(sx->creds) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -687,9 +686,9 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, unsigned char buf[2]; CURLcode result; - if(sx->user && sx->passwd) { - ulen = strlen(sx->user); - plen = strlen(sx->passwd); + if(sx->creds) { + ulen = strlen(sx->creds->user); + plen = strlen(sx->creds->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -714,7 +713,8 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, + &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -723,7 +723,8 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, + &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -1185,6 +1186,7 @@ static void socks_proxy_ctx_free(struct socks_ctx *ctx) { if(ctx) { Curl_peer_unlink(&ctx->dest); + Curl_creds_unlink(&ctx->creds); Curl_bufq_free(&ctx->iobuf); curlx_free(ctx); } @@ -1259,10 +1261,8 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, out: *done = (bool)cf->connected; - if(*done || result) { - ctx->user = NULL; - ctx->passwd = NULL; - } + if(*done || result) + Curl_creds_unlink(&ctx->creds); return result; } @@ -1361,8 +1361,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd) + struct Curl_creds *creds) { struct Curl_cfilter *cf; struct socks_ctx *ctx; @@ -1391,8 +1390,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; - ctx->user = user; - ctx->passwd = passwd; + Curl_creds_link(&ctx->creds, creds); Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, BUFQ_OPT_SOFT_LIMIT); diff --git a/lib/socks.h b/lib/socks.h index e17b761f1c..fca10c8332 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -28,6 +28,7 @@ #ifndef CURL_DISABLE_PROXY struct Curl_peer; +struct Curl_creds; /* * Helper read-from-socket functions. Does the same as Curl_read() but it @@ -58,8 +59,7 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd); + struct Curl_creds *creds); extern struct Curl_cftype Curl_cft_socks_proxy; diff --git a/lib/telnet.c b/lib/telnet.c index c5ce9c2c97..d1faec87a8 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -840,13 +840,14 @@ static CURLcode check_telnet_options(struct Curl_easy *data, /* Add the username as an environment variable if it was given on the command line */ - if(data->state.aptr.user) { + if(data->state.creds) { char buffer[256]; - if(str_is_nonascii(data->conn->user)) { + if(str_is_nonascii(Curl_creds_user(data->conn->creds))) { DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } - curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + curl_msnprintf(buffer, sizeof(buffer), "USER,%s", + Curl_creds_user(data->conn->creds)); beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); diff --git a/lib/transfer.c b/lib/transfer.c index fd1a903dab..721ad8d9ce 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -438,40 +438,6 @@ void Curl_init_CONNECT(struct Curl_easy *data) data->state.upload = (data->state.httpreq == HTTPREQ_PUT); } -/* - * Restore the user credentials to those set in options. - */ -CURLcode Curl_reset_userpwd(struct Curl_easy *data) -{ - CURLcode result; - if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); - return result; -} - -/* - * Restore the proxy credentials to those set in options. - */ -CURLcode Curl_reset_proxypwd(struct Curl_easy *data) -{ -#ifndef CURL_DISABLE_PROXY - CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); - return result; -#else - (void)data; - return CURLE_OK; -#endif -} - /* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass @@ -524,6 +490,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #endif data->state.httpreq = data->set.method; + /* initial transfer request coming up, forget the initial origin + * from a previous perform() on this handle. */ + Curl_peer_unlink(&data->state.initial_origin); data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ @@ -625,11 +594,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } - if(!result) - result = Curl_reset_userpwd(data); - if(!result) - result = Curl_reset_proxypwd(data); - data->req.headerbytecount = 0; Curl_headers_cleanup(data); return result; diff --git a/lib/transfer.h b/lib/transfer.h index b29e70b9ec..41ec0357f6 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -31,8 +31,6 @@ char *Curl_checkheaders(const struct Curl_easy *data, void Curl_init_CONNECT(struct Curl_easy *data); -CURLcode Curl_reset_userpwd(struct Curl_easy *data); -CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data); diff --git a/lib/url.c b/lib/url.c index ba662d6a0b..298e5478a2 100644 --- a/lib/url.c +++ b/lib/url.c @@ -249,7 +249,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - Curl_peer_unlink(&data->state.first_origin); + Curl_peer_unlink(&data->state.initial_origin); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -281,6 +281,7 @@ CURLcode Curl_close(struct Curl_easy **datap) DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); + Curl_creds_unlink(&data->state.creds); curlx_safefree(data->state.aptr.uagent); curlx_safefree(data->state.aptr.accept_encoding); curlx_safefree(data->state.aptr.rangeline); @@ -292,12 +293,6 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_RTSP curlx_safefree(data->state.aptr.rtsp_transport); #endif - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); -#ifndef CURL_DISABLE_PROXY - curlx_safefree(data->state.aptr.proxyuser); - curlx_safefree(data->state.aptr.proxypasswd); -#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); @@ -511,18 +506,13 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) } #ifndef CURL_DISABLE_PROXY - curlx_safefree(conn->http_proxy.user); - curlx_safefree(conn->socks_proxy.user); - curlx_safefree(conn->http_proxy.passwd); - curlx_safefree(conn->socks_proxy.passwd); Curl_peer_unlink(&conn->http_proxy.peer); Curl_peer_unlink(&conn->socks_proxy.peer); + Curl_creds_unlink(&conn->http_proxy.creds); + Curl_creds_unlink(&conn->socks_proxy.creds); #endif - curlx_safefree(conn->user); - curlx_safefree(conn->passwd); - curlx_safefree(conn->sasl_authzid); + Curl_creds_unlink(&conn->creds); curlx_safefree(conn->options); - curlx_safefree(conn->oauth_bearer); curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); @@ -567,11 +557,8 @@ static bool proxy_info_matches(const struct proxy_info *data, const struct proxy_info *needle) { if((data->proxytype == needle->proxytype) && - Curl_peer_same_destination(data->peer, needle->peer)) { - - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; + Curl_peer_same_destination(data->peer, needle->peer) && + Curl_creds_same(data->creds, needle->creds)) { return TRUE; } return FALSE; @@ -950,16 +937,11 @@ static bool url_match_proto_config(struct connectdata *conn, static bool url_match_auth(struct connectdata *conn, struct url_conn_match *m) { - if(!(m->needle->scheme->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we are using the same name and password as well */ - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd) || - Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) || - Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) { - /* one of them was different */ + if(!Curl_creds_same(m->needle->creds, conn->creds)) { + if(m->needle->creds) + return FALSE; + if(!Curl_creds_same(m->data->state.creds, conn->creds)) return FALSE; - } } #ifdef HAVE_GSSAPI /* GSS delegation differences do not actually affect every connection @@ -1030,8 +1012,7 @@ static bool url_match_auth_ntlm(struct connectdata *conn, possible. (Especially we must not reuse the same connection if partway through a handshake!) */ if(m->want_ntlm_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) { + if(!Curl_creds_same(m->data->state.creds, conn->creds)) { /* we prefer a credential match, but this is at least a connection that can be reused and "upgraded" to NTLM if it does not have any auth ongoing. */ @@ -1056,13 +1037,10 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(m->want_proxy_ntlm_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { @@ -1102,8 +1080,7 @@ static bool url_match_auth_nego(struct connectdata *conn, already authenticating with the right credentials. If not, keep looking so that we can reuse Negotiate connections if possible. */ if(m->want_nego_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) + if(!Curl_creds_same(m->needle->creds, conn->creds)) return FALSE; } else if(conn->http_negotiate_state != GSS_AUTHNONE) { @@ -1116,13 +1093,10 @@ static bool url_match_auth_nego(struct connectdata *conn, if(m->want_proxy_nego_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { @@ -1265,7 +1239,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NTLM) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1277,7 +1251,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_nego_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1339,7 +1313,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.socksproxy = TRUE; } - conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser; conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; #endif /* CURL_DISABLE_PROXY */ @@ -1452,13 +1425,76 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, #define hsts_upgrade(x, y, z, a, b) CURLE_OK #endif +static CURLcode url_set_data_creds(struct Curl_easy *data, + struct connectdata *conn, + CURLU *uh) +{ + CURLcode result = CURLE_OK; + + /* We reset any existing credentials on the transfer. Then + * set the CURLOPT_* credentials ONLY IF the origin is the initial one. */ + Curl_creds_unlink(&data->state.creds); + if((data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD] || + data->set.str[STRING_SASL_AUTHZID] || + data->set.str[STRING_BEARER]) && + (data->set.allow_auth_to_other_hosts || + Curl_peer_same_destination(data->state.initial_origin, conn->origin))) { + result = Curl_creds_create(data->set.str[STRING_USERNAME], + data->set.str[STRING_PASSWORD], + data->set.str[STRING_SASL_AUTHZID], + data->set.str[STRING_BEARER], + CREDS_OPTION, &data->state.creds); + if(result) + return result; + } + + /* Extract credentials from the URL only if there are none OR + * if no CURLOPT_USER was set. */ + if(!data->state.creds || !Curl_creds_has_user(data->state.creds)) { + char *udecoded = NULL; + char *pdecoded = NULL; + CURLUcode uc; + + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(uc && (uc != CURLUE_NO_USER)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(uc && (uc != CURLUE_NO_PASSWORD)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + if(data->state.up.user) { + result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, + conn->scheme->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + } + if(!result && data->state.up.password) { + result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, + conn->scheme->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + } + if(!result) + result = Curl_creds_merge(udecoded, pdecoded, data->state.creds, + CREDS_URL, &data->state.creds); +out: + curlx_free(udecoded); + curlx_free(pdecoded); + if(result) + failf(data, "error extracting credentials from URL"); + } + return result; +} + /* * Parse URL and fill in the relevant members of the connection struct. */ static CURLcode parseurlandfillconn(struct Curl_easy *data, struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; CURLU *uh; CURLUcode uc; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); @@ -1472,8 +1508,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uh = data->state.uh = curl_url_dup(data->set.uh); else uh = data->state.uh = curl_url(); - if(!uh) - return CURLE_OUT_OF_MEMORY; + if(!uh) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } /* Calculate the *real* URL this transfer uses, applying defaults * where information is missing. */ @@ -1482,8 +1520,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, char *url = curl_maprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], Curl_bufref_ptr(&data->state.url)); - if(!url) - return CURLE_OUT_OF_MEMORY; + if(!url) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } Curl_bufref_set(&data->state.url, url, 0, curl_free); } @@ -1497,13 +1537,16 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); if(uc) { failf(data, "URL rejected: %s", curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); + result = Curl_uc_to_curlcode(uc); + goto out; } /* after it was parsed, get the generated normalized version */ uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } Curl_bufref_set(&data->state.url, newurl, 0, curl_free); } @@ -1515,86 +1558,64 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, result = Curl_peer_from_url(uh, data, port_override, scope_id, &data->state.up, &conn->origin); if(result) - return result; + goto out; result = hsts_upgrade(data, conn, uh, port_override, scope_id); if(result) - return result; + goto out; /* now that the origin is fixed, check and set the connection scheme */ result = url_set_conn_scheme(data, conn, conn->origin->scheme); if(result) - return result; + goto out; - /* - * username and password set with their own options override the credentials - * possibly set in the URL, but netrc does not. - */ - if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->passwd = decoded; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; - data->state.creds_from = CREDS_URL; - } - else if(uc != CURLUE_NO_PASSWORD) - return Curl_uc_to_curlcode(uc); - } + /* When the transfers initial_origin is not set, this is the initial + * request. Remember this starting point. This is used to + * select credentials. */ + if(!data->state.initial_origin) + Curl_peer_link(&data->state.initial_origin, conn->origin); - if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { - /* we do not use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->user = decoded; - result = Curl_setstropt(&data->state.aptr.user, decoded); - data->state.creds_from = CREDS_URL; - } - else if(uc != CURLUE_NO_USER) - return Curl_uc_to_curlcode(uc); - if(result) - return result; - } + result = url_set_data_creds(data, conn, uh); + if(result) + goto out; uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, CURLU_URLDECODE); if(!uc) { conn->options = curlx_strdup(data->state.up.options); - if(!conn->options) - return CURLE_OUT_OF_MEMORY; + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(uc != CURLUE_NO_OPTIONS) { + result = Curl_uc_to_curlcode(uc); + goto out; } - else if(uc != CURLUE_NO_OPTIONS) - return Curl_uc_to_curlcode(uc); uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); - if(uc && (uc != CURLUE_NO_QUERY)) - return CURLE_OUT_OF_MEMORY; + if(uc && (uc != CURLUE_NO_QUERY)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef USE_IPV6 /* Fill in the conn parts that do not use authority, yet. */ conn->scope_id = conn->origin->scopeid; #endif - return CURLE_OK; +#ifdef CURLVERBOSE + Curl_creds_trace(data, data->state.creds, "transfer credentials"); +#endif + +out: + return result; } /* @@ -1770,14 +1791,15 @@ static char *url_detect_proxy(struct Curl_easy *data, * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, const char *proxy, - uint8_t proxytype) + const char *proxy, + bool for_pre_proxy, + struct proxy_info *proxyinfo) { char *proxyuser = NULL; char *proxypasswd = NULL; - struct proxy_info *proxyinfo = NULL; CURLcode result = CURLE_OK; - struct Curl_peer *peer = NULL; + /* Set the start proxy type for url scheme guessing */ + uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; CURLU *uhp = curl_url(); CURLUcode uc; @@ -1797,7 +1819,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, } result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, - &peer, &proxytype); + &proxyinfo->peer, &proxytype); if(result) goto error; @@ -1806,22 +1828,21 @@ static CURLcode parse_proxy(struct Curl_easy *data, case CURLPROXY_HTTP_1_0: case CURLPROXY_HTTPS: case CURLPROXY_HTTPS2: - proxyinfo = &conn->http_proxy; + if(for_pre_proxy) { + failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - proxyinfo = &conn->socks_proxy; break; default: - break; - } - - if(!proxyinfo) { - failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; } /* Is there a username and password given in this proxy URL? */ @@ -1837,35 +1858,29 @@ static CURLcode parse_proxy(struct Curl_easy *data, } if(proxyuser || proxypasswd) { - curlx_free(proxyinfo->user); - proxyinfo->user = proxyuser; - result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); - proxyuser = NULL; + result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, + CREDS_URL, &proxyinfo->creds); if(result) goto error; - curlx_safefree(proxyinfo->passwd); - if(!proxypasswd) { - proxypasswd = curlx_strdup(""); - if(!proxypasswd) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - proxyinfo->passwd = proxypasswd; - result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); - proxypasswd = NULL; - if(result) - goto error; - conn->bits.proxy_user_passwd = TRUE; /* enable it */ } + else if(!for_pre_proxy && + (data->set.str[STRING_PROXYUSERNAME] || + data->set.str[STRING_PROXYPASSWORD])) { + /* No user/passwd in URL, if this is not a pre-proxy, the + * CURLOPT_PROXY* settings apply. */ + result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], + data->set.str[STRING_PROXYPASSWORD], + NULL, NULL, + CREDS_OPTION, &proxyinfo->creds); + } + else + Curl_creds_unlink(&proxyinfo->creds); - Curl_peer_link(&proxyinfo->peer, peer); proxyinfo->proxytype = proxytype; error: curlx_free(proxyuser); curlx_free(proxypasswd); - Curl_peer_unlink(&peer); curl_url_cleanup(uhp); #ifdef DEBUGBUILD if(!result) { @@ -1876,46 +1891,14 @@ error: return result; } -/* - * Extract the user and password from the authentication string - */ -static CURLcode parse_proxy_auth(struct Curl_easy *data, - struct connectdata *conn) -{ - const char *proxyuser = data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : ""; - const char *proxypasswd = data->state.aptr.proxypasswd ? - data->state.aptr.proxypasswd : ""; - CURLcode result = CURLE_OUT_OF_MEMORY; - - conn->http_proxy.user = curlx_strdup(proxyuser); - if(conn->http_proxy.user) { - conn->http_proxy.passwd = curlx_strdup(proxypasswd); - if(conn->http_proxy.passwd) - result = CURLE_OK; - else - curlx_safefree(conn->http_proxy.user); - } - return result; -} - static CURLcode url_set_conn_proxies(struct Curl_easy *data, struct connectdata *conn) { char *proxy = NULL; - char *socksproxy = NULL; + char *pre_proxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result) - goto out; - } - /************************************************************* * Detect what (if any) proxy to use *************************************************************/ @@ -1930,9 +1913,9 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, } if(data->set.str[STRING_PRE_PROXY]) { - socksproxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); + pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); /* if global socks proxy is set, this is it */ - if(!socksproxy) { + if(!pre_proxy) { failf(data, "memory shortage"); result = CURLE_OUT_OF_MEMORY; goto out; @@ -1954,10 +1937,10 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { curlx_safefree(proxy); - curlx_safefree(socksproxy); + curlx_safefree(pre_proxy); } #ifndef CURL_DISABLE_HTTP - else if(!proxy && !socksproxy) + else if(!proxy && !pre_proxy) /* if the host is not in the noproxy list, detect proxy. */ proxy = url_detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ @@ -1968,12 +1951,12 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, or if the protocol does not work with network */ proxy = NULL; } - if(socksproxy && (!*socksproxy || + if(pre_proxy && (!*pre_proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(socksproxy); /* Do not bother with an empty socks proxy string - or if the protocol does not work with - network */ - socksproxy = NULL; + curlx_free(pre_proxy); /* Do not bother with an empty socks proxy string + or if the protocol does not work with + network */ + pre_proxy = NULL; } /*********************************************************************** @@ -1981,21 +1964,37 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, * name, proxy type and port number, so that we can reuse an existing * connection that may exist registered to the same proxy host. ***********************************************************************/ - if(proxy || socksproxy) { - if(proxy) { - result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); - curlx_safefree(proxy); /* parse_proxy copies the proxy string */ + if(proxy || pre_proxy) { + if(pre_proxy) { + result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); if(result) goto out; } - if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, - conn->socks_proxy.proxytype); - /* parse_proxy copies the socks proxy string */ - curlx_safefree(socksproxy); + if(proxy) { + result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); if(result) goto out; + switch(conn->http_proxy.proxytype) { + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + /* Whoops, it's not a HTTP proxy */ + if(conn->socks_proxy.peer) { + /* and we already have a SOCKS pre-proxy. Cannot have both */ + failf(data, "Having a SOCKS pre-proxy and proxy is not " + "supported with \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + /* switch */ + conn->socks_proxy = conn->http_proxy; + memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); + break; + default: + break; + } } if(conn->http_proxy.peer) { @@ -2013,34 +2012,14 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, /* if not converting to HTTP over the proxy, enforce tunneling */ conn->bits.tunnel_proxy = TRUE; } - conn->bits.httpproxy = TRUE; #endif } - else { - conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ - conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ - } - - if(conn->socks_proxy.peer) { - if(!conn->http_proxy.peer) { - /* once a socks proxy */ - if(!conn->socks_proxy.user) { - conn->socks_proxy.user = conn->http_proxy.user; - conn->http_proxy.user = NULL; - curlx_free(conn->socks_proxy.passwd); - conn->socks_proxy.passwd = conn->http_proxy.passwd; - conn->http_proxy.passwd = NULL; - } - } - conn->bits.socksproxy = TRUE; - } else - conn->bits.socksproxy = FALSE; /* not a socks proxy */ - } - else { - conn->bits.socksproxy = FALSE; - conn->bits.httpproxy = FALSE; + conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ } + + conn->bits.socksproxy = !!conn->socks_proxy.peer; + conn->bits.httpproxy = !!conn->http_proxy.peer; conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; if(!conn->bits.proxy) { @@ -2048,7 +2027,6 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, conn->bits.proxy = FALSE; conn->bits.httpproxy = FALSE; conn->bits.socksproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; conn->bits.tunnel_proxy = FALSE; /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need to signal that CURLPROXY_HTTPS is not used for this connection */ @@ -2057,7 +2035,7 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data, out: - curlx_free(socksproxy); + curlx_free(pre_proxy); curlx_free(proxy); return result; } @@ -2180,41 +2158,71 @@ static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn) { CURLUcode uc; - char **userp = &conn->user; - char **passwdp = &conn->passwd; char **optionsp = &conn->options; +#ifndef CURL_DISABLE_NETRC + struct Curl_creds *ncreds_in = NULL; + struct Curl_creds *ncreds_out = NULL; +#endif + CURLcode result = CURLE_OK; + bool creds_changed = FALSE; if(data->set.str[STRING_OPTIONS]) { curlx_free(*optionsp); *optionsp = curlx_strdup(data->set.str[STRING_OPTIONS]); - if(!*optionsp) - return CURLE_OUT_OF_MEMORY; + if(!*optionsp) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } #ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - curlx_safefree(*userp); - curlx_safefree(*passwdp); - } - conn->bits.netrc = FALSE; - if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { - bool url_provided = FALSE; - - if(data->state.aptr.user && - (data->state.creds_from != CREDS_NETRC)) { - /* there was a username with a length in the URL. Use the URL decoded - version */ - userp = &data->state.aptr.user; - url_provided = TRUE; + if(data->set.use_netrc) { + /* Determine how to react on already existing credentials */ + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + Curl_creds_unlink(&conn->creds); } - if(!*passwdp) { - NETRCcode ret = Curl_parsenetrc(&data->state.netrc, - conn->origin->hostname, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - if(ret == NETRC_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; + if(data->state.creds) { + switch(data->state.creds->source) { + case CREDS_OPTION: + /* we never override credentials set via CURLOPT_* */ + goto out; + case CREDS_URL: + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + /* use the URL user to search netrc */ + result = Curl_creds_create( + data->state.creds->user, NULL, NULL, NULL, CREDS_URL, &ncreds_in); + if(result) + goto out; + } + else if(data->state.creds) { + /* only search when something is still missing */ + Curl_creds_link(&ncreds_in, data->state.creds); + } + break; + default: + /* ignore credentials from other sources */ + break; + } + } + + /* Only search in netrc when the creds are not already complete */ + if(!Curl_creds_has_passwd(ncreds_in)) { + NETRCcode ret; + + CURL_TRC_M(data, "netrc: find credentials for %s, user %s", + conn->origin->hostname, + Curl_creds_has_user(ncreds_in) ? ncreds_in->user : "*"); + ret = Curl_parsenetrc(&data->state.netrc, + conn->origin->hostname, + ncreds_in, + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } else if(ret && ((ret == NETRC_NO_MATCH) || (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { infof(data, "Could not find host %s in the %s file; using defaults", @@ -2225,113 +2233,76 @@ static CURLcode override_login(struct Curl_easy *data, else if(ret) { const char *m = Curl_netrc_strerror(ret); failf(data, ".netrc error: %s", m); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + goto out; } - else { + else if(ncreds_out) { if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { /* if the protocol cannot handle control codes in credentials, make sure there are none */ - if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { failf(data, "control code detected in .netrc credentials"); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + goto out; } } - /* set bits.netrc TRUE to remember that we got the name from a .netrc - file, so that it is safe to use even if we followed a Location: to a - different host or similar. */ - conn->bits.netrc = TRUE; + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + conn->origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + data->state.creds, CREDS_NETRC, + &data->state.creds); + if(result) + goto out; + creds_changed = TRUE; } - } - if(url_provided) { - curlx_free(conn->user); - conn->user = curlx_strdup(*userp); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - /* no user was set but a password, set a blank user */ - if(!*userp && *passwdp) { - *userp = curlx_strdup(""); - if(!*userp) - return CURLE_OUT_OF_MEMORY; + else + DEBUGASSERT(0); } } + #endif - /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result; - if(data->state.aptr.user != *userp) { - /* nothing to do then */ - result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); + if(creds_changed) { + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(data->state.creds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(data->state.creds), CURLU_URLENCODE); if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = curlx_strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } - if(*passwdp) { - CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - if(data->state.aptr.passwd) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - data->state.aptr.passwd, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*passwdp) { - *passwdp = curlx_strdup(data->state.aptr.passwd); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; - } + result = Curl_uc_to_curlcode(uc); } - return CURLE_OK; +out: +#ifndef CURL_DISABLE_NETRC + Curl_creds_unlink(&ncreds_in); + Curl_creds_unlink(&ncreds_out); +#endif + return result; } /* * Set the login details so they are available in the connection */ -static CURLcode set_login(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode url_set_conn_login(struct Curl_easy *data, + struct connectdata *conn) { - CURLcode result = CURLE_OK; - const char *setuser = CURL_DEFAULT_USER; - const char *setpasswd = CURL_DEFAULT_PASSWORD; - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) - ; - else { - setuser = ""; - setpasswd = ""; + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) { + if(data->state.creds) + Curl_creds_link(&conn->creds, data->state.creds); + else + return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD, + NULL, NULL, CREDS_NONE, &conn->creds); } - /* Store the default user */ - if(!conn->user) { - conn->user = curlx_strdup(setuser); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; + else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { + /* for protocols that do not handle credentials per request, + * the connection credentials are set by the initial transfer. */ + Curl_creds_link(&conn->creds, data->state.creds); } - /* Store the default password */ - if(!conn->passwd) { - conn->passwd = curlx_strdup(setpasswd); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - - return result; + return CURLE_OK; } /* @@ -2549,33 +2520,15 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, /* get the user+password information from the needle since it may * be new for this request even when we reuse conn */ - if(needle->user) { + if(needle->creds) { /* use the new username and password though */ - curlx_free(conn->user); - curlx_free(conn->passwd); - conn->user = needle->user; - conn->passwd = needle->passwd; - needle->user = NULL; - needle->passwd = NULL; + Curl_creds_link(&conn->creds, needle->creds); } #ifndef CURL_DISABLE_PROXY - conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy username and proxy password though */ - curlx_free(conn->http_proxy.user); - curlx_free(conn->socks_proxy.user); - curlx_free(conn->http_proxy.passwd); - curlx_free(conn->socks_proxy.passwd); - conn->http_proxy.user = needle->http_proxy.user; - conn->socks_proxy.user = needle->socks_proxy.user; - conn->http_proxy.passwd = needle->http_proxy.passwd; - conn->socks_proxy.passwd = needle->socks_proxy.passwd; - needle->http_proxy.user = NULL; - needle->socks_proxy.user = NULL; - needle->http_proxy.passwd = NULL; - needle->socks_proxy.passwd = NULL; - } + /* use the new proxy username and proxy password though */ + Curl_creds_link(&conn->http_proxy.creds, needle->http_proxy.creds); + Curl_creds_link(&conn->socks_proxy.creds, needle->socks_proxy.creds); #endif /* Finding a connection for reuse in the cpool matches, among other @@ -2686,29 +2639,13 @@ static CURLcode url_create_needle(struct Curl_easy *data, } #endif /* CURL_DISABLE_PROXY */ - if(data->set.str[STRING_SASL_AUTHZID]) { - needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!needle->sasl_authzid) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_BEARER]) { - needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!needle->oauth_bearer) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - /* Check for overridden login details and set them accordingly so that they are known when protocol->setup_connection is called! */ result = override_login(data, needle); if(result) goto out; - result = set_login(data, needle); /* default credentials */ + result = url_set_conn_login(data, needle); /* default credentials */ if(result) goto out; diff --git a/lib/urldata.h b/lib/urldata.h index 2889e936a4..6ad4666280 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -55,6 +55,7 @@ #include "asyn.h" #include "cookie.h" +#include "creds.h" #include "psl.h" #include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ @@ -206,10 +207,9 @@ struct digestdata { BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* copy of user/passwd used to make the identity for http_context. - either may be NULL. */ - char *user; - char *passwd; + /* linked credentials used to make the identity for http_context. + may be NULL. */ + struct Curl_creds *creds; #else char *nonce; char *cnonce; @@ -249,7 +249,6 @@ struct ConnectBits { #ifndef CURL_DISABLE_PROXY BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ - BIT(proxy_user_passwd); /* user+password for the proxy? */ BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. This is implicit when SSL-protocols are used through proxies, but can also be enabled explicitly by @@ -275,9 +274,6 @@ struct ConnectBits { EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ -#endif -#ifndef CURL_DISABLE_NETRC - BIT(netrc); /* name+password provided by netrc */ #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ @@ -328,9 +324,8 @@ struct ip_quadruple { struct proxy_info { struct Curl_peer *peer; /* proxy to this peer */ + struct Curl_creds *creds; /* use these credentials, maybe NULL */ uint8_t proxytype; /* what kind of proxy that is in use */ - char *user; /* proxy username string, allocated */ - char *passwd; /* proxy password string, allocated */ }; /* @@ -370,11 +365,8 @@ struct connectdata { struct proxy_info socks_proxy; struct proxy_info http_proxy; #endif - char *user; /* username string, allocated */ - char *passwd; /* password string, allocated */ + struct Curl_creds *creds; /* When connection itself is tied to credentials */ char *options; /* options string, allocated */ - char *sasl_authzid; /* authorization identity string, allocated */ - char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ @@ -652,11 +644,6 @@ struct urlpieces { char *query; }; -#define CREDS_NONE 0 -#define CREDS_URL 1 /* from URL */ -#define CREDS_OPTION 2 /* set with a CURLOPT_ */ -#define CREDS_NETRC 3 /* found in netrc */ - struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ @@ -672,10 +659,10 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* origin of the first (not followed) request. - if set, this is the origin we sent authorization to, none else. - Used to make Location: following not keep sending user+password. */ - struct Curl_peer *first_origin; + /* Origin of the initial (e.g. not followed) request of a transfer. + Credentials from CURLOPT_* are only valid for this origin. + Always set once a transfer starts searching for connections. */ + struct Curl_peer *initial_origin; int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ @@ -765,6 +752,8 @@ struct UrlState { struct store_netrc netrc; #endif + struct Curl_creds *creds; /* Credentials for the origin only */ + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { @@ -776,14 +765,6 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif - - /* transfer credentials */ - char *user; - char *passwd; -#ifndef CURL_DISABLE_PROXY - char *proxyuser; - char *proxypasswd; -#endif } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; @@ -793,8 +774,6 @@ struct UrlState { CONN_MAX_RETRIES */ uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - unsigned int creds_from:2; /* where is the server credentials originating - from, see the CREDS_* defines above */ /* when curl_easy_perform() is called, the multi handle is "owned" by the easy handle so curl_easy_cleanup() on such an easy handle will diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c index 7976adec9c..ca0c9967ff 100644 --- a/lib/vauth/cleartext.c +++ b/lib/vauth/cleartext.c @@ -40,24 +40,21 @@ * * Parameters: * - * authzid [in] - The authorization identity. - * authcid [in] - The authentication identity. + * creds [in] - The credentials. * passwd [in] - The password. * out [out] - The result storage. * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out) { size_t len; char *auth; - size_t zlen = (authzid == NULL ? 0 : strlen(authzid)); - size_t clen = strlen(authcid); - size_t plen = strlen(passwd); + size_t zlen = strlen(Curl_creds_sasl_authzid(creds)); + size_t clen = strlen(Curl_creds_user(creds)); + size_t plen = strlen(Curl_creds_passwd(creds)); if((zlen > CURL_MAX_INPUT_LENGTH) || (clen > CURL_MAX_INPUT_LENGTH) || (plen > CURL_MAX_INPUT_LENGTH)) @@ -65,8 +62,10 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, len = zlen + clen + plen + 2; - auth = curl_maprintf("%s%c%s%c%s", authzid ? authzid : "", '\0', - authcid, '\0', passwd); + auth = curl_maprintf("%s%c%s%c%s", + Curl_creds_sasl_authzid(creds), '\0', + Curl_creds_user(creds), '\0', + Curl_creds_passwd(creds)); if(!auth) return CURLE_OUT_OF_MEMORY; Curl_bufref_set(out, auth, len, curl_free); diff --git a/lib/vauth/cram.c b/lib/vauth/cram.c index d3f2d137b7..023c31b58b 100644 --- a/lib/vauth/cram.c +++ b/lib/vauth/cram.c @@ -47,18 +47,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out) { struct HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char *response; + const char *user = Curl_creds_user(creds); + const char *passwd = Curl_creds_passwd(creds); /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, - (const unsigned char *)passwdp, - curlx_uztoui(strlen(passwdp))); + (const unsigned char *)passwd, + curlx_uztoui(strlen(passwd))); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -73,7 +74,7 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, /* Generate the response */ response = curl_maprintf( "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - userp, digest[0], digest[1], digest[2], digest[3], digest[4], + user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); if(!response) diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 3c6f6e8e95..f7080e2ece 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -332,13 +332,14 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out) { size_t i; struct MD5_context *ctxt; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; @@ -666,8 +667,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The username. - * passwdp [in] - The user's password. + * creds [in] - The credentials * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. @@ -679,8 +679,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, */ static CURLcode auth_create_digest_http_message( struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -689,6 +688,8 @@ static CURLcode auth_create_digest_http_message( CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); unsigned char hashbuf[32]; /* 32 bytes/256 bits */ unsigned char request_digest[65]; unsigned char ha1[65]; /* 64 digits and 1 zero byte */ @@ -986,29 +987,28 @@ oom: * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { if(digest->algo <= ALGO_MD5SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_md5_to_ascii, Curl_md5it); if(digest->algo <= ALGO_SHA256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, Curl_sha256it); #ifdef CURL_HAVE_SHA512_256 if(digest->algo <= ALGO_SHA512_256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index f351a76986..31dfebfa51 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -28,6 +28,7 @@ #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) +#include "creds.h" #include "vauth/vauth.h" #include "vauth/digest.h" #include "curlx/multibyte.h" @@ -83,8 +84,7 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out) { @@ -137,9 +137,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &identity); if(result) { curlx_free(spn); curlx_free(output_token); @@ -381,8 +382,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -421,16 +421,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* If the user/passwd that was used to make the identity for http_context has changed then delete that context. */ - if((userp && !digest->user) || (!userp && digest->user) || - (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || - (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || - (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { + if(!Curl_creds_same(creds, digest->creds)) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } if(digest->http_context) { @@ -473,13 +469,13 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, unsigned long attrs; TCHAR *spn; - /* free the copy of user/passwd used to make the previous identity */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + /* free the credentials used to make the previous identity */ + Curl_creds_unlink(&digest->creds); - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - if(Curl_create_sspi_identity(userp, passwdp, &identity)) { + if(Curl_create_sspi_identity(creds->user, creds->passwd, + &identity)) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -499,26 +495,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Use the current Windows user */ p_identity = NULL; - if(userp) { - digest->user = curlx_strdup(userp); - - if(!digest->user) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - return CURLE_OUT_OF_MEMORY; - } - } - - if(passwdp) { - digest->passwd = curlx_strdup(passwdp); - - if(!digest->passwd) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - curlx_safefree(digest->user); - return CURLE_OUT_OF_MEMORY; - } - } + if(creds) + Curl_creds_link(&digest->creds, creds); /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, @@ -649,8 +627,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } /* Free the copy of user/passwd used to make the identity for http_context */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 958f4ffab7..3ea77eecd1 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -54,15 +54,14 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, } CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl) { #if GSASL_VERSION_NUMBER >= 0x010b00 int res; res = #endif - gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); + gsasl_property_set(gsasl->client, GSASL_AUTHID, creds->user); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting AUTHID failed: %s", gsasl_strerror(res)); @@ -73,7 +72,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, #if GSASL_VERSION_NUMBER >= 0x010b00 res = #endif - gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); + gsasl_property_set(gsasl->client, GSASL_PASSWORD, creds->passwd); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting PASSWORD failed: %s", gsasl_strerror(res)); diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index 64c735be58..ad2c04facf 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -74,8 +74,7 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -90,8 +89,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - (void)userp; - (void)passwdp; + (void)creds; if(!krb5->spn) { gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index e7491be022..dfac639bf9 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -79,8 +79,7 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -128,9 +127,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &krb5->identity); if(result) return result; diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 1e485ed34d..bd914f3c8c 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -421,8 +421,7 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -453,8 +452,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size_t domoff = hostoff + hostlen; /* This is 0: remember that host and domain are empty */ (void)data; - (void)userp; - (void)passwdp; + (void)creds; (void)service; (void)host; @@ -542,8 +540,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -579,6 +576,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* The fixed hostname we provide, in order to not leak our real local host name. Copy the name used by Firefox. */ static const char host[] = "WORKSTATION"; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); const char *user; const char *domain = ""; size_t hostoff = 0; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 4c41eb21f4..5fe78a622d 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -76,8 +76,7 @@ bool Curl_auth_is_ntlm_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -111,11 +110,12 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, if(!ntlm->output_token) return CURLE_OUT_OF_MEMORY; - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { CURLcode result; /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &ntlm->identity); if(result) return result; @@ -227,8 +227,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -240,8 +239,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SECURITY_STATUS status; unsigned long attrs; - (void)passwdp; - (void)userp; + (void)creds; /* Setup the type-2 "input" security buffer */ type_2_desc.ulVersion = SECBUFFER_VERSION; diff --git a/lib/vauth/oauth2.c b/lib/vauth/oauth2.c index 4541d1d551..f597114638 100644 --- a/lib/vauth/oauth2.c +++ b/lib/vauth/oauth2.c @@ -47,21 +47,22 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out) { char *oauth; /* Generate the message */ if(port == 0 || port == 80) - oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, - bearer); + oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), host, + Curl_creds_oauth_bearer(creds)); else oauth = curl_maprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", - user, host, port, bearer); + Curl_creds_user(creds), host, port, + Curl_creds_oauth_bearer(creds)); if(!oauth) return CURLE_OUT_OF_MEMORY; @@ -83,12 +84,13 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out) { /* Generate the message */ - char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), + Curl_creds_oauth_bearer(creds)); if(!xoauth) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 38bb4c1422..631480fa76 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -70,8 +70,7 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, @@ -90,8 +89,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct gss_channel_bindings_struct chan; #endif - (void)user; - (void)password; + (void)creds; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index eeae021484..ba4c4186a0 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -78,8 +78,7 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, @@ -133,9 +132,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(user, password, &nego->identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &nego->identity); if(result) return result; diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index 81c29cd497..76de85cb28 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "vauth/vauth.h" +#include "creds.h" #include "curlx/multibyte.h" #include "url.h" @@ -111,15 +112,16 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, * * Returns TRUE on success; otherwise FALSE. */ -bool Curl_auth_user_contains_domain(const char *user) +bool Curl_auth_user_contains_domain(struct Curl_creds *creds) { bool valid = FALSE; - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Check we have a domain name or UPN present */ - const char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(creds->user, "\\/@"); - valid = (p != NULL && p > user && p < user + strlen(user) - 1); + valid = (p != NULL) && (p > creds->user) && + (p < (creds->user + strlen(creds->user) - 1)); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else @@ -133,14 +135,12 @@ bool Curl_auth_user_contains_domain(const char *user) /* * Curl_auth_allowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. + * "sensitive data" can be sent to the connection's origin. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) { - return !data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_origin && - Curl_peer_equal(data->state.first_origin, data->conn->origin)); + return data->set.allow_auth_to_other_hosts || + Curl_peer_equal(data->state.initial_origin, data->conn->origin); } #ifdef USE_NTLM diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 279da60be2..cdd64a1cfb 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -30,6 +30,7 @@ #include "urldata.h" struct Curl_easy; +struct Curl_creds; struct connectdata; #ifndef CURL_DISABLE_DIGEST_AUTH @@ -69,12 +70,10 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, #endif /* This is used to test if the user contains a Windows domain name */ -bool Curl_auth_user_contains_domain(const char *user); +bool Curl_auth_user_contains_domain(struct Curl_creds *creds); /* This is used to generate a PLAIN cleartext message */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out); /* This is used to generate a LOGIN cleartext message */ @@ -86,8 +85,7 @@ void Curl_auth_create_external_message(const char *user, struct bufref *out); #ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out); /* This is used to evaluate if DIGEST is supported */ @@ -96,8 +94,7 @@ bool Curl_auth_is_digest_supported(void); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, struct bufref *out); @@ -107,8 +104,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* This is used to generate an HTTP DIGEST response message */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -140,8 +136,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, struct gsasldata *gsasl); /* This is used to start a gsasl method */ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl); /* This is used to process and generate a new SASL token */ @@ -197,8 +192,7 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, struct ntlmdata *ntlm, @@ -211,8 +205,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, /* This is used to generate a base64 encoded NTLM type-3 message */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out); @@ -221,15 +214,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #endif /* USE_NTLM */ /* This is used to generate a base64 encoded OAuth 2.0 message */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out); /* This is used to generate a base64 encoded XOAuth 2.0 message */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out); #ifdef USE_KERBEROS5 @@ -260,8 +251,7 @@ bool Curl_auth_is_gssapi_supported(void); /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token message */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const char *service, const char *host, const bool mutual_auth, @@ -330,8 +320,7 @@ Curl_auth_nego_get(struct connectdata *conn, bool proxy); /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, + struct Curl_creds *creds, const char *service, const char *host, const char *chlg64, diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index c6a6e0cfdf..49f9d3f93d 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -633,7 +633,8 @@ restart: if(nprompts != 1) return SSH_ERROR; - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, + Curl_creds_passwd(conn->creds)); if(rc < 0) return SSH_ERROR; @@ -920,7 +921,8 @@ static int myssh_in_AUTH_PASS_INIT(struct Curl_easy *data, static int myssh_in_AUTH_PASS(struct Curl_easy *data, struct ssh_conn *sshc) { - int rc = ssh_userauth_password(sshc->ssh_session, NULL, data->conn->passwd); + int rc = ssh_userauth_password(sshc->ssh_session, NULL, + Curl_creds_passwd(data->conn->creds)); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; else if(rc == SSH_AUTH_SUCCESS) { @@ -2571,9 +2573,10 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - if(conn->user && conn->user[0] != '\0') { - infof(data, "User: %s", conn->user); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user); + if(Curl_creds_has_user(conn->creds)) { + infof(data, "User: %s", conn->creds->user); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, + conn->creds->user); if(rc != SSH_OK) { failf(data, "Could not set user"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 1f36934f6d..118bc594f6 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -148,11 +148,12 @@ static void kbd_callback(const char *name, int name_len, #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; + const char *passwd = Curl_creds_passwd(conn->creds); /* this function must allocate memory that can be freed by libssh2, which uses the LIBSSH2_FREE_FUNC callback */ - responses[0].text = Curl_cstrdup(conn->passwd); + responses[0].text = Curl_cstrdup(passwd); responses[0].length = - responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + responses[0].text == NULL ? 0 : curlx_uztoui(strlen(passwd)); } (void)prompts; } /* kbd_callback */ @@ -1496,9 +1497,9 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, * Therefore always specify it here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); sshc->authlist = libssh2_userauth_list(sshc->ssh_session, - conn->user, - curlx_uztoui(strlen(conn->user))); + user, curlx_uztoui(strlen(user))); if(!sshc->authlist) { int rc; @@ -1527,11 +1528,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, /* The function below checks if the files exists, no need to stat() here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, + curlx_uztoui(strlen(user)), sshc->rsa_pub, sshc->rsa, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -1579,11 +1580,13 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, struct ssh_conn *sshc) { struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); + const char *passwd = Curl_creds_passwd(conn->creds); int rc = - libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - curlx_uztoui(strlen(conn->user)), - conn->passwd, - curlx_uztoui(strlen(conn->passwd)), + libssh2_userauth_password_ex(sshc->ssh_session, user, + curlx_uztoui(strlen(user)), + passwd, + curlx_uztoui(strlen(passwd)), NULL); if(rc == LIBSSH2_ERROR_EAGAIN) { return CURLE_AGAIN; @@ -1680,7 +1683,7 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, if(rc == 0) { struct connectdata *conn = data->conn; - rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + rc = libssh2_agent_userauth(sshc->ssh_agent, Curl_creds_user(conn->creds), sshc->sshagent_identity); if(rc < 0) { @@ -1727,11 +1730,10 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, { /* Authentication failed. Continue with keyboard-interactive now. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, curlx_uztoui(strlen(user)), &kbd_callback); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -3452,9 +3454,9 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(!sshc) return CURLE_FAILED_INIT; - infof(data, "User: '%s'", conn->user); + infof(data, "User: '%s'", Curl_creds_user(conn->creds)); #ifdef CURL_LIBSSH2_DEBUG - infof(data, "Password: %s", conn->passwd); + infof(data, "Password: %s", Curl_creds_passwd(conn->creds)); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index 4f3ef11fd0..c13c5e863c 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -46,7 +46,6 @@ static CURLcode test_lib1978(const char *URL) test_setopt(curl, CURLOPT_INFILESIZE, 0L); test_setopt(curl, CURLOPT_VERBOSE, 1L); test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); test_setopt(curl, CURLOPT_HEADER, 0L); test_setopt(curl, CURLOPT_URL, URL); diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c index 72b2ab5a32..d66fe796e9 100644 --- a/tests/unit/unit1304.c +++ b/tests/unit/unit1304.c @@ -25,17 +25,37 @@ #ifndef CURL_DISABLE_NETRC #include "netrc.h" +#include "creds.h" -static void t1304_stop(char **password, char **login) +static void t1304_stop(struct Curl_creds **pc1, struct Curl_creds **pc2) { - curlx_safefree(*password); - curlx_safefree(*login); + Curl_creds_unlink(pc1); + Curl_creds_unlink(pc2); +} + +static bool t1304_set_creds(const char *user, const char *passwd, + struct Curl_creds **pcreds) +{ + Curl_creds_unlink(pcreds); + if(user || passwd) + return !Curl_creds_create(user, passwd, NULL, NULL, CREDS_NONE, pcreds); + else + return TRUE; +} + +static bool t1304_no_user(struct Curl_creds *creds) +{ + return !creds || !creds->user[0]; +} + +static bool t1304_no_passwd(struct Curl_creds *creds) +{ + return !creds || !creds->passwd[0]; } static CURLcode test_unit1304(const char *arg) { - char *login = NULL; - char *password = NULL; + struct Curl_creds *cr_out = NULL, *cr_in = NULL; UNITTEST_BEGIN_SIMPLE @@ -46,126 +66,119 @@ static CURLcode test_unit1304(const char *arg) * Test a non existent host in our netrc file. */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", &login, &password, arg); - fail_unless(result == 1, "Host not found should return 1"); - abort_unless(password == NULL, "password did not return NULL!"); - abort_unless(login == NULL, "user did not return NULL!"); + result = Curl_parsenetrc(&store, "test.example.com", NULL, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(cr_out == NULL, "creds did not return NULL!"); Curl_netrc_cleanup(&store); /* * Test a non existent login in our netrc file. */ - login = curlx_strdup("me"); + fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login and host in our netrc file. */ - login = curlx_strdup("me"); + fail_unless(t1304_set_creds("me", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "test.example.com", &login, &password, arg); - fail_unless(result == 1, "Host not found should return 1"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "test.example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login (substring of an existing one) in our * netrc file. */ - login = curlx_strdup("admi"); /* spellchecker:disable-line */ + fail_unless(t1304_set_creds( + "admi", NULL, &cr_in), "err set creds"); /* spellchecker:disable-line */ Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test a non existent login (superstring of an existing one) * in our netrc file. */ - login = curlx_strdup("adminn"); + fail_unless(t1304_set_creds("adminn", NULL, &cr_in), "err set creds"); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); - fail_unless(result == 0, "Host should have been found"); - abort_unless(password == NULL, "password is not NULL!"); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); + fail_unless(result == 1, "expected no match"); + abort_unless(t1304_no_passwd(cr_out), "password is not NULL!"); Curl_netrc_cleanup(&store); - curlx_free(login); /* * Test for the first existing host in our netrc file * with login[0] = 0. */ - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "passwd", 6) == 0, + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, + "login should be 'admin'"); Curl_netrc_cleanup(&store); /* * Test for the first existing host in our netrc file * with login[0] != 0. */ - curlx_free(password); - curlx_free(login); - password = NULL; - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "passwd", 6) == 0, + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "passwd", 6) == 0, "password should be 'passwd'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "admin", 5) == 0, + "login should be 'admin'"); Curl_netrc_cleanup(&store); /* * Test for the second existing host in our netrc file * with login[0] = 0. */ - curlx_free(password); - password = NULL; - curlx_free(login); - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "none", 4) == 0, "password should be 'none'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, + "password should be 'none'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, + "login should be 'none'"); Curl_netrc_cleanup(&store); /* * Test for the second existing host in our netrc file * with login[0] != 0. */ - curlx_free(password); - password = NULL; - curlx_free(login); - login = NULL; + Curl_creds_unlink(&cr_in); Curl_netrc_init(&store); - result = Curl_parsenetrc(&store, "curl.example.com", &login, &password, arg); + result = Curl_parsenetrc(&store, "curl.example.com", cr_in, arg, &cr_out); fail_unless(result == 0, "Host should have been found"); - abort_unless(password != NULL, "returned NULL!"); - fail_unless(strncmp(password, "none", 4) == 0, "password should be 'none'"); - abort_unless(login != NULL, "returned NULL!"); - fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + abort_unless(!t1304_no_passwd(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_passwd(cr_out), "none", 4) == 0, + "password should be 'none'"); + abort_unless(!t1304_no_user(cr_out), "returned NULL!"); + fail_unless(strncmp(Curl_creds_user(cr_out), "none", 4) == 0, + "login should be 'none'"); Curl_netrc_cleanup(&store); - UNITTEST_END(t1304_stop(&password, &login)) + UNITTEST_END(t1304_stop(&cr_in, &cr_out)) } #else