SOLVED: VB.NET Impersonation Code Problem

Apathetic

Platinum Member
Dec 23, 2002
2,587
6
81
I'm getting a very strange InvalidOperationException whenever I try to refer to Environment.MachineName after I do an impersonation. Environment.UserName correctly reports the new user name and everything else seems to be functioning correctly. Does anyone have any ideas?

Here's my stripped down impersonation class (VB.NET 2.0 :()


Code:
Option Explicit On
Option Strict On

Imports System.Security.Principal
Imports System.Runtime.InteropServices

Public Class Impersonation

    'These constants are based on values in the Win32 API file WINBASE.H
    'Refer to http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx for their meanings
    Const LOGON32_LOGON_INTERACTIVE As Integer = 2
    Const LOGON32_PROVIDER_DEFAULT As Integer = 0

    Private 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

    Private Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
                            ByVal ExistingTokenHandle As IntPtr, _
                            ByVal ImpersonationLevel As SECURITY_IMPERSONATION_LEVEL, _
                            ByRef DuplicateTokenHandle As IntPtr) As Integer

    Private Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long

    Private Enum SECURITY_IMPERSONATION_LEVEL As Integer
        SecurityAnonymous = 0
        SecurityIdentification = 1
        SecurityImpersonation = 2
        SecurityDelegation = 3
    End Enum

    Private m_oImpersonationContext As WindowsImpersonationContext
    Private m_bImpersonationActive As Boolean

    Public Sub New()

    End Sub

    Public ReadOnly Property ImpersonationActive() As Boolean
        Get
            Return m_bImpersonationActive
        End Get
    End Property

    Public Function StartImpersonation(ByVal sUserName As String, ByVal sDomain As String, ByVal sPassword As String) As Boolean
        Dim bResults As Boolean = False
        Dim sErrorMessage As String
        Dim oWindowsIdentity As WindowsIdentity
        Dim hPrimaryToken As IntPtr = IntPtr.Zero           'a Win32 handle to our authentication token
        Dim hImpersonationToken As IntPtr = IntPtr.Zero     'a Win32 handle to our impersonation token

        If String.IsNullOrEmpty(sUserName) Then
            Throw New ArgumentException("UserName may not be MULL or String.Empty")
        End If

        'If no domain is given, assume the account is a local one
        If sDomain = String.Empty Then
            sDomain = Environment.MachineName
        End If

        Try
            'Validate the provided userid, password and domain.
            If LogonUserA(sUserName, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hPrimaryToken) <> 0 Then

                'Convert our token to one whos handle has TOKEN_IMPERSONATE set
                If DuplicateToken(hPrimaryToken, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, hImpersonationToken) <> 0 Then

                    'Create a new identity object based on our impersonation token
                    oWindowsIdentity = New WindowsIdentity(hImpersonationToken)

                    'Switch to our new identity
                    m_oImpersonationContext = oWindowsIdentity.Impersonate()
                    If m_oImpersonationContext IsNot Nothing Then
                        m_bImpersonationActive = True
                        bResults = True
                    End If
                Else
                    sErrorMessage = String.Format("DuplicateToken failed (rc={0})", Runtime.InteropServices.Marshal.GetLastWin32Error)
                    Throw New Security.Authentication.AuthenticationException(sErrorMessage)
                End If
            Else
                sErrorMessage = String.Format("LogonUser failed (rc={0})", Runtime.InteropServices.Marshal.GetLastWin32Error)
                Throw New Security.Authentication.AuthenticationException(sErrorMessage)
            End If

        Finally
            If Not hImpersonationToken.Equals(IntPtr.Zero) Then
                CloseHandle(hImpersonationToken)
                hImpersonationToken = IntPtr.Zero
            End If
            If Not hPrimaryToken.Equals(IntPtr.Zero) Then
                CloseHandle(hPrimaryToken)
                hPrimaryToken = IntPtr.Zero
            End If
        End Try

        Return bResults
    End Function

    Public Sub EndImpersonation()
        If m_oImpersonationContext IsNot Nothing AndAlso m_bImpersonationActive Then
            m_oImpersonationContext.Undo()
            m_oImpersonationContext.Dispose()
            m_oImpersonationContext = Nothing
            m_bImpersonationActive = False
        End If
    End Sub
End Class

Here's a sample tester:
Code:
        Dim oImp1 As Impersonation
        Dim oImp2 As Impersonation

        MessageBox.Show("User " & Environment.UserName, "", MessageBoxButtons.OK)

        oImp1 = New Impersonation("some_domain_user", "some_domain", "some_pw")
        oImp2 = New Impersonation("some_local_user", "", "some_pw")

        oImp1.StartImpersonation()
        MessageBox.Show("User = " & Environment.UserName, "", MessageBoxButtons.OK)

        oImp2.StartImpersonation()
        MessageBox.Show("User = " & Environment.UserName, "", MessageBoxButtons.OK)

        oImp2.EndImpersonation()
        MessageBox.Show("User = " & Environment.UserName, "", MessageBoxButtons.OK)

        oImp1.EndImpersonation()
        MessageBox.Show("User = " & Environment.UserName, "", MessageBoxButtons.OK)

Dave
 
Last edited:

Apathetic

Platinum Member
Dec 23, 2002
2,587
6
81
I belive it does because the impersonation seems to be working fine. As the impersonated account, I can access file shares which are otherwise restricted.

I only stubled across this issue because I wanted the code to assume a local machine account if no domain was provided.

Dave
 

KIAman

Diamond Member
Mar 7, 2001
3,342
23
81
You most likely need to set the EnvironmentPermissionAccess.Read permissions.
 

Apathetic

Platinum Member
Dec 23, 2002
2,587
6
81
Well, I figured it out. I'm an idiot. :p

The problem was on the DuplicateToken call. I was passing SECURITY_IMPERSONATION_LEVEL.SecurityIdentification when I should have been passing SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation.

It just goes to show that one of the best debigging techniques out there is to put down the code and step away from it for a while.

Dave
 

KIAman

Diamond Member
Mar 7, 2001
3,342
23
81
Congrats, although it is strange you get the UserName returned but not the MachineName from the previous bug.
 

toldi.arpad

Junior Member
May 3, 2012
1
0
0
Hello!

I'm trying to use this code, but sg is wrong, please help me. I add a new class to my project (VisualStudio2008 - VB) CRTL+c, CTRL+V your code from here, then in my Module1 I'm trying to use this, but when I try this:

oImp1 = New Impersonation("some_domain_user", "some_domain", "some_pw")

when I start the bracket I expect that I can enter 3 parameters, but it only offers "New()" :(

Do you have any idea? I'm not so good in this...
Thank you in advance...