creds: hold credentials
No binary (build failed)
—
(log yok)
| @@ -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 \ | |
| @@ -0,0 +1,75 @@ | |
| + | <!-- |
| + | Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | |
| + | SPDX-License-Identifier: curl |
| + | --> |
| + | |
| + | # 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. |
| @@ -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 \ | |
| @@ -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); | |
| @@ -0,0 +1,183 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 <stddef.h> /* 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 |
| @@ -0,0 +1,86 @@ | |
| + | #ifndef HEADER_CURL_CREDS_H |
| + | #define HEADER_CURL_CREDS_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */ |
| @@ -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 */ | |
| @@ -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); | |
| } | |
| @@ -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); |
| - | #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; |
| + | { |
| + | 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 |
| } | |
| 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) { | |
| @@ -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 */ | |
| @@ -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; | |
| @@ -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; | |
| } | |
| @@ -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); | |
| @@ -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); | |
| @@ -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); | |
| @@ -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; | |
| } | |
| @@ -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; | |
| } | |
| @@ -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. | |
| @@ -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 */ | |
| @@ -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); | |
| @@ -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. | |
| @@ -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)); | |
| @@ -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); | |
| @@ -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; | |
| @@ -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); | |
| @@ -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; | |
| @@ -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); | |
| @@ -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 = ""; |
| - | } |
| - | /* Store the default user */ |
| - | if(!conn->user) { |
| - | conn->user = curlx_strdup(setuser); |
| - | if(!conn->user) |
| - | return CURLE_OUT_OF_MEMORY; |
| + | 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 password */ |
| - | if(!conn->passwd) { |
| - | conn->passwd = curlx_strdup(setpasswd); |
| - | if(!conn->passwd) |
| - | result = 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); |
| } | |
| - | 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; | |
| @@ -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 | |
| @@ -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); | |
| @@ -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) | |
| @@ -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, | |
| @@ -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 */ | |
| @@ -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)); | |
| @@ -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; | |
| @@ -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; | |
| @@ -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; | |
| @@ -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; | |
| @@ -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; | |
| @@ -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 | |
| @@ -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; | |
| @@ -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 | |
| @@ -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, | |
| @@ -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; | |
| @@ -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 */ | |
| @@ -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); | |
| @@ -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 | |