Skip to content

Commit 298493e

Browse files
Use ImpersonationToken for IIS Windows Auth (#58041)
1 parent f83c97f commit 298493e

File tree

4 files changed

+68
-4
lines changed

4 files changed

+68
-4
lines changed

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,13 @@ http_get_authentication_information(
516516
)
517517
{
518518
*pstrAuthType = SysAllocString(pInProcessHandler->QueryHttpContext()->GetUser()->GetAuthenticationType());
519+
// prefer GetPrimaryToken over GetImpersonationToken as that's what we've been using since before .NET 10
520+
// we'll fallback to GetImpersonationToken if GetPrimaryToken is not available
519521
*pvToken = pInProcessHandler->QueryHttpContext()->GetUser()->GetPrimaryToken();
522+
if (*pvToken == nullptr)
523+
{
524+
*pvToken = pInProcessHandler->QueryHttpContext()->GetUser()->GetImpersonationToken();
525+
}
520526

521527
return S_OK;
522528
}

src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,11 +819,20 @@ FORWARDING_HANDLER::GetHeaders(
819819
(_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"negotiate") == 0 ||
820820
_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"ntlm") == 0))
821821
{
822-
if (m_pW3Context->GetUser()->GetPrimaryToken() != nullptr &&
823-
m_pW3Context->GetUser()->GetPrimaryToken() != INVALID_HANDLE_VALUE)
822+
// prefer GetPrimaryToken over GetImpersonationToken as that's what we've been using since before .NET 10
823+
// we'll fallback to GetImpersonationToken if GetPrimaryToken is not available
824+
HANDLE authToken = m_pW3Context->GetUser()->GetPrimaryToken();
825+
if (authToken == nullptr ||
826+
authToken == INVALID_HANDLE_VALUE)
827+
{
828+
authToken = m_pW3Context->GetUser()->GetImpersonationToken();
829+
}
830+
831+
if (authToken != nullptr &&
832+
authToken != INVALID_HANDLE_VALUE)
824833
{
825834
HANDLE hTargetTokenHandle = nullptr;
826-
RETURN_IF_FAILED(pServerProcess->SetWindowsAuthToken(m_pW3Context->GetUser()->GetPrimaryToken(),
835+
RETURN_IF_FAILED(pServerProcess->SetWindowsAuthToken(authToken,
827836
&hTargetTokenHandle));
828837

829838
//

src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/RequiresIISAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public RequiresIISAttribute(IISCapability capabilities)
136136
IsMet &= available;
137137
if (!available)
138138
{
139-
SkipReason += $"The machine does have {module.Capability} available.";
139+
SkipReason += $"The machine does not have {module.Capability} available.";
140140
}
141141
}
142142
}

src/Servers/IIS/IIS/test/Common.FunctionalTests/WindowsAuthTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Net;
56
using System.Net.Http;
7+
using System.Security.Principal;
68
using System.Threading.Tasks;
79
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
810
using Microsoft.AspNetCore.Server.IntegrationTesting;
@@ -58,4 +60,51 @@ public async Task WindowsAuthTest(TestVariant variant)
5860
Assert.StartsWith("Windows:", responseText);
5961
Assert.Contains(Environment.UserName, responseText);
6062
}
63+
64+
[ConditionalTheory]
65+
[RequiresIIS(IISCapability.WindowsAuthentication)]
66+
[MemberData(nameof(TestVariants))]
67+
public async Task WindowsAuthWithImpersonationLevelTest(TestVariant variant)
68+
{
69+
var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);
70+
deploymentParameters.SetAnonymousAuth(enabled: false);
71+
deploymentParameters.SetWindowsAuth();
72+
73+
// The default in hosting sets windows auth to true.
74+
var deploymentResult = await DeployAsync(deploymentParameters);
75+
76+
var impersonationLevels = new TokenImpersonationLevel[]
77+
{
78+
TokenImpersonationLevel.None,
79+
TokenImpersonationLevel.Identification,
80+
TokenImpersonationLevel.Impersonation,
81+
TokenImpersonationLevel.Delegation,
82+
TokenImpersonationLevel.Anonymous
83+
};
84+
85+
foreach (var impersonationLevel in impersonationLevels)
86+
{
87+
// TokenImpersonationLevel is not supported by HttpClient so we need to use HttpWebRequest to test it.
88+
#pragma warning disable SYSLIB0014 // Type or member is obsolete
89+
var request = HttpWebRequest.CreateHttp($"{deploymentResult.HttpClient.BaseAddress}Auth");
90+
#pragma warning restore SYSLIB0014 // Type or member is obsolete
91+
request.ImpersonationLevel = impersonationLevel;
92+
request.Method = "GET";
93+
request.UseDefaultCredentials = true;
94+
95+
using var response = request.GetResponse();
96+
using var reader = new StreamReader(response.GetResponseStream());
97+
var responseText = await reader.ReadToEndAsync();
98+
99+
try
100+
{
101+
Assert.StartsWith("Windows:", responseText);
102+
Assert.Contains(Environment.UserName, responseText);
103+
}
104+
catch (Exception ex)
105+
{
106+
Assert.Fail($"'TokenImpersonationLevel.{impersonationLevel}' failed with: {ex.Message}");
107+
}
108+
}
109+
}
61110
}

0 commit comments

Comments
 (0)