Windows Identity Impersonation Made Easy(ish)

There are lots of different reasons why you would want to use identity impersonation in an application. The most common situation that I see is that you need to access a network resource, and you’ve been given a domain user account and password that has rights.

It would be nice to have an easy way to do the impersonation without having to do a lot of mucking around. I sat down to do this and came up with a nice, simple solution that does just that. I used this MSDN article as the basis for my solution, and there was a small amount of copying/pasting involved. I’m going to work my way backwards through the solution and then present the entire solution at the end.

What I wanted to have in the end was an extension method that could be used from any object. This was the goal usage:

var f = new Foo();
f.ExecuteAs(domainName, userName, password, x => x.Bar("Hi!"));

In order to have the extension method available, I needed to have a static class with a static method. The static method should accept the user credentials necessary to authenticate and the actual method to be executed.

public static class ExecuteAsExtension
{
    public static void ExecuteAs<T>(this T source, string domainName,
        string userName, string password, Action<T> action)
    {
        var token = GetToken(domainName, userName, password);
        using (token)
        {
            var identity = new WindowsIdentity(token.DangerousGetHandle());
            using (identity.Impersonate())
            {
                action(source);
            }
        }
    }
}

So that’s pretty easy! Now we just need to implement the GetToken method. This is the part of my solution that relies on the aforementioned MSDN article. I’ll just post the entire contents of my ExecuteAsExtension.cs file so that you can see it all:

using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;

namespace MyImpersonationNamespace
{
    public static class ExecuteAsExtension
    {
        [DllImport("advapi32.dll", SetLastError = true,
            CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername,
            String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        internal static SafeTokenHandle GetToken(string domainName,
            string userName, string password)
        {
            SafeTokenHandle safeTokenHandle;

            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(userName, domainName, password,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(ret);
            }

            return safeTokenHandle;
        }

        public static void ExecuteAs<T>(this T source, string domainName,
            string userName, string password, Action<T> action)
        {
            var token = GetToken(domainName, userName, password);
            using (token)
            {
                var identity = new WindowsIdentity(token.DangerousGetHandle());
                using (identity.Impersonate())
                {
                    action(source);
                }
            }
        }
    }

    public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle()
            : base(true)
        {
        }

        [DllImport("kernel32.dll")]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }
    }
}

To recap, using this new extension method to execute a function as another use requires just two steps:

  1. Add a using statement for your extension method’s namespace
  2. Use the extension method to execute a function

For reference, here is my test console application:

using System;
using System.Security.Principal;
using MyImpersonationNamespace;

namespace MyImpersonationNamespace
{
    class Program
    {
        static void Main(string[] args)
        {
            var domainName = "XP";
            var userName = "Administrator";
            var password = "SecretPassword";

            Console.WriteLine("Starting as {0}",
                WindowsIdentity.GetCurrent().Name);
            var f = new Foo();
            f.ExecuteAs(domainName, userName, password, x => x.Bar("dude"));
            Console.WriteLine("Finishing as {0}",
                WindowsIdentity.GetCurrent().Name);
            Console.ReadLine();
        }
    }

    class Foo
    {
        internal void Bar(string name)
        {
            Console.WriteLine("Executing as {0}",
                WindowsIdentity.GetCurrent().Name);
            Console.WriteLine("Hello, {0}!", name);
        }
    }
}

9/28/2012 Update:
Fixed a bug in the ExecuteAs methods above where the “Action action” argument should be “Action<T> action”
Also, here’s the static class implementation provided by ericrouse in his comment. Thanks, Eric!

public static void Action(SecurityKey securityKey, Action action)
{
	using (SafeTokenHandle token = GetToken(securityKey.Domain, securityKey.UserName, securityKey.Password))
	{
		WindowsIdentity identity = new WindowsIdentity(token.DangerousGetHandle());

		using (identity.Impersonate())
		{
			action();
		}
	}
}

/* Usage:
ExecuteAs.Action(SecurityKey,
    () =>
    {
        // Perform some action
    });
*/

11/5/2012 Update:
I was having some problems using this code to access a UNC path from within a unit test. The solution was to change the Logon Type parameter from “Interactive” to “New Credentials.”

const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

bool returnValue = LogonUser(userName, domainName, password,
                LOGON32_LOGON_NEW_CREDENTIALS, 
                LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);
Advertisements

8 thoughts on “Windows Identity Impersonation Made Easy(ish)”

    1. Can you post code? The only advice I can offer without additional detail is to ensure the Action you’re trying to execute takes the correct number of parameters.

      In my example, I execute the following:
      x => x.Bar(“dude”)

      Foo.Bar(string) is my Action, which takes one parameter.

      I hope this helps!

      9/28/2012: The error is caused by the missing “<T>” on the Action argument of the ExecuteAs method. It should be “Action<T> action.” I fixed the code above.

  1. Your class was very helpful. I did however change it from an Extension method to a static. The reason was the class I need to execute is returned through a static method. You call this static to get the object and then use it’s methods.

    Also I wrapped the domain, user, password in a SecurityKey class in my main application so I adjusted that here as well.

    For anyone else with this implementation:

    public static void Action(SecurityKey securityKey, Action action)
    {
    using (SafeTokenHandle token = GetToken(securityKey.Domain, securityKey.UserName, securityKey.Password))
    {
    WindowsIdentity identity = new WindowsIdentity(token.DangerousGetHandle());

    using (identity.Impersonate())
    {
    action();
    }
    }
    }

    I call it like this:

    ExecuteAs.Action(SecurityKey, () =>
    {
    // Perform some action
    });

  2. I’m searching for a solution to integrate Domain authentication to integrate websockets – I’ve been thinking on using the token and WindowsIdentity to try and use a redirected token from an authenticated site from ad + https-> annon + https + wss

    The reason being that server / websockets at the moment on II8 simply closes the socket… so I can’t use domain authentication on my ws sites!

    I’ll postback if I make any progress from the works “domain”…

  3. have a requirement, where I need to impersonate a logon user using the database account (the database account has access to system tables). I don’t want to expose the password, by providing in a web.config file. is there a way to impersonate the logon user using database account without providing the password.

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s