summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/transports/http.c322
1 files changed, 156 insertions, 166 deletions
diff --git a/src/transports/http.c b/src/transports/http.c
index be7dee025..b1946b34d 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -77,12 +77,14 @@ typedef struct {
git_net_url url;
git_stream *stream;
- git_http_authtype_t server_types;
+ git_http_authtype_t authtypes;
+ git_credtype_t credtypes;
+
git_cred *cred;
unsigned url_cred_presented : 1;
git_vector auth_challenges;
- git_vector auth_contexts;
+ git_http_auth_context *auth_context;
} http_server;
typedef struct {
@@ -125,79 +127,24 @@ typedef struct {
size_t *bytes_read;
} parser_context;
-static bool credtype_match(git_http_auth_scheme *scheme, void *data)
-{
- unsigned int credtype = *(unsigned int *)data;
-
- return !!(scheme->credtypes & credtype);
-}
-
-static bool challenge_match(git_http_auth_scheme *scheme, void *data)
-{
- const char *scheme_name = scheme->name;
- const char *challenge = (const char *)data;
- size_t scheme_len;
-
- scheme_len = strlen(scheme_name);
- return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
- (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,
- bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
- void *data)
+static git_http_auth_scheme *scheme_for_challenge(const char *challenge)
{
git_http_auth_scheme *scheme = NULL;
- git_http_auth_context *context = NULL, *c;
size_t i;
- *out = NULL;
-
for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
- if (scheme_match(&auth_schemes[i], data)) {
- scheme = &auth_schemes[i];
- break;
- }
- }
+ const char *scheme_name = auth_schemes[i].name;
+ size_t scheme_len;
- if (!scheme)
- return 0;
-
- /* See if authentication has already started for this scheme */
- git_vector_foreach(&server->auth_contexts, i, c) {
- if (c->type == scheme->type) {
- context = c;
+ scheme_len = strlen(scheme_name);
+ if (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
+ (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' ')) {
+ scheme = &auth_schemes[i];
break;
}
}
- if (!context) {
- if (scheme->init_context(&context, &server->url) < 0)
- return -1;
- else if (!context)
- return 0;
- else if (git_vector_insert(&server->auth_contexts, context) < 0)
- return -1;
- }
-
- *out = context;
-
- return 0;
+ return scheme;
}
static int apply_credentials(
@@ -205,43 +152,13 @@ static int apply_credentials(
http_server *server,
const char *header_name)
{
- git_cred *cred = server->cred;
- git_http_auth_context *context;
- authmatch_data data = {0};
git_buf token = GIT_BUF_INIT;
int error = 0;
- if (!server->server_types)
- goto done;
-
- /* Get or create a context for the best scheme for this cred type */
- if ((error = auth_context_match(&context, server,
- credtype_match, &cred->credtype)) < 0)
+ if (!server->auth_context)
goto done;
- if (!context)
- goto done;
-
- /*
- * If we do have creds, find the first mechanism supported by both
- * the server and ourselves that supports the credential type.
- */
- if (!cred)
- goto done;
-
- data.server_types = server->server_types;
- data.credtype = cred->credtype;
-
- if ((error = auth_context_match(&context, server, auth_match, &data)) < 0)
- goto done;
-
- if (!context) {
- git_error_set(GIT_ERROR_NET, "no suitable mechanism found for authentication");
- error = -1;
- goto done;
- }
-
- if ((error = context->next_token(&token, context, cred)) < 0)
+ if ((error = server->auth_context->next_token(&token, server->auth_context, server->cred)) < 0)
goto done;
error = git_buf_printf(buf, "%s: %s\r\n", header_name, token.ptr);
@@ -313,32 +230,53 @@ static int gen_request(
return 0;
}
-static int parse_authenticate_response(
- http_server *server,
- int *allowed_types)
+static int set_authentication_challenge(http_server *server)
+{
+ const char *challenge;
+
+ if (git_vector_length(&server->auth_challenges) > 1) {
+ git_error_set(GIT_ERROR_NET, "received multiple authentication challenges");
+ return -1;
+ }
+
+ challenge = git_vector_get(&server->auth_challenges, 0);
+
+ if (server->auth_context->set_challenge)
+ return server->auth_context->set_challenge(server->auth_context, challenge);
+ else
+ return 0;
+}
+
+static int set_authentication_types(http_server *server)
{
- git_http_auth_context *context;
+ git_http_auth_scheme *scheme;
char *challenge;
size_t i;
git_vector_foreach(&server->auth_challenges, i, challenge) {
- if (auth_context_match(&context, server,
- challenge_match, challenge) < 0)
- return -1;
- else if (!context)
- continue;
-
- if (context->set_challenge &&
- context->set_challenge(context, challenge) < 0)
- return -1;
-
- server->server_types |= context->type;
- *allowed_types |= context->credtypes;
+ if ((scheme = scheme_for_challenge(challenge)) != NULL) {
+ server->authtypes |= scheme->type;
+ server->credtypes |= scheme->credtypes;
+ }
}
return 0;
}
+static int parse_authenticate_response(http_server *server)
+{
+ /*
+ * If we've begun authentication, give the challenge to the context.
+ * Otherwise, set up the types to prepare credentials.
+ */
+ if (git_vector_length(&server->auth_challenges) == 0)
+ return 0;
+ else if (server->auth_context)
+ return set_authentication_challenge(server);
+ else
+ return set_authentication_types(server);
+}
+
static int on_header_ready(http_subtransport *t)
{
git_buf *name = &t->parse_header_name;
@@ -427,6 +365,17 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0;
}
+static void free_auth_context(http_server *server)
+{
+ if (!server->auth_context)
+ return;
+
+ if (server->auth_context->free)
+ server->auth_context->free(server->auth_context);
+
+ server->auth_context = NULL;
+}
+
GIT_INLINE(void) free_cred(git_cred **cred)
{
if (*cred) {
@@ -450,6 +399,37 @@ static int apply_url_credentials(
return GIT_PASSTHROUGH;
}
+static int init_auth(http_server *server)
+{
+ git_http_auth_scheme *s, *scheme = NULL;
+ char *c, *challenge = NULL;
+ size_t i;
+
+ git_vector_foreach(&server->auth_challenges, i, c) {
+ s = scheme_for_challenge(c);
+
+ if (s && !!(s->credtypes & server->credtypes)) {
+ scheme = s;
+ challenge = c;
+ break;
+ }
+ }
+
+ if (!scheme) {
+ git_error_set(GIT_ERROR_NET, "no authentication mechanism could be negotiated");
+ return -1;
+ }
+
+ if (scheme->init_context(&server->auth_context, &server->url) < 0)
+ return -1;
+
+ if (server->auth_context->set_challenge &&
+ server->auth_context->set_challenge(server->auth_context, challenge) < 0)
+ return -1;
+
+ return 0;
+}
+
static int on_auth_required(
git_cred **creds,
http_parser *parser,
@@ -457,24 +437,24 @@ static int on_auth_required(
const char *url,
const char *type,
git_cred_acquire_cb callback,
- void *callback_payload,
- int allowed_types)
+ void *callback_payload)
{
parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t;
int error = 1;
- if (!allowed_types) {
+ if (!server->credtypes) {
git_error_set(GIT_ERROR_NET, "%s requested authentication but did not negotiate mechanisms", type);
t->parse_error = PARSE_ERROR_GENERIC;
return t->parse_error;
}
+ free_auth_context(server);
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);
+ error = apply_url_credentials(creds, server->credtypes, server->url.username, server->url.password);
server->url_cred_presented = 1;
if (error == GIT_PASSTHROUGH) {
@@ -484,7 +464,7 @@ static int on_auth_required(
}
if (error > 0 && callback) {
- error = callback(creds, url, server->url.username, allowed_types, callback_payload);
+ error = callback(creds, url, server->url.username, server->credtypes, callback_payload);
if (error == GIT_PASSTHROUGH) {
/* treat GIT_PASSTHROUGH as if callback isn't set */
@@ -505,13 +485,18 @@ static int on_auth_required(
assert(*creds);
- if (!((*creds)->credtype & allowed_types)) {
+ if (!((*creds)->credtype & server->credtypes)) {
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. */
+ /* Successfully acquired a credential. Start an auth context. */
+ if (init_auth(server) < 0) {
+ t->parse_error = PARSE_ERROR_GENERIC;
+ return t->parse_error;
+ }
+
t->parse_error = PARSE_ERROR_REPLAY;
return 0;
}
@@ -522,29 +507,39 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t;
http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT;
- int proxy_auth_types = 0, server_auth_types = 0;
-
- /* Enforce a reasonable cap on the number of replays */
- if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
- git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
/* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */
- if (VALUE == t->last_cb)
- if (on_header_ready(t) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
+ if (t->last_cb == VALUE && on_header_ready(t) < 0)
+ return t->parse_error = PARSE_ERROR_GENERIC;
/*
* Capture authentication headers for the proxy or final endpoint,
* these may be 407/401 (authentication is not complete) or a 200
* (informing us that auth has completed).
*/
- if (parse_authenticate_response(&t->proxy, &proxy_auth_types) < 0 ||
- parse_authenticate_response(&t->server, &server_auth_types) < 0)
+ if (parse_authenticate_response(&t->proxy) < 0 ||
+ parse_authenticate_response(&t->server) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
+ /* If we're in the middle of challenge/response auth, continue */
+ if (parser->status_code == 407 || parser->status_code == 401) {
+ http_server *server = parser->status_code == 407 ? &t->proxy : &t->server;
+
+ if (server->auth_context &&
+ server->auth_context->is_complete &&
+ !server->auth_context->is_complete(server->auth_context)) {
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
+ }
+ }
+
+ /* Enforce a reasonable cap on the number of replays */
+ if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
+ git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
+ return t->parse_error = PARSE_ERROR_GENERIC;
+ }
+
/* Check for a proxy authentication failure. */
if (parser->status_code == 407 && get_verb == s->verb)
return on_auth_required(&t->proxy.cred,
@@ -553,8 +548,7 @@ static int on_headers_complete(http_parser *parser)
t->proxy_opts.url,
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
- t->proxy_opts.payload,
- proxy_auth_types);
+ t->proxy_opts.payload);
/* Check for an authentication failure. */
if (parser->status_code == 401 && get_verb == s->verb)
@@ -564,8 +558,7 @@ static int on_headers_complete(http_parser *parser)
t->owner->url,
SERVER_TYPE_REMOTE,
t->owner->cred_acquire_cb,
- t->owner->cred_acquire_payload,
- server_auth_types);
+ t->owner->cred_acquire_payload);
/* Check for a redirect.
* Right now we only permit a redirect to the same hostname. */
@@ -840,18 +833,10 @@ static int proxy_headers_complete(http_parser *parser)
{
parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t;
- int proxy_auth_types = 0;
-
- /* Enforce a reasonable cap on the number of replays */
- if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
- git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
/* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */
- if (VALUE == t->last_cb)
- if (on_header_ready(t) < 0)
+ if (t->last_cb == VALUE && on_header_ready(t) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
/*
@@ -859,8 +844,24 @@ static int proxy_headers_complete(http_parser *parser)
* these may be 407/401 (authentication is not complete) or a 200
* (informing us that auth has completed).
*/
- if (parse_authenticate_response(&t->proxy, &proxy_auth_types) < 0)
+ if (parse_authenticate_response(&t->proxy) < 0)
+ return t->parse_error = PARSE_ERROR_GENERIC;
+
+ /* If we're in the middle of challenge/response auth, continue */
+ if (parser->status_code == 407) {
+ if (t->proxy.auth_context &&
+ t->proxy.auth_context->is_complete &&
+ !t->proxy.auth_context->is_complete(t->proxy.auth_context)) {
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
+ }
+ }
+
+ /* Enforce a reasonable cap on the number of replays */
+ if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
+ git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
return t->parse_error = PARSE_ERROR_GENERIC;
+ }
/* Check for a proxy authentication failure. */
if (parser->status_code == 407)
@@ -870,8 +871,7 @@ static int proxy_headers_complete(http_parser *parser)
t->proxy_opts.url,
SERVER_TYPE_PROXY,
t->proxy_opts.credentials,
- t->proxy_opts.payload,
- proxy_auth_types);
+ t->proxy_opts.payload);
if (parser->status_code != 200) {
git_error_set(GIT_ERROR_NET, "unexpected status code from proxy: %d",
@@ -1014,6 +1014,12 @@ static int http_connect(http_subtransport *t)
t->proxy.stream = NULL;
}
+ free_auth_context(&t->server);
+ free_auth_context(&t->proxy);
+
+ t->server.url_cred_presented = false;
+ t->proxy.url_cred_presented = false;
+
t->connected = 0;
if (t->proxy_opts.type == GIT_PROXY_SPECIFIED) {
@@ -1466,19 +1472,6 @@ static int http_action(
return -1;
}
-static void free_auth_contexts(git_vector *contexts)
-{
- git_http_auth_context *context;
- size_t i;
-
- git_vector_foreach(contexts, i, context) {
- if (context->free)
- context->free(context);
- }
-
- git_vector_clear(contexts);
-}
-
static int http_close(git_smart_subtransport *subtransport)
{
http_subtransport *t = GIT_CONTAINER_OF(subtransport, http_subtransport, parent);
@@ -1502,8 +1495,8 @@ static int http_close(git_smart_subtransport *subtransport)
free_cred(&t->server.cred);
free_cred(&t->proxy.cred);
- free_auth_contexts(&t->server.auth_contexts);
- free_auth_contexts(&t->proxy.auth_contexts);
+ free_auth_context(&t->server);
+ free_auth_context(&t->proxy);
t->server.url_cred_presented = false;
t->proxy.url_cred_presented = false;
@@ -1522,9 +1515,6 @@ static void http_free(git_smart_subtransport *subtransport)
http_subtransport *t = GIT_CONTAINER_OF(subtransport, http_subtransport, parent);
http_close(subtransport);
-
- git_vector_free(&t->server.auth_contexts);
- git_vector_free(&t->proxy.auth_contexts);
git__free(t);
}