@@ -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+
268291static 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-
381405GIT_INLINE (void ) free_cred (git_cred * * cred )
382406{
383407 if (* cred ) {
@@ -433,7 +457,6 @@ static int init_auth(http_server *server)
433457}
434458
435459static 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