如何从ASP.NET应用程序启动/停止Windows服务 - 安全问题

时间:2009-05-04 01:37:00

标签: asp.net windows-services impersonation security

这是我的Windows / .NET安全堆栈:

  • 在Windows Server 2003上作为LocalSystem运行的Windows服务。
  • 在同一个框中运行的.NET 3.5网站,在“默认”生产服务器IIS设置下(可能与NETWORKSERVICE用户一样?)

在我的默认VS2008 DEV环境中,我有这个方法,可以从ASP.NET应用程序调用,该方法工作正常:

private static void StopStartReminderService() {

    ServiceController svcController = new ServiceController("eTimeSheetReminderService");

    if (svcController != null) {
        try {
            svcController.Stop();
            svcController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10));
            svcController.Start();
        } catch (Exception ex) {
            General.ErrorHandling.LogError(ex);
        }
    }
}

当我在生产服务器上运行它时,我从ServiceController中收到以下错误:

  

来源:System.ServiceProcess - >   System.ServiceProcess.ServiceController - > IntPtr的   GetServiceHandle(Int32) - > System.InvalidOperationException消息:   无法在计算机上打开eTimeSheetReminderService服务'。'。

为什么会发生这种情况,我该如何解决?

修改

答案如下,主要是在评论中,但澄清一下:

  1. 问题与安全相关,是因为NETWORKSERVICE帐户没有足够的权限来启动/停止服务
  2. 我创建了一个本地用户帐户,并将其添加到PowerUsers Group(该组几乎拥有管理员权限)
  3. 我不希望我的整个Web应用程序一直冒充该用户,所以我只在我操纵服务的方法中冒充。我通过使用以下资源帮助我在代码中执行此操作:
  4. MS KB articlethis, just to get a better understanding

    注意:我没有通过web.config模仿,我在代码中执行此操作。请参阅上面的MS KB文章。

6 个答案:

答案 0 :(得分:13)

授予IIS启动/停止特定服务的权限:

  • 下载并安装Subinacl.exe。 (一定要获得最新版本!在某些资源包中分发的早期版本不起作用!
  • 发出类似于subinacl /service {yourServiceName} /grant=IIS_WPG=F
  • 的命令

这将为该特定服务授予对内置IIS_WPG组的完整服务控制权限。 (这适用于IIS6 / Win2k3。)YMMV适用于较新版本的IIS。)

答案 1 :(得分:6)

尝试将此添加到您的Web.Config。

<identity impersonate="true"/>

答案 2 :(得分:1)

这是一个很好的问题,也引起了我的兴趣...

以下是我为解决这个问题所做的工作:

  • 步骤1:在本地计算机上以最小的权限创建Windows用户帐户。
  • 步骤2:授予此用户通过subinacl.exe启动和停止服务的权限
  • 即。 subinacl.exe / service WindowsServiceName / GRANT = PCNAME \ TestUser = STOE
  • http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • 下载
  • 步骤3:使用模拟模拟步骤1中创建的用途以启动和停止服务

    public const int LOGON32_PROVIDER_DEFAULT = 0;
    
    WindowsImpersonationContext _impersonationContext;
    
    [DllImport("advapi32.dll")]
    // ReSharper disable once MemberCanBePrivate.Global
    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)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);
    
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool RevertToSelf();
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool CloseHandle(IntPtr handle);
    
    private bool _impersonate;
    
    public bool ImpersonateValidUser(String userName, String domain, String password)
    {
        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)
                {
                    var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    _impersonationContext = tempWindowsIdentity.Impersonate();
                    if (_impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        _impersonate = true;
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        _impersonate = false;
        return false;
    }
    
    #region Implementation of IDisposable
    
    
    
    
    #endregion
    
    #region Implementation of IDisposable
    
    private void Dispose(bool dispose)
    {
        if (dispose)
        {
            if (_impersonate)
                _impersonationContext.Undo();
            _impersonationContext.Dispose();
        }
    }
    
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion
    
    public static void StartStopService(bool startService, string serviceName)
    {
        using (var impersonateClass = new Impersonation())
        {
            impersonateClass.ImpersonateValidUser(Settings.Default.LocalUsername, Settings.Default.Domain, Settings.Default.Password);
            using (var sc = new ServiceController(serviceName))
            {
                if (startService)
                    sc.Start();
                else if (sc.CanStop)
                    sc.Stop();
            }
    
        }
    }
    

答案 3 :(得分:1)

IIS 8的更新(可能还有一些稍早的版本)

用户组 IIS_WPG 不再存在。它已更改为 IIS_IUSRS

此外,要开始停止服务,不必提供完全权限(F)。启动,停止和暂停服务(TOP)的权限应该足够了。因此命令应该是:

  

subinacl / service {yourServiceName} / grant = IIS_IUSRS = TOP

请注意,在运行此命令之前,您需要将命令提示符(最好提升为以管理员身份运行)指向C:\Windows\System32文件夹。

如果出现错误,还要确保已将subinacl.exe文件从安装目录复制到C:\Windows\System32

答案 4 :(得分:0)

只是预感,但我觉得错误不一定与安全有关。您是否在生产服务器上为该服务提供了相同的名称?

答案 5 :(得分:0)

如果您的Web应用程序具有数据库且Windows服务可以访问它,您只需使用DB中的标志即可重新启动该服务。在服务中,您可以读取此标志,如果不忙则重新启动等。仅在您可以修改服务代码的情况下。        如果是第三方服务,您可以创建自己的Windows服务并使用数据库配置来控制(重新启动)服务。它是安全的方式,为您提供更多的灵活性和安全性。