Chris Hayes
Chris Hayes

Reputation: 4035

How does asp.net impersonation work with System.Threading.Task?

I want to access a database with the credentials of the client user.

my code:

Task.Run(() => 
{
        // Connect to DB.
}

I have the identity tag with impersonation attribute in the system.web node:

<identity impersonate="true" />

I have the validation tag in the system.webServer node:

<validation validateIntegratedModeConfiguration="false" />

I have the authentication set to windows:

<authentication mode="Windows" />

I have impersonation and windows set on my app in iis:

iis settings

I have the app set to use passthrough:

enter image description here

I have restarted iis.

I am at a loss at how this works or I totally misunderstand it.

Upvotes: 4

Views: 1559

Answers (2)

Adam Milecki
Adam Milecki

Reputation: 1786

Had this lying around:

Windows Interop...

public static class Win32LogonInterop
{
    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public static WindowsIdentity LogOn(string domain, string userName, string password)
    {
        IntPtr token = IntPtr.Zero;

        if (NativeMethods.LogonUser(userName, domain, password, ConnectionKind.NewCredentials, Provider.Default, out token))
        {
            return new WindowsIdentity(token);
        }
        else
        {
            RaiseError();
            return null;
        }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public static void LogOff(WindowsIdentity identity)
    {
        if (identity != null)
        {
            if (!NativeMethods.CloseHandle(identity.Token))
                RaiseError();
        }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    private static void RaiseError()
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new Win32Exception(errorCode);
    }
}

internal static class NativeMethods
{
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool LogonUser(
            string userName,
            string domain,
            string password,
            ConnectionKind connectionKind,
            Provider provider,
        out IntPtr token);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseHandle(IntPtr handle);
}

public enum Provider
{
    Default = 0,
    WindowsNT35 = 1,
    WindowsNT40 = 2,
    WindowsNT50 = 3
}

public enum SecurityLevel
{
    Anonymous = 0,
    Identification = 1,
    Impersonation = 2,
    Delegation = 3
}

public enum ConnectionKind
{
    Unknown = 0,
    Interactive = 2,
    Network = 3,
    Batch = 4,
    Service = 5,
    Unlock = 7,
    NetworkClearText = 8,
    NewCredentials = 9
}

Impersonation method...

private void MyMethod(string domain, string userName, string password)
{
    WindowsIdentity _identity = Win32LogonInterop.LogOn(domain, userName, password);
    WindowsImpersonationContext impersonation = null;
    try
    {
        impersonation = _identity.Impersonate();

        // Stuff that needs impersonation
    }
    finally
    {
        if (impersonation != null)
        {
            impersonation.Undo();
            impersonation.Dispose();
        }
        if (_identity != null)
        {
            Win32LogonInterop.LogOff(_identity);
        }
    }
}

Upvotes: 1

Arian Motamedi
Arian Motamedi

Reputation: 7423

Based on your comment, it seems like you are running System.Security.Principal.WindowsIdentity.GetCurrent().Name in a Task, and getting App Pool's identity instead of impersonated user's. This is a common issue (take a look at this question I asked a while back).

There is however, a workaround. If you still want your Task to run as the impersonated user, you'll have to save current user's token before the Task runs and try to "re-impersonate" the Task's context like this:

IntPtr currentUser = WindowsIdentity.GetCurrent().Token; // This token points to the impersonated user

Task.Run(() => 
{
    using (WindowsIdentity.Impersonate(token))
    {
        // Connect to DB.
    }
}

This ensures anything in the Using block runs under impersonated user's identity.

Upvotes: 5

Related Questions