IMPERSONATION - Not commonly used in applications. But when the time calls for it, there are a number of ways for impersonating a User or Windows account in your applications.

This can be achieved by:

  • Changing the App.Config or Web.Config of your application, or
  • Setting IIS to run under the context of a Windows Service Account, or
  • Specifying the application pools to run under the context of a Windows Service Account

However, most of the above options are very locked down. Once this has been set up it may be very troublesome to gain Server access to change any of these settings. But what if you want the flexibility to be able to run sections of your code using different Windows Service Accounts? What this means is that it will give you, as the developer, control over which Service Account your functions will be executing under.

This opens up a wide variety of design decisions and security implications to consider. For example: You can have one Account for all database operations, a second account for reading files from the file system, and a third account for writing to the file system.

In this article, I will show you how you can enable Impersonation across multiple sections of your application code…


Problem

I was coding a component interacts with SQL Server FileStream types and in order to access the FileStream content, I discovered that a SQL Connection using Integrated Windows Security was required.

But I didn’t want my Web Application to run under the same Service Account as the Filestream Account. So this is where Impersonation came into play.  I was able to run my Application (Windows, Web App, or Silverlight App) under the current user context.  But when it came to accessing the files stored in my SQL FileStream I would impersonate a Windows Service Account created explicitly for this purpose.

What this allowed me to do was separate the security context of the Application away from the security context of the FileStream data access component.

Solution

Step One, you’ll need to include some references to the WinAPI libraries within your Impersonation.CS file.

using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

Then create your Impersonation Class. This can be a simple copy & paste into your project.

public class Impersonation
{
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(
            String lpszUsername,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);

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

    private static IntPtr tokenHandle = new IntPtr(0);
    private static WindowsImpersonationContext impersonatedUser;

    // If you incorporate this code into a DLL, be sure to demand that it
    // runs with FullTrust.
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public void Impersonate(string domainName, string userName, string password)
    {
        try
        {
            // Use the unmanaged LogonUser function to get the user token for
            // the specified user, domain, and password.
            const int LOGON32_PROVIDER_DEFAULT = 0;
            // Passing this parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;
            tokenHandle = IntPtr.Zero;

            // ---- Step - 1
            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(
                                userName,
                                domainName,
                                password,
                                LOGON32_LOGON_INTERACTIVE,
                                LOGON32_PROVIDER_DEFAULT,
                                ref tokenHandle);         // tokenHandle - new security token

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                Console.WriteLine("LogonUser call failed with error code : " + ret);
                throw new System.ComponentModel.Win32Exception(ret);
            }

            // ---- Step - 2
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);

            // ---- Step - 3
            impersonatedUser = newId.Impersonate();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occurred. " + ex.Message);
        }
    }

    // Stops impersonation
    public void Undo()
    {
        impersonatedUser.Undo();
        // Free the tokens.
        if (tokenHandle != IntPtr.Zero)
            CloseHandle(tokenHandle);
    }
}

Once that’s done, you’re ready to go!

Below is an example on how to use your new Impersonation Class. I have a custom Config.Utility Library that returns the 3 parameters needed for Impersonation:

1 – Domain
2 – UserName
3 – Password

Follow the template below, and have fun impersonating accounts!

        public void ProcessRequest(HttpContext context)
        {
            Impersonation imp = new Impersonation();
            imp.Impersonate(Common.Server.Utility.Config.ConfigUtility.FileStream_Domain
                            , Common.Server.Utility.Config.ConfigUtility.FileStream_UserName
                            , Common.Server.Utility.Config.ConfigUtility.FileStream_Password);

            // do processing under the impersonated Windows Account

            // finished processing.  Undo the impersonation.
            imp.Undo();
        {

Conclusion

Very handy to have available for sure.  And in most systems you probably wouldn’t need impersonation. But if your application calls for this feature, then this is one way of achieving it.

Hope this helped! :-)