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

Commit 7bb81c6

Browse files
davidshdanmoseley
authored andcommitted
Port Kerberos auth fixes to 2.1 branch (#40109)
This PR ports some important Kerberos auth fixes from the 3.0 to 2.1 LTS branch. These fixes help enterprise customers that have complex Kerberos authentication scenarios that involve cross Windows (Active Directory) and Linux (Kerberos) domains/realms. These fixes are from PRs: * #38465 - Use 'Host' header when calculating SPN for Kerberos auth * #38377 - Use GSS_C_NT_HOSTBASED_SERVICE format for Linux Kerberos SPN and are related to issue #36329.
1 parent a38b2f0 commit 7bb81c6

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

src/Native/Unix/System.Net.Security.Native/pal_gssapi.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ static uint32_t NetSecurityNative_DisplayStatus(uint32_t* minorStatus,
111111
assert(minorStatus != nullptr);
112112
assert(outBuffer != nullptr);
113113

114-
uint32_t messageContext;
114+
uint32_t messageContext = 0; // Must initialize to 0 before calling gss_display_status.
115115
GssBuffer gssBuffer{.length = 0, .value = nullptr};
116116
uint32_t majorStatus =
117117
gss_display_status(minorStatus, statusValue, statusType, GSS_C_NO_OID, &messageContext, &gssBuffer);
@@ -155,19 +155,36 @@ extern "C" uint32_t NetSecurityNative_ImportPrincipalName(uint32_t* minorStatus,
155155
assert(outputName != nullptr);
156156
assert(*outputName == nullptr);
157157

158-
gss_OID nameType;
159-
160-
if (strchr(inputName, '/') != nullptr)
158+
// Principal name will usually be in the form SERVICE/HOST. But SPNEGO protocol prefers
159+
// GSS_C_NT_HOSTBASED_SERVICE format. That format uses '@' separator instead of '/' between
160+
// service name and host name. So convert input string into that format.
161+
char* ptrSlash = (char*) memchr(inputName, '/', inputNameLen);
162+
char* inputNameCopy = NULL;
163+
if (ptrSlash != NULL)
161164
{
162-
nameType = const_cast<gss_OID>(GSS_KRB5_NT_PRINCIPAL_NAME);
165+
inputNameCopy = (char*) malloc(inputNameLen);
166+
if (inputNameCopy != NULL)
167+
{
168+
memcpy(inputNameCopy, inputName, inputNameLen);
169+
inputNameCopy[ptrSlash - inputName] = '@';
170+
inputName = inputNameCopy;
171+
}
172+
else
173+
{
174+
*minorStatus = 0;
175+
return GSS_S_BAD_NAME;
176+
}
163177
}
164-
else
178+
179+
GssBuffer inputNameBuffer = {.length = inputNameLen, .value = inputName};
180+
uint32_t result = gss_import_name(minorStatus, &inputNameBuffer, GSS_C_NT_HOSTBASED_SERVICE, outputName);
181+
182+
if (inputNameCopy != NULL)
165183
{
166-
nameType = const_cast<gss_OID>(GSS_C_NT_HOSTBASED_SERVICE);
184+
free(inputNameCopy);
167185
}
168186

169-
GssBuffer inputNameBuffer{.length = inputNameLen, .value = inputName};
170-
return gss_import_name(minorStatus, &inputNameBuffer, nameType, outputName);
187+
return result;
171188
}
172189

173190
extern "C" uint32_t NetSecurityNative_InitSecContext(uint32_t* minorStatus,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,9 @@
592592
<Compile Include="System\Net\Http\HttpClientHandler.Core.cs" />
593593
<Compile Include="uap\System\Net\HttpClientHandler.cs" />
594594
</ItemGroup>
595+
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'">
596+
<Reference Include="System.Net.NameResolution" />
597+
</ItemGroup>
595598
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' OR '$(TargetGroup)' == 'uap'">
596599
<Reference Include="Microsoft.Win32.Primitives" />
597600
<Reference Include="System.Buffers" />

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6+
using System.Net;
67
using System.Net.Http.Headers;
78
using System.Threading;
89
using System.Threading.Tasks;
@@ -76,7 +77,44 @@ private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMe
7677

7778
string challengeData = challenge.ChallengeData;
7879

79-
string spn = "HTTP/" + authUri.IdnHost;
80+
// Calculate SPN (Service Principal Name) using the host name of the request.
81+
// Use the request's 'Host' header if available. Otherwise, use the request uri.
82+
// Ignore the 'Host' header if this is proxy authentication since we need to use
83+
// the host name of the proxy itself for SPN calculation.
84+
string hostName;
85+
if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null)
86+
{
87+
// Use the host name without any normalization.
88+
hostName = request.Headers.Host;
89+
if (NetEventSource.IsEnabled)
90+
{
91+
NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}");
92+
}
93+
}
94+
else
95+
{
96+
// Need to use FQDN normalized host so that CNAME's are traversed.
97+
// Use DNS to do the forward lookup to an A (host) record.
98+
// But skip DNS lookup on IP literals. Otherwise, we would end up
99+
// doing an unintended reverse DNS lookup.
100+
UriHostNameType hnt = authUri.HostNameType;
101+
if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4)
102+
{
103+
hostName = authUri.IdnHost;
104+
}
105+
else
106+
{
107+
IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false);
108+
hostName = result.HostName;
109+
}
110+
}
111+
112+
string spn = "HTTP/" + hostName;
113+
if (NetEventSource.IsEnabled)
114+
{
115+
NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}");
116+
}
117+
80118
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
81119
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
82120
try

0 commit comments

Comments
 (0)