(Print this page)

Impersonate in .Net
Published date: Tuesday, September 9, 2014
On: Moer and Éric Moreau's web site

Since 2008, I am one of the so-called experts on Experts-Exchange on which I answer to some questions when time permit (and when I know the answer – or can contribute to find one).

Lately, one question was about to copy a file from a network share to which the current user does not have sufficient privileges (but for which the application could provide credentials).

Demo code

The downloadable solution that you can download has been created using Visual Studio 2013. But there is no feature requiring it. If I am not mistaken, it should work all the way back to .Net Framework 1.0 if anybody is still using it!

The downloadable solution contains both VB and C#.

Figure 1: The demo application in action

LogonUser: The important method

There is really only one thing that is hard to figure out. What do we call in order to connect to a share on another computer of the same network? I don’t think that the .Net Framework provides this but a single call to a Windows function can do it.

This method is LogonUser and it is made available directly in Windows (in advapi32.dll).

If you ever been required to call a method of Windows, you know that finding the correct signature can be a pain. In previous articles we needed to do such a thing. As always, when I need this kind of declaration, I refer to a web site made available by Red Gate called PInvoke.net. This specific method can be found at http://pinvoke.net/default.aspx/advapi32.LogonUser.

Calling LogonUser

This method takes 6 arguments which in my opinion are not in the correct order:

 

  • The user name (string)
  • The domain name (string)
  • The password (string – not encrypted)
  • The logon type (an integer)
  • The logon provider (an integer)
  • A pointer to a token (a handle - IntPtr)

 

The first 3 arguments are self-explained.

As for the logon type, because it is an application connecting to a share, we will use the value 9 from the enumeration (provided by PInvoke) which means to use new credentials, the one we are providing instead of using the credentials of the current user.

For the provider, the default value 0 should be good for you.

The last argument is byref (it will be filled by the method), no value as to be provided. This returned value will be used later.

The LogonUser can return many errors like invalid user or domain so you need to be sure to encapsulate the call into a Try … Catch structure.

Interesting fact about LogonUser

I have tested this code in 2 scenarios. The first one is with a real server which I was connected through VPN. The second test was another computer on my LAN (not belonging to a domain). In the second case, the domain name cannot be left blank but can be filled with anything. It is a good practice to always put the IP address in this case.

The (almost) full code

The code of my sample of this month (which copies a file from a local folder to a network share) stands in a single event handler (the click of a button) and reads like this:

Private Sub btnCopy_Click(sender As Object, e As EventArgs) Handles btnCopy.Click
    Dim adminToken As IntPtr
    Dim widAdmin As WindowsIdentity
    Dim wic As WindowsImpersonationContext = Nothing

    Try
        If LogonUser(txtUserName.Text,
                     txtDomain.Text,
                     txtPassword.Text,
                     LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
                     LogonProvider.LOGON32_PROVIDER_DEFAULT,
                     adminToken) <> 0 Then
            widAdmin = New WindowsIdentity(adminToken)
            wic = widAdmin.Impersonate()
            IO.File.Copy(txtSource.Text, txtDestination.Text, True)
            MessageBox.Show("Copy succeeded")
        Else
            MessageBox.Show("Copy Failed")
        End If

    Catch se As Exception
        Dim ret As Integer = Marshal.GetLastWin32Error()
        If ret <> 0 Then
            MessageBox.Show(ret.ToString(), "Error code: " + ret.ToString())
        End If
        MessageBox.Show(se.Message)

    Finally
        If wic IsNot Nothing Then
            wic.Undo()
        End If
    End Try
End Sub

As you can see, LogonUser is called and if it returns a value other then 0, it uses the last argument filled by LogonUser to create a new WindowsIdentity instance. The Impersonate method of this instance is then called to pass the credentials. If you reach this line without any exception, you are successfully connected.

The next line copy a file to the network share. You might still get problems here if the share is not valid or if the impersonated user does not have privileges for this folder.

Once everything has been completed, the Undo of the WindowsIdentity instance is called to log out.

Conclusion

I admit, it was a short one but something that is useful and not easy to find. Now you have a working sample!


(Print this page)