Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

WinHttpHandler: Check available credentials when choosing authentication scheme #28105

Merged
merged 5 commits into from
Mar 16, 2018

Conversation

rmkerr
Copy link
Contributor

@rmkerr rmkerr commented Mar 15, 2018

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. We need to instead pick the most secure scheme offered by the server and supported by the client.

The incorrect behavior occurs under the following conditions:

  • There are at least two authentication schemes enabled on the server.
  • The user only provides credentials that have an authentication scheme less secure than the most secure option offered by the server.

In the conditions described in the original report, the user provides NTLM credentials, but the server supports both NTLM and Negotiate (which is considered more secure). WinHttpHandler erroneously chooses to attempt authentication with Negotiate. When we later detect that there are no credentials in the cache that support Negotiate, we close the connection.

The fix is a simple change to WinHttpAuthHelper, and an additional test. There is an open issue tracking auth problems with Windows Nano, so depending on the results in CI I may disable the test there.

Fixes: #27672

@rmkerr rmkerr self-assigned this Mar 15, 2018
@rmkerr rmkerr requested review from stephentoub and a team March 15, 2018 17:23
{
if (IsCurlHandler && authenticateHeader.Contains("Digest"))
{
// TODO: #27113: Fix failing authentication test cases on different httpclienthandlers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change issue number to #28065. #27113 is closed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

@caesar-chen
Copy link
Contributor

The fix LGTM.

@rmkerr rmkerr force-pushed the WinHttp_Auth_ChecksSchemeAvailability branch from a463cfe to dfa416b Compare March 15, 2018 17:48
Copy link
Member

@wfurt wfurt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good.

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 15, 2018

The new test is consistently failing on CurlHandler. Looking into it now.

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 16, 2018

I think this test is failing because of the aggressive approach to pre-authentication that Curl takes. Looking at the network traces, CurlHandler is always sending the credentials on the initial request, even if HttpClientHandler.PreAuthenticate is set to false.

It seems like this is the default behavior for Curl. Once authentication has been set up using the option CURLOPT_HTTPAUTH, Curl will immediately start including the authorization header. Since we set up CURLOPT_HTTPAUTH in the EasyRequest constructor, we're always doing pre-authentication.

I think that the issue with Curl is substantially different from the issue being fixed by this pr, so I will open a separate issue to track it.

@stephentoub
Copy link
Member

CurlHandler is always sending the credentials on the initial request, even if HttpClientHandler.PreAuthenticate is set to false.

libcurl will send credentials with a request if those credentials have been set with only one auth scheme. So if you set Credentials to a NetworkCredential, then it won't send the credentials with the first request, because those credentials will be tagged with all auth schemes. And if you set Credentials to a CredentialCache that has the same credentials stored for multiple auth schemes, it similarly won't send the credentials on the first request. But if you set Credentials to a CredentialCache that has credentials for just one auth scheme, libcurl may send those with the first request.

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 16, 2018

Thanks for the info! That makes a lot of sense, since setting Credentials to a CredentialCache with only one auth scheme is exactly what the test here does. Do you think that the behavior difference is something we should look into? Otherwise I can just update the test here.

@stephentoub
Copy link
Member

Do you think that the behavior difference is something we should look into?

I don't know of a good way to influence libcurl to do exactly what other handlers do for PreAuthenticate. Honestly, I think libcurl's behavior makes more sense: if you were given a credential to use and that credential only works with a single auth scheme, why not send it?

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 16, 2018

That sounds reasonable to me. I'll update the test.

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 16, 2018

@dotnet-bot test Outerloop Windows x64 Debug Build

@rmkerr
Copy link
Contributor Author

rmkerr commented Mar 16, 2018

Like the rest of the auth tests using the loopback server, this fails on windows nano server. That's something I'd like to look into, but I don't think it's high priority right now. For now I'm going to skip the test in nano server and merge.

@rmkerr rmkerr merged commit 6d031a0 into dotnet:master Mar 16, 2018
@karelz karelz added this to the 2.1.0 milestone Mar 18, 2018
ericstj pushed a commit to ericstj/corefx that referenced this pull request Mar 28, 2018
…et#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: #27672
@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

Hi! You guys have tracked down and fixed a very nasty and subtle bug that didn't allow people to work with some server calls having their code deployed on linux-based machines! Kudos!
I have one question though, do you have plans to update https://www.nuget.org/packages/System.Net.Http.WinHttpHandler/ package in the foreseeable future? without the ties to the netcore 2.1 release date. That would help very very much!

@karelz
Copy link
Member

karelz commented Apr 5, 2018

The package is updated as part of shipping .NET Core -- version 2.1 is the current .NET Core wave. We do not have plans to update it on different ship cycle. Shipping standalone packages creates huge complexity and cost not worth it (we've tried it in the past).

Why do you need the NuGet package without ties on .NET Core 2.1? Do you need to use it in your .NET Core 1.0 / 1.1 / 2.0 application/library? Or potentially in your .NET Framework application/library?

@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

We are developing a netcore 2.0 application that is hosted in linux amazon docker image as a self-contained application. The server we are calling from our code supports Negotiate and NTLM, and since this is linux, Negotiate is not supported, so our code provides Credential Cache with only NTLM scheme specified.
And because of that bug, since Negotiate cred is not in our cache, it fails. So we are currently looking for a way to get this fix from this pull request to use it in our code... any suggestions except for waiting for 2.1 release?

@davidsh
Copy link
Contributor

davidsh commented Apr 5, 2018

The server we are calling from our code supports Negotiate and NTLM, and since this is Linux

This particular bug fix is only in WinHttpHandler which is used only on Windows.

Your scenario is for a Linux client. So, perhaps you are describing a different bug?

@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

Well, the behavior is exactly the same... we have two server calls, one server supports NTLM only and it returns 401 with WWW-Authenticate: NTLM, and everything works with this call. Second server along with NTLM supports Negotiate, and this server call fails with Unauthorized eventually. What will be the component we want to look at?

@karelz
Copy link
Member

karelz commented Apr 5, 2018

@Lakmus85 what OS is your client on? Linux or Windows?
Maybe best next step is to try 2.1 - check if it fixes your problem at all. Then you can decide to either wait for 2.1 or to create private patch from release/2.0 branch with the fix backported. There are no other options (unless it impact lots of customers).

@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

Sorry, I probably described it not clear enough
our application IS a client. In our code we create HttpClient object and make a call to the server.
Our application is hosted on linux.
Could the same code be used to select authentication scheme?
But how can we try 2.1? Is there an RC or early preview version? Nightly build maybe?

@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

And I believe that current Preview 1 of 2.1 doesn't include this commit, is that correct?

@karelz
Copy link
Member

karelz commented Apr 5, 2018

Just to be super-clear: You say that your client application is Linux app, right?
In that case this change is not going to help you at all. WinHttpHandler is Windows-only code.

Daily builds are fairly stable - see link on our main page on how to use them.

It would be best to take the discussion into separate issue. There is no evidence at this point it is related to this PR.

@Lakmus85
Copy link

Lakmus85 commented Apr 5, 2018

One last question, which package will contain WinHttpAuthHelper class? It's neither in the System.Net.Http nor in System.Net.Http.WinHttpHandler

@karelz
Copy link
Member

karelz commented Apr 5, 2018

That's internal class - see the sources.

@mauricemarkvoort
Copy link

mauricemarkvoort commented Apr 23, 2018

This might be a stupid question. But how can I test this new version inside my UWP app? I have updated my .NET Core version to preview 2, but doesn't seem to work. Still getting the 401. I want to use the UseDefaultCredentials. It does work setting credentials like this handler.Credentials = new NetworkCredential(Username, Password); but that is not what I want.

@davidsh
Copy link
Contributor

davidsh commented Apr 23, 2018

This might be a stupid question. But how can I test this new version inside my UWP app?

WinHttpHandler is not available for a UWP app. The binary implementation doesn't get used for UWP apps. And .NET Core 2.1 Preview 2 doesn't have any new fixes for UWPs.

Also, UWP apps use different rules for when DefaultCredentials are used and transmitted to server. Your UWP apps must have the EnterpriseAuthentication capability. And the server endpoint must be considered to be in the "Intranet" zone.

davidsh added a commit that referenced this pull request Jun 8, 2018
This was a regression from .NET Core 2.0 due to PR #28105.

Proxy authentication using system default proxy settings that involve a PAC file
(either autodiscovery or explicit PAC file) cause a NullReferenceException in WinHttpHandler in the
CheckResponseForAuthentication() method.

This problem is only discovered when using an authenticating proxy server (any auth scheme) that is
discovered using PAC files. This is considered the "system default" proxy. When this occurs, the
handler's Proxy property is null and dereferencing it caused the exception.

Due to the problems described in #6997, the uri of the proxy can't be determined yet since it is only
known to WinHTTP. Fixing #6997 is complicated and impacts performance. However, in most cases, as
long as the credentials are a NetworkCredential object, knowing the uri of the proxy is needed.

I tested this manually using the steps I described in #30191. I did not add any tests to this PR since
they can't be run in CI. However, I am working on a task that will eventually add Enterprise-Scenario
testing like this (PAC files, authenticating proxies, etc.) to our systems.

Fixes #30191
Contributes to #6997
stephentoub pushed a commit that referenced this pull request Jun 27, 2018
This was a regression from .NET Core 2.0 due to PR #28105.

Proxy authentication using system default proxy settings that involve a PAC file
(either autodiscovery or explicit PAC file) cause a NullReferenceException in WinHttpHandler in the
CheckResponseForAuthentication() method.

This problem is only discovered when using an authenticating proxy server (any auth scheme) that is
discovered using PAC files. This is considered the "system default" proxy. When this occurs, the
handler's Proxy property is null and dereferencing it caused the exception.

Due to the problems described in #6997, the uri of the proxy can't be determined yet since it is only
known to WinHTTP. Fixing #6997 is complicated and impacts performance. However, in most cases, as
long as the credentials are a NetworkCredential object, knowing the uri of the proxy is needed.

I tested this manually using the steps I described in #30191. I did not add any tests to this PR since
they can't be run in CI. However, I am working on a task that will eventually add Enterprise-Scenario
testing like this (PAC files, authenticating proxies, etc.) to our systems.

Fixes #30191
Contributes to #6997
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…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
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…0196)

This was a regression from .NET Core 2.0 due to PR dotnet/corefx#28105.

Proxy authentication using system default proxy settings that involve a PAC file
(either autodiscovery or explicit PAC file) cause a NullReferenceException in WinHttpHandler in the
CheckResponseForAuthentication() method.

This problem is only discovered when using an authenticating proxy server (any auth scheme) that is
discovered using PAC files. This is considered the "system default" proxy. When this occurs, the
handler's Proxy property is null and dereferencing it caused the exception.

Due to the problems described in dotnet/corefx#6997, the uri of the proxy can't be determined yet since it is only
known to WinHTTP. Fixing dotnet/corefx#6997 is complicated and impacts performance. However, in most cases, as
long as the credentials are a NetworkCredential object, knowing the uri of the proxy is needed.

I tested this manually using the steps I described in dotnet/corefx#30191. I did not add any tests to this PR since
they can't be run in CI. However, I am working on a task that will eventually add Enterprise-Scenario
testing like this (PAC files, authenticating proxies, etc.) to our systems.

Fixes dotnet/corefx#30191
Contributes to dotnet/corefx#6997

Commit migrated from dotnet/corefx@69c2e20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NTLM authentication sometimes broken by multiple WWW-Authenticate headers
8 participants