internals/CLIENT-WRITERS.md \
internals/CODE_STYLE.md \
internals/CONNECTION-FILTERS.md \
+ internals/CREDENTIALS.md \
internals/CURLX.md \
internals/DYNBUF.md \
internals/HASH.md \
--- /dev/null
+<!--
+Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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.
connect.c \
content_encoding.c \
cookie.c \
+ creds.c \
cshutdn.c \
curl_addrinfo.c \
curl_endian.c \
connect.h \
content_encoding.h \
cookie.h \
+ creds.h \
curl_addrinfo.h \
curl_ctype.h \
curl_endian.h \
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);
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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
--- /dev/null
+#ifndef HEADER_CURL_CREDS_H
+#define HEADER_CURL_CREDS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 */
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 */
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;
{
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;
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,
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;
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);
}
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);
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;
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;
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;
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;
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);
*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:
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;
/* 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;
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
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);
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,
}
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:
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
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;
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);
}
}
"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);
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 */
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);
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);
}
*
* 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
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;
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;
* 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
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)
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);
}
}
#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,
/* 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;
}
#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);
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;
}
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;
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
return CURLE_OUT_OF_MEMORY;
}
else {
- bool same_origin;
- CURLcode result;
CURLU *u = curl_url();
if(!u)
return CURLE_OUT_OF_MEMORY;
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);
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);
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
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) {
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 };
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 */
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;
#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
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;
{
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);
}
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;
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;
#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;
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);
#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
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;
}
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;
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;
}
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 */
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);
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);
/* 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 : "",
/* 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);
#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;
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);
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 */
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);
/* 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;
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;
}
#endif
#include "netrc.h"
+#include "creds.h"
#include "strcase.h"
#include "curl_get_line.h"
#include "curlx/fopen.h"
/* bundled parser state to keep function signatures compact */
struct netrc_state {
+ struct Curl_creds *existing;
char *login;
char *password;
enum host_lookup_state state;
unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */
bool our_login;
bool done;
- bool specific_login;
};
/*
ns->found = 0;
ns->our_login = FALSE;
curlx_safefree(ns->password);
- if(!ns->specific_login)
- curlx_safefree(ns->login);
+ curlx_safefree(ns->login);
}
/*
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);
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;
* 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;
}
*/
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) {
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)
* 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;
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) {
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;
}
#include "curlx/dynbuf.h"
+struct Curl_creds;
+
struct store_netrc {
struct dynbuf filebuf;
char *filename;
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.
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);
}
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;
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 */
/* 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;
/* 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);
/* 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;
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);
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);
}
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);
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.
/* 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];
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 */
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;
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;
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));
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;
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
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;
"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;
req[1 + nauths] = 1; /* GSS-API */
}
#endif
- if(sx->user) {
+ if(sx->creds) {
++nauths;
req[1 + nauths] = 2; /* username/password */
}
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");
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;
}
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;
}
{
if(ctx) {
Curl_peer_unlink(&ctx->dest);
+ Curl_creds_unlink(&ctx->creds);
Curl_bufq_free(&ctx->iobuf);
curlx_free(ctx);
}
out:
*done = (bool)cf->connected;
- if(*done || result) {
- ctx->user = NULL;
- ctx->passwd = NULL;
- }
+ if(*done || result)
+ Curl_creds_unlink(&ctx->creds);
return result;
}
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;
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);
#ifndef CURL_DISABLE_PROXY
struct Curl_peer;
+struct Curl_creds;
/*
* Helper read-from-socket functions. Does the same as Curl_read() but it
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;
/* 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);
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
#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 */
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;
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);
/* 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);
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);
#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);
}
#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);
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;
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
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. */
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) {
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) {
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) {
(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
(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
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 */
#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);
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. */
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);
}
(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);
}
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;
}
/*
* 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;
}
result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
- &peer, &proxytype);
+ &proxyinfo->peer, &proxytype);
if(result)
goto error;
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? */
}
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) {
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
*************************************************************/
}
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;
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 */
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;
}
/***********************************************************************
* 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) {
/* 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) {
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 */
out:
- curlx_free(socksproxy);
+ curlx_free(pre_proxy);
curlx_free(proxy);
return result;
}
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",
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;
}
/*
/* 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
}
#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;
#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 */
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;
#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
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 */
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 */
};
/*
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 */
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 */
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 */
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 {
#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;
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
*
* 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))
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);
* 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;
/* 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)
*/
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];
* 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.
*/
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,
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 */
* 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,
#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"
*/
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)
{
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);
* 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,
/* 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) {
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;
}
/* 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,
}
/* 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 */
}
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));
#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));
* 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,
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;
* 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,
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;
* 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,
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;
* 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)
{
/* 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;
* 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,
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;
* 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)
{
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;
*
* 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;
*
* 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;
* 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,
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
* 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,
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;
#include "curl_setup.h"
#include "vauth/vauth.h"
+#include "creds.h"
#include "curlx/multibyte.h"
#include "url.h"
*
* 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
/*
* 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
#include "urldata.h"
struct Curl_easy;
+struct Curl_creds;
struct connectdata;
#ifndef CURL_DISABLE_DIGEST_AUTH
#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 */
#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 */
/* 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);
/* 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,
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 */
/* 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,
/* 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);
#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
/* 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,
/* 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,
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;
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) {
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;
#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 */
* 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;
/* 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)
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;
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) {
{
/* 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;
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 */
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);
#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
* 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