-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Split from #25827
As of .NET Core 2.1, the default HTTP stack is based on SocketsHttpHandler. SocketsHttpHandler uses the GSSAPI for handling the HTTP AUTH scheme of 'Negotiate'.
On Windows, support for the 'Negotiate' scheme includes the use of Kerberos where possible. Otherwise, it falls back to using NTLM. This is handled in the Windows SSPI Negotiate module.
On *Nix and OSX machines, Negotiate to NTLM fallback is not working. A related issue #28530 addresses the problem with the specific HTTP AUTH scheme 'NTLM' and errors caused by not installing the optional GSSAPI gss-ntlmssp support package. However, even after installing that optional package, Negotiate to NTLM fallback is still not working.
Currently when HttpClient tries to authenticate to a server that provides a single 'Www-Authenticate: Negotiate' scheme, it throw an exception similar to this:
Unhandled Exception: System.ComponentModel.Win32Exception: GSSAPI operation failed with error - An invalid status code was supplied (Cannot find KDC for realm "domain").
at System.Net.Security.NegotiateStreamPal.AcquireCredentialsHandle(String package, Boolean isServer, NetworkCredential credential)
Repo code
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace NtlmErrorTest
{
class Program
{
static void Main()
{
using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
listener.Listen(int.MaxValue);
var ep = (IPEndPoint)listener.LocalEndPoint;
var uri = new Uri($"http://{ep.Address}:{ep.Port}/");
Task.Run(async () =>
{
while (true)
{
Socket s = await listener.AcceptAsync();
var ignored = Task.Run(() =>
{
using (var ns = new NetworkStream(s))
using (var reader = new StreamReader(ns))
using (var writer = new StreamWriter(ns) { AutoFlush = true })
{
while (true)
{
while (!string.IsNullOrEmpty(reader.ReadLine())) ;
writer.Write("HTTP/1.1 401 OK\r\nWww-Authenticate: Negotiate\r\nContent-Length: 5\r\n\r\nhello");
}
}
});
}
});
var handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("user", "password", "domain");
using (var client = new HttpClient(handler))
{
Console.WriteLine(uri.AbsoluteUri.ToString());
using (HttpResponseMessage response = client.GetAsync(uri).GetAwaiter().GetResult())
{
Console.WriteLine($"{(int)response.StatusCode} {response.ReasonPhrase}");
}
}
}
}
}
}