WindowsImpersonator

.NET Framework versions 1.x & 2.0 lack the ability to provide full, managed impersonation facilities to developers. The 'Run As' function in Windows operating systems is not directly exposed in .NET Framework, so you have to revert to Win32 API calls (unmanaged code).

Points of interest in sample code are:

  • Login as the desired user (you should provide username, password and domain)
  • Duplicate your current security token (the original token is provided by the login process)
  • Use the duplicate token to create a new windows identity and get the impersonation context
  • Execute the code as the impersonated user (a callback function is necessary to be pased as a parameter)
  • Release the impersonation context
  • Release any open handles / tokens

Sample Code

Public Class WindowsImpersonator

    Public Delegate Function DelegateImpersonateExecuteFunction( _

                ByVal args() As Object, _

                ByRef ReturnValue As Object, _

                ByRef ExceptionMessage As String) As Integer

 

#Region " Win32 API Declarations"

    Private Declare Auto Function LogonUser 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 Boolean

 

    Public Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean

 

 

    Public Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _

                ByVal ExistingTokenHandle As IntPtr, _

                ByVal SECURITY_IMPERSONATION_LEVEL As Integer, _

                ByRef DuplicateTokenHandle As IntPtr) As Boolean

 

#End Region

 

    ' If you incorporate this code into a DLL, be sure to demand FullTrust.

    ' _

    Public Shared Function Impersonate(ByVal UserName As String, _

                        ByVal Password As String, _

                        ByVal Domain As String, _

                        ByVal ExecuteFunction As DelegateImpersonateExecuteFunction, _

                        ByVal FunctionArguments() As Object, _

                        ByRef FunctionReturnValue As Object, _

                        ByRef ExceptionMessage As String) As Integer

 

        Dim TokenHandle As New IntPtr(0)

        Dim DupTokenHandle As New IntPtr(0)

        Dim ret As Integer

 

        Try

            Const LOGON32_PROVIDER_DEFAULT As Integer = 0

            'This parameter causes LogonUser to create a primary token.

            Const LOGON32_LOGON_INTERACTIVE As Integer = 2

            Const SecurityImpersonation As Integer = 2

 

            TokenHandle = IntPtr.Zero

            DupTokenHandle = IntPtr.Zero

 

            'Call LogonUser to obtain a handle to an access token.

            'Windows 2000 raises error 1314 : 'A required privilege is not held by the client'

            If Not LogonUser(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, TokenHandle) Then

                Dim ret1 As Integer = System.Runtime.InteropServices.Marshal.GetLastWin32Error()

                Throw New Exception(String.Format("{0}Error: [{1}]{0}{2}", Environment.NewLine, ret1, New System.ComponentModel.Win32Exception(ret1).Message))

            End If

 

            Debug.WriteLine(("Value of Windows NT token: " & TokenHandle.ToString()))

            Debug.WriteLine(("Before impersonation: " & System.Security.Principal.WindowsIdentity.GetCurrent().Name))

 

            If Not DuplicateToken(TokenHandle, SecurityImpersonation, DupTokenHandle) Then

                CloseHandle(TokenHandle)

                Throw New Exception("Error in trying to duplicate token.")

            End If

 

            ' The token that is passed to the following constructor must

            ' be a primary token in order to use it for impersonation.

            Dim NewId As New System.Security.Principal.WindowsIdentity(DupTokenHandle)

            Dim ImpersonatedUser As System.Security.Principal.WindowsImpersonationContext = NewId.Impersonate()

 

            Debug.WriteLine(("After impersonation: " & System.Security.Principal.WindowsIdentity.GetCurrent().Name))

 

            If Not ExecuteFunction Is Nothing Then

                If ExecuteFunction(FunctionArguments, FunctionReturnValue, ExceptionMessage) = -1 Then

                    ret = -1

                End If

            End If

 

            ' Stop impersonating the user.

            ImpersonatedUser.Undo()

 

            Debug.WriteLine(("After Undo: " & System.Security.Principal.WindowsIdentity.GetCurrent().Name))

 

        Catch ex As Exception

            ret = -1

            ExceptionMessage = ex.ToString

        Finally

            ' Free the tokens.

            If Not System.IntPtr.op_Equality(TokenHandle, IntPtr.Zero) Then

                CloseHandle(TokenHandle)

            End If

            If Not System.IntPtr.op_Equality(DupTokenHandle, IntPtr.Zero) Then

                CloseHandle(DupTokenHandle)

            End If

        End Try

 

        Return ret

    End Function

End Class

Note: Due to changes in local policy settings, the above solution doesn't work in Windows 2000 Pro & Windows 2000 Server (it's not permitted to call LogonUser).

Note 2: In case you are trying to accesss a resource in another machine, the username used must exist in both machines (the one executing code and the target machine) and furthermore the passwords are identical.

Downloads

 TitleOwnerCategoryModified DateSize 
WindowsImpersonator.vbSuperUser AccountVS 2003, .NET FW 1.112/27/20062.90 KBDownload