Κλήση εξωτερικής εφαρμογής με διαφορετικά user credentials (run as) προγραμματιστικά (.NET 1.1)
Μετά από αρκετή διερεύνηση, επιχειρώντας να λύσω ένα συγκεκριμένο πρόβλημα, κατέληξα σε ένα "μπούσουλα" για τον τρόπο με τον οποίο μπορεί κάποιος να καλέσει με κώδικα μέσα από μια εφαρμογή ένα εξωτερικό process (ενα executable) χρησιμοποιώντας διαφορετικά user credentials από αυτά του χρήστη που "τρέχει" την εφαρμογή (ουσιαστικά δηλαδή να μιμηθούμε το "run as.." που μας δίνει το shell των Windows).
Το θέμα αυτό έχει λόγο ύπαρξης μόνο στο .NET 1.1, μια και στο .NET 2.0 μπορεί κανείς να προσδιορίσει user credentials στο System.Diagnostics.Process.Start().
Θερμές ευχαριστίες, φυσικά, στους συναδέλφους του dotNetZone.gr (είναι αυτονόητο οτι χωρίς τη βοήθειά τους δεν θα επιζούσα):
Ο παρακάτω κώδικας λειτουργεί για χρήστη που βρίσκεται σε domain και η κλήση έχει ως εξής:
clsSpawnProcess.CreateProcess( _
"myUser" _
, "myDomain" _
, "myPassword" _
, "c:\myfolder\myexecutable.exe" _
, " " + "myCommandLineArguments")
Η χρήση των υποδειγματικών strings που αναφέρονται στην κλήση, φαντάζομαι, είναι αυτονόητη. Ο κώδικας δουλεύει σε WinXP, Win2000 και Win2003.
Ακολουθεί ο κώδικας:
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Imports System.Runtime.InteropServices
Imports System.IO
Public Class clsSpawnProcess
<Flags()> _
Enum LogonFlags
LOGON_WITH_PROFILE = &H1
LOGON_NETCREDENTIALS_ONLY = &H2
End Enum
<Flags()> _
Enum CreationFlags
CREATE_SUSPENDED = &H4
CREATE_NEW_CONSOLE = &H10
CREATE_NEW_PROCESS_GROUP = &H200
CREATE_UNICODE_ENVIRONMENT = &H400
CREATE_SEPARATE_WOW_VDM = &H800
CREATE_DEFAULT_ERROR_MODE = &H4000000
End Enum
<StructLayout(LayoutKind.Sequential)> _
Structure ProcessInfo
Public hProcess As IntPtr
Public hThread As IntPtr
Public dwProcessId As UInt32
Public dwThreadId As UInt32
End Structure
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Structure StartupInfo
Public cb As Int32
Public reserved1 As String
Public desktop As String
Public title As String
Public dwX As UInt32
Public dwY As UInt32
Public dwXSize As UInt32
Public dwYSize As UInt32
Public dwXCountChars As UInt32
Public dwYCountChars As UInt32
Public dwFillAttribute As UInt32
Public dwFlags As UInt32
Public wShowWindow As Short
Public reserved2 As Short
Public reserved3 As Integer
Public hStdInput As IntPtr
Public hStdOutput As IntPtr
Public hStdError As IntPtr
End Structure
<DllImport("advapi32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function CreateProcessWithLogonW( _
ByVal principal As String, _
ByVal authority As String, _
ByVal password As String, _
ByVal logonFlags As LogonFlags, _
ByVal appName As String, _
ByVal cmdLine As String, _
ByVal creationFlags As CreationFlags, _
ByVal environmentBlock As IntPtr, _
ByVal currentDirectory As String, _
ByRef startupInfo As StartupInfo, _
ByRef processInfo As ProcessInfo) As Boolean
End Function
<DllImport("kernel32.dll")> _
Public Shared Function CloseHandle(ByVal h As IntPtr) As Boolean
End Function
'/// -----------------------------------------------------------------------------
'/// <summary>
'/// This is the class entry point. A call to this method allows us to
'/// spawn an executable using alternate user credentials.
'/// </summary>
'/// <param name="username">The user's username</param>
'/// <param name="domain">The domain name</param>
'/// <param name="password">The user's password</param>
'/// <param name="applicationPath">The full path to the executable</param>
'/// <param name="commandLineArgs">Any command line arguments</param>
'/// <remarks>
'/// It is important that command line arguments start with a space, or else
'/// the call will not work.
'/// </remarks>
'/// -----------------------------------------------------------------------------
Public Shared Sub CreateProcess( _
ByVal username As String _
, ByVal domain As String _
, ByVal password As String _
, ByVal applicationPath As String _
, ByVal commandLineArgs As String)
Dim si As StartupInfo = New StartupInfo
si.cb = Marshal.SizeOf(GetType(StartupInfo))
Dim pi As ProcessInfo = New ProcessInfo
If (CreateProcessWithLogonW(username, domain, password, _
LogonFlags.LOGON_NETCREDENTIALS_ONLY, _
applicationPath, commandLineArgs, _
0, IntPtr.Zero, Nothing, _
si, pi)) Then
CloseHandle(pi.hProcess)
CloseHandle(pi.hThread)
Else
'TODO: throw exception here or something
Console.WriteLine("Error code: {0}", Marshal.GetLastWin32Error())
End If
End Sub
End Class