Windows Impersonation using C#






4.77/5 (43 votes)
An article demonstrating how to use Windows impersonation in your C# code
Introduction
I've been a member of the CodeProject for over 3 years now, and still haven't contributed any articles - until now.
While designing a Windows Forms-based application, to administrate containers in
our Active Directory, I needed a way to allow binding to the AD using alternate
credentials. Windows impersonation was the answer. This sample app demonstrates
how to use unmanaged code by calling LogonUser()
contained within the
advapi32.dll, and pass a token handle back to your .NET application using WindowsImpersonationContext
.
One of the downfalls to the LogonUser
()
function is that the password get
passed in clear-text.
Partial Source Code
using System.Runtime.InteropServices; // DllImport
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute
...
public WindowsImpersonationContext
ImpersonateUser(string sUsername, string sDomain, string sPassword)
{
// initialize tokens
IntPtr pExistingTokenHandle = new IntPtr(0);
IntPtr pDuplicateTokenHandle = new IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
// if domain name was blank, assume local machine
if (sDomain == "")
sDomain = System.Environment.MachineName;
try
{
string sResult = null;
const int LOGON32_PROVIDER_DEFAULT = 0;
// create token
const int LOGON32_LOGON_INTERACTIVE = 2;
//const int SecurityImpersonation = 2;
// get handle to token
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref pExistingTokenHandle);
// did impersonation fail?
if (false == bImpersonated)
{
int nErrorCode = Marshal.GetLastWin32Error();
sResult = "LogonUser() failed with error code: " +
nErrorCode + "\r\n";
// show the reason why LogonUser failed
MessageBox.Show(this, sResult, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Get identity before impersonation
sResult += "Before impersonation: " +
WindowsIdentity.GetCurrent().Name + "\r\n";
bool bRetVal = DuplicateToken(pExistingTokenHandle,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (false == bRetVal)
{
int nErrorCode = Marshal.GetLastWin32Error();
// close existing handle
CloseHandle(pExistingTokenHandle);
sResult += "DuplicateToken() failed with error code: "
+ nErrorCode + "\r\n";
// show the reason why DuplicateToken failed
MessageBox.Show(this, sResult, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
else
{
// create new identity using new primary token
WindowsIdentity newId = new WindowsIdentity
(pDuplicateTokenHandle);
WindowsImpersonationContext impersonatedUser =
newId.Impersonate();
// check the identity after impersonation
sResult += "After impersonation: " +
WindowsIdentity.GetCurrent().Name + "\r\n";
MessageBox.Show(this, sResult, "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return impersonatedUser;
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if (pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
Points of Interest
This code won't work on Windows 98 or ME because they do not utilize user tokens. Code was built and run using Visual Studio.NET 2002 on Windows XP Service Pack 1.
One of the other uses for this code I've found is, for instantiating COM components that must run in an alternate security context to that of the logged-on user.
If anyone has a more secure method of achieving the same thing, please let me know.
History
- Version 1.0 - 04.25.03 - First release version