Skip to content

Commit 7ea8630

Browse files
committed
http: free auth context on failure
When we send HTTP credentials but the server rejects them, tear down the authentication context so that we can start fresh. To maintain this state, additionally move all of the authentication handling into `on_auth_required`.
1 parent 005b5bc commit 7ea8630

File tree

1 file changed

+63
-50
lines changed

1 file changed

+63
-50
lines changed

src/transports/http.c

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,43 @@ static int set_authentication_types(http_server *server)
265265
return 0;
266266
}
267267

268+
static bool auth_context_complete(http_server *server)
269+
{
270+
/* If there's no is_complete function, we're always complete */
271+
if (!server->auth_context->is_complete)
272+
return true;
273+
274+
if (server->auth_context->is_complete(server->auth_context))
275+
return true;
276+
277+
return false;
278+
}
279+
280+
static void free_auth_context(http_server *server)
281+
{
282+
if (!server->auth_context)
283+
return;
284+
285+
if (server->auth_context->free)
286+
server->auth_context->free(server->auth_context);
287+
288+
server->auth_context = NULL;
289+
}
290+
268291
static int parse_authenticate_response(http_server *server)
269292
{
293+
/*
294+
* If we think that we've completed authentication (ie, we've either
295+
* sent a basic credential or we've sent the NTLM/Negotiate response)
296+
* but we've got an authentication request from the server then our
297+
* last authentication did not succeed. Start over.
298+
*/
299+
if (server->auth_context && auth_context_complete(server)) {
300+
free_auth_context(server);
301+
302+
server->authenticated = 0;
303+
}
304+
270305
/*
271306
* If we've begun authentication, give the challenge to the context.
272307
* Otherwise, set up the types to prepare credentials.
@@ -367,17 +402,6 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
367402
return 0;
368403
}
369404

370-
static void free_auth_context(http_server *server)
371-
{
372-
if (!server->auth_context)
373-
return;
374-
375-
if (server->auth_context->free)
376-
server->auth_context->free(server->auth_context);
377-
378-
server->auth_context = NULL;
379-
}
380-
381405
GIT_INLINE(void) free_cred(git_cred **cred)
382406
{
383407
if (*cred) {
@@ -433,7 +457,6 @@ static int init_auth(http_server *server)
433457
}
434458

435459
static int on_auth_required(
436-
git_cred **creds,
437460
http_parser *parser,
438461
http_server *server,
439462
const char *url,
@@ -445,18 +468,37 @@ static int on_auth_required(
445468
http_subtransport *t = ctx->t;
446469
int error = 1;
447470

471+
if (parse_authenticate_response(server) < 0) {
472+
t->parse_error = PARSE_ERROR_GENERIC;
473+
return t->parse_error;
474+
}
475+
476+
/* If we're in the middle of challenge/response auth, continue */
477+
if (parser->status_code == 407 || parser->status_code == 401) {
478+
if (server->auth_context && !auth_context_complete(server)) {
479+
t->parse_error = PARSE_ERROR_REPLAY;
480+
return 0;
481+
}
482+
}
483+
484+
/* Enforce a reasonable cap on the number of replays */
485+
if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
486+
git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
487+
return t->parse_error = PARSE_ERROR_GENERIC;
488+
}
489+
448490
if (!server->credtypes) {
449491
git_error_set(GIT_ERROR_NET, "%s requested authentication but did not negotiate mechanisms", type);
450492
t->parse_error = PARSE_ERROR_GENERIC;
451493
return t->parse_error;
452494
}
453495

454496
free_auth_context(server);
455-
free_cred(creds);
497+
free_cred(&server->cred);
456498

457499
/* Start with URL-specified credentials, if there were any. */
458500
if (!server->url_cred_presented && server->url.username && server->url.password) {
459-
error = apply_url_credentials(creds, server->credtypes, server->url.username, server->url.password);
501+
error = apply_url_credentials(&server->cred, server->credtypes, server->url.username, server->url.password);
460502
server->url_cred_presented = 1;
461503

462504
if (error == GIT_PASSTHROUGH) {
@@ -466,7 +508,7 @@ static int on_auth_required(
466508
}
467509

468510
if (error > 0 && callback) {
469-
error = callback(creds, url, server->url.username, server->credtypes, callback_payload);
511+
error = callback(&server->cred, url, server->url.username, server->credtypes, callback_payload);
470512

471513
if (error == GIT_PASSTHROUGH) {
472514
/* treat GIT_PASSTHROUGH as if callback isn't set */
@@ -485,9 +527,9 @@ static int on_auth_required(
485527
return t->parse_error;
486528
}
487529

488-
assert(*creds);
530+
assert(server->cred);
489531

490-
if (!((*creds)->credtype & server->credtypes)) {
532+
if (!(server->cred->credtype & server->credtypes)) {
491533
git_error_set(GIT_ERROR_NET, "%s credential provider returned an invalid cred type", type);
492534
t->parse_error = PARSE_ERROR_GENERIC;
493535
return t->parse_error;
@@ -521,36 +563,9 @@ static int on_headers_complete(http_parser *parser)
521563
if (t->last_cb == VALUE && on_header_ready(t) < 0)
522564
return t->parse_error = PARSE_ERROR_GENERIC;
523565

524-
/*
525-
* Capture authentication headers for the proxy or final endpoint,
526-
* these may be 407/401 (authentication is not complete) or a 200
527-
* (informing us that auth has completed).
528-
*/
529-
if (parse_authenticate_response(&t->proxy) < 0 ||
530-
parse_authenticate_response(&t->server) < 0)
531-
return t->parse_error = PARSE_ERROR_GENERIC;
532-
533-
/* If we're in the middle of challenge/response auth, continue */
534-
if (parser->status_code == 407 || parser->status_code == 401) {
535-
http_server *server = parser->status_code == 407 ? &t->proxy : &t->server;
536-
537-
if (server->auth_context &&
538-
server->auth_context->is_complete &&
539-
!server->auth_context->is_complete(server->auth_context)) {
540-
t->parse_error = PARSE_ERROR_REPLAY;
541-
return 0;
542-
}
543-
}
544-
545-
/* Enforce a reasonable cap on the number of replays */
546-
if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
547-
git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
548-
return t->parse_error = PARSE_ERROR_GENERIC;
549-
}
550-
551566
/* Check for a proxy authentication failure. */
552567
if (parser->status_code == 407 && get_verb == s->verb)
553-
return on_auth_required(&t->proxy.cred,
568+
return on_auth_required(
554569
parser,
555570
&t->proxy,
556571
t->proxy_opts.url,
@@ -562,7 +577,7 @@ static int on_headers_complete(http_parser *parser)
562577

563578
/* Check for an authentication failure. */
564579
if (parser->status_code == 401 && get_verb == s->verb)
565-
return on_auth_required(&t->server.cred,
580+
return on_auth_required(
566581
parser,
567582
&t->server,
568583
t->owner->url,
@@ -861,9 +876,7 @@ static int proxy_headers_complete(http_parser *parser)
861876

862877
/* If we're in the middle of challenge/response auth, continue */
863878
if (parser->status_code == 407) {
864-
if (t->proxy.auth_context &&
865-
t->proxy.auth_context->is_complete &&
866-
!t->proxy.auth_context->is_complete(t->proxy.auth_context)) {
879+
if (t->proxy.auth_context && !auth_context_complete(&t->proxy)) {
867880
t->parse_error = PARSE_ERROR_REPLAY;
868881
return 0;
869882
}
@@ -877,7 +890,7 @@ static int proxy_headers_complete(http_parser *parser)
877890

878891
/* Check for a proxy authentication failure. */
879892
if (parser->status_code == 407)
880-
return on_auth_required(&t->proxy.cred,
893+
return on_auth_required(
881894
parser,
882895
&t->proxy,
883896
t->proxy_opts.url,

0 commit comments

Comments
 (0)