Recently, I had to develop a web application that had to grab pictures off a network share, manipulate them, and show them on a page. The users of this application would log in automatically with their Windows user credentials and based on the groups they were a member of, they would or would not see certain content.
To do this, I had to use the 'Integrated Windows Authentication' option in IIS and add <identity impersonate="true"/> to the Web.config.
The thing I noticed is that even though the logged on user had the required rights to access the network share, I would be getting 'Access denied' or 'Path not found' error messages.
After some research I found out that, using impersonation like this, the IIS worker process would get a so called 'impersonation token' of the logged on user, while the token needed to access network resources has to be a 'primary token'. One workaround is to add credentials to the impersonation tag, like <identity impersonation="true" userName="DOMAIN\User" password="Pass"/>. This would cause the IIS worker process to run all code in the security context of 'User' using a primary token.
This workaround was useless for my application because I needed to run most code in the context of the logged on user. What I needed was to get a primary token of a user that has access to the network share, and run pieces of code under the security context of this user.
To do this I wrote a little function to get a WindowsIndentity containing a primary token;
Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
Dim LOGON32_PROVIDER_DEFAULT As Integer = 0
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 Function GetFixedIdentity(ByVal Domain As String, _
ByVal UserName As String, _
ByVal Password As String) As WindowsIdentity
Dim tempIdentity As WindowsIdentity = Nothing
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
If RevertToSelf() Then
If LogonUserA(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, _
LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempIdentity = New WindowsIdentity(tokenDuplicate)
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
Return tempIdentity
End Function
Now, this piece of unmanaged code could be a performance hit if it is used often, because it will validate the user credentials against the domain controller every single time. I figured I only get this WindowsIdentity on application startup and keep it in the Application Class, so I added this in Global.asax.vb;
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Application("FixedIdentity") = GetFixedIdentity("DOMAIN", "user", "pass")
End Sub
So whenever I needed access to a network share, I would run that piece of code in the ImpersonationContext of the WindowsIdentity I kept in the Application Class, like this;
Dim ImpersonationContext As WindowsImpersonationContext = Nothing
Try
If Not Application("FixedIdentity") Is Nothing Then _
ImpersonationContext = CType(Application("FixedIdentity"), _
WindowsIdentity).Impersonate
'This code is executed by the Fixed Identity
For Each File In Directory.GetFiles("\\SERVER02\D$\Images")
Debug.WriteLine(File)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
Finally
' This always has to be executed, so put it in Finally. Otherwise
' you will keep executing code as the Fixed Identity.
If Not ImpersonationContext Is Nothing Then ImpersonationContext.Undo()
End Try
Now you will be able to access the share, and be happy! :D