Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 684114f

Browse files
authored
Fix SPN used for Negotiate authentication (#33426)
SocketsHttpHandler was not normalizing the DNS name prior to using it for the SPN (Service Principal Name). So, when using URI's that involve a CNAME, it was using the CNAME directly and not evaluating it to the normalized FQDN A record of the host. This change fixes the behavior to match .NET Framework so that CNAMEs are resolved properly. We can use the standard Dns.GetHostEntryAsync() API to resolve the name. From a performance perspective, this additional DNS API call is limited to just the SPN calculation for NT Auth. Calling this API doesn't impact the performance on the wire since the OS will cache DNS calls. Wireshark confirms that no additional DNS protocol packets will be sent. .NET Framework actually caches the normalized DNS resolution on the ServicePoint object when it opens up a connections. Thus, it doesn't have to call Dns.GetHostEntryAsync() for the SPN calculation. While a future PR could further optimize SocketsHttpHandler to also cache this DNS host name, it isn't clear it would result in measurable performance gain. I tested this change in a separate Enterprise testing environment I set up. I created a CNAME for a Windows IIS server in a Windows domain-joined environment and demonstrated that the Negotiate protocol results in a Kerberos authentication (and doesn't fall back to NTLM). Fixes #32328
1 parent 811e41a commit 684114f

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

src/System.Net.Http/src/System.Net.Http.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@
599599
<Compile Include="System\Net\Http\HttpClientHandler.Core.cs" />
600600
<Compile Include="uap\System\Net\HttpClientHandler.cs" />
601601
</ItemGroup>
602+
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'">
603+
<Reference Include="System.Net.NameResolution" />
604+
</ItemGroup>
602605
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' OR '$(TargetGroup)' == 'netcoreappaot' OR '$(TargetGroup)' == 'uap'">
603606
<Reference Include="Microsoft.Win32.Primitives" />
604607
<Reference Include="System.Buffers" />

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.Net;
78
using System.Net.Http.Headers;
89
using System.Threading;
910
using System.Threading.Tasks;
@@ -77,7 +78,28 @@ private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMe
7778

7879
string challengeData = challenge.ChallengeData;
7980

80-
string spn = "HTTP/" + authUri.IdnHost;
81+
// Need to use FQDN normalized host so that CNAME's are traversed.
82+
// Use DNS to do the forward lookup to an A (host) record.
83+
// But skip DNS lookup on IP literals. Otherwise, we would end up
84+
// doing an unintended reverse DNS lookup.
85+
string spn;
86+
UriHostNameType hnt = authUri.HostNameType;
87+
if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4)
88+
{
89+
spn = authUri.IdnHost;
90+
}
91+
else
92+
{
93+
IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false);
94+
spn = result.HostName;
95+
}
96+
spn = "HTTP/" + spn;
97+
98+
if (NetEventSource.IsEnabled)
99+
{
100+
NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {authUri.IdnHost}, SPN: {spn}");
101+
}
102+
81103
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
82104
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
83105
try

0 commit comments

Comments
 (0)