Создание процесса в SQL CLR
От: SokolTry  
Дата: 11.02.09 12:12
Оценка:
Задача простая: запустить процесс под определенным пользователям.

Просто создать процесс получается без проблем:
        [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.

Заранее спасибо.
Re: Создание процесса в SQL CLR
От: meowth  
Дата: 11.02.09 15:25
Оценка:
Здравствуйте, SokolTry, Вы писали:

ST>Задача простая: запустить процесс под определенным пользователям.


ST>Видимо не хватает прав, но кому и каких?

ST>Сборка регистрируется как UNSAFE.

ST>Заранее спасибо.


Вы случаем, не под Вистой?
Если да, то при попытке сделать что-либо от лица администратора система требует elevation (вспомните окно UAC). Ваш System.Process.Start() его показать не может, что приравнивается к отказу.

Как пофиксить — копайте в сторону аттрибутов для сборки с декларативным описанием привилегий, которые ей понадобятся. Или используйте .net config.
Для любителей все делать кодом — смотрите security и persmission elevation requests.

Надеюсь, полезно.
Re: Создание процесса в SQL CLR
От: SokolTry  
Дата: 11.02.09 18:00
Оценка:
Если кому интересно, то дело вот в чем
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);
Re[2]: Создание процесса в SQL CLR
От: SokolTry  
Дата: 11.02.09 18:04
Оценка:
Здравствуйте, meowth, Вы писали:

M>Вы случаем, не под Вистой?

M>Если да, то при попытке сделать что-либо от лица администратора система требует elevation (вспомните окно UAC). Ваш System.Process.Start() его показать не может, что приравнивается к отказу.


Нет, под ХР. Но решение уже нашел и поместил в ответ на своё первое сообщение.
Спасибо за ответ.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.