Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using Impersonation in VB.NET 2008

Status
Not open for further replies.

PaulDalyTT

Programmer
Jun 4, 2009
15
0
0
IE
Hi folks,

Has anyone any experience in using impersonation in VB.NET?

I have developed my own task scheuler which runs as a service under 'Local System' account on my laptop. The scheduler works fine and I can get the tasks to start as and when I need them.

However, I would like to be able to run tasks under a user other than the Local System account. Specifically, I would like to use my own domain user so that I can copy a file from the laptop to the LAN. (one of the tasks is a weekly backup of my laptop and want to copy this bkf file to the LAN for safe storage.)


I have created a commandline program 'VBRunAs' which my scheduler calls if the task specifies a username / password. This uses impersonation to run the task under the specified username and password. (Otherwise my scheduler runs the task directly under the System Account)

When I call the VBRunAs program, either from command line or via the scheduler, the process starts but does not appear to be impersonating the user. ie the process is still run under the calling user, not the impersonated user (according to task manager)

I have confirmed that the correct username and password is being supplied to the runas command using a messagebox in the command line app.


I have also tried using the processinfo arguments to set username and password (you'll see them commented out in the code). This works when I run at command line, but I get access denied when I run via scheduler.


Code below...

any ideas???

Thanks all!

Paul.



Main VBRunAs code
Code:
Module VBRunAs

    Private Function GetSecurePassword(ByVal pwd As String) As System.Security.SecureString
        GetSecurePassword = Nothing
        If Not String.IsNullOrEmpty(pwd) Then
            GetSecurePassword = New System.Security.SecureString
            For Each character As Char In pwd.ToCharArray
                GetSecurePassword.AppendChar(character)
            Next
            GetSecurePassword.MakeReadOnly()
        End If
    End Function

    Private exe As String
    Private args As String
    Private DomainName As String
    Private UserName As String
    Private Password As String


    'Sub main()
    '   MsgBox("insufficieng args: " & _
    '         "ByVal DomainName As String" & vbCr & _
    '      "ByVal UserName As String" & vbCr & _
    '     "ByVal Password As String" & vbCr & _
    '    "ByVal Exe As String" & vbCr & _
    '   "ByVal Args As String")

    'End Sub


    Sub Main(ByVal cmdArgs() As String)

        If cmdArgs.Length = 5 Then
            DomainName = cmdArgs(0)
            UserName = cmdArgs(1)
            Password = cmdArgs(2)
            exe = cmdArgs(3)
            args = cmdArgs(4)


            '            Dim imp As New RunAs_Impersonator
            Dim imp As New AliasAccount(DomainName & "\" & UserName, Password)

            Try

                'imp.ImpersonateStart(DomainName, UserName, Password)  'creates new context using token for user
                imp.BeginImpersonation()

                'everything between BeginImpersonation and EndImpersonation will be run as the impersonated user           
                Dim pInfo As New ProcessStartInfo()
                Dim p As New Process

                pInfo.FileName = exe

                If Not (args = "" Or _
                   args Is Nothing Or _
                   args = "UnDef") Then
                    pInfo.Arguments = args
                End If

                'pInfo.Domain = DomainName
                'pInfo.UserName = UserName
                'pInfo.Password = GetSecurePassword(Password)


                pInfo.WindowStyle = ProcessWindowStyle.Normal
                pInfo.UseShellExecute = False
                pInfo.CreateNoWindow = False

                MsgBox("About to start " & pInfo.FileName & vbCr & _
                       "As " & DomainName & "\" & UserName & vbCr & _
                       "using " & Password _
                       )

                p.StartInfo = pInfo
                p.Start()

                imp.EndImpersonation()

            Catch ex As Exception  'make sure impersonation is stopped whether code succeeds or not           

                MsgBox("Exception trapped in VBRunAs: " & ex.Message)
                imp.EndImpersonation()

            End Try
        Else
            MsgBox("Incorrect Number of args." & vbCr & vbCr & _
            "You supplied " & cmdArgs.Length & " args. " & vbCr & _
            "You SHOULD have supplied 5 args. " & vbCr & _
                  "DomainName As String" & vbCr & _
               "UserName As String" & vbCr & _
              "Password As String" & vbCr & _
             "Exe As String" & vbCr & _
            """Args"" As String")

        End If


    End Sub

End Module



AliasAccount class (took this 'as is' from a VB site
Code:
Public Class AliasAccount
    Private _username, _password, _domainname As String

    Private _tokenHandle As New IntPtr(0)
    Private _dupeTokenHandle As New IntPtr(0)
    Private _impersonatedUser As System.Security.Principal.WindowsImpersonationContext


    Public Sub New(ByVal username As String, ByVal password As String)
        Dim nameparts() As String = username.Split("\")
        If nameparts.Length > 1 Then
            _domainname = nameparts(0)
            _username = nameparts(1)
        Else
            _username = username
        End If
        _password = password
    End Sub

    Public Sub New(ByVal username As String, ByVal password As String, ByVal domainname As String)
        _username = username
        _password = password
        _domainname = domainname
    End Sub


    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()
    End Sub


    Public Sub EndImpersonation()
        If Not _impersonatedUser Is Nothing Then
            _impersonatedUser.Undo()
            _impersonatedUser = Nothing

            If Not System.IntPtr.op_Equality(_tokenHandle, IntPtr.Zero) Then
                CloseHandle(_tokenHandle)
            End If
            If Not System.IntPtr.op_Equality(_dupeTokenHandle, IntPtr.Zero) Then
                CloseHandle(_dupeTokenHandle)
            End If
        End If
    End Sub


    Public ReadOnly Property username() As String
        Get
            Return _username
        End Get
    End Property

    Public ReadOnly Property domainname() As String
        Get
            Return _domainname
        End Get
    End Property


    Public ReadOnly Property currentWindowsUsername() As String
        Get
            Return System.Security.Principal.WindowsIdentity.GetCurrent().Name
        End Get
    End Property


#Region "Exception Class"
    Public Class ImpersonationException
        Inherits System.Exception

        Public ReadOnly win32ErrorNumber As Integer

        Public Sub New(ByVal win32ErrorNumber As Integer, ByVal msg As String, ByVal username As String, ByVal domainname As String)
            MyBase.New(String.Format("Impersonation of {1}\{0} failed! [{2}] {3}", username, domainname, win32ErrorNumber, msg))
            Me.win32ErrorNumber = win32ErrorNumber
        End Sub
    End Class
#End Region


#Region "External Declarations and Helpers"
    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


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


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


    <System.Runtime.InteropServices.DllImport("kernel32.dll")> _
    Private Shared Function FormatMessage(ByVal dwFlags As Integer, ByRef lpSource As IntPtr, _
            ByVal dwMessageId As Integer, ByVal dwLanguageId As Integer, ByRef lpBuffer As [String], _
            ByVal nSize As Integer, ByRef Arguments As IntPtr) As Integer
    End Function


    Private Function GetErrorMessage(ByVal errorCode As Integer) As String
        Dim FORMAT_MESSAGE_ALLOCATE_BUFFER As Integer = &H100
        Dim FORMAT_MESSAGE_IGNORE_INSERTS As Integer = &H200
        Dim FORMAT_MESSAGE_FROM_SYSTEM As Integer = &H1000

        Dim messageSize As Integer = 255
        Dim lpMsgBuf As String
        Dim dwFlags As Integer = FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS

        Dim ptrlpSource As IntPtr = IntPtr.Zero
        Dim prtArguments As IntPtr = IntPtr.Zero

        Dim retVal As Integer = FormatMessage(dwFlags, ptrlpSource, errorCode, 0, lpMsgBuf, messageSize, prtArguments)
        If 0 = retVal Then
            Throw New System.Exception("Failed to format message for error code " + errorCode.ToString() + ". ")
        End If

        Return lpMsgBuf
    End Function

#End Region

End Class

 
hmm,

after some more reasearch and tring various other things it seems that it may not be possible to launch processes under alternate credentials from the Local System account.

I have even tried shell out to a VBS script to execute Windows runAs command, but none of it works. I can't get the LocalSystem account to launch in any user other than System.


So if anyone has any suggestions as to how I might work around this, or some suggestions to another approach I could take, it'd be much appreciated!

Cheers,

Paul.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top