|
Impersonate User Account To Access Network Resources
In this age and time of development there is hardly any application that does
not access resources across network machines. A very simple example could be
that an application needs to read a file that exists on another machine. When
doing file I/O operations, all the operations have to go through operating
system security checks. Meaning unless the user has proper authorization to
read or write a file, the operating system will deny the access to it and you
will get very familiar error code 5. Microsoft .Net Framework
runs on top of Windows Operating System and for that matter any
operating system that can host Microsoft .Net Frameowrk. Therefore
when an application wants to access a resource, the call goes through Win32
layer.
What will happen if the application is running under the security context of a
user who does not have authorization to access the resource on network machine?
The answer is very simple. When network machine checks the authorization for
that user and finds that he does not have rights for the I/O operation and will
deny access to it. So how do we solve this problem so that user can access the
resource on network machine. There are two options. Either we add that user's
account in the DACL of that object or that user impersonates as someone who is
authorized to access that resource.
In this article we will show how you can impersonate a user account to create a
folder on a network machine. System.Seurity.Principal namespace
provides a class WindowsIdentity that does precisely what we
intend to do. It has a method Impersonate that you can call to
impersonate as any authenticated user. The only catch with this method if that
it requires access token of the user who you want to impersonate as. Framework
does not provide any means of getting security access token of a user other
than the one who is already authenticated. This is where Interop/PInvoke
comes to our rescue.
You can call LogonUser Win32 API to authenticate user credentials.
If the user's credentials (login, password and domain tupple) are verified,
this call returns the access token for the user. If the user could not be
verified, this call fails and you can call GetLastError to find
the reason for failure. For invalid crdentials, the error code would be 1326.
For more information on Win32 error codes you can look at list of Win32 error
codes.
WindowsIdentity newId = new WindowsIdentity(accessTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
After you have authenticated the user, the returned access token can be used to
call Impersonate method of WindowsIdentity class.
After this call, the execution thread will run under the context of
impersonated user. After you are done with the process, you should always call
Undo method on WindowsImpersonationContext object. This
method will revert the thread execution to the security context that it was
running under before Impersonate method was called.
There is a very detailed sample in WindowsIdentity class in Microsoft
.Net Framework Documentation that shows how you can use PInvoke
to use LogonUser API and other Win32 APIs. Therefore we would not
spend time showing you that same example here. We have wrapped that
implementation in NetworkSecurity class in the sample for this
article. But there are some very important points that we would like to discuss
about these Win32 APIs and network resource access.
-
LogonUser API requires SE_TCB_NAME previlege enabled for
the process. What this previlege means is that the process should be part of
the Trusted Computing Block. In simple words what this means is that the
process should have unrestricted access on the system. Operating system does
not give this previlege to any process other than processes running as drivers
and services. You can enable this previlege for any process from Local System
Policy console of your system. We do not recommend this approach
because this opens up a huge security hole in your application by opening up
access to any malicious code that may get executed under your application's
context. For Windows XP and higher operating systems, LogonUser
API does not require this previlege enabled any more. The API takes care of it
for you.
-
If you are unsure if
Impersonate method actually did its job or
not, enable auditing of Logon/Logoff events from security policy. Now when you
call Impersonate method, there will be an entry for Logon/Logoff
event in the Security Log in event viewer whiich will show the
impersonated user's login name. And if you have enabled auditing for file I/O
on remote machine, every file access by your application will have an entry
under the impersonated user's account. If you don't see the impersonated user's
login in event log that would mean that impersonation did not succeed.
-
LogonUser API returns impersonation token when LOGON32_LOGON_NETWORK
option is specified in the call. What this means is that this token can only be
used for threads and can not be attached to processes. To get primary token
from impersonation token you should call DuplicateToken API. When
you call DuplicateToken API, you can modiy the type of
impersonation level for the token. Meaning if you don't want the network
machine to use the credentails to make any other network call for that user,
you shoud always set the impersonation level to SecurityImpersonation.
-
To access a folder on a remote machine it is important that a share for that
folder exists on the remote machine. If there is no user share then atleast an Admin
Share (with $ sign at the end) should exists for that folder. Otherwise
all your network operations on that folder will end up with "Folder XXXX does
not exist or not found" error.
We have created a small ASP.Net application using Visual Studio .Net 2003. This
application creates a new folder on a network machine after impersonating a
user account who has the right kind of access rights on that folder. This
sample has been tested on Windows XP and Windows 2003 Web Server.
|