Programmatically Impersonate a user in ASP.NET
When you run an ASP.NET application, it accesses files and resources on the web
server using a specified user account. At times, you may want ASP.NET to access
resources as though it were someone, or something, else. Impersonation
is when ASP.NET executes with the security credentials of a different user
account. This may be necessary, for instance, to access a file in a protected
directory.
The account ASP.NET uses to access resources varies depending on the version of
Windows and IIS running. You can poke around the web for specifics but, in
general, the following discussion is what happens. In a web application you
have several layers of security. IIS, Windows and the .NET framework all play
roles.
Windows forces all access to resources such as web files to be accessed through
an existing user account. For HTML and ASP pages and components of ASP V3.0 and
earlier, the IUSR_machinename and
IWAM_machine name accounts were automatically created and added to
every web folder by IIS and were used as the account to access these pages.
These are termed anonymous accounts because every
request went through them and looked the same regardless of the user.
In Windows 2003 and with IIS V6, these anonymous accounts still exist but,
depending on IIS's configuration, the account used for anonymous access may be
different. If you tell IIS to use anonymous access it defaults to using the
IUSR_machinename account. However, you can specify a different account to use
when setting up the web site in IIS.
If you tell IIS not to allow anonymous access, you can instruct it to use Windows
Authentication. In this case, the credentials of the user making the
request are used to access resources. IIS will authenticate the user to verify
they are a valid user. If so the request then goes to Windows.
Once Windows gets the request for a resource, it verifies the anonymous account
or user account has sufficient access to it. For instance, the resource can be
protected by a global group to which the account is not part of. If not, access
is denied. Otherwise, the request is passed to ASP.NET.
The first thing ASP.NET does when it gets the request is look to see if
impersonation is used. ASP.NET uses impersonation similar to how IIS uses
anonymous accounts. If impersonation is not used, .NET uses a special account
to process the request. Under IIS 5 on Windows 2000, NT or XP, ASP.NET uses the
ASPNET account. When it spawns a worker process, the process uses
the LOCAL SYSTEM account. .NET uses these accounts
because of its dynamic compilation features. The IUSR and IWAM IIS accounts do
not have write access to folders required to dynamically compile ASP.NET
applications.
The ASPNET account is really a monikor. The real account name and
password used are specified in the <processModel>
section of the Machine.config file. Typically,
they are Machine and AutoGenerate respectively. Confused? I don't
blame blame you. It gets worse.
Under IIS 6, ASP.NET doesn't use the process model settings since IIS 6.0 uses
application pools. Instead, it uses a NETWORK
SERVICE account which is part of the IIS_WPG
group.
When creating an ASP.NET application, you must make sure the ASPNET and/or
NETWORK SERVICE accounts and/or IIS_WPG group have suficient authority to the
application specific folders and files that are used.
Back to impersonation. If impersonation is not used, ASP.NET uses the ASPNET
account or the IIS_WPG group regardless of the anonynous access settings. If
impersonation is used, along with anynomous access, then .NET uses theses
credentials as well.
If impersonation is used but anonymous access is not, .NET uses requesting
client user's credentials if Windows Authentication is specified. That is,
unless you tell it to use a different user ID and password!
You can use impersonation adding this line to the web.config file:
<system.web>
. . . .
<identity impersonate="true" />
. . . .
</system.web>
To impersonate a specific user:
<system.web>
. . . .
<identity impersonate="true" userName="username" password="password" />
. . . .
</system.web>
This will impersonate a different user for the entire duration of your
application's execution. You can programmatically turn impersonation on and off
using the following VB.NET code.
Imports System.Threading
Imports System.Configuration
Imports SR = System.Reflection
Imports System.Text
Imports System.Web.UI.WebControls
Imports System.Security.Principal
Public Class Utilities
Private Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Private Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Private Const LOGON32_LOGON_NETWORK As Integer = 3
Private Const LOGON32_LOGON_BATCH As Integer = 4
Private Const LOGON32_LOGON_SERVICE As Integer = 5
Private Const LOGON32_LOGON_UNLOCK As Integer = 7
Private Const LOGON32_LOGON_NETWORK_CLEARTEXT As Integer = 8
Private Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9
Private Shared 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
' NOTE:
' The identity of the process that impersonates a specific user on a thread must have
' "Act as part of the operating system" privilege. If the the Aspnet_wp.exe process runs
' under a the ASPNET account, this account does not have the required privileges to
' impersonate a specific user. This information applies only to the .NET Framework 1.0.
' This privilege is not required for the .NET Framework 1.1.
'
' Sample call:
'
' If impersonateValidUser("username", "domain", "password") Then
' 'Insert your code here.
'
' undoImpersonation()
' Else
' 'Impersonation failed. Include a fail-safe mechanism here.
' End If
'
Public Shared Function ImpersonateValidUser(ByVal strUserName As String, _
ByVal strDomain As String, ByVal strPassword As String) As Boolean
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Dim tempWindowsIdentity As WindowsIdentity
ImpersonateValidUser = False
If RevertToSelf() <> 0 Then
If LogonUserA(strUserName, strDomain, _
strPassword, _
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
If Not tokenDuplicate.Equals(IntPtr.Zero) Then
CloseHandle(tokenDuplicate)
End If
If Not token.Equals(IntPtr.Zero) Then
CloseHandle(token)
End If
End Function
Public Shared Sub UndoImpersonation()
ImpersonationContext.Undo()
End Sub
End CLass
|