【问题标题】:MVC: start process as different user - not workingMVC:以不同的用户身份启动进程 - 不工作
【发布时间】:2016-11-14 14:18:33
【问题描述】:

我需要从 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 本身没有问题。

编辑:

我的描述的一部分似乎不正确:使用模拟和 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 会覆盖进程的模拟??

但仍然不行,因为现在我收到“访问被拒绝”错误。尽管是本地管理员。这很奇怪。

编辑 2: 这就是我最近一次尝试的方式:

  • 切换回调用帮助程序 .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")]
  • 通过向 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:

帮手跑了! [通过论据] 错误运行过程:System.ComponentModel.Win32Exception (0x80004005): 访问被拒绝 System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) 在 System.Diagnostics.Process.Start() 在 System.Diagnostics.Process.Start(ProcessStartInfo startInfo) 在 RunClient.ImpersonationDemo.RunClient(String settingsPath) 运行为:[管理员组中的正确域用户]

所以我可以启动帮助程序 exe,但 无法在 Program Files 中启动真正的 exe,因为尽管在本地管理员帐户下运行并且所有文件都在本地访问,而不是在网络驱动器上,但访问被拒绝。

我不明白这个逻辑。

编辑 3

更新:我还向目标 .exe 添加了清单,

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

这意味着我现在:

  • 从控制器调用辅助 exe:有效
  • 帮助程序 .exe 有一个清单,可以使用提升的权限(管理员)运行
  • 帮助程序 .exe 使用模拟来假定本地管理员的身份来启动进程
  • 所述进程使用 ProcessStartInfo 启动,其中用户名、域和密码另外设置为相同的本地管理员用户
  • helper exe 然后尝试使用 Process.Start(Startinfo) 和本地管理员用户集运行目标 exe,同时仍然模拟该用户的 Windows 身份

并且仍然错误日志显示“访问被拒绝”,同时以本地管理员的身份正确返回 WindowsIdentity.GetCurrent().Name

现在,最重要的事情发生了:我在该服务器上创建了一个新的本地用户,将他添加到本地管理员组并使用 用户进行模拟,以防域出现问题用户。你猜怎么了?现在我收到一个错误Access denied to ...\error.log - 写入 effing 错误日志。

真的吗?

编辑 4

我想我会尝试 TopShelf 将这个 shebang 转换为服务。希望能在周末完成这项工作。

【问题讨论】:

  • [域][用户名]是否有权在您看到错误的计算机上运行?
  • @MattThurston 该用户拥有完全权限。要运行应用程序,请更改注册表项等。如果我以同样的用户身份通过​​ RDP 登录,我可以做任何我喜欢的事情。它本身不是用户权限,但显然某种信任设置仅在从 asp.net / MVC / IIS / 调用第二个进程模拟该用户时生效。我想如果我可以自行确定 Hangfire 运行的应用程序池身份,我应该能够解决这个问题。
  • @LocEngineer,如何编写 Windows 服务并使用 IPC(套接字、管道等)与其通信。该服务的功能将只是启动一些 exe 文件并返回其 ExitCode。

标签: c# asp.net-mvc process impersonation


【解决方案1】:

根据this article,您的 mvc 控制器线程应该具有运行进程的完全信任权限:

该类包含类级别的链接需求,适用于 所有成员。当直接调用者时抛出 SecurityException 没有完全信任权限。有关安全性的详细信息 需求,请参阅链接需求。

看来你的问题不是用户而是完全信任。我不知道您使用的是哪个版本的 MVC,但您可以阅读文章 Trust LevelsCode Access 以找出配置应用程序的最佳方法。似乎您可以仅向特定的 .exe 文件授予完全信任权限或向应用程序池用户授予完全信任权限(不要忘记文件夹权限)。

但最好的方法是编写一些windows服务并运行它,而不是直接运行一些.exe文件。

【讨论】:

  • 是的,我很确定这是由某些信任设置引起的。你的建议听起来不错。唉,当我尝试发布时,在 web.config 中调整信任设置会给我一个错误It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level.。已经尝试了 SO 上提到的几种解决方法,到目前为止没有任何效果。因此,我试图通过单击一次来简单地设置 exe 本身的信任设置。虽然我设法破坏了我的应用程序,甚至我最新的 SourceTree 恢复也没有解决新问题,但这需要一点时间。 :-?会尽快回来
  • 你能指定你的 .net 和 mvc 版本吗?只是为了在同一页面上
  • targeting Framework: 4.5, MVC 5, .exe 需要通过 Hangfire 1.6 BackgroundJob 启动(即通过 DefaultAppPool 而不是特定于应用程序的 appPool;我应该包含该信息)。
  • 那么,您不是从控制器方法调用 Process.Start,而是从 BackgroundJob 调用?
  • 我有一个控制器方法叫做DoCheckIn。在我的控制器中的一个 ActionResult (POST) 中,我在返回视图之前调用 BackgroundJob.Enqueue(() => DoCheckIn(dict, rootpfad, projid) );,并让 Hangfire 在后台调用该方法。
【解决方案2】:

您可以在您的应用程序的web.config 中尝试信任级别

<system.web>
  <securityPolicy>
    <trustLevel name="Full" policyFile="internal"/>
  </securityPolicy>
</system.web>

【讨论】:

  • 去过那里,完成了,得到了 T 恤。不起作用,请参阅我对已接受答案的第一条评论。
猜你喜欢
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 2010-09-26
  • 2013-02-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多