3

Solution (kinda):

Turns out this impersonation with .NET's security only allows application-level access. Since the COM object is at the system level, the impersonated user still cannot instantiate it. I figured this out by right-clicking the executable and selecting "Run As...", the program functioned fine. I found out that launches the program with system access (assuming the user you are running it with has those credentials). Now I am in the process of creating an external program that will launch this application using this method.

Thanks for the tips :D


I have a windows XP installation on a virtual machine. It is part of my domain, but the logged in user is a local user only. Obviously, if I try to access a network share it will prompt for a user/password:

alt text

The program I am testing out on the virtual machine uses a COM object to interface with data from another program. If I do not impersonate, I get errors because I do not have the proper credentials.

I did some research into the matter and found a number of websites that had a decent amount of VB.NET information. The problem I am having with the code I wrote is I can access the network resources, but I cannot instantiate the COM object.

If I fill and submit the credential prompt (above) before attempting to instantiate it, it works fine. That leads me to believe there must be something that the WinXP credential prompt is doing that I am not. Below is the code I am using for Impersonation:

Public Sub BeginImpersonation()
    Const LOGON32_PROVIDER_DEFAULT As Integer = 0
    Const LOGON32_LOGON_INTERACTIVE As Integer = 2
    Const SecurityImpersonation As Integer = 2

    Dim win32ErrorNumber As Integer

    _tokenHandle = IntPtr.Zero
    _dupeTokenHandle = IntPtr.Zero

    If Not LogonUser(_username, _domainname, _password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, _tokenHandle) Then
        win32ErrorNumber = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
        Throw New ImpersonationException(win32ErrorNumber, GetErrorMessage(win32ErrorNumber), _username, _domainname)
    End If

    If Not DuplicateToken(_tokenHandle, SecurityImpersonation, _dupeTokenHandle) Then
        win32ErrorNumber = System.Runtime.InteropServices.Marshal.GetLastWin32Error()

        CloseHandle(_tokenHandle)
        Throw New ImpersonationException(win32ErrorNumber, "Unable to duplicate token!", _username, _domainname)
    End If

    Dim newId As New System.Security.Principal.WindowsIdentity(_dupeTokenHandle)
    _impersonatedUser = newId.Impersonate()
    _impersonating = True
End Sub

I have also tried sending different flags to the impersonator method, but nothing seems to be working. Here are the different flags I found:

Enum LOGON32_LOGON
    INTERACTIVE = 2
    NETWORK = 3
    BATCH = 4
    SERVICE = 5
    UNLOCK = 7
    NETWORK_CLEARTEXT = 8
    NEW_CREDENTIALS = 9
End Enum
Enum LOGON32_PROVIDER
    [DEFAULT] = 0
    WINNT35 = 1
    WINNT40 = 2
    WINNT50 = 3
End Enum
Enum SECURITY_LEVEL
    Anonymous = 0
    Identification = 1
    Impersonation = 2
    Delegation = 3
End Enum
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Anders
  • 12,088
  • 34
  • 98
  • 146
  • I have code that does impersonation, and based on testing it appears that with a VM of a Windows 2008 system, you can successfully use LogonUser, and get a successful impersonation session for the user. At least it works for my code. My code also fails for vms of older windows systems. – Jon Ediger Apr 25 '12 at 23:38

3 Answers3

1

I have run into this before, and used two different soloution - the easiest was using a third party app: TqcRunas: http://www.quimeras.com/Products/products.asp which allows you to package the required creentials in an encrypted file. However is a pain if the password is forced to expire.

The other solution that I have used is to call a new process with alternative credentials:

        Dim myProcessStartInfo As ProcessStartInfo = New ProcessStartInfo

    With myProcessStartInfo

        .FileName = "file path and name"

        .Domain = "domainname"
        .UserName = "username"

        'password needs to be a SerureString
        Using NewPassword As New Security.SecureString
            With NewPassword
                For Each c As Char In "password".ToCharArray
                    .AppendChar(c)
                Next c
                .MakeReadOnly()
            End With
            .Password = NewPassword.Copy
        End Using

        'UseShellExecute must be false for impersonated process
        .UseShellExecute = False

    End With

    Using Process As New System.Diagnostics.Process
        With Process
            .StartInfo = myProcessStartInfo
            .Start()
        End With
    End Using
Bob
  • 1,353
  • 1
  • 7
  • 8
  • this is what I ended up using, even though I implemented it before you suggested it. credits for you for providing an accurate solution :) – Anders Jun 10 '09 at 15:31
  • Not works for RedirectIO and runas: http://kiquenet.wordpress.com/2014/08/22/uac-run-as-administrator-elevated-process/ – Kiquenet Oct 15 '14 at 17:32
0

With your definitions, I use

LogonUser(_username, _domainname, _password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, _tokenHandle)

in my code that is authenticating across the network. I am impersonating a local user on the remote box, as you are. It was so long ago that I don't remember the rationale for using these values, however.

Don
  • 3,654
  • 1
  • 26
  • 47
  • Thanks for the reply! Unfortunately, I've tried those values to no avail. There has to be SOMETHING (probably small and stupid) that I am overlooking somewhere that is causing this to not work – Anders May 04 '09 at 21:14
  • I see now that I misunderstood - it was in trying to access the data through the COM object that you were having the problem. I see also that you've come up with a work around - not ideal, but as you say, since the impersonation is at the application level, one way to do it. – Don May 07 '09 at 16:39
0

I do a similar thing to map network drives for copying files between machines. I didn't write the code but it's pretty much the same as yours, except for two things:

  • After the Impersonate method returns I close both tokens using the CloseHandle routine, before I exit my impersonator method.

  • At the top of the impersonator the first thing that happens is a call to RevertToSelf, presumably to cancel any previous impersonation.

I don't know if they would make a difference but it's worth a try. Here are the relevant declarations:

Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Chris Tybur
  • 1,612
  • 2
  • 23
  • 38