MVC:以不同用户身份启动进程 - 不工作
MVC: start process as different user - not working
我需要从 MVC 控制器 运行 服务器上的可执行文件。问题:可执行文件位于 Program Files 文件夹中,并且还会从注册表中读取一个值。
我已将相应文件夹的执行权限授予我的应用程序池。
所以这是我的问题:
运行 带有 Process.Start(exe)
的 exe 将启动可执行文件,该可执行文件又会因错误退出,因为它无法读取注册表值(无法访问)。
将本地管理员用户分配给 ProcessStartInfo
失败:
var exe = @"C:\Program Files (x86)\[path to exe]";
var secString = new SecureString();
secString.AppendChar('character');
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
UserName = "[username]",
Domain = "[domain]",
Password = secString,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Verb = "runas"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
这将导致conhost 和可执行文件崩溃。
像这样使用模拟:
var impers = new ImpersonationService();
impers.PerformImpersonatedTask("[user]", "[domain]", "[password]",
ImpersonationService.LOGON32_LOGON_INTERACTIVE, ImpersonationService.LOGON32_PROVIDER_DEFAULT, new Action(RunClient));
...使用方法 RunClient()
简单地使用 Process.Start(exe)
绝对没有任何作用!方法是 运行 但进程未启动。我知道该方法是 运行 因为我在其中添加了这一行:
_logger.Debug("Impersonated: {0}", Environment.UserName);
这正确地为我提供了进程应使用的所需用户名。该用户具有本地管理员权限,因此应该没有问题。
我什至尝试从我的 Controller 启动一个不同的可执行文件,并且那个使用模拟(两种变体)来启动目标可执行文件 - 结果相同。
所以现在我走投无路了。任何人都可以告诉我我做错了什么以及我必须做些什么才能让它发挥作用吗?
P.S:运行当以本地管理员用户身份登录时,直接在服务器上运行目标可执行文件工作得很好,所以 exe 本身没有问题。
Edit:
我的描述似乎有一部分不正确:我实际上使用模拟和 RunClient 方法 不 使用 Process.Start(exe)
但是这个:
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
};
_logger.Debug("Impersonated: {0}", Environment.UserName);
var proc = Process.Start(procInfo);
出于绝望,我现在绕过了 procInfo
(实际上并不需要它)并且 真的 调用了
var proc = Process.Start(exe, argument);
现在 .exe 启动了!似乎使用 ProcessStartInfo 覆盖了进程的模拟??
不过还是不行,因为现在我收到 "Access denied" 错误。尽管是本地管理员。这很奇怪。
Edit 2:
This is how my latest attempt went:
- 切换回调用 helper .exe,传递稍后用于 Program Files 中实际目标 exe 的相同参数
- 使用
level="requireAdministrator"
向该帮助程序 exe 添加了一个清单
- 根据 https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx 将模拟添加到我的助手 exe,并在启动目标进程的方法之前添加了
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
。
- 通过为所有 jazz 提供 ProcessStartInfo 来启动流程
结果代码:
try
{
var secString = new SecureString();
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo()
{
FileName = Path.GetFileName(exe),
UserName = "[UserName]",
Domain = "[domain]",
Password = secString,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Arguments = settingsPath,
WorkingDirectory = @"C:\Program Files (x86)\[rest]"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
if (proc.ExitCode != 0)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}", proc.ExitCode.ToString());
}
}
}
catch (Exception ex)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}\r\nRunning as: {1}", ex.ToString(), WindowsIdentity.GetCurrent().Name);
}
}
结果输出到 error.log:
Helper running!
[passed argument]
Error running process: System.ComponentModel.Win32Exception
(0x80004005): Access denied at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at System.Diagnostics.Process.Start() at
System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at
RunClient.ImpersonationDemo.RunClient(String settingsPath)
Running as: [correct domain user in Admin group]
所以我可以启动帮助程序 exe,但是 无法在程序文件中启动真正的 exe,因为访问被拒绝,尽管 运行 在本地管理员帐户下和所有文件在本地访问,而不是在网络驱动器上。
我不明白其中的逻辑。
Edit 3
更新:我也向目标 .exe 添加了一个清单,
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
这意味着我现在:
- 从控制器调用助手 exe:有效
- 帮助程序 .exe 有一份 运行 的清单,具有提升的权限(管理员)
- helper .exe 使用模拟来假定本地管理员的身份来启动进程
- 所述进程使用 ProcessStartInfo 启动,其中用户名、域和密码另外设置为同一本地管理员用户
- 助手 exe 然后尝试使用
Process.Start(Startinfo)
和本地管理员用户集 运行 目标 exe,同时仍然模拟该用户的 windows 身份
并且仍然错误日志喷出"Access denied"同时正确返回WindowsIdentity.GetCurrent().Name
作为本地管理员。
现在,最重要的事情发生了:我在该服务器上创建了一个新的本地用户,将他添加到本地管理员组并使用 那个 用户进行模拟以防万一是域用户的问题。你猜怎么着?现在我得到一个错误 Access denied to ...\error.log
- 写入有效错误日志。
真的吗?
Edit 4
我想我会尝试使用 TopShelf 将这个 shebang 转换为服务。希望能在周末完成。
根据 this article,您的 mvc 控制器线程应该对 运行 进程具有完全信任权限:
This class contains a link demand at the class level that applies to
all members. A SecurityException is thrown when the immediate caller
does not have full-trust permission. For details about security
demands, see Link Demands.
看来你的问题不是用户而是完全信任。我不知道您使用的是哪个版本的 MVC,但您可以阅读文章 Trust Levels and Code Access 以找出配置应用程序的最佳方法。似乎您只能授予对特定 .exe 文件的完全信任权限或授予应用程序池用户完全信任权限(不要忘记文件夹权限)。
但最好的方法是编写一些 windows 服务和 运行 它而不是 运行 直接编写一些 .exe 文件。
您可以在您的应用程序web.config
中尝试信任级别
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>
我需要从 MVC 控制器 运行 服务器上的可执行文件。问题:可执行文件位于 Program Files 文件夹中,并且还会从注册表中读取一个值。 我已将相应文件夹的执行权限授予我的应用程序池。 所以这是我的问题:
运行 带有 Process.Start(exe)
的 exe 将启动可执行文件,该可执行文件又会因错误退出,因为它无法读取注册表值(无法访问)。
将本地管理员用户分配给 ProcessStartInfo
失败:
var exe = @"C:\Program Files (x86)\[path to exe]";
var secString = new SecureString();
secString.AppendChar('character');
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
UserName = "[username]",
Domain = "[domain]",
Password = secString,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Verb = "runas"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
这将导致conhost 和可执行文件崩溃。
像这样使用模拟:
var impers = new ImpersonationService();
impers.PerformImpersonatedTask("[user]", "[domain]", "[password]",
ImpersonationService.LOGON32_LOGON_INTERACTIVE, ImpersonationService.LOGON32_PROVIDER_DEFAULT, new Action(RunClient));
...使用方法 RunClient()
简单地使用 Process.Start(exe)
绝对没有任何作用!方法是 运行 但进程未启动。我知道该方法是 运行 因为我在其中添加了这一行:
_logger.Debug("Impersonated: {0}", Environment.UserName);
这正确地为我提供了进程应使用的所需用户名。该用户具有本地管理员权限,因此应该没有问题。
我什至尝试从我的 Controller 启动一个不同的可执行文件,并且那个使用模拟(两种变体)来启动目标可执行文件 - 结果相同。
所以现在我走投无路了。任何人都可以告诉我我做错了什么以及我必须做些什么才能让它发挥作用吗?
P.S:运行当以本地管理员用户身份登录时,直接在服务器上运行目标可执行文件工作得很好,所以 exe 本身没有问题。
Edit:
我的描述似乎有一部分不正确:我实际上使用模拟和 RunClient 方法 不 使用 Process.Start(exe)
但是这个:
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
};
_logger.Debug("Impersonated: {0}", Environment.UserName);
var proc = Process.Start(procInfo);
出于绝望,我现在绕过了 procInfo
(实际上并不需要它)并且 真的 调用了
var proc = Process.Start(exe, argument);
现在 .exe 启动了!似乎使用 ProcessStartInfo 覆盖了进程的模拟??
不过还是不行,因为现在我收到 "Access denied" 错误。尽管是本地管理员。这很奇怪。
Edit 2: This is how my latest attempt went:
- 切换回调用 helper .exe,传递稍后用于 Program Files 中实际目标 exe 的相同参数
- 使用
level="requireAdministrator"
向该帮助程序 exe 添加了一个清单
- 根据 https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx 将模拟添加到我的助手 exe,并在启动目标进程的方法之前添加了
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
。 - 通过为所有 jazz 提供 ProcessStartInfo 来启动流程
结果代码:
try
{
var secString = new SecureString();
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo()
{
FileName = Path.GetFileName(exe),
UserName = "[UserName]",
Domain = "[domain]",
Password = secString,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Arguments = settingsPath,
WorkingDirectory = @"C:\Program Files (x86)\[rest]"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
if (proc.ExitCode != 0)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}", proc.ExitCode.ToString());
}
}
}
catch (Exception ex)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}\r\nRunning as: {1}", ex.ToString(), WindowsIdentity.GetCurrent().Name);
}
}
结果输出到 error.log:
Helper running! [passed argument] Error running process: System.ComponentModel.Win32Exception (0x80004005): Access denied at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) at System.Diagnostics.Process.Start() at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at RunClient.ImpersonationDemo.RunClient(String settingsPath) Running as: [correct domain user in Admin group]
所以我可以启动帮助程序 exe,但是 无法在程序文件中启动真正的 exe,因为访问被拒绝,尽管 运行 在本地管理员帐户下和所有文件在本地访问,而不是在网络驱动器上。
我不明白其中的逻辑。
Edit 3
更新:我也向目标 .exe 添加了一个清单,
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
这意味着我现在:
- 从控制器调用助手 exe:有效
- 帮助程序 .exe 有一份 运行 的清单,具有提升的权限(管理员)
- helper .exe 使用模拟来假定本地管理员的身份来启动进程
- 所述进程使用 ProcessStartInfo 启动,其中用户名、域和密码另外设置为同一本地管理员用户
- 助手 exe 然后尝试使用
Process.Start(Startinfo)
和本地管理员用户集 运行 目标 exe,同时仍然模拟该用户的 windows 身份
并且仍然错误日志喷出"Access denied"同时正确返回WindowsIdentity.GetCurrent().Name
作为本地管理员。
现在,最重要的事情发生了:我在该服务器上创建了一个新的本地用户,将他添加到本地管理员组并使用 那个 用户进行模拟以防万一是域用户的问题。你猜怎么着?现在我得到一个错误 Access denied to ...\error.log
- 写入有效错误日志。
真的吗?
Edit 4
我想我会尝试使用 TopShelf 将这个 shebang 转换为服务。希望能在周末完成。
根据 this article,您的 mvc 控制器线程应该对 运行 进程具有完全信任权限:
This class contains a link demand at the class level that applies to all members. A SecurityException is thrown when the immediate caller does not have full-trust permission. For details about security demands, see Link Demands.
看来你的问题不是用户而是完全信任。我不知道您使用的是哪个版本的 MVC,但您可以阅读文章 Trust Levels and Code Access 以找出配置应用程序的最佳方法。似乎您只能授予对特定 .exe 文件的完全信任权限或授予应用程序池用户完全信任权限(不要忘记文件夹权限)。
但最好的方法是编写一些 windows 服务和 运行 它而不是 运行 直接编写一些 .exe 文件。
您可以在您的应用程序web.config
中尝试信任级别
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>