![]() |
Show Changes |
![]() |
Edit |
![]() |
|
![]() |
Recent Changes |
![]() |
Subscriptions |
![]() |
Lost and Found |
![]() |
Find References |
![]() |
Rename |
| Search |
History
| 4/4/2005 9:50:09 PM |
![]() |
List all versions |
If you have a token for a user, it will be represented in the .NET Framework as a WindowsPrincipal (WhatIsWindowsIdentityAndWindowsPrincipal). You can impersonate that user by telling Windows to attach the token to your thread (WhatIsImpersonation). Here's an example from an ASP.NET Web application.
<%@page language='c#'%>
<%@import namespace='System.Security.Principal'%>
<script runat='server'>
void Page_Load(object sender, EventArgs args) {
IPrincipal p = this.User;
WindowsIdentity id = (WindowsIdentity)p.Identity;
Response.Output.Write("<h2>Process running as {0}</h2>",
WindowsIdentity.GetCurrent().Name);
// impersonate temporarily
WindowsImpersonationContext wic = id.Impersonate();
try {
// do some work while impersonating the client
Response.Output.Write("<h2>Now impersonating {0}</h2>",
WindowsIdentity.GetCurrent().Name);
}
finally {
// restore our old security context
wic.Undo();
}
Response.Output.Write("<h2>Once again running as {0}</h2>",
WindowsIdentity.GetCurrent().Name);
}
</script>
To make this page work, drop it in a virtual directory that uses integrated Windows authentication and use the following web.config file to force authentication:
<configuration>
<system.web>
<authentication mode='Windows'/>
<authorization>
<deny users='?'/>
</authorization>
</system.web>
</configuration>
Let's analyze what's going on in the page's Load event. First of all, I ask ASP.NET for the client's token, represented by an IPrincipal. I then ask the principal object for its corresponding identity, which I need to impersonate the user. Note that this code assumes I've got a Windows token for the user because I'm casting to WindowsIdentity and this cast will throw an exception at runtime if I end up with some other type of identity (such as the GenericIdentity used by Forms Authentication). So this code is making some assumptions about its environment. It assumes that the Web server has been configured to allow Windows authentication to occur, typically using Kerberos (WhatIsKerberos).
Before impersonating, I print out the current security context (WhatIsSecurityContext) and, since I'm not impersonating yet, this should be the process identity (Network Service by default on Windows Server 2003 or ASPNET on older platforms). Next I call the WindowsIdentity.Impersonate method to ask the operating system to put the client's token (held inside the WindowsIdentity object) on my current thread. This method returns a WindowsImpersonationContext that allows me to Undo the impersonation later. I then enter a try block. This is critical! Changing security contexts is a dangerous business, and I need to ensure that my code doesn't accidentally leave the function without reverting back to my normal security context. In the corresponding finally block, I Undo the impersonation using the only interesting method on WindowsImpersonationContext. The output from the Web page looks like this on my Windows XP box:
Process running as XYZZY\ASPNET Now impersonating XYZZY\Keith Once again running as XYZZY\ASPNET
The reason the .NET Framework uses this class to revert impersonation is so that it can preserve the previous security context for the thread. For example, look at the following code snippet.
void foo(WindowsIdentity alice, WindowsIdentity bob) {
WindowsImpersonationContext wic = alice.Impersonate();
try {
Console.WriteLine(WindowsIdentity.GetCurrent().Name);
bar(bob);
Console.WriteLine(WindowsIdentity.GetCurrent().Name);
}
finally {
wic.Undo();
}
}
void bar(WindowsIdentity bob) {
WindowsImpersonationContext wic = bob.Impersonate();
try {
Console.WriteLine(WindowsIdentity.GetCurrent().Name);
}
finally {
wic.Undo();
}
}
This program should print out something like the following (assuming Alice and Bob have accounts in a domain called QUUX).
QUUX\Alice QUUX\Bob QUUX\Alice
But let's get real. You’ll rarely want to write programs that use nested impersonation like this. Trust me. Impersonation is tricky (as I tried to point out in WhatIsImpersonation), and you should limit your use of it: Keep it as simple as possible wherever you do decide to use it.
The example used ASP.NET, but if you were implementing some other type of server that used Windows authentication, the procedure for impersonating would be the same. Get the WindowsIdentity for the user, and call the Impersonate method, being very careful to Undo when you're done. See HowToCreateAWindowsPrincipalGivenAToken if you've got a raw token instead of a WindowsPrincipal; it's easy to convert back and forth between the two.
ASP.NET provides a configuration option that causes all threads servicing Web requests in an application to impersonate by default. Here's the first way this can be done:
<configuration>
<system.web>
...
<identity impersonate='true'/>
</system.web>
</configuration>
What this says is that the ASP.NET thread calling into your application should impersonate the same account that IIS normally impersonates in a classic ASP or ISAPI application: the IUSR_MACHINE account for anonymous requests or the client’s account for authenticated requests.
A second way to use this element is to impersonate a fixed account, which can be useful if you're stuck using IIS 5, where all ASP.NET applications are forced to share a single worker process (IIS 6 has a much more robust process model). Here's how to impersonate a fixed identity.
<configuration>
<system.web>
...
<identity impersonate='true' userName='...' password='...'/>
</system.web>
</configuration>
You should use the aspnet_setreg tool to set the user name and password attributes, as I describe in HowToStoreSecretsOnAMachine.
Regardless of which mechanism you choose, you're very likely going to eventually find the need to stop impersonating temporarily. For example, if there's a file that your ASPX page needs to read regardless of who your client is, you shouldn’t be impersonating when you open it. This is one particular place where WindowsImpersonationContext.Undo can come in really handy (see Figure 32.1).
WindowsImpersonationContext wic =
WindowsIdentity.Impersonate(IntPtr.Zero); // revert to self
try {
// do some stuff while not impersonating
}
finally {
wic.Undo(); // resume impersonating
}
//note i am not sure whether this security context is applied to the whole process or just the thread
//i think its the whole process
//someone may want to try Thread.CurrentPrincipal = New WindowsPrincipal( myWindowsIdentity )
//just something to think about
Figure 32.1 Temporarily reverting impersonation
Keith's first book-in-a-wiki. If you would like to read the book online or order a physical copy to throw at annoying coworkers, surf to the HomePage. Please note that due to overwhelming wikispam, this particular wiki is no longer editable.
About FlexWiki.
Recent Topics