来自Process.Execute的0xC0000142具有不同的凭据

时间:2017-01-30 21:50:52

标签: c# process credentials account

尝试以不同的用户身份运行进程:0xC0000142。

在常规本地帐户下运行的现有流程中,为什么此流程在尝试使用带有任何其他凭据(本地帐户或域帐户)的ProcessStartInfo的Process.Execute启动另一个流程时会收到0xC0000142?

不提供凭据是可以的,但它确实在与调用进程相同的凭据下运行(我想避免)。

使用任何凭据帐户在计算机上记录direclty并运行该过程也正常(两个测试均使用具有管理员权限的帐户完成)。

注意:每个可执行文件都是控制台应用程序(没有CreateWindow()...)。

上下文(可能会被跳过):

我正在使用HTCondor。 HTCondor是一个'Windows服务',作为'系统'(正常服务)运行,它的主要工作是执行作业。为了执行每个作业,HtCondor使用名为“condor-slot [1-4]”的动态创建的普通用户帐户启动流程(请注意,该帐户是lusrmgr.msc中“用户”组的一部分,我看到它作为本地帐户中的用户。)

实际上HTCondor应该运行的工作是我自己的应用程序“ForwardExecution.exe”。此过程自动采用与(普通用户本地帐户)下运行的用户“condor-slot [1-4]”相同的环境。

作业'ForwardExecution.Exe'是开始另一个过程。

ForwardExecution.Exe正在使用“Process.Execute”。我将调用包装到下面提供的类中。

ForwardExecution.exe有2种不同的行为,具体取决于它的调用方式。如果可能的话,我希望它们是相同的:

启动流程而不提供凭据(使用当前用户帐户/本地常规帐户)是完美的。执行相同的操作,但使用我自己的凭据(机器的域,用户和密码/管理员)传递给Process.Execute返回OxC0000142。此外,如果我使用我的凭据直接从常规会话调用ForwardExecution.exe,一切正常。

using System;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Text;
using System.Threading;

namespace HQ.Util.General
{
    public class ProcessExecutionWithOutputCapture
    {
        // ************************************************************************
        public class ProcessWithOutputCaptureResult
        {
            public string Error { get; internal set; }

            public string Output { get; internal set; }

            public string ExceptionOrError
            {
                get
                {
                    StringBuilder error = new StringBuilder();

                    if (ExitCode != 0)
                    {
                        error.AppendLine($"Exit code: {ExitCode.ToString("X")}.");
                    }

                    if (! String.IsNullOrEmpty(Error))
                    {
                        error.AppendLine($"Error : {Error}.");
                    }

                    if (Exception != null && !String.IsNullOrEmpty(Exception.ToString()))
                    {
                        error.AppendLine($"Exception : {Exception}.");
                    }

                    return error.ToString();
                }
            }

            public bool HasTimeout { get; internal set; }

            /// <summary>
            /// Can be cancel through the eventCancel which will cancel the wait (and if set, will kill the process)
            /// </summary>
            public bool HasBeenCanceled { get; internal set; }

            public int ExitCode { get; internal set; }

            public Exception Exception { get; internal set; }

            public bool HasSucceded => !HasTimeout && Exception == null;

            public override string ToString()
            {
                if (ExceptionOrError != null)
                {
                    return "Error: " + ExceptionOrError;
                }

                return "Succes with output: " + Output;
            }
        }

        // ************************************************************************
        private StringBuilder _sbOutput = new StringBuilder();
        private StringBuilder _sbError = new StringBuilder();

        private AutoResetEvent _outputWaitHandle = null;
        private AutoResetEvent _errorWaitHandle = null;

        // Could be usefull when user want to exit to not wait for process to end and kill it (if wanted)
        public EventWaitHandle HandleCancelWait { get; set; }
        public bool IsAdditionalConditionToStopWaitingProcessShouldAlsoKill { get; set; }

        public ProcessWindowStyle ProcessWindowStyle { get; set; } = ProcessWindowStyle.Hidden;
        public bool CreateNoWindow { get; set; } = true;
        public string WorkingDirectory { get; set; }


        public string UserName { get; set; }
        public string Password { get; set; }

        // ************************************************************************
        public static ProcessWithOutputCaptureResult ExecuteWith(string executablePath, string arguments, int timeout = Timeout.Infinite, ProcessWindowStyle processWindowStyle = ProcessWindowStyle.Hidden, bool createNoWindow = true)
        {
            var p = new ProcessExecutionWithOutputCapture();
            p.ProcessWindowStyle = processWindowStyle;
            p.CreateNoWindow = createNoWindow;
            return p.Execute(executablePath, arguments, timeout);
        }

        // ************************************************************************
        public static ProcessWithOutputCaptureResult ExecuteWith(string executablePath, string arguments, EventWaitHandle handleCancelWait)
        {
            var p = new ProcessExecutionWithOutputCapture();
            p.HandleCancelWait = handleCancelWait;
            return p.Execute(executablePath, arguments);
        }

        // ************************************************************************
        public static ProcessWithOutputCaptureResult ExecuteWith(string executablePath, string arguments, string workingDirectory, EventWaitHandle handleCancelWait)
        {
            var p = new ProcessExecutionWithOutputCapture();
            p.HandleCancelWait = handleCancelWait;
            p.WorkingDirectory = workingDirectory;
            return p.Execute(executablePath, arguments);
        }

        public static ProcessWithOutputCaptureResult ExecuteWith(string executablePath, string arguments, string userName, string password)
        {
            var p = new ProcessExecutionWithOutputCapture();
            p.UserName = userName;
            p.Password = password;
            return p.Execute(executablePath, arguments);
        }

        // ************************************************************************
        /// <summary>
        /// Only support existing exectuable (no association or dos command which have no executable like 'dir').
        /// But accept full path, partial path or no path where it will use regular system/user path.
        /// </summary>
        /// <param name="executablePath"></param>
        /// <param name="arguments"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        private ProcessWithOutputCaptureResult Execute(string executablePath, string arguments = null, int timeout = Timeout.Infinite)
        {
            ProcessWithOutputCaptureResult processWithOutputCaptureResult = null;

            using (Process process = new Process())
            {
                process.StartInfo.FileName = executablePath;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.UseShellExecute = false; // required to redirect output to appropriate (output or error) process stream

                if (UserName != null)
                {
                    string userName;
                    string domain;

                    int indexAt = UserName.IndexOf('@');
                    if (indexAt > 0)
                    {
                        userName = UserName.Substring(0, indexAt);
                        domain = UserName.Substring(indexAt + 1, UserName.Length - indexAt - 1);
                    }
                    else
                    {
                        int indexBackslash = UserName.IndexOf('\\');
                        if (indexBackslash > 0)
                        {
                            domain = UserName.Substring(0, indexBackslash);
                            userName = UserName.Substring(indexBackslash + 1, UserName.Length - indexBackslash - 1);
                        }
                        else
                        {
                            userName = UserName;
                            domain = null;
                        }
                    }

                    process.StartInfo.UserName = userName;
                    var secure = new SecureString();
                    if (Password != null)
                    {
                        foreach (char c in Password)
                        {
                            secure.AppendChar(c);
                        }
                        process.StartInfo.Password = secure;
                    }

                    process.StartInfo.Domain = domain;
                }
                process.StartInfo.WindowStyle = ProcessWindowStyle;
                process.StartInfo.CreateNoWindow = CreateNoWindow;

                process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; // Same working directory as the one we are in right now
                process.StartInfo.Verb = "runas";

                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;

                _outputWaitHandle = new AutoResetEvent(false);
                _errorWaitHandle = new AutoResetEvent(false);

                bool isAsyncReadStarted = false;

                try
                {
                    process.OutputDataReceived += ProcessOnOutputDataReceived;
                    process.ErrorDataReceived += ProcessOnErrorDataReceived;

                    process.Start();

                    // Here there is a race condition. See: https://connect.microsoft.com/VisualStudio/feedback/details/3119134/race-condition-in-process-asynchronous-output-stream-read
                    // It is highly probable that it will not occur due to time to create a process should be pretty long.
                    // Time to create a process should be big enough to make sure we will process
                    // the 2 next lines before the newly created process will write to the console.

                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine();

                    isAsyncReadStarted = true;

                    // See: ProcessStartInfo.RedirectStandardOutput Property:
                    //      https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Diagnostics.ProcessStartInfo.RedirectStandardOutput);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true
                    // All 4 next lines should only be called when not using asynchronous read (process.BeginOutputReadLine() and process.BeginErrorReadLine())
                    //_sbOutput.AppendLine(process.StandardOutput.ReadToEnd());
                    //_sbError.AppendLine(process.StandardError.ReadToEnd());
                    //_sbOutput.AppendLine(process.StandardOutput.ReadToEnd());
                    //_sbError.AppendLine(process.StandardError.ReadToEnd());

                    var waitHandles = new WaitHandle[1 + (HandleCancelWait == null ? 0 : 1)];

                    waitHandles[0] = new ProcessWaitHandle(process);
                    if (HandleCancelWait != null)
                    {
                        waitHandles[1] = HandleCancelWait;
                    }

                    bool hasSucceded = false;
                    int waitResult = WaitHandle.WaitAny(waitHandles, timeout);
                    if (waitResult == 1) // The wait has been interrrupted by an external event
                    {
                        if (IsAdditionalConditionToStopWaitingProcessShouldAlsoKill)
                        {
                            process.Kill();
                        }
                    }
                    else if (waitResult == 0) // Process has completed normally, no timeout or external event
                    {
                        // Ensure internal process code has completed like ensure to wait until stdout et stderr had been fully completed
                        hasSucceded = process.WaitForExit(timeout);
                        if (_outputWaitHandle.WaitOne(timeout) && _errorWaitHandle.WaitOne(timeout))
                        {
                            processWithOutputCaptureResult = new ProcessWithOutputCaptureResult();
                            processWithOutputCaptureResult.ExitCode = process.ExitCode;
                            processWithOutputCaptureResult.Output = _sbOutput.ToString();
                            processWithOutputCaptureResult.Error = _sbError.ToString();
                        }
                    }
                    else // Process timeout
                    {
                        processWithOutputCaptureResult = new ProcessWithOutputCaptureResult();
                        processWithOutputCaptureResult.HasTimeout = true;
                    }
                }
                catch (Exception ex)
                {
                    if (ex.HResult == -2147467259)
                    {
                        processWithOutputCaptureResult = new ProcessWithOutputCaptureResult();
                        processWithOutputCaptureResult.Exception = new FileNotFoundException("File not found: " + executablePath, ex);
                    }
                    else
                    {
                        processWithOutputCaptureResult = new ProcessWithOutputCaptureResult();
                        processWithOutputCaptureResult.Exception = ex;
                    }
                }
                finally
                {
                    if (isAsyncReadStarted)
                    {
                        process.CancelOutputRead();
                        process.CancelErrorRead();
                    }

                    process.OutputDataReceived -= ProcessOnOutputDataReceived;
                    process.ErrorDataReceived -= ProcessOnOutputDataReceived;

                    _outputWaitHandle.Close();
                    _outputWaitHandle.Dispose();

                    _errorWaitHandle.Close();
                    _errorWaitHandle.Dispose();
                }
            }

            return processWithOutputCaptureResult;
        }

        // ************************************************************************
        private void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data == null)
            {
                _outputWaitHandle.Set();
            }
            else
            {
                _sbOutput.AppendLine(e.Data);
            }
        }

        // ************************************************************************
        private void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data == null)
            {
                _errorWaitHandle.Set();
            }
            else
            {
                _sbError.AppendLine(e.Data);
            }
        }

        // ************************************************************************
    }
}

0 个答案:

没有答案