Задача простая: запустить процесс под определенным пользователям.
Просто создать процесс получается без проблем:
[Microsoft.SqlServer.Server.SqlFunction]
public static string CreateProcess()
{
Process.Start(@"C:\WINDOWS\notepad.exe");
return "";
}
После вызова функции появляется процесс "notepad" запущенный под пользователем "SYSTEM"
Если же попытаться запустить процесс под другим пользователем:
[Microsoft.SqlServer.Server.SqlFunction]
public static string CreateProcess()
{
SecureString ssPassword = new SecureString();
foreach (char c in "password")
ssPassword.AppendChar(c);
Process.Start(@"C:\WINDOWS\notepad.exe", "admin", ssPassword, "");
return "";
}
возникает следующее сообщение об ошибке:
A .NET Framework error occurred during execution of user defined routine or aggregate 'CreateProcess':
System.ComponentModel.Win32Exception: Access is denied
System.ComponentModel.Win32Exception:
at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(String fileName, String userName, SecureString password, String domain)
at HFUDF.HFUDFFunctions.CreateProcess()
Видимо не хватает прав, но кому и каких?
Сборка регистрируется как UNSAFE.
Заранее спасибо.
Если кому интересно, то дело вот в чем
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.startinfo.aspx
If you call the Start(ProcessStartInfo) method with the ProcessStartInfo.UserName and
ProcessStartInfo.Password properties set, the unmanaged CreateProcessWithLogonW function
is called, which starts the process in a new window even if the CreateNoWindow property
value is true or the WindowStyle property value is Hidden.
Попытка создать окно под SYSTEM и вызывает “System.ComponentModel.Win32Exception: Access is denied”.
Решить получилось так:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security.Principal;
using System.Text;
namespace HFUDF
{
/// <summary>
/// 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.
/// "Replace a process level token"/SE_ASSIGNPRIMARYTOKEN_NAME/SeAssignPrimaryTokenPrivilege
/// "Adjust memory quotas for a process"/SE_INCREASE_QUOTA_NAME/SeIncreaseQuotaPrivilege
///
/// 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.
/// </summary>
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("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern bool CloseHandle(HandleRef handle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool
CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, StringBuilder lpCommandLine, SecurityAttributes lpProcessAttributes, SecurityAttributes lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, CreateProcessStartupInfo lpStartupInfo, CreateProcessProcessInformation lpProcessInformation);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessAsUserW(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, [MarshalAs(UnmanagedType.LPTStr)] string lpCommandLine, SecurityAttributes lpProcessAttributes, SecurityAttributes lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, CreateProcessStartupInfo lpStartupInfo, CreateProcessProcessInformation lpProcessInformation);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetStdHandle(int whichHandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, SecurityAttributes lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HandleRef hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances, int outBufSize, int inBufSize, int timeout, SecurityAttributes lpPipeAttributes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetConsoleOutputCP();
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DuplicateTokenEx(HandleRef hToken, int access, SecurityAttributes tokenAttributes, int impersonationLevel, int tokenType, ref IntPtr hNewToken);
// 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, IntPtr.Zero);
/// <summary>
/// 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.
/// </summary>
/// <param name="process">The process to start.</param>
public void StartAsUser()
{
StartAsUser(WindowsIdentity.GetCurrent().Token);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="process"></param>
public void StartAsUser(IntPtr userToken)
{
if (StartInfo.UseShellExecute)
{
throw new InvalidOperationException("can't call this with shell execute");
}
// 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, out stdoutWriteHandle, false);
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();
int creationFlags = 0;
IntPtr environment = IntPtr.Zero;
string workingDirectory = GetWorkingDirectory();
// create the process or fail trying.
if (!CreateProcessAsUserW(
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, processInformation.hThread));
}
// close client stdout handle
CloseHandle(new HandleRef(this, stdoutWriteHandle));
}
}
/// <summary>
/// Creates a primayToken out of an existing token.
/// </summary>
/// <param name="userToken"></param>
private IntPtr CreatePrimaryToken(IntPtr userToken)
{
SecurityAttributes securityAttributes = new SecurityAttributes();
IntPtr primaryUserToken = IntPtr.Zero;
if (!DuplicateTokenEx(new HandleRef(this, userToken), GENERIC_ALL, securityAttributes, SECURITY_IMPERSONATION, TOKEN_PRIMARY, ref
primaryUserToken))
{
throw new Win32Exception();
}
return primaryUserToken;
}
/// <summary>
/// Gets the appropriate commandLine from the process.
/// </summary>
/// <param name="process"></param>
/// <returns></returns>
private string GetCommandLine()
{
StringBuilder builder1 = new StringBuilder();
string text1 = StartInfo.FileName.Trim();
string text2 = StartInfo.Arguments;
bool flag1 = text1.StartsWith("\"") && text1.EndsWith("\"");
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();
}
/// <summary>
/// Gets the working directory or returns null if an empty string was found.
/// </summary>
/// <returns></returns>
private string GetWorkingDirectory()
{
return (StartInfo.WorkingDirectory != string.Empty) ?
StartInfo.WorkingDirectory : null;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="parentHandle"></param>
/// <param name="childHandle"></param>
/// <param name="parentInputs">Specifies whether the parent will be performing the writes.</param>
public static void MyCreatePipe(out IntPtr parentHandle, out IntPtr childHandle, bool parentInputs)
{
string pipename = @"\\.\pipe\Global\" + Guid.NewGuid().ToString();
SecurityAttributes attributes2 = new SecurityAttributes();
attributes2.bInheritHandle = false;
parentHandle = CreateNamedPipe(pipename, 0x40000003, 0, 0xff, 0x1000, 0x1000, 0, attributes2);
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, num1, 3, attributes3, 3, 0x40000080, NullHandleRef);
if (childHandle == INVALID_HANDLE_VALUE)
{
throw new Win32Exception();
}
}
}
}
и вызвать
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
IntPtr tokenHandle = new IntPtr(0);
bool returnValue = UserSpecificProcess.LogonUser(login, "", password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
UserSpecificProcess proc = new UserSpecificProcess();
proc.StartInfo.WorkingDirectory = @"C:\";
proc.StartInfo.FileName = @"C:\HFUDFClient.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartAsUser(tokenHandle);