Remote controlling of windows service installed on intranet machine

匿名 (未验证) 提交于 2019-12-03 02:29:01

问题:

I have web application deployed on my local IIS 7, with application pool configured to work under built-in NETWORK SERVICE account. From this web application I need to check the status of my windows service (if it is started, stopped, etc). I've used such statements to get it:

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

The machine is an IP address of the host in my intranet (let it be 192.168.0.7), where the windows service is running - also under built-in NETWORK SERVICE account.

Unfortunately the code gives an exception:

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

Where is the problem ?

回答1:

Try If you can use impersonation

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



回答2:

The problem is NETWORK SERVICE doesn't have sufficient rights for controlling windows services. I needed to switch to another user context to be able to control it. But I didn't want to do it for entire application. Instead I was searching for arbitrary piece of code execution under specific identity.

I've checked a lot of resources for impersonation included that shown by Malcolm Frexner. Because I'm working with Windows 7 (64bit) and also with Windows Server 2008 R2 (64bit), that I've found were not working for me. I ended up with such generic solution:

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();                 }             }                     }     } } 

In addition I needed to create new user on my machine where the service is installed. User has to have permissions for controlling windows services - for that purpose it can be added to Administrators group.

Now I can start / stop my services and getting theirs current statuses in such way:

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 is just a function which takes parameters which are working with my operating system. Other similar solutions from the web used dwLogonType parameter passed to win 32 api function LogonUserA with values 2 or 9, while under my system value 8 is correct.

BTW: Impersonate is a wrapper function which sets up the impersonation and then passes it a lambda which does the actual work. The fancy computer science term for this style of writing code is higher-order programming.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!