Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision | |||
ms_windows_ms_sql:web_gui_for_powershell_scripts [2019/05/15 09:49] – [Еще немного полезных ссылок] admin | ms_windows_ms_sql:web_gui_for_powershell_scripts [2019/05/15 14:03] (current) – [Запуск процесса (powershell.exe) от имени конкретного пользователя] admin | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== PowerShell using ASP.NET application ====== | ||
+ | У меня получилось сделать все что нужно (запустить от имени пользователя оболочку powershell, передать ей скрипт и получить обратно консольный вывод с помощью информации из этих ссылок): | ||
+ | Описано расширение стандартного класса **Process** и его примемение: | ||
+ | Вот тут написано как вызывать эти методы: | ||
+ | \\ | ||
+ | Если необходимо, | ||
+ | \\ | ||
+ | Например, | ||
+ | < | ||
+ | { | ||
+ | ResultBox.Text = string.Empty; | ||
+ | |||
+ | var current = System.Security.Principal.WindowsIdentity.GetCurrent(); | ||
+ | WindowsIdentity clientId = (WindowsIdentity)User.Identity; | ||
+ | |||
+ | using (WindowsImpersonationContext wic = clientId.Impersonate()) | ||
+ | { | ||
+ | UserSpecificProcess myProcess = new UserSpecificProcess(); | ||
+ | myProcess.StartInfo.FileName = " | ||
+ | myProcess.StartInfo.Arguments = @" | ||
+ | myProcess.StartInfo.UseShellExecute = false; | ||
+ | myProcess.StartInfo.CreateNoWindow = false; | ||
+ | myProcess.StartInfo.WorkingDirectory = @" | ||
+ | myProcess.StartAsUser(); | ||
+ | StreamReader output = myProcess.StandardOutput; | ||
+ | |||
+ | string line; | ||
+ | StringBuilder builder = new StringBuilder(); | ||
+ | while ((line = output.ReadLine()) != null) | ||
+ | { | ||
+ | // We use a string builder ton create our result text | ||
+ | builder.Append(line + " | ||
+ | } | ||
+ | |||
+ | // Encode the string in HTML (prevent security issue with ' | ||
+ | ResultBox.Text = Server.HtmlEncode(builder.ToString()); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Пример ASP.NET-приложения, | ||
+ | В итоге вот что у меня получилось. {{ : | ||
+ | Протестировано на **IIS** под **Windows Server 2012R2**. **Application pool .NET 4** работает в **Integrated Mode**, от имени **Local System**. \\ | ||
+ | Приложение опубликовано без каких-либо двоичных файлов. То есть изменения можно вносить прямо в код опубликованного приложения. | ||
+ | ===== IIS Impersonation и делегирование прав доступа к ресурсам ===== | ||
+ | На эту тему написано очень много. Обычно это именуется **impersonation double hop issue**. Имеется в виду следующее. \\ | ||
+ | Когда пользователь успешно имперсонирован (олицетворен) в приложении **ASP.NET**, | ||
+ | Для того, чтобы все работало, | ||
+ | Права серверу на делегирование пользователей выдаются с помощью оснастки **Active Directory Users and Computers**. Нужно открыть свойства учетной записи сервера, | ||
+ | При этом, если приложение **ASP.NET** запускает локальный процесс на web-сервере (например - **powershell**), | ||
+ | ==== Когда Windows Authentication и делегирование работает на **localhost**, | ||
+ | Еще один важный момент. \\ | ||
+ | Часто можно наблюдать ситуацию, | ||
+ | Это происходит из-за того, что **FQDN**-имя сервера не внесено в список **Intranet** (локальных) сайтов в конфигурации браузера. В результате, | ||
+ | А вот тут подробнее от **Microsoft**: | ||
+ | Вот тут: https:// | ||
+ | приведен пример тестовой страницы, | ||
+ | |||
+ | ==== Настройки браузеров для корректной работы имперсонации ==== | ||
+ | === Internet Explorer === | ||
+ | Параметр настроек **Internet Explorer**, который предписывает использовать **KERBEROS** находится в **Internet Explorer Optinons -> Security -> Custom Level** и называется **Automatic logon with current user name and password**. Если он включен, | ||
+ | Если для доступа к приложению используется **Firefox**, | ||
+ | === Chrome === | ||
+ | **Chrome** в **windows** использует настройки **Internet Explorer**. \\ | ||
+ | В других ОС передать Chrome список доверенных сайтов можно с помощью аргумента командной строки: | ||
+ | < | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Чтобы в приложении ASP.NET работала сквозная Windows-аутентификация, | ||
+ | В итоге. Чтобы в приложении **ASP.NET** работала сквозная **Windows**-аутентификация, | ||
+ | * Для приложения вцелом (**Classic Application Pool mode**) или для куска кода (**Integrated Application Pool mode**) включена имперсонация. | ||
+ | * Для приложения отключена аутентификация анонимных пользователей, | ||
+ | * Принудительно включен **Kerberos** (**Windows Authentication** -> **Providers** (справа) оставитьтолько **Negotiate: | ||
+ | * Если адрес к которому обращаются пользователи отличается от имени web-сервера, | ||
+ | * Для учетной записи web-сервера разрешено делегирование. | ||
+ | * Адрес сайта, на котором размещено приложение добавлен в список **Интранет** сайтов. | ||
+ | В противном случае - не будет корректно работать делегирование и будут ошибки при доступе к сетевым ресурсам и **Active Directory** (ошибки типа **Exception calling " | ||
+ | Траблшутинг аутентификации Kerberos толково описан тут: https:// | ||
+ | |||
+ | Для одного из сервисов я описал настройку web-сервера на базе Windows Server 2016: {{ : | ||
+ | |||
+ | ===== Еще немного полезных ссылок ===== | ||
+ | Вот этот гайд описывает как запускать процесс от имени имперсонированного пользователя. Вторая ссылка - как получить то что выводится в стандартный вывод. \\ | ||
+ | ASP.NET Run Process As Impersonated User: https:// | ||
+ | Вот тут написано как человек пытается получить вывод от процесса запущенного с помощью **CreateProcessAsUser**: | ||
+ | https:// | ||
+ | А вот тут представлен класс, который по идее это все делает. но у меня не получилось :( | ||
+ | https:// | ||
+ | \\ | ||
+ | Вот тут написано как из приложения ASP.NET запустить процесс от имени импесонированного пользователя, | ||
+ | \\ | ||
+ | Вот тут понятные классы, | ||
+ | |||
+ | Вот ссылки описывают как создать проект, | ||
+ | http:// | ||
+ | https:// | ||
+ | Change ASP.NET Settings per pool. https:// | ||
+ | https:// | ||
+ | |||
+ | ====== PowerShell UI Using Jenkins ====== | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ====== Powershell UI Using ASP ====== | ||
+ | http:// | ||
+ | http:// | ||
+ | |||
+ | Для создания **web**-страницы, | ||
+ | * Разместить **aspx**-файл в корневой папке web-проекта. | ||
+ | * Разместить **web.config** в корневой папке web-проекта. | ||
+ | * Если web-приложения работает от имени **Network Service**, то дать этой службе права на чтение. | ||
+ | * Собственно сам скрипт **PowerShell** | ||
+ | * Библиотека (dll) **System.Management.Automation** в папке **.\bin** | ||
+ | Для вывода строк следует использовать **Out-String**. Для передачи параметров следует использовать **$args**. | ||
+ | |||
+ | ===== web.config ===== | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ===== Powershell скрипт test.ps1 ===== | ||
+ | < | ||
+ | { $text = $args[1]} | ||
+ | write-output $text | ||
+ | </ | ||
+ | |||
+ | ===== Страница aspx - Test.aspx ===== | ||
+ | < | ||
+ | <%@ Import Namespace=" | ||
+ | <%@ Import Namespace=" | ||
+ | <%@ Import Namespace=" | ||
+ | <%@ Import Namespace=" | ||
+ | <%@ Import Namespace=" | ||
+ | |||
+ | |||
+ | <script language=" | ||
+ | |||
+ | // The previous lines use < | ||
+ | // http:// | ||
+ | |||
+ | |||
+ | private void Button3_Click(object sender, System.EventArgs e) | ||
+ | { | ||
+ | String fp = Server.MapPath(" | ||
+ | StreamReader sr = new StreamReader(fp); | ||
+ | tPowerShellScriptCode.Text = sr.ReadToEnd(); | ||
+ | sr.Close(); | ||
+ | } | ||
+ | |||
+ | private void Button2_Click(object sender, System.EventArgs e) | ||
+ | { | ||
+ | tPowerShellScriptResult.Text = RunScript(tPowerShellScriptCode.Text); | ||
+ | } | ||
+ | |||
+ | // http:// | ||
+ | |||
+ | private string RunScript(string scriptText) | ||
+ | { | ||
+ | Runspace runspace = RunspaceFactory.CreateRunspace(); | ||
+ | runspace.Open(); | ||
+ | Pipeline pipeline = runspace.CreatePipeline(); | ||
+ | |||
+ | // Create a new runspaces.command object of type script | ||
+ | Command cmdScript = new Command(scriptText, | ||
+ | cmdScript.Parameters.Add(" | ||
+ | pipeline.Commands.Add(cmdScript); | ||
+ | //You could also use: pipeline.Commands.AddScript(scriptText); | ||
+ | |||
+ | // Re-format all output to strings | ||
+ | pipeline.Commands.Add(" | ||
+ | |||
+ | // Invoke the pipeline | ||
+ | Collection< | ||
+ | |||
+ | //String sresults = pipeline.Output.Count.ToString(); | ||
+ | //sresults = sresults + "," | ||
+ | String sresults = ""; | ||
+ | |||
+ | foreach (PSObject obj in results) | ||
+ | { | ||
+ | sresults = sresults + obj.ToString(); | ||
+ | } | ||
+ | |||
+ | // close the runspace and set to null | ||
+ | runspace.Close(); | ||
+ | runspace = null; | ||
+ | |||
+ | return sresults; | ||
+ | } | ||
+ | |||
+ | |||
+ | </ | ||
+ | |||
+ | <form id=" | ||
+ | <P> < | ||
+ | < | ||
+ | <P> < | ||
+ | <P> < | ||
+ | <P> < | ||
+ | < | ||
+ | <P> < | ||
+ | <P> < | ||
+ | </ | ||
+ | </ | ||
+ | ====== Запуск процессов от имени имперсонированного пользователя ====== | ||
+ | < | ||
+ | using System.Runtime.InteropServices; | ||
+ | using System.Security.Principal; | ||
+ | using System.Text; | ||
+ | using System.Diagnostics; | ||
+ | using System.IO; | ||
+ | using System.ComponentModel; | ||
+ | using System.Reflection; | ||
+ | using Microsoft.Win32.SafeHandles; | ||
+ | |||
+ | namespace vmware_net | ||
+ | { | ||
+ | |||
+ | |||
+ | /// < | ||
+ | /// UserSpecificProcess extends the standard Process object to | ||
+ | /// create new processes under a different user than the calling parent process. | ||
+ | /// Also, the standard output of the child process redirected back to the parent process. | ||
+ | /// This is class is designed to operate inside an ASP.NET web application. | ||
+ | /// The assumption is that the calling thread is operating with an impersonated security token. | ||
+ | /// The this class will change the imperonated security token to a primary token, | ||
+ | /// and call CreateProcessAsUser. | ||
+ | /// A System.Diagnostics.Process object will be returned with appropriate properties set. | ||
+ | /// To use this function, the following security priviliges need to be set for the ASPNET account | ||
+ | /// using the local security policy MMC snap-in. CreateProcessAsUser requirement. | ||
+ | /// " | ||
+ | /// " | ||
+ | /// | ||
+ | /// This class was designed for .NET 1.1 for operating systems W2k an higher. | ||
+ | /// Any other features or platform support can be implemented by using the .NET reflector and | ||
+ | /// investigating the Process class. | ||
+ | /// </ | ||
+ | public class UserSpecificProcess : Process | ||
+ | { | ||
+ | [StructLayout(LayoutKind.Sequential)] | ||
+ | public class CreateProcessStartupInfo | ||
+ | { | ||
+ | public int cb; | ||
+ | public string lpReserved; | ||
+ | public string lpDesktop; | ||
+ | public string lpTitle; | ||
+ | public int dwX; | ||
+ | public int dwY; | ||
+ | public int dwXSize; | ||
+ | public int dwYSize; | ||
+ | public int dwXCountChars; | ||
+ | public int dwYCountChars; | ||
+ | public int dwFillAttribute; | ||
+ | public int dwFlags; | ||
+ | public short wShowWindow; | ||
+ | public short cbReserved2; | ||
+ | public IntPtr lpReserved2; | ||
+ | public IntPtr hStdInput; | ||
+ | public IntPtr hStdOutput; | ||
+ | public IntPtr hStdError; | ||
+ | public CreateProcessStartupInfo() | ||
+ | { | ||
+ | this.cb = Marshal.SizeOf(typeof(CreateProcessStartupInfo)); | ||
+ | this.lpReserved = null; | ||
+ | this.lpDesktop = null; | ||
+ | this.lpTitle = null; | ||
+ | this.dwX = 0; | ||
+ | this.dwY = 0; | ||
+ | this.dwXSize = 0; | ||
+ | this.dwYSize = 0; | ||
+ | this.dwXCountChars = 0; | ||
+ | this.dwYCountChars = 0; | ||
+ | this.dwFillAttribute = 0; | ||
+ | this.dwFlags = 0; | ||
+ | this.wShowWindow = 0; | ||
+ | this.cbReserved2 = 0; | ||
+ | this.lpReserved2 = IntPtr.Zero; | ||
+ | this.hStdInput = IntPtr.Zero; | ||
+ | this.hStdOutput = IntPtr.Zero; | ||
+ | this.hStdError = IntPtr.Zero; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | [StructLayout(LayoutKind.Sequential)] | ||
+ | public class CreateProcessProcessInformation | ||
+ | { | ||
+ | public IntPtr hProcess; | ||
+ | public IntPtr hThread; | ||
+ | public int dwProcessId; | ||
+ | public int dwThreadId; | ||
+ | public CreateProcessProcessInformation() | ||
+ | { | ||
+ | this.hProcess = IntPtr.Zero; | ||
+ | this.hThread = IntPtr.Zero; | ||
+ | this.dwProcessId = 0; | ||
+ | this.dwThreadId = 0; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | [StructLayout(LayoutKind.Sequential)] | ||
+ | public class SecurityAttributes | ||
+ | { | ||
+ | public int nLength; | ||
+ | public IntPtr lpSecurityDescriptor; | ||
+ | public bool bInheritHandle; | ||
+ | public SecurityAttributes() | ||
+ | { | ||
+ | this.nLength = Marshal.SizeOf(typeof(SecurityAttributes)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern bool CloseHandle(HandleRef handle); | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern bool | ||
+ | CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern int LogonUserA(String lpszUserName, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern bool CreateProcessAsUserW(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern bool CreateProcessAsUser(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern IntPtr GetStdHandle(int whichHandle); | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances, | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern int GetConsoleOutputCP(); | ||
+ | |||
+ | [DllImport(" | ||
+ | public static extern bool DuplicateTokenEx(HandleRef hToken, int access, SecurityAttributes tokenAttributes, | ||
+ | |||
+ | // WinNT.h ACCESS TYPES | ||
+ | const int GENERIC_ALL = 0x10000000; | ||
+ | |||
+ | // WinNT.h enum SECURITY_IMPERSONATION_LEVEL | ||
+ | const int SECURITY_IMPERSONATION = 2; | ||
+ | |||
+ | // WinNT.h TOKEN TYPE | ||
+ | const int TOKEN_PRIMARY = 1; | ||
+ | |||
+ | // WinBase.h | ||
+ | const int STD_INPUT_HANDLE = -10; | ||
+ | const int STD_ERROR_HANDLE = -12; | ||
+ | |||
+ | // WinBase.h STARTUPINFO | ||
+ | const int STARTF_USESTDHANDLES = 0x100; | ||
+ | |||
+ | // Microsoft.Win23.NativeMethods | ||
+ | static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); | ||
+ | public static HandleRef NullHandleRef = new HandleRef(null, | ||
+ | |||
+ | public const int LOGON32_LOGON_INTERACTIVE = 2; | ||
+ | public const int LOGON32_PROVIDER_DEFAULT = 0; | ||
+ | |||
+ | /// < | ||
+ | /// Starts the process with the security token of the calling thread. | ||
+ | /// If the security token has a token type of TokenImpersonation, | ||
+ | /// the token will be duplicated to a primary token before calling | ||
+ | /// CreateProcessAsUser. | ||
+ | /// </ | ||
+ | /// <param name=" | ||
+ | public void StartAsUser() | ||
+ | { | ||
+ | StartAsUser(WindowsIdentity.GetCurrent().Token); | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// Starts the process with the given security token. | ||
+ | /// If the security token has a token type of TokenImpersonation, | ||
+ | /// the token will be duplicated to a primary token before calling | ||
+ | /// CreateProcessAsUser. | ||
+ | /// </ | ||
+ | /// <param name=" | ||
+ | public void StartAsUser(IntPtr userToken) | ||
+ | { | ||
+ | if (StartInfo.UseShellExecute) | ||
+ | { | ||
+ | throw new InvalidOperationException(" | ||
+ | } | ||
+ | |||
+ | // just assume that the securityToken is of TokenImpersonation and create a primary. | ||
+ | IntPtr primayUserToken = CreatePrimaryToken(userToken); | ||
+ | |||
+ | CreateProcessStartupInfo startupInfo = new CreateProcessStartupInfo(); | ||
+ | CreateProcessProcessInformation processInformation = new CreateProcessProcessInformation(); | ||
+ | |||
+ | IntPtr stdinHandle; | ||
+ | IntPtr stdoutReadHandle; | ||
+ | IntPtr stdoutWriteHandle = IntPtr.Zero; | ||
+ | IntPtr stderrHandle; | ||
+ | try | ||
+ | { | ||
+ | stdinHandle = GetStdHandle(STD_INPUT_HANDLE); | ||
+ | MyCreatePipe(out stdoutReadHandle, | ||
+ | stderrHandle = GetStdHandle(STD_ERROR_HANDLE); | ||
+ | |||
+ | //assign handles to startup info | ||
+ | startupInfo.dwFlags = STARTF_USESTDHANDLES; | ||
+ | startupInfo.hStdInput = stdinHandle; | ||
+ | startupInfo.hStdOutput = stdoutWriteHandle; | ||
+ | startupInfo.hStdError = stderrHandle; | ||
+ | |||
+ | string commandLine = GetCommandLine(); | ||
+ | //Set Creation Flags value 0x8000000 to hide window. To show window set it to 0 | ||
+ | int creationFlags = 0x8000000; | ||
+ | //int creationFlags = 0; | ||
+ | IntPtr environment = IntPtr.Zero; | ||
+ | string workingDirectory = GetWorkingDirectory(); | ||
+ | |||
+ | // create the process or fail trying. | ||
+ | //if (!CreateProcessAsUserW( | ||
+ | if (!CreateProcessAsUser( | ||
+ | primayUserToken, | ||
+ | null, | ||
+ | commandLine, | ||
+ | null, | ||
+ | null, | ||
+ | true, | ||
+ | creationFlags, | ||
+ | environment, | ||
+ | workingDirectory, | ||
+ | startupInfo, | ||
+ | processInformation)) | ||
+ | { | ||
+ | throw new Win32Exception(); | ||
+ | } | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | // close thread handle | ||
+ | if (processInformation.hThread != INVALID_HANDLE_VALUE) | ||
+ | { | ||
+ | CloseHandle(new HandleRef(this, | ||
+ | } | ||
+ | |||
+ | // close client stdout handle | ||
+ | CloseHandle(new HandleRef(this, | ||
+ | } | ||
+ | // } | ||
+ | // get reader for standard output from the child | ||
+ | Encoding encoding = Encoding.GetEncoding(GetConsoleOutputCP()); | ||
+ | // | ||
+ | // | ||
+ | SafeFileHandle SafeStdOutReadHandle = new SafeFileHandle(stdoutReadHandle, | ||
+ | StreamReader standardOutput = new StreamReader(new FileStream(SafeStdOutReadHandle, | ||
+ | |||
+ | // set this on the object accordingly. | ||
+ | typeof(Process).InvokeMember(" | ||
+ | BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, | ||
+ | null, this, new object[] { standardOutput }); | ||
+ | |||
+ | // scream if a process wasn't started instead of returning false. | ||
+ | if (processInformation.hProcess == IntPtr.Zero) | ||
+ | { | ||
+ | throw new Exception(" | ||
+ | } | ||
+ | |||
+ | // configure the properties of the Process object correctly | ||
+ | // | ||
+ | // | ||
+ | //null, this, new object[] { processInformation.hProcess }); | ||
+ | typeof(Process).InvokeMember(" | ||
+ | BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, | ||
+ | null, this, new object[] { processInformation.dwProcessId }); | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// Starts the process as user with provided username, domain name and password. | ||
+ | /// Method loggs user with provided cretential, get token and pass it to StartAsUser method. | ||
+ | /// </ | ||
+ | public void StartAsUserWithLogon(string UserName, string Domain, string Password) | ||
+ | { | ||
+ | IntPtr UserToken = IntPtr.Zero; | ||
+ | if (LogonUserA(UserName, | ||
+ | { | ||
+ | StartAsUser(CreatePrimaryToken(UserToken)); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | throw new Win32Exception(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// Creates a primayToken out of an existing token. | ||
+ | /// </ | ||
+ | /// <param name=" | ||
+ | private IntPtr CreatePrimaryToken(IntPtr userToken) | ||
+ | { | ||
+ | SecurityAttributes securityAttributes = new SecurityAttributes(); | ||
+ | IntPtr primaryUserToken = IntPtr.Zero; | ||
+ | if (!DuplicateTokenEx(new HandleRef(this, | ||
+ | primaryUserToken)) | ||
+ | { | ||
+ | throw new Win32Exception(); | ||
+ | } | ||
+ | return primaryUserToken; | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// Gets the appropriate commandLine from the process. | ||
+ | /// </ | ||
+ | /// <param name=" | ||
+ | /// < | ||
+ | private string GetCommandLine() | ||
+ | { | ||
+ | StringBuilder builder1 = new StringBuilder(); | ||
+ | string text1 = StartInfo.FileName.Trim(); | ||
+ | string text2 = StartInfo.Arguments; | ||
+ | bool flag1 = text1.StartsWith(" | ||
+ | if (!flag1) | ||
+ | { | ||
+ | builder1.Append(" | ||
+ | } | ||
+ | builder1.Append(text1); | ||
+ | if (!flag1) | ||
+ | { | ||
+ | builder1.Append(" | ||
+ | } | ||
+ | if ((text2 != null) && (text2.Length > 0)) | ||
+ | { | ||
+ | builder1.Append(" | ||
+ | builder1.Append(text2); | ||
+ | } | ||
+ | return builder1.ToString(); | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// Gets the working directory or returns null if an empty string was found. | ||
+ | /// </ | ||
+ | /// < | ||
+ | private string GetWorkingDirectory() | ||
+ | { | ||
+ | return (StartInfo.WorkingDirectory != string.Empty) ? | ||
+ | StartInfo.WorkingDirectory : null; | ||
+ | } | ||
+ | |||
+ | /// < | ||
+ | /// A clone of Process.CreatePipe. This is only implemented because reflection with | ||
+ | /// out parameters are a pain. | ||
+ | /// Note: This is only finished for w2k and higher machines. | ||
+ | /// </ | ||
+ | /// <param name=" | ||
+ | /// <param name=" | ||
+ | /// <param name=" | ||
+ | public static void MyCreatePipe(out IntPtr parentHandle, | ||
+ | { | ||
+ | string pipename = @" | ||
+ | |||
+ | SecurityAttributes attributes2 = new SecurityAttributes(); | ||
+ | attributes2.bInheritHandle = false; | ||
+ | |||
+ | parentHandle = CreateNamedPipe(pipename, | ||
+ | if (parentHandle == INVALID_HANDLE_VALUE) | ||
+ | { | ||
+ | throw new Win32Exception(); | ||
+ | } | ||
+ | |||
+ | SecurityAttributes attributes3 = new SecurityAttributes(); | ||
+ | attributes3.bInheritHandle = true; | ||
+ | int num1 = 0x40000000; | ||
+ | if (parentInputs) | ||
+ | { | ||
+ | num1 = -2147483648; | ||
+ | } | ||
+ | childHandle = CreateFile(pipename, | ||
+ | if (childHandle == INVALID_HANDLE_VALUE) | ||
+ | { | ||
+ | throw new Win32Exception(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ===== Примеры использования ===== | ||
+ | ==== Запуск процесса (powershell.exe) от имени залогиненного имперсонированного пользователя ==== | ||
+ | Для доступа к ресурсам за пределами сервера нужна аутентификация kerberos (обязательно), | ||
+ | На уровне всего приложения **< | ||
+ | < | ||
+ | //Get User Identity for Initializing Impersonation Context | ||
+ | var current = System.Security.Principal.WindowsIdentity.GetCurrent(); | ||
+ | WindowsIdentity clientId = (WindowsIdentity)User.Identity; | ||
+ | string ScriptFilePath = @""; | ||
+ | string Arguments = @""; | ||
+ | |||
+ | using (WindowsImpersonationContext wic = clientId.Impersonate()) | ||
+ | { | ||
+ | UserSpecificProcess myProcess = new UserSpecificProcess(); | ||
+ | myProcess.StartInfo.FileName = " | ||
+ | myProcess.StartInfo.Arguments = @" | ||
+ | |||
+ | // | ||
+ | |||
+ | myProcess.StartInfo.UseShellExecute = false; | ||
+ | myProcess.StartInfo.CreateNoWindow = false; | ||
+ | myProcess.StartInfo.WorkingDirectory = ScriptWorkingDirectory; | ||
+ | myProcess.StartAsUser(); | ||
+ | StreamReader output = myProcess.StandardOutput; | ||
+ | |||
+ | //wait then kill process | ||
+ | if (!myProcess.WaitForExit(ScriptExecutionTimeout)) | ||
+ | { | ||
+ | myProcess.Kill(); | ||
+ | } | ||
+ | |||
+ | string line; | ||
+ | while ((line = output.ReadLine()) != null) | ||
+ | { | ||
+ | // We use a string builder ton create our result text | ||
+ | ResultBuilder.Append(line + " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Запуск процесса (powershell.exe) от имени конкретного пользователя ==== | ||
+ | < | ||
+ | protected void RunScript() | ||
+ | { | ||
+ | string ScriptFilePath = @" | ||
+ | string Arguments = @""; | ||
+ | string ScriptWorkingDirectory = @" | ||
+ | string UserName = " | ||
+ | string Domain = " | ||
+ | string Password = " | ||
+ | |||
+ | UserSpecificProcess proc = new UserSpecificProcess(); | ||
+ | |||
+ | proc.StartInfo.UseShellExecute = false; | ||
+ | proc.StartInfo.CreateNoWindow = false; | ||
+ | proc.StartInfo.RedirectStandardError = true; | ||
+ | proc.StartInfo.RedirectStandardOutput = true; | ||
+ | proc.StartInfo.FileName = " | ||
+ | proc.StartInfo.WorkingDirectory = ScriptWorkingDirectory; | ||
+ | proc.StartInfo.Arguments = @" | ||
+ | |||
+ | proc.StartAsUserWithLogon(UserName, | ||
+ | |||
+ | while (!proc.HasExited && proc.Responding) | ||
+ | { | ||
+ | System.Threading.Thread.Sleep(1000); | ||
+ | } | ||
+ | } | ||
+ | </ |