使用ServiceController和模拟启动远程Windows服务

问题描述:

我有一个需要能够打开和关闭远程服务的.NET MVC3应用程序。为了做到这一点,我通过WindowsIdentity.Impersonate()模拟了一个特定的用户帐户。要测试用户的权限,我可以以用户身份登录并从命令提示符下执行sc.exe \\[server] start [service]。我也知道impersonate命令按预期工作,因为应用程序匿名运行,因此无法控制我的本地计算机上的服务(.),但没有模拟,但可以使用控制带有模拟的本地服务。然而,当我把它放在一起,并尝试启动远程服务,而不是当地的服务,我总是得到错误使用ServiceController和模拟启动远程Windows服务

有没有人经历过类似的问题,“不能计算机‘[server]’上打开[service]服务”?我希望它是一个服务器配置,而不是.NET的问题,直到我意识到sc.exe的工作没有问题。下面是类的简化版本,我使用:

public class Service 
{ 
    public string Name; 
    public bool Running; 
    private ServiceController serviceController; 

    public Service(string name, string host) 
    { 
     Name = name; 

     serviceController = new ServiceController(Name, host); 
     Running = serviceController.Status == ServiceControllerStatus.Running; 
    } 

    public bool StartService() 
    { 
     ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, serviceController.MachineName, Name); 
     scp.Assert(); 

     serviceController.Start(); 
     serviceController.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 5)); 
     serviceController.Refresh(); 

     Running = serviceController.Status == ServiceControllerStatus.Running; 

     return Running; 
    } 
} 

,还应注意:如果不是服务器我指向域上的另一台Windows 7 PC的应用,改变模拟凭据这些的那台PC的拥有者,我实际上能够远程控制他们的服务而没有问题。

根据要求,我在此处添加了模拟代码。它长一点,所以忍耐一下:

public class Impersonate 
{ 
    public const int LOGON32_LOGON_INTERACTIVE = 2; 
    public const int LOGON32_PROVIDER_DEFAULT = 0; 

    WindowsImpersonationContext impersonationContext; 

    [DllImport("advapi32.dll")] 
    public static extern int LogonUserA(String lpszUserName, 
     String lpszDomain, 
     String lpszPassword, 
     int dwLogonType, 
     int dwLogonProvider, 
     ref IntPtr phToken); 
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern int DuplicateToken(IntPtr hToken, 
     int impersonationLevel, 
     ref IntPtr hNewToken); 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern bool RevertToSelf(); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public static extern bool CloseHandle(IntPtr handle); 

    public bool impersonateValidUser(String userName, String domain, String password) 
    { 
     WindowsIdentity tempWindowsIdentity; 
     IntPtr token = IntPtr.Zero; 
     IntPtr tokenDuplicate = IntPtr.Zero; 

     if (RevertToSelf()) 
     { 
      if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, 
       LOGON32_PROVIDER_DEFAULT, ref token) != 0) 
      { 
       if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
       { 
        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
        impersonationContext = tempWindowsIdentity.Impersonate(); 
        if (impersonationContext != null) 
        { 
         CloseHandle(token); 
         CloseHandle(tokenDuplicate); 
         return true; 
        } 
       } 
      } 
     } 
     if (token != IntPtr.Zero) 
      CloseHandle(token); 
     if (tokenDuplicate != IntPtr.Zero) 
      CloseHandle(tokenDuplicate); 
     return false; 
    } 

    public void undoImpersonation() 
    { 
     impersonationContext.Undo(); 
    } 
} 

我只是尝试启动或停止服务前调用此代码:

Service s = new Service(ServiceName, MachineName); 

if (Impersonation.impersonateValidUser(Username, Domain, Password)) 
{ 
    if (s.Running) 
     s.StopService(); 
    else 
     s.StartService(); 

    Impersonation.undoImpersonation(); 
} 

这可能是值得指出的是,我可以列出的服务和获得个人服务的状态(就像我在这里所做的那样)就好 - 只有当我开始或停止服务时,我才会遇到麻烦。

+0

您可以显示代码在哪里执行模拟?我怀疑这可能是问题所在。 – 2011-12-20 21:17:36

+0

@competent_tech感谢您的帮助。我已将模拟相关代码添加到问题中。 – 2011-12-20 21:25:35

+0

您是使用AD域来运行服务的域还是本地计算机? – *Loser 2017-11-15 18:15:09

也许我已经找到了答案:

在模拟方法LogonUserA

使用LOGON32_LOGON_SERVICE(= 5),而不是LOGON32_LOGON_INTERACTIVE

确保您模拟的用户与运行该服务的用户相同。

在我的情况下,用户被包含在本地策略 - >启动会话作为服务。我没有对未包含在本地策略中的用户进行测试。

希望它能帮助!

+0

那么,如果你想模仿的用户是机器管理员,那是正确的。 – Fenli 2013-10-01 08:52:18

那么,如果你想模仿的用户是机器管理员,那是正确的。如果不是,那么你必须给予用户启动服务的特定权利。

由于我不是一个网络管理员,我不知道怎么做,但我发现这个链接,介绍了如何用SUBINACL工具来完成:

下载

最后,在命令行是:

  • SUBINACL /服务SERVICENAME /批=域名\用户名用户= TO

用户=赠款启动/停止权限用户

在这种情况下,你必须模仿与INTERACTIVE模式,而不是与服务的用户模式