Welcome to MSDN Blogs Sign in | Join | Help

How to Impersonate

Guillermo recently started blogging about some Whidbey enhancements around impersonation.  However, figuring out how to impersonate in the first place can be a little less than obvious.

WindowsIdentity contains an Impersonate method, but it doesn't accept any parameters.  That means that we'll need to supply the user and password through some other means.  The constructor for WindowsIdentity that takes a string looks promising, however there doesn't seem to be any way to get a password in.  Instead we'll have to use the constructor that takes a token (as an IntPtr).  If we intend to go that route, then we might as well just use the static WindowsIdentity.Impersonate overload that takes a token directly.

OK, so how do we get that token?  Well, we can P/Invoke to LogonUserLogonUser takes a user name, domain, and password, and returns us a token that we can use with WindowsIdentity.  In addition to these parameters, LogonUser wants to know which logon provider to use (the default should suffice for most cases), and a logon type  Selecting a logon type can be a tricky, so lets take a look at our options.

Logon Type
(LOGON32_LOGON_xxx)

Integer Value
(From WinBase.h)
Associated Rights
(From NTSecAPI.h)
Description
BATCH 4 SeBatchLogonRight /
SeDenyBatchLogonRight
Perform a batch logon.  This is intended for servers where logon performance is vital.  LogonUser will not cache credentials for a user logged in with this type.
INTERACTIVE 2 SeInteractiveLogonRight /
SeDenyInteractiveLogonRight
Log a user into the computer if they will be interactively using the machine (for instance if your code is going to provide them with a shell).  LogonUser will cache credentials for a user logged in with this type.
NETWORK 3 SeBatchNetworkRight /
SeDenyNetworkLogonRight
Similar to batch logon in that this logon type is intended for servers where logon performance is important.  Credentials will not be cached, and the token returned is not a primary token but an impersonation token instead.  It can be converted to a primary token with DuplicateHandle.
NETWORK_CLEARTEXT 8 SeBatchNetworkRight /
SeDenyNetworkLogonRight
Similar to a network logon, except that the credentials are stored with the authentication package that validates the user.  This logon type is only available with the WINNT50 logon provider, and on Windows 2000 or higher.
NEW_CREDENTIALS 9 SeBatchNetworkRight /
SeDenyNetworkLogonRight
Create a new set of credentials for network connections.  (For instance runas /netonly).  This logon type is only available with the WINNT50 logon provider and on Windows 2000 or higher.
SERVICE 5 SeServiceLogonRight /
SeDenyServiceLogonRight
Logon as a service.
UNLOCK 7 Reserved for use by GINA implementations.

One important thing to note is that in order to use WindowsIdentity.Impersonate(), we need a primary token, so if we go with LOGON32_LOGON_NETWORK, we'll need to use DuplicateHandle in order to get at the primary token.  Since I like to avoid extra work like that, it looks like LOGON32_LOGON_BATCH or LOGON32_LOGON_INTERACTIVE would both be more appropriate choices.  Selecting between them will depend on the rights that the account we're trying to logon has.

Once your call to LogonUser has gotten you a user token for the user you'd like to impersonate, you can then call WindowsIdentity.Impersonate() and have your thread take over the identity of the Windows user you just logged on.  The code might look something like this:

// Call LogonUser to get a token for the user
IntPtr userHandle = IntPtr.Zero();
bool loggedOn = LogonUser(
    user,
    domain,
    password,
    LogonType.Interactive,
    LogonProvider.Default,
    out userHandle);
if(!loggedOn)
    throw new Win32Exception(Marshal.GetLastWin32Error());

// Begin impersonating the user
WindowsImpersonationContext impersonationContext = WindowsIdentity.Impersonate(userHandle.Token);

DoSomeWorkWhileImpersonating();

// Clean up
CloseHandle(userHandle);
impersonationContext.Undo();

However this code has a very subtle security issue that's just waiting to bite you.  Before I fix the problem tomorrow,  any guesses as to what might be wrong with the code?

Update 4:35 PM: Corrected an issue that wasn't the bug I'm referring to above.

Published Monday, March 21, 2005 4:06 PM by shawnfa
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: How to Impersonate

Monday, March 21, 2005 4:30 PM by laimis
The first thing that comes to my mind is that you don't restore the process to its previous identity. First save the existing security context, impersonate, do priviledged work, restore back the security context to the original one.

# re: How to Impersonate

Monday, March 21, 2005 4:41 PM by Shawn
Shoot -- you're right, but that wasn't the bug I was looking for :-) I've just updated to code to correct the issue.

You're on the right track though. Any other guesses?

-Shawn

# re: How to Impersonate

Monday, March 21, 2005 5:07 PM by Dean Harding
Well, if the username/password supplied is more privileged than you are, and an exception occurs in DoSomeWorkWhileImpersonating, then you don't restore the original context either.

Also, when you impersonate, the impersonated user's registry hive isn't loaded (i.e. HKEY_CURRENT_USER still points to the impersonating user's hive, not the impersonatee) so that might be a problem if you try to access the registry.

# re: How to Impersonate

Monday, March 21, 2005 5:19 PM by Eric Lippert
Ah, this is one of my favourites. I wrote about this issue in my blog a few months ago:

http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx

If an exception is thrown in DoSomeWorkWhileImpersonating then the caller up the stack -- which might be less-privileged hostile code -- gets to handle the exception. The caller then gets its privileges elevated.

The obvious thing to do is use a finally block to ensure that the cleanup happens, but actually that is not sufficient either. The hostile caller could register an exception filter which runs BEFORE the finally block.

You've got to be SUPER CAREFUL when impersonating in managed code if your caller can be partially trusted.

# re: How to Impersonate

Monday, March 21, 2005 6:48 PM by Steve Dinn
Something like this is begging for a little utility object that implements IDisposable so that you can use it with a <i>using</i> block. That way you <b>can't</b> forget to set things back.

# Safely Impersonating Another User

Tuesday, March 22, 2005 9:51 PM by .Net Security Blog

# Safely Impersonating Another User

Wednesday, March 23, 2005 10:28 AM by .Net Security Blog

# Safe Impersonation With Whidbey

Thursday, March 24, 2005 5:14 PM by .Net Security Blog

# Safe Impersonation With Whidbey

Thursday, March 24, 2005 5:16 PM by .Net Security Blog

# re: How to Impersonate

Monday, April 04, 2005 10:32 PM by kevin s
can someone confirm that the impersonation context is on a per thread basis and not a per process basis. thx.

# re: How to Impersonate

Tuesday, April 05, 2005 12:01 PM by shawnfa
Yes, impersonation is per-thread.

-Shawn

# re: How to Impersonate

Monday, August 07, 2006 5:27 AM by Ali
hi
i do impersonation according to Microsoft Recommend .
i use impersonation to access a file that it's
Folder have Acls permission for one user.
the impersonation is ok and impersonate anonymous to user that have permission to access the file ,but again a popoup appear
to enter username and password eo view file.

my code is :

Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
Dim LOGON32_PROVIDER_DEFAULT As Integer = 0

Dim impersonationContext As WindowsImpersonationContext

Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _
ByVal lpszDomain As String, _
ByVal lpszPassword As String, _
ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, _
ByRef phToken As IntPtr) As Integer

Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
ByVal ExistingTokenHandle As IntPtr, _
ByVal ImpersonationLevel As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As Integer

Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long


Public Sub Page_Load(ByVal s As Object, ByVal e As EventArgs)
If impersonateValidUser("Doroudian", "ws9", "123") Then
response.write("ok")
image1.imageurl="http://ws9/Docs/36.jpg"

'Insert your code that runs under the security context of a specific user here.
'undoImpersonation()
Else
'Your impersonation failed. Therefore, include a fail-safe mechanism here.
End If
End Sub

Private Function impersonateValidUser(ByVal userName As String, _
ByVal domain As String, ByVal password As String) As Boolean

Dim tempWindowsIdentity As WindowsIdentity
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Dim princs As GenericPrincipal
Dim rAdmin(0) As String
impersonateValidUser = False

If RevertToSelf() Then
If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
impersonationContext = tempWindowsIdentity.Impersonate()

If Not impersonationContext Is Nothing Then
impersonateValidUser = True
End If
End If
End If
End If
End Function
Many thanks for any guides

Regard
Ali

# re: How to Impersonate

Sunday, October 08, 2006 1:50 PM by Klaus
(I am a newbie in VB 2005) I tried the code from Ali, however I cannot get it work. I did run it with ClientPC1\LocalUser and tried to stop a service by WMI on RemotePC1 with RemotePC1\User1. It always result in Login failed. What is wrong? I have too less knowledge to find the error. Thanks in advance Klaus

# re: How to Impersonate

Tuesday, October 31, 2006 11:31 PM by Collin Roth

So, can you tell me which friendly security policy name is associated with SeBatchNetworkRight?

# re: How to Impersonate

Monday, November 06, 2006 2:28 PM by Joel

How does this work across active directory, or does it?

I have a user in active directory that I need to impersonate in Sharepoint, but cannot seem to use LogonUser for this.

# tips on how to blog &raquo; How To Blog -

Wednesday, May 23, 2007 4:30 AM by tips on how to blog » How To Blog -

# Saving and Retrieving File Using FileStream SQL Server 2008 | Agha Usman Ahmed

# Saving and Retrieving File Using FileStream SQL Server 2008

Sunday, March 15, 2009 4:08 PM by Agha Usman

FileStream data type is a very important feature of SQL Server 2008 and gradually getting popular amongst

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker