Trying to run a process as a different user gave me: 0xC0000142.
From an existing process running under a regular local account, why do this process receive 0xC0000142 when it try to start another process using Process.Execute with ProcessStartInfo having any other credentials (either a local account or a domain account)?
Not supplying credentials is OK but it does run under the same credentials as the calling process (which I want to avoid).
Logging direclty on the machine with any credentials account and running the process works OK too (both tests were done using account with admin rights).
Note: Every executable are console app (no CreateWindow()...).
Context (which could be potentially skipped):
I'm using HTCondor. HTCondor is a 'windows service' running as 'System' (normal service) that it's main work is execute jobs. To execute each job, HtCondor start a process using a normal user account created on the fly named "condor-slot[1-4]" (Note that the account is part of the 'User' group in lusrmgr.msc and I see it as user in local accounts).
Actually the job that HTCondor should run is my own application "ForwardExecution.exe". This process take automatically the same environment as user "condor-slot[1-4]" which is running under (normal user local account).
The job 'ForwardExecution.Exe' is to start another process.
ForwardExecution.Exe is using "Process.Execute". I wrap the call into a class provided below.
ForwardExecution.exe has 2 differents behavior, depending on the way it is called. I would want them to be the same, if possible:
Starting a process without supplying credentials (using current user account / local regular account) is working perfect. Doing the same action, but passing with my own credentials (domain, user and password / admin of the machine) to Process.Execute return OxC0000142. Also, if I call ForwardExecution.exe directly from a regular session with my credentials, everything works fine.
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);
}
}
// ************************************************************************
}
}
User contributions licensed under CC BY-SA 3.0