Search code examples
asp.netvb.netwinapiiisimpersonation

Does advapi32.dll's LogonUserA affect the whole application pool in ASP.net?


Note the following article:

How to implement impersonation in an ASP.NET application

Specifically the "Impersonate a Specific User in Code" bit.

It makes use of the advapi32.dll's LogonUserA() & DuplicateToken() methods (Windows API?).

If we use this method in a specific aspx file (function) to emulate "DOMAIN\John Smith" for a specific task - can we say for certainty that only that single aspx request impersonates John, or does the whole application pool run as John Smith until undoImpersonation() is called at the completion of said function?

Code in question (in case above link goes dark):

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("username", "domain", "password") Then
        '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
    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
    If Not tokenDuplicate.Equals(IntPtr.Zero) Then
        CloseHandle(tokenDuplicate)
    End If
    If Not token.Equals(IntPtr.Zero) Then
        CloseHandle(token)
    End If
End Function

Private Sub undoImpersonation()
    impersonationContext.Undo()
End Sub

Solution

  • The impersonation is Thread specific, not for the whole application. I modified your page_load to include a simple logic that demonstrate that fact.

    Demonstration

    The original user (app pool) and Impersonated user will both write in the debug console at different interval. Notice that the impersonated user is running from another thread and will output its username once per second while the app pool user (main thread) output remains on the main thread and output its name each 100 ms.

    Public Sub Page_Load(ByVal s As Object, ByVal e As EventArgs)
        Dim Task As New System.Threading.Tasks.Task(Sub()
                                                        If impersonateValidUser("Username", "Domain", "Password") Then
    
                                                            Dim Watch As New Diagnostics.Stopwatch()
                                                            Watch.Start()
    
                                                            While Watch.ElapsedMilliseconds < 10000
                                                                System.Threading.Thread.Sleep(1000)
                                                                Diagnostics.Debug.WriteLine(WindowsIdentity.GetCurrent.Name)
                                                            End While
    
                                                            '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)
    
        Task.Start()
        While Not Task.IsCompleted
            System.Threading.Thread.Sleep(100)
            Diagnostics.Debug.WriteLine("--" & WindowsIdentity.GetCurrent.Name)
        End While
    
    End Sub