runtime: ASP.NET core 2.2.4 impersonation not working with SocketsHttpHandler
environment: DC: windows server 2012R2 server1: windows server 2016, ASP.NET core 2.2.4 web API on IIS with windows authentication enabled server2: windows server 2012R2, ASP.NET core 2.2 web api on IIS with windows authentication enabled client: any browsers client -> server1 -> server2. the ASP.NET core app in server1 will impersonate the authenticated user then call server 2 web api. i have configured app pools on both servers to use service account, and have configured SPN accordingly, added delegation setting for the service account of server1.
sample code of how i do impersonate:
var windowsIdentity = (WindowsIdentity)this.HttpContext.User.Identity;
await (WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "http://winsvr12r2-1.jiangya1.lab:4000/api/values"))
{
string user_name = WindowsIdentity.GetCurrent().Name;
try
{
var response = await _client.SendAsync(requestMessage);
var responseString = await response.Content.ReadAsStringAsync();
result = ($"Called API as: {WindowsIdentity.GetCurrent().Name}.\n\n" +
$"{responseString}\n");
}
catch (Exception ex)
{
result = ex.ToString();
}
}
}));
return result;
expected scenario: the impersonation process would succeed,and server2 gets requests from the initial credential. real scenario: server1 would throw following exception:
System.Net.Http.HttpRequestException: This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server ---> System.Net.Sockets.SocketException: This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at WebApplication2.Controllers.PassThroughController.<>c__DisplayClass5_0.<<Get>b__0>d.MoveNext() in C:\Users\jiangya\source\repos\WebApplication2\WebApplication2\Controllers\PassThroughController.cs:line 92
further troubleshooting: while doing debugging, i found that if i turn off SocketsHttpHandler following this and use HTTPClientHandler, the issue will get resolved. which seems indicate that ASP.NET core impersonation is not working with SocketsHttpHandler. i also attached full callstack here: working-callstack.txt non-working-callstack.txt
I understand the new socket handler is designed to eliminate platform dependencies, however can you suggest how to make it work with impersonation?
non-working callstack with SocketsHttpHandler:
0:030> !\\**********\shared\Microsoft.NETCore.App\2.2.4\sos.clrstack
OS Thread Id: 0xe00 (30)
Child SP IP Call Site
000000d9e19fbf78 00007ffca5a60160 Interop+Winsock.GetAddrInfoExW(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fbf78 00007ffc343c65a4 Interop+Winsock.GetAddrInfoExW(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fbf20 00007ffc343c65a4 DomainBoundILStubClass.IL_STUB_PInvoke(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fc040 00007ffc8aaafba8 System.Net.NameResolutionPal.GetAddrInfoAsync(System.Net.DnsResolveAsyncResult)
000000d9e19fc110 00007ffc8aaac27b System.Net.Dns.HostResolutionBeginHelper(System.String, Boolean, Boolean, System.AsyncCallback, System.Object)
000000d9e19fc190 00007ffc8aaacea8 System.Net.Dns.BeginGetHostAddresses(System.String, System.AsyncCallback, System.Object)
000000d9e19fc1e0 00007ffc8ab1bcc2 System.Net.Sockets.MultipleConnectAsync.StartConnectAsync(System.Net.Sockets.SocketAsyncEventArgs, System.Net.DnsEndPoint)
000000d9e19fc250 00007ffc8ab08f23 System.Net.Sockets.Socket.ConnectAsync(System.Net.Sockets.SocketType, System.Net.Sockets.ProtocolType, System.Net.Sockets.SocketAsyncEventArgs)
000000d9e19fc2b0 00007ffc343c5d98 System.Net.Http.ConnectHelper+d__2.MoveNext()
000000d9e19fc3a0 00007ffc343c5759 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fc410 00007ffc8a95726e System.Net.Http.ConnectHelper.ConnectAsync(System.String, Int32, System.Threading.CancellationToken)
000000d9e19fc4d0 00007ffc8a95afae System.Net.Http.HttpConnectionPool+d__44.MoveNext()
000000d9e19fc6e0 00007ffc343c51e9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fc750 00007ffc8a95950e System.Net.Http.HttpConnectionPool.CreateConnectionAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fc850 00007ffc343c4d29 System.Net.Http.HttpConnectionPool.GetConnectionAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fc9f0 00007ffc8a95a662 System.Net.Http.HttpConnectionPool+d__39.MoveNext()
000000d9e19fcaf0 00007ffc343c3ff9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fcb60 00007ffc8a959225 System.Net.Http.HttpConnectionPool.SendWithRetryAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcc00 00007ffc8a9593ee System.Net.Http.HttpConnectionPool.SendWithProxyAuthAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcc60 00007ffc8a9457a2 System.Net.Http.AuthenticationHelper+d__16.MoveNext()
000000d9e19fcd60 00007ffc343c3cf9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fcdd0 00007ffc8a9443fb System.Net.Http.AuthenticationHelper.SendWithAuthAsync(System.Net.Http.HttpRequestMessage, System.Uri, System.Net.ICredentials, Boolean, Boolean, Boolean, System.Net.Http.HttpConnectionPool, System.Threading.CancellationToken)
000000d9e19fce90 00007ffc8a95946c System.Net.Http.HttpConnectionPool.SendAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcef0 00007ffc8a95c79c System.Net.Http.HttpConnectionPoolManager.SendAsyncCore(System.Net.Http.HttpRequestMessage, System.Uri, Boolean, Boolean, System.Threading.CancellationToken)
000000d9e19fcfd0 00007ffc8a95c93a System.Net.Http.HttpConnectionPoolManager.SendAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fd030 00007ffc8a958cea System.Net.Http.HttpAuthenticatedConnectionHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd060 00007ffc8a95f388 System.Net.Http.RedirectHandler+d__4.MoveNext()
000000d9e19fd100 00007ffc343c2729 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd170 00007ffc8a95efba System.Net.Http.RedirectHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd1f0 00007ffc8a95ead2 System.Net.Http.SocketsHttpHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd240 00007ffc8a93a259 System.Net.Http.HttpClientHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd290 00007ffc8a93ce42 System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd2e0 00007ffc8a936954 System.Net.Http.HttpClient.SendAsync(System.Net.Http.HttpRequestMessage, System.Net.Http.HttpCompletionOption, System.Threading.CancellationToken)
000000d9e19fd350 00007ffc343bf2fd Error: Failed to load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\coreclr.dll
WebApplication2.Controllers.PassThroughController+c__DisplayClass5_0+b__0>d.MoveNext()
000000d9e19fd3c0 00007ffc343bf109 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd430 00007ffc343bf098 WebApplication2.Controllers.PassThroughController+c__DisplayClass5_0.b__0()
000000d9e19fd4a0 00007ffc8ba09446 System.Security.Principal.WindowsIdentity+c__DisplayClass60_0`1 .b__0()
000000d9e19fd4d0 00007ffc8ba09517 System.Security.Principal.WindowsIdentity+c__DisplayClass64_0.b__0(System.Object)
000000d9e19fd510 00007ffc8d3c2019 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000d9e19fd590 00007ffc8ba07993 System.Security.Principal.WindowsIdentity.RunImpersonatedInternal(Microsoft.Win32.SafeHandles.SafeAccessTokenHandle, System.Action)
000000d9e19fd5e0 00007ffc8ba077df System.Security.Principal.WindowsIdentity.RunImpersonated
000000d9e19fd630 00007ffc343be842 WebApplication2.Controllers.PassThroughController+d__5.MoveNext()
000000d9e19fd690 00007ffc343be5d9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd700 00007ffc343be558 WebApplication2.Controllers.PassThroughController.Get()
working callstack with HttpClientHandler:
0:014> k
Child-SP RetAddr Call Site
00 000000b3`80a7e140 00007ffc`a5a5ec39
ws2_32!WinsockThreadpool_CreateWorkContext+0x9b
01 000000b3`80a7e180 00007ffc`a5a59684 ws2_32!WinsockThreadpool_SubmitWork+0x45
02 000000b3`80a7e1d0 00007ffc`a5a58009 ws2_32!NSQUERY::LookupServiceNextAsync+0x154
03 000000b3`80a7e250 00007ffc`a5a56b67 ws2_32!WSALookupServiceNextAsyncW+0x169
04 000000b3`80a7e2b0 00007ffc`a5a57070 ws2_32!NSSUBJOB::LookupAsyncAndHandleResultsQDNS4F+0xb7
05 000000b3`80a7e340 00007ffc`a5a5717e ws2_32!NSSUBJOB::TrySubmitQDNS4F+0x6c
06 000000b3`80a7e390 00007ffc`a5a56814 ws2_32!NSSUBJOB::TrySubmit+0x8e
07 000000b3`80a7e3f0 00007ffc`a5a55f06 ws2_32!NSJOB::SubmitChild+0x44
08 000000b3`80a7e440 00007ffc`a5a56329 ws2_32!NSJOB::ForEachChild+0xd6
09 (Inline Function) --------`-------- ws2_32!NSJOB::ForEachChild+0x12
0a 000000b3`80a7e490 00007ffc`a5a57201 ws2_32!NSJOB::TrySubmit+0x89
0b 000000b3`80a7e4f0 00007ffc`a5a56814 ws2_32!NSSUBJOB::TrySubmit+0x111
0c 000000b3`80a7e550 00007ffc`a5a55f06 ws2_32!NSJOB::SubmitChild+0x44
0d 000000b3`80a7e5a0 00007ffc`a5a56329 ws2_32!NSJOB::ForEachChild+0xd6
0e (Inline Function) --------`-------- ws2_32!NSJOB::ForEachChild+0x12
0f 000000b3`80a7e5f0 00007ffc`a5a55913 ws2_32!NSJOB::TrySubmit+0x89
10 000000b3`80a7e650 00007ffc`a5a6145c ws2_32!NSMASTERJOB::TrySubmit+0x53
11 000000b3`80a7e690 00007ffc`99109ac1 ws2_32!GetAddrInfoExW+0x12fc
12 000000b3`80a7e8d0 00007ffc`991099d1 webio!WapDnsResolveGeneric+0xd9
13 000000b3`80a7e9c0 00007ffc`99108e3e webio!WapStartDnsIoContext+0x31
14 000000b3`80a7e9f0 00007ffc`99108a6e webio!WaStartDnsQuery+0x21e
15 000000b3`80a7eab0 00007ffc`99108fa4 webio!WapTcpStartDnsQuery+0x38e
16 000000b3`80a7eb40 00007ffc`991090b7 webio!WaTcpConnectRequest+0x54
17 000000b3`80a7eb90 00007ffc`99102f44 webio!WapHttpConnect+0x107
18 000000b3`80a7ec20 00007ffc`99102da8 webio!WaQueueHttpRequest+0x144
19 (Inline Function) --------`-------- webio!WapStartQueueRequest+0x1c
1a (Inline Function) --------`-------- webio!WapGetConnectionCompletionRoutine+0x5c
1b 000000b3`80a7ecc0 00007ffc`99102c88 webio!WapStartGetConnection+0xf8
1c 000000b3`80a7ed10 00007ffc`99102b16 webio!WapStartGetEndpoints+0x44
1d 000000b3`80a7ed40 00007ffc`99105008 webio!WapAsynchronousSendHttpRequest+0x266
1e (Inline Function) --------`-------- webio!WapSendHttpRequest+0x2f
1f 000000b3`80a7ee20 00007ffc`9f001c65 webio!WebSendHttpRequest+0x38
20 (Inline Function) --------`-------- winhttp!WEBIO_SENDER::SendRequest+0x21d
21 000000b3`80a7ee80 00007ffc`9f007528 winhttp!WEBIO_REQUEST::SendRequest+0x415
22 000000b3`80a7ef70 00007ffc`9f007b60 winhttp!HTTP_USER_REQUEST::_SysSendRequest+0x1028
23 000000b3`80a7f170 00007ffc`9f00783b winhttp!HTTP_USER_REQUEST::_SendRequestWithDrainComplete+0x2d0
24 000000b3`80a7f200 00007ffc`9f0023fb winhttp!HTTP_USER_REQUEST::_SendRequestWithProxyInitialized+0x6b
25 000000b3`80a7f230 00007ffc`9efe3a57 winhttp!HTTP_USER_REQUEST::OnProxyResolvedOnSend+0xbf
26 000000b3`80a7f280 00007ffc`9efe3710 winhttp!UserRequestProxyCompletion::OnProxyResolved+0x27
27 000000b3`80a7f2b0 00007ffc`9efe351e winhttp!WxProxyManager::WxProxyFailoverCompletionRoutine+0x90
28 000000b3`80a7f2e0 00007ffc`a60dec59 winhttp!RefCountWorkItemThread+0x1e
29 000000b3`80a7f310 00007ffc`a60c328a ntdll!RtlpTpWorkCallback+0x129
2a 000000b3`80a7f3f0 00007ffc`a4e98364 ntdll!TppWorkerThread+0x4aa
2b 000000b3`80a7f7f0 00007ffc`a60f7091 KERNEL32!BaseThreadInitThunk+0x14
2c 000000b3`80a7f820 00000000`00000000 ntdll!RtlUserThreadStart+0x21
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 29 (15 by maintainers)
@MarcoRossignoli Thanks for the additional repro case!
I have done further analysis and opened up a new issue, dotnet/corefx#38646 that is the root cause of this issue.
BeginGetHostAddresses doesn’t work with impersonation. GetHostAddresses internally call NameResolutionPal.TryGetAddrInfo, while BeginGetHostAddresses calls NameResolutionPal.GetAddrInfoAsync. Both method have different way for resolving host. TryGetAddrInfo uses Interop.Winsock.GetAddrInfoW for resolving host GetAddrInfoAsync uses Interop.Winsock.GetAddrInfoExW for resolving host which always returns “WSATRY_AGAIN” as error code. Surely, their is some problem with Dns.BeginGetHostAddress
this is the callstack i got while debugging:
and i found inner exception is access deny error when opening thread token:
and, if you need a lab to do debugging, i can share mine to you through Microsoft corporate network.