Skip to content

Commit

Permalink
Check available credentials when choosing authentication scheme (dotn…
Browse files Browse the repository at this point in the history
…et/corefx#28105)

When choosing what authentication scheme to use, WinHttpHandler picks the most secure scheme supported by the server. When the client does not support this scheme it closes the connection. This fix updates the authentication logic to instead pick the most secure scheme offered by the server that is also supported by the client.

This change also adds a test of the relevant behavior.

Fixes: dotnet/corefx#27672

Commit migrated from dotnet/corefx@6d031a0
  • Loading branch information
rmkerr authored Mar 16, 2018
1 parent 17dad01 commit afb8358
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void CheckResponseForAuthentication(
// But we can validate with assert.
Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER);

serverAuthScheme = ChooseAuthScheme(supportedSchemes);
serverAuthScheme = ChooseAuthScheme(supportedSchemes, state.RequestMessage.RequestUri, state.ServerCredentials);
if (serverAuthScheme != 0)
{
if (SetWinHttpCredential(
Expand Down Expand Up @@ -155,7 +155,7 @@ public void CheckResponseForAuthentication(
// But we can validate with assert.
Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY);

proxyAuthScheme = ChooseAuthScheme(supportedSchemes);
proxyAuthScheme = ChooseAuthScheme(supportedSchemes, state.Proxy.GetProxy(state.RequestMessage.RequestUri), proxyCreds);
state.RetryRequest = true;
break;

Expand Down Expand Up @@ -371,11 +371,16 @@ private bool SetWinHttpCredential(
return true;
}

private static uint ChooseAuthScheme(uint supportedSchemes)
private static uint ChooseAuthScheme(uint supportedSchemes, Uri uri, ICredentials credentials)
{
if (credentials == null)
{
return 0;
}

foreach (uint authScheme in s_authSchemePriorityOrder)
{
if ((supportedSchemes & authScheme) != 0)
if ((supportedSchemes & authScheme) != 0 && credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]) != null)
{
return authScheme;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public abstract class HttpClientHandler_Authentication_Test : HttpClientTestBase

private static readonly NetworkCredential s_credentials = new NetworkCredential(Username, Password, Domain);

private static readonly Func<HttpClientHandler, Uri, HttpStatusCode, NetworkCredential, Task> s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
private static readonly Func<HttpClientHandler, Uri, HttpStatusCode, ICredentials, Task> s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
{
handler.Credentials = credentials;

Expand Down Expand Up @@ -90,6 +90,32 @@ await LoopbackServer.CreateServerAsync(async (server, url) =>
}, options);
}

[Theory]
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: NTLM\r\n", "Basic", "Negotiate")]
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: NTLM\r\n", "Digest", "Negotiate")]
public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth)
{
if (PlatformDetection.IsWindowsNanoServer || (IsCurlHandler && authenticateHeader.Contains("Digest")))
{
// TODO: #28065: Fix failing authentication test cases on different httpclienthandlers.
return;
}

var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
HttpClientHandler handler = CreateHttpClientHandler();
handler.UseDefaultCredentials = false;

var credentials = new CredentialCache();
credentials.Add(url, supportedAuth, new NetworkCredential(Username, Password, Domain));
credentials.Add(url, unsupportedAuth, new NetworkCredential(Username, Password, Domain));

Task serverTask = server.AcceptConnectionPerformAuthenticationAndCloseAsync(authenticateHeader);
await TestHelper.WhenAllCompletedOrAnyFailed(s_createAndValidateRequest(handler, url, HttpStatusCode.OK, credentials), serverTask);
}, options);
}

[Theory]
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\n")]
[InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\n")]
Expand Down

0 comments on commit afb8358

Please sign in to comment.