【问题标题】:Remote controlling of windows service installed on intranet machine远程控制安装在内网机器上的windows服务
【发布时间】:2013-01-15 15:39:39
【问题描述】:

我在本地 IIS 7 上部署了 Web 应用程序,应用程序池配置为在内置 NETWORK SERVICE 帐户下工作。从这个 Web 应用程序中,我需要检查我的 Windows 服务的状态(如果它已启动、停止等)。我用这样的语句来得到它:

public string GetServiceStatus(string machine, string service)
{
    var service = new ServiceController(machine, service);
    service.Refresh();
    return service.Status;
}

machine 是我的 Intranet 中主机的 IP 地址(假设为 192.168.0.7),Windows 服务在其中运行 - 也在内置的 NETWORK SERVICE 帐户下。

不幸的是代码给出了一个异常:

service.Status threw an exception of type 'System.InvalidOperationException' 
Cannot open MyService service on computer '192.168.0.7'. Access is denied.

问题出在哪里?

【问题讨论】:

    标签: .net asp.net-mvc windows-services


    【解决方案1】:

    问题是 NETWORK SERVICE 没有足够的权限来控制 Windows 服务。我需要切换到另一个用户上下文才能控制它。但我不想为整个应用程序这样做。相反,我正在搜索特定身份下的任意代码执行。

    我检查了很多关于冒充的资源,包括 Malcolm Frexner 展示的资源。因为我使用的是 Windows 7(64 位)和 Windows Server 2008 R2(64 位),所以我发现它不适合我。我最终得到了这样的通用解决方案:

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    
    namespace Thing.Namespace
    {
        public enum LogOnType
        {
            LogOn32LogOnInteractive = 2,
            LogOn32LogOnNetwork = 3,
            LogOn32LogOnBatch = 4,
            LogOn32LogOnService = 5,
            LogOn32LogOnUnlock = 7,
            LogOn32LogOnNetworkCleartext = 8,
            LogOn32LogOnNewCredentials = 9
        }
    
        public enum LogOnProvider
        {
            LogOn32ProviderDefault = 0,
            LogOn32ProviderWinnt35 = 1,
            LogOn32ProviderWinnt40 = 2,
            LogOn32ProviderWinnt50 = 3
        }
    
        public enum ImpersonationLevel
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3
        }
    
        public static class IdentityBoss
        {
            private static WindowsImpersonationContext _impersonationContext;
            private static readonly object _locker = new object();
    
            private static class NativeMethods
            {
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
                public static extern int LogonUser(String lpszUserName,
                                                   String lpszDomain,
                                                   String lpszPassword,
                                                   int dwLogonType,
                                                   int dwLogonProvider,
                                                   ref IntPtr phToken);
    
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
                public static extern int DuplicateToken(IntPtr hToken,
                                                        int impersonationLevel,
                                                        ref IntPtr hNewToken);
    
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool RevertToSelf();
    
                [DllImport("kernel32.dll", SetLastError = true)]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool CloseHandle(IntPtr handle);
            }  
    
            public static void Impersonate(Action action, string user, string domain, string password,
                                           LogOnType logOnType, LogOnProvider logOnProvider,
                                           ImpersonationLevel impersonationLevel)
            {
                try
                {
                    ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel);
                    action();
                }
                finally
                {
                    UndoImpersonation();
                }
            }
    
            public static void ImpersonateHappily(Action action, string user, string domain, string password)
            {
                Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext,
                            LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation);
            }
    
            public static TResult Impersonate<TResult>(Func<TResult> action, string user, string domain, string password,
                                                       LogOnType logOnType, LogOnProvider logOnProvider,
                                                       ImpersonationLevel impersonationLevel)
            {
                try
                {
                    ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel);
                    return action();
                }
                finally
                {
                    UndoImpersonation();
                }
            }
    
            public static TResult ImpersonateHappily<TResult>(Func<TResult> action, string user, string domain, string password)
            {
                return Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext,
                                   LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation);
            }
    
            private static void ImpersonateValidUser(String userName, String domain, String password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel)
            {
                lock (_locker)
                {
                    var token = IntPtr.Zero;
                    var tokenDuplicate = IntPtr.Zero;
                    WindowsIdentity tempWindowsIdentity = null;
    
                    try
                    {
                        if (!NativeMethods.RevertToSelf())
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        if (NativeMethods.LogonUser(userName, domain, password, (int) logonType, (int) logonProvider,ref token) == 0)
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        if (NativeMethods.DuplicateToken(token, (int) impersonationLevel, ref tokenDuplicate) == 0)
                            throw new Win32Exception(Marshal.GetLastWin32Error());
    
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        _impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                    finally
                    {
                        if (token != IntPtr.Zero)
                            NativeMethods.CloseHandle(token);
                        if (tokenDuplicate != IntPtr.Zero)
                            NativeMethods.CloseHandle(tokenDuplicate);
                        if (tempWindowsIdentity != null)
                            tempWindowsIdentity.Dispose();
                    }
                }
            }
    
            private static void UndoImpersonation()
            {
                lock (_locker)
                {
                    if (_impersonationContext != null)
                    {
                        _impersonationContext.Undo();
                    }
                }            
            }
        }
    }
    

    此外,我需要在安装了该服务的机器上创建新用户。用户必须具有控制 Windows 服务的权限 - 为此,可以将其添加到管理员组。

    现在我可以启动/停止我的服务并以这种方式获取它们的当前状态:

    private const string user = "MyUser";  
    private const string domain = ".";
    private const string password = "MyPa$$w0rd";
    
    public string StartService(string machine, string service)
    {
        IdentityBoss.ImpersonateHappily(
            () =>
                {
                    Controller.Instance.StartService(machine, service);
                }, user, domain, password
            );
    }
    
    public string GetServiceStatus(string machine, string service)
    {
        return IdentityBoss.ImpersonateHappily(
            () =>
                {
                    return Controller.Instance.GetServiceStatus(machine, service);
                }, user, domain, password
            );
    }
    

    ImpersonateHappily 只是一个函数,它接受与我的操作系统一起使用的参数。来自网络的其他类似解决方案使用dwLogonType参数传递给win 32 api函数LogonUserA,值为2或9,而在我的系统下,值8是正确的。

    顺便说一句:Impersonate 是一个包装函数,它设置模拟,然后传递给它一个执行实际工作的 lambda。这种编写代码风格的计算机科学术语是higher-order programming

    【讨论】:

    • 这适用于我的本地机器,不适用于 Intranet 机器。它说用户/通行证组合无法识别。这怎么可能?
    • 我不知道你的问题的根源是什么。尝试提供完整的堆栈跟踪以显示更多信息。
    【解决方案2】:

    如果您可以使用模拟,请尝试

    http://support.microsoft.com/kb/306158/en-us

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-19
      • 1970-01-01
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 1970-01-01
      相关资源
      最近更新 更多