diff --git a/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs b/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs
index 920cf1214c08..4ceed132dabc 100644
--- a/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs
+++ b/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs
@@ -209,6 +209,46 @@ private WindowsIdentity(IntPtr userToken, string authType, int isAuthenticated)
_isAuthenticated = isAuthenticated;
}
+ private static SafeAccessTokenHandle DuplicateAccessToken(IntPtr accessToken)
+ {
+ SafeAccessTokenHandle duplicateAccessToken = SafeAccessTokenHandle.InvalidHandle;
+ IntPtr currentProcessHandle = Interop.Kernel32.GetCurrentProcess();
+ if (!Interop.Kernel32.DuplicateHandle(
+ currentProcessHandle,
+ accessToken,
+ currentProcessHandle,
+ ref duplicateAccessToken,
+ 0,
+ true,
+ Interop.DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
+ {
+ throw new SecurityException(new Win32Exception().Message);
+ }
+
+ return duplicateAccessToken;
+ }
+
+ private static SafeAccessTokenHandle DuplicateAccessToken(SafeAccessTokenHandle accessToken)
+ {
+ if (accessToken.IsInvalid)
+ {
+ return accessToken;
+ }
+
+ bool refAdded = false;
+ try
+ {
+ accessToken.DangerousAddRef(ref refAdded);
+ return DuplicateAccessToken(accessToken.DangerousGetHandle());
+ }
+ finally
+ {
+ if (refAdded)
+ {
+ accessToken.DangerousRelease();
+ }
+ }
+ }
private void CreateFromToken(IntPtr userToken)
{
@@ -222,14 +262,7 @@ private void CreateFromToken(IntPtr userToken)
if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_INVALID_HANDLE)
throw new ArgumentException(SR.Argument_InvalidImpersonationToken);
- if (!Interop.Kernel32.DuplicateHandle(Interop.Kernel32.GetCurrentProcess(),
- userToken,
- Interop.Kernel32.GetCurrentProcess(),
- ref _safeTokenHandle,
- 0,
- true,
- Interop.DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
- throw new SecurityException(new Win32Exception().Message);
+ _safeTokenHandle = DuplicateAccessToken(userToken);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2229", Justification = "Public API has already shipped.")]
@@ -640,6 +673,8 @@ public void Dispose()
private static void RunImpersonatedInternal(SafeAccessTokenHandle token, Action action)
{
+ token = DuplicateAccessToken(token);
+
bool isImpersonating;
int hr;
SafeAccessTokenHandle previousToken = GetCurrentToken(TokenAccessLevels.MaximumAllowed, false, out isImpersonating, out hr);
diff --git a/src/System.Security.Principal.Windows/tests/System.Security.Principal.Windows.Tests.csproj b/src/System.Security.Principal.Windows/tests/System.Security.Principal.Windows.Tests.csproj
index f0daf2c1b900..ab70abfa35fb 100644
--- a/src/System.Security.Principal.Windows/tests/System.Security.Principal.Windows.Tests.csproj
+++ b/src/System.Security.Principal.Windows/tests/System.Security.Principal.Windows.Tests.csproj
@@ -14,5 +14,10 @@
Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs
+
+
+ CommonTest\System\Threading\ThreadTestHelpers.cs
+
+
\ No newline at end of file
diff --git a/src/System.Security.Principal.Windows/tests/WindowsIdentityTests.cs b/src/System.Security.Principal.Windows/tests/WindowsIdentityTests.cs
index 5445388cd139..95859cd8eee7 100644
--- a/src/System.Security.Principal.Windows/tests/WindowsIdentityTests.cs
+++ b/src/System.Security.Principal.Windows/tests/WindowsIdentityTests.cs
@@ -5,8 +5,12 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Security.Claims;
using System.Security.Principal;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Tests;
using Xunit;
public class WindowsIdentityTests
@@ -120,6 +124,56 @@ public static void CheckUserClaims()
}
}
+ [Fact]
+ public static void RunImpersonatedAsyncTest()
+ {
+ var testData = new RunImpersonatedAsyncTestInfo();
+ BeginTask(testData);
+
+ // Wait for the SafeHandle that was disposed in BeginTask() to actually be closed
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.WaitForPendingFinalizers();
+
+ testData.continueTask.Release();
+ testData.task.CheckedWait();
+ if (testData.exception != null)
+ {
+ throw new AggregateException(testData.exception);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void BeginTask(RunImpersonatedAsyncTestInfo testInfo)
+ {
+ testInfo.continueTask = new SemaphoreSlim(0, 1);
+ using (SafeAccessTokenHandle token = WindowsIdentity.GetCurrent().AccessToken)
+ {
+ WindowsIdentity.RunImpersonated(token, () =>
+ {
+ testInfo.task = Task.Run(async () =>
+ {
+ try
+ {
+ Task task = testInfo.continueTask.WaitAsync(ThreadTestHelpers.UnexpectedTimeoutMilliseconds);
+ Assert.True(await task.ConfigureAwait(false));
+ }
+ catch (Exception ex)
+ {
+ testInfo.exception = ex;
+ }
+ });
+ });
+ }
+ }
+
+ private class RunImpersonatedAsyncTestInfo
+ {
+ public Task task;
+ public SemaphoreSlim continueTask;
+ public Exception exception;
+ }
+
private static void CheckDispose(WindowsIdentity identity, bool anonymous = false)
{
Assert.False(identity.AccessToken.IsClosed);