User Impersonation in Windows Forms Application

By | June 8, 2010

Impersonation in ASP.Net is easy as you can do it from the web.config and done like such

<identity impersonate="true" />

Doing this would  impersonate the IIS authenticating user on every request for every page in your ASP.NET application.


You can also impersonate a specific user like such

<identity impersonate="true" userName="username" password="password" />

That’s the easy part, but if you want it to be doing it only on specific parts of the application the its where the fun begins.

Now doing it in Windows Forms Application is similar to doing it in ASP if you want to specify a specific user to do specific tasks on your application.  Now why would you want to do that?  I guess you need to perform specific tasks on a machine with different identity context, for example you have 10 remote machines not on your domain with 20 different admin users not on Active Directory and you want to perform File Copy operation on the C$ drive, doing this manually would be a great deal of copy and pasting and logging in as a specific user.

Now here is the class to do the impersonation for a specific user which you can use on certain parts of your code.

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

public static class Impersonate
{
public static WindowsImpersonationContext oImpersonatedUser;

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string sUsername, string sDomain, string sPassword, int iLogonType, int iLogonProvider, ref IntPtr oToken);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private unsafe static extern int FormatMessage(int iFlags, ref IntPtr oSource, int iMessageId, int iLanguageId, ref String sBuffer, int iSize, IntPtr* Arguments);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CloseHandle(IntPtr oHandle);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr oExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr oDuplicateTokenHandle);

// Log On Types
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

// Log On Providers
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT35 = 1;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT50 = 3;

const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

/// <summary>
/// Performs the Impersonation
/// </summary>
/// <param name="sServer">The machine you want to log in</param>
/// <param name="sUserName">The username in that machine</param>
/// <param name="sPassword">The password for the username</param>
public static void ImpersonateNow(string sServer, string sUserName, string sPassword)
{ 
 string sIP = sServer;

 IntPtr oTokenPointer = IntPtr.Zero;
 IntPtr oDuplicateToken = IntPtr.Zero;

 bool isSuccess = LogonUser(sUserName, sIP, sPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref oTokenPointer);
 if (!isSuccess) { RaiseLastError(); }

 isSuccess = DuplicateToken(oTokenPointer, 2, ref oDuplicateToken);
 if (!isSuccess) { RaiseLastError(); }

 WindowsIdentity oNewIdentity = new WindowsIdentity(oDuplicateToken);
 oImpersonatedUser = oNewIdentity.Impersonate();
}

/// <summary>
/// Terminateds the Impersonation
/// </summary>
public static void ImpersonateEnd()
{
 oImpersonatedUser.Undo();
}

/// <summary>
/// Raises the last error on the Marshal object
/// </summary>
private static void RaiseLastError()
{
 int iErrorCode = Marshal.GetLastWin32Error();
 string sErrorMessage = GetErrorMessage(iErrorCode);

 throw new ApplicationException(sErrorMessage);
}

/// <summary>
/// Returns the readable error message
/// </summary>
/// <param name="iErrorCode">Error code thrown by the Marshall</param>
/// <returns></returns>
public unsafe static string GetErrorMessage(int iErrorCode)
{
 int iMessageSize = 255; string sMessageBuffer = "";
 int iFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

 IntPtr oSourcePointer = IntPtr.Zero;
 IntPtr oArgumentsPointer = IntPtr.Zero;

 int iReturnValue = FormatMessage(iFlags, ref oSourcePointer, iErrorCode, 0, ref sMessageBuffer, iMessageSize, &oArgumentsPointer);
 if (iReturnValue == 0) 
{
 throw new ApplicationException(string.Format("Format message failed with error code '{0}'.", iErrorCode)); 
}

 return sMessageBuffer;
}
}

And this is how you use it

Impersonate.ImpersonateNow(sServerIP, sUserName, sPassword);//Do your own stuff hereImpersonate.ImpersonateEnd();

Now one more final note since you are using Pointers and Fixed Size Buffers it means you have to use the method in an unsafe context which means by default if you debug the application you will be having an error message of “Unsafe code may only appear if compiling with /unsafe”.  Which you can change the Project Build Properties to ignore the error or allow the unsafe code.


One thought on “User Impersonation in Windows Forms Application

Leave a Reply