summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2019-03-21 16:49:25 -1000
committerEdward Thomson <ethomson@edwardthomson.com>2019-06-10 19:58:22 +0100
commit79fc8281879b804657de6db440630b4dc45423d5 (patch)
treee6f9377165c824152e4d1251fd4570162677ca51
parent5ad992107f1220c988261c6764ed46b1e303d7f9 (diff)
downloadlibgit2-79fc8281879b804657de6db440630b4dc45423d5.tar.gz
http: validate server's authentication types
Ensure that the server supports the particular credential type that we're specifying. Previously we considered credential types as an input to an auth mechanism - since the HTTP transport only supported default credentials (via negotiate) and username/password credentials (via basic), this worked. However, if we are to add another mechanism that uses username/password credentials, we'll need to be careful to identify the types that are accepted.
-rw-r--r--src/transports/http.c139
1 files changed, 97 insertions, 42 deletions
diff --git a/src/transports/http.c b/src/transports/http.c
index eb5c35284..6ac13ca3c 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -75,8 +75,9 @@ typedef struct {
git_net_url url;
git_stream *stream;
+ git_http_authtype_t server_types;
git_cred *cred;
- git_cred *url_cred;
+ unsigned url_cred_presented : 1;
git_vector auth_challenges;
git_vector auth_contexts;
@@ -140,6 +141,19 @@ static bool challenge_match(git_http_auth_scheme *scheme, void *data)
(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
}
+typedef struct {
+ git_http_authtype_t server_types;
+ unsigned int credtype;
+} authmatch_data;
+
+static bool auth_match(git_http_auth_scheme *scheme, void *_data)
+{
+ authmatch_data *data = (authmatch_data *)_data;
+
+ return !!(data->server_types & scheme->type) &&
+ !!(scheme->credtypes & data->credtype);
+}
+
static int auth_context_match(
git_http_auth_context **out,
http_server *server,
@@ -191,18 +205,9 @@ static int apply_credentials(
{
git_cred *cred = server->cred;
git_http_auth_context *context;
+ authmatch_data data = {0};
- /* Apply the credentials given to us in the URL */
- if (!cred && server->url.username && server->url.password) {
- if (!server->url_cred &&
- git_cred_userpass_plaintext_new(&server->url_cred,
- server->url.username, server->url.password) < 0)
- return -1;
-
- cred = server->url_cred;
- }
-
- if (!cred)
+ if (!server->server_types)
return 0;
/* Get or create a context for the best scheme for this cred type */
@@ -213,6 +218,24 @@ static int apply_credentials(
if (!context)
return 0;
+ /*
+ * If we do have creds, find the first mechanism supported by both
+ * the server and ourselves that supports the credential type.
+ */
+ if (!cred)
+ return 0;
+
+ data.server_types = server->server_types;
+ data.credtype = cred->credtype;
+
+ if (auth_context_match(&context, server, auth_match, &data) < 0)
+ return -1;
+
+ if (!context) {
+ git_error_set(GIT_ERROR_NET, "no suitable mechanism found for authentication");
+ return -1;
+ }
+
return context->next_token(buf, context, header_name, cred);
}
@@ -297,6 +320,7 @@ static int parse_authenticate_response(
context->set_challenge(context, challenge) < 0)
return -1;
+ server->server_types |= context->type;
*allowed_types |= context->credtypes;
}
@@ -399,19 +423,34 @@ GIT_INLINE(void) free_cred(git_cred **cred)
}
}
+static int apply_url_credentials(
+ git_cred **cred,
+ unsigned int allowed_types,
+ const char *username,
+ const char *password)
+{
+ if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)
+ return git_cred_userpass_plaintext_new(cred, username, password);
+
+ if ((allowed_types & GIT_CREDTYPE_DEFAULT) && *username == '\0' && *password == '\0')
+ return git_cred_default_new(cred);
+
+ return GIT_PASSTHROUGH;
+}
+
static int on_auth_required(
git_cred **creds,
http_parser *parser,
+ http_server *server,
const char *url,
const char *type,
git_cred_acquire_cb callback,
void *callback_payload,
- const char *username,
int allowed_types)
{
parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t;
- int ret;
+ int error = 1;
if (!allowed_types) {
git_error_set(GIT_ERROR_NET, "%s requested authentication but did not negotiate mechanisms", type);
@@ -419,35 +458,50 @@ static int on_auth_required(
return t->parse_error;
}
- if (callback) {
- free_cred(creds);
- ret = callback(creds, url, username, allowed_types, callback_payload);
+ free_cred(creds);
+
+ /* Start with URL-specified credentials, if there were any. */
+ if (!server->url_cred_presented && server->url.username && server->url.password) {
+ error = apply_url_credentials(creds, allowed_types, server->url.username, server->url.password);
+ server->url_cred_presented = 1;
- if (ret == GIT_PASSTHROUGH) {
+ if (error == GIT_PASSTHROUGH) {
/* treat GIT_PASSTHROUGH as if callback isn't set */
- } else if (ret < 0) {
- t->error = ret;
- t->parse_error = PARSE_ERROR_EXT;
- return t->parse_error;
- } else {
- assert(*creds);
-
- if (!((*creds)->credtype & allowed_types)) {
- git_error_set(GIT_ERROR_NET, "%s credential provider returned an invalid cred type", type);
- t->parse_error = PARSE_ERROR_GENERIC;
- return t->parse_error;
- }
+ error = 1;
+ }
+ }
- /* Successfully acquired a credential. */
- t->parse_error = PARSE_ERROR_REPLAY;
- return 0;
+ if (error > 0 && callback) {
+ error = callback(creds, url, server->url.username, allowed_types, callback_payload);
+
+ if (error == GIT_PASSTHROUGH) {
+ /* treat GIT_PASSTHROUGH as if callback isn't set */
+ error = 1;
}
}
- git_error_set(GIT_ERROR_NET, "%s authentication required but no callback set",
- type);
- t->parse_error = PARSE_ERROR_GENERIC;
- return t->parse_error;
+ if (error > 0) {
+ git_error_set(GIT_ERROR_NET, "%s authentication required but no callback set",
+ type);
+ t->parse_error = PARSE_ERROR_GENERIC;
+ return t->parse_error;
+ } else if (error < 0) {
+ t->error = error;
+ t->parse_error = PARSE_ERROR_EXT;
+ return t->parse_error;
+ }
+
+ assert(*creds);
+
+ if (!((*creds)->credtype & allowed_types)) {
+ git_error_set(GIT_ERROR_NET, "%s credential provider returned an invalid cred type", type);
+ t->parse_error = PARSE_ERROR_GENERIC;
+ return t->parse_error;
+ }
+
+ /* Successfully acquired a credential. */
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
}
static int on_headers_complete(http_parser *parser)
@@ -483,22 +537,22 @@ static int on_headers_complete(http_parser *parser)
if (parser->status_code == 407 && get_verb == s->verb)
return on_auth_required(&t->proxy.cred,
parser,
+ &t->proxy,
t->proxy_opts.url,
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
t->proxy_opts.payload,
- t->proxy.url.username,
proxy_auth_types);
/* Check for an authentication failure. */
if (parser->status_code == 401 && get_verb == s->verb)
return on_auth_required(&t->server.cred,
parser,
+ &t->server,
t->owner->url,
SERVER_TYPE_REMOTE,
t->owner->cred_acquire_cb,
t->owner->cred_acquire_payload,
- t->server.url.username,
server_auth_types);
/* Check for a redirect.
@@ -800,11 +854,11 @@ static int proxy_headers_complete(http_parser *parser)
if (parser->status_code == 407)
return on_auth_required(&t->proxy.cred,
parser,
+ &t->proxy,
t->proxy_opts.url,
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
t->proxy_opts.payload,
- t->proxy.url.username,
proxy_auth_types);
if (parser->status_code != 200) {
@@ -1435,13 +1489,14 @@ static int http_close(git_smart_subtransport *subtransport)
}
free_cred(&t->server.cred);
- free_cred(&t->server.url_cred);
free_cred(&t->proxy.cred);
- free_cred(&t->proxy.url_cred);
free_auth_contexts(&t->server.auth_contexts);
free_auth_contexts(&t->proxy.auth_contexts);
+ t->server.url_cred_presented = false;
+ t->proxy.url_cred_presented = false;
+
git_net_url_dispose(&t->server.url);
git_net_url_dispose(&t->proxy.url);