Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SafeHandle has been closed when using WindowsIdentity.RunImpersonated #26446

Closed
ge0rgi opened this issue Jun 11, 2018 · 20 comments
Closed

SafeHandle has been closed when using WindowsIdentity.RunImpersonated #26446

ge0rgi opened this issue Jun 11, 2018 · 20 comments

Comments

@ge0rgi
Copy link

ge0rgi commented Jun 11, 2018

Hello everyone.

I am having trouble using Impersonation in asp.net core. I have an .NET core web API and .NET web application both running .NET core 2.1 and using Windows Authentication.

I am trying to access the web service from the web application like this:

var identity = User.Identity as WindowsIdentity;

var test = WindowsIdentity.RunImpersonated(identity.AccessToken, () =>
{
   var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new Uri(MY_URL) };
    try
    {
        var rq = new HttpRequestMessage(HttpMethod.Get, "mymethod");
        var response = client.SendAsync(rq).Result;
        var responseString = response.Content.ReadAsStringAsync().Result;
        return responseString;
    }
    catch (Exception e)
    {
        return null;
    }
});

I am getting the following Error on the production server and occasionally on my local machine

The process was terminated due to an unhandled exception.System.ObjectDisposedException: Safe handle has been closed

Full stack trace:

> Application: dotnet.exe
CoreCLR Version: 4.6.26515.7
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: An exception was not handled in an AsyncLocal<T> notification callback.
Description: The process was terminated due to an unhandled exception.System.ObjectDisposedException: Safe handle has been closed
   at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
   at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
   at Interop.Advapi32.ImpersonateLoggedOnUser(SafeAccessTokenHandle userToken)
   at System.Security.Principal.WindowsIdentity.CurrentImpersonatedTokenChanged(AsyncLocalValueChangedArgs`1 args)
   at System.Threading.AsyncLocal`1.System.Threading.IAsyncLocal.OnValueChanged(Object previousValueObj, Object currentValueObj, Boolean contextChanged)
   at System.Threading.ExecutionContext.OnValuesChanged(ExecutionContext previousExecutionCtx, ExecutionContext nextExecutionCtx)
Stack:
   at System.Environment.FailFast(System.String, System.Exception)
   at System.Threading.ExecutionContext.OnValuesChanged(System.Threading.ExecutionContext, System.Threading.ExecutionContext)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationAsyncFailure(System.Exception, Int32, System.Net.Sockets.SocketFlags)
   at System.Net.Sockets.MultipleConnectAsync.AsyncFail(System.Exception)
   at System.Net.Sockets.MultipleConnectAsync.Fail(Boolean, System.Exception)
   at System.Net.Sockets.MultipleConnectAsync.DoDnsCallback(System.IAsyncResult, Boolean)
   at System.Net.Sockets.MultipleConnectAsync.DnsCallback(System.IAsyncResult)
   at System.Net.LazyAsyncResult.Complete(IntPtr)
   at System.Net.ContextAwareResult.CompleteCallback()
   at System.Net.ContextAwareResult+<>c.<Complete>b__15_0(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Net.ContextAwareResult.Complete(IntPtr)
   at System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr)
   at System.Net.NameResolutionPal+GetAddrInfoExState+<>c.<CompleteAsyncResult>b__5_0(System.Object)
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task+<>c.<.cctor>b__278_1(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)
   at System.Threading.Tasks.Task.ExecuteEntryUnsafe()
   at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

Am I misusing the the access token somehow and how do I prevent the handle from being disposed. I tried wrapping my code inside a using block, however this did not help.

PS Probably related to #24233 I had the same issues when attempting to create a middleware calling WindowsIdentity.RunImpersonated

@stephentoub
Copy link
Member

@kouvel, do you think this could be caused by https://github.com/dotnet/coreclr/issues/17758?

@kouvel
Copy link
Member

kouvel commented Jun 12, 2018

The context changed handler does not dispose the handle passed in, so I don't think dotnet/coreclr#17758 would affect it. I suspect some async callback is trying to impersonate again using the access handle, but after it has been disposed elsewhere. Netfx gets around this by duplicating the access token, that may be missing in Core.

@kouvel
Copy link
Member

kouvel commented Jun 12, 2018

Repro:

        Task task = null;
        using (var token = WindowsIdentity.GetCurrent().AccessToken)
        {
            WindowsIdentity.RunImpersonated(token, () =>
                task = Task.Run(async () =>
                    await Task.Delay(1000).ConfigureAwait(false)));
        }

        task.Wait();

@kouvel
Copy link
Member

kouvel commented Jun 12, 2018

Workaround for now:

    private static void Main()
    {
        Task task = null;
        using (var token = WindowsIdentity.GetCurrent().AccessToken)
        {
            WindowsIdentity.RunImpersonated(DuplicateAccessToken(token), () =>
                task = Task.Run(async () =>
                    await Task.Delay(1000).ConfigureAwait(false)));
        }

        task.Wait();
    }

    private static SafeAccessTokenHandle DuplicateAccessToken(SafeAccessTokenHandle accessToken)
    {
        if (accessToken.IsInvalid)
        {
            return accessToken;
        }

        SafeAccessTokenHandle duplicateAccessToken = SafeAccessTokenHandle.InvalidHandle;
        IntPtr currentProcessHandle = GetCurrentProcess();
        if (!DuplicateHandle(
                currentProcessHandle,
                accessToken,
                currentProcessHandle,
                ref duplicateAccessToken,
                0,
                true,
                DUPLICATE_SAME_ACCESS))
        {
            throw new SecurityException(new Win32Exception().Message);
        }

        return duplicateAccessToken;
    }

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool DuplicateHandle(
        IntPtr hSourceProcessHandle,
        SafeAccessTokenHandle hSourceHandle,
        IntPtr hTargetProcessHandle,
        ref SafeAccessTokenHandle lpTargetHandle,
        uint dwDesiredAccess,
        bool bInheritHandle,
        uint dwOptions);

    private const uint DUPLICATE_SAME_ACCESS = 0x00000002;

@ge0rgi, could you please try something like this as a workaround for now and let me know if that resolves the issue? Meanwhile, I've marked this issue for 2.1 servicing.

kouvel referenced this issue in kouvel/corefx Jun 13, 2018
So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Fix for https://github.com/dotnet/corefx/issues/30275
@ge0rgi
Copy link
Author

ge0rgi commented Jun 13, 2018

@kouvel I can confirm your workaround resolves the issue.

@kouvel
Copy link
Member

kouvel commented Jun 13, 2018

Thanks @ge0rgi, I have updated the workaround code inline above based on feedback in the PR

stephentoub referenced this issue in dotnet/corefx Jun 13, 2018
…30346)

* Duplicate the access token passed to WindowsIdentity.RunImpersonated

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Fix for https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
kouvel referenced this issue in kouvel/corefx Jun 13, 2018
…otnet#30346)

* Duplicate the access token passed to WindowsIdentity.RunImpersonated

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Fix for https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
kouvel referenced this issue in kouvel/corefx Jun 13, 2018
…otnet#30346)

* Duplicate the access token passed to WindowsIdentity.RunImpersonated

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Fix for https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
kouvel referenced this issue in kouvel/corefx Jun 13, 2018
…otnet#30346)

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Port of dotnet#30346 to release/2.1
Fixes https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
kouvel referenced this issue in kouvel/corefx Jun 14, 2018
…otnet#30346)

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Port of dotnet#30346 to release/2.1
Fixes https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
kouvel referenced this issue in kouvel/corefx Jun 14, 2018
…otnet#30346)

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Port of dotnet#30346 and dotnet#30377 to release/2.1
Fixes https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release
stephentoub referenced this issue in dotnet/corefx Jun 27, 2018
…nImpersonated (#30346) (#30379)

* Duplicate the access token passed to WindowsIdentity.RunImpersonated (#30346)

So that callbacks for async work initiated while impersonated may continue to impersonate even after the original access token had been disposed.

Port of #30346 and #30377 to release/2.1
Fixes https://github.com/dotnet/corefx/issues/30275

* Small fix

* Address feedback

* Change to use ref counting, remove assert (no null check on access token parameter)

* Manual add/release

* Add test

* Change test
@karelz
Copy link
Member

karelz commented Jul 8, 2018

Fixed in 2.1 servicing in PR dotnet/corefx#30379. Closing.
Should be part of 2.1.3 release.

@karelz karelz closed this as completed Jul 8, 2018
@LeroyPakade
Copy link

I upgraded to 2.1.3 , the bug is still there

@danmoseley danmoseley reopened this Apr 4, 2019
@danmoseley
Copy link
Member

@kouvel ?

@kouvel
Copy link
Member

kouvel commented Apr 4, 2019

@LeroyPakade, based on dotnet/corefx#30379 (comment) it does not sound like the error is related to impersonation. Since the error mentions something about host name resolution not working, does it work if the IP address is used directly?

@LeroyPakade
Copy link

I doesn't work even if i try use the IP , it works for some users but fail for others, if i change the user's context to WindowsIdentity.GetCurrent().AccessToken than it works but i want to use ((WindowsIdentity)this.User.Identity).AccessToken , Because i want to impersonate the logged in user and not the IIS user

@kouvel
Copy link
Member

kouvel commented Apr 5, 2019

Do you get the same error when using IP?

@LeroyPakade
Copy link

Yes ,only thing i was wondering was this windows policy , impersonate a client after authentication

@Cynthia-Jiang
Copy link

Cynthia-Jiang commented Apr 23, 2019

Yes ,only thing i was wondering was this windows policy , impersonate a client after authentication

are you getting similar error like me? 37120
during my testing, i also checked this post, it claims that this is fixed after 2.1.3, but i still tried the same workaround even i'm using 2.2.4, and it didn't make a difference.
and i further found that if i turn off SocketsHttpHandler, it could work. can you try as well?

@davidsh davidsh closed this as completed Apr 23, 2019
@davidsh
Copy link
Contributor

davidsh commented Apr 23, 2019

and i further found that if i turn off SocketsHttpHandler, it could work. can you try as well?

As mentioned in dotnet/corefx#37120, RunAsImpersonated is a synchronous call. So, are you able to reproduce your error if don't use async calls?

@LeroyPakade
Copy link

i turned SocketsHttpHandler off still had the issue for some users only when i change HttpClientHandler to WinHttpHandler it works but than i need to supply ServerCredentials which i dont have as previously i used , UseDefaultCredentials = true

@davidsh davidsh reopened this Apr 25, 2019
@davidsh
Copy link
Contributor

davidsh commented Apr 25, 2019

@LeroyPakade If you're still seeing this problem in .NET Core 3.0, please open a new issue. Thx.

You can try out .NET Core 3.0 Preview 4:
https://devblogs.microsoft.com/dotnet/announcing-net-core-3-preview-4/

@davidsh davidsh closed this as completed Apr 25, 2019
@LeroyPakade
Copy link

Its working now with WinHttpHandler

@satyendrasinghpanwar
Copy link

@davidsh SocketHttpHandler works If I use ip. But with localhost or any other hostname it gives me "This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server" error. If i use WinHttpHandler then everything works.

@davidsh
Copy link
Contributor

davidsh commented Apr 26, 2019

@davidsh SocketHttpHandler works If I use ip. But with localhost or any other hostname it gives me "This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server" error. If i use WinHttpHandler then everything works.

dotnet/corefx#37120 is already discussing this error. Let's keep the conversation about this error on that open issue only.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.1.x milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants