[英]Remote controlling of windows service installed on intranet machine
I have web application deployed on my local IIS 7, with application pool configured to work under built-in NETWORK SERVICE account. 我在本地IIS 7上部署了Web应用程序,并将应用程序池配置为在内置NETWORK SERVICE帐户下工作。 From this web application I need to check the status of my windows service (if it is started, stopped, etc).
从此Web应用程序中,我需要检查Windows服务的状态(如果已启动,已停止等)。 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. 该
machine
是我的Intranet(运行Windows服务的主机)中的主机的IP地址(也是在内置的NETWORK SERVICE帐户下)。
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 ? 问题出在哪儿 ?
The problem is NETWORK SERVICE doesn't have sufficient rights for controlling windows services. 问题是网络服务没有足够的权限来控制Windows服务。 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. 我检查了很多用于模仿的资源,包括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.
因为我正在使用Windows 7(64位)以及Windows Server 2008 R2(64位),所以发现对我不起作用。 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.
用户必须具有控制Windows服务的权限-为此,可以将其添加到Administrators组。
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. ImpersonateHappily
只是一个函数,其参数与我的操作系统一起使用。 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. 来自网络的其他类似解决方案使用
dwLogonType
参数传递给Win 32 API函数LogonUserA
,其值为2或9,而在我的系统值8下是正确的。
BTW: Impersonate
is a wrapper function which sets up the impersonation and then passes it a lambda which does the actual work. 顺便说一句:
Impersonate
是包装函数,用于设置模拟,然后将其传递给lambda,以完成实际工作。 The fancy computer science term for this style of writing code is higher-order programming . 这种写代码风格的计算机科学术语是高阶编程 。
Try If you can use impersonation 如果可以使用模拟,请尝试
http://support.microsoft.com/kb/306158/en-us http://support.microsoft.com/kb/306158/en-us
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.