某些用户启动远程服务的控制台应用程序失败
Console app to start remote service fails for some users
我知道这个问题已经以多种形式被问到,但我目前已经无计可施了。我已经编写了一个控制台应用程序来重新启动远程机器上的服务。
我们域中的其他用户也需要能够为此目的使用此应用程序,即使他们的凭据不足以重新启动该服务。不过,他们确实拥有对该机器的普通用户访问(读取)权限。因此,我正在使用模拟。不幸的是,该应用程序仅适用于某些用户,而对其他用户则无效。
所有这些用户都属于同一个 AD 组,都拥有他们正在操作的计算机的本地管理员权限,并激活了 UAC。
代码如下:
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
static void Main(string[] args)
{
SafeTokenHandle safeTokenHandle;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_SERVICE = 5;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
// Use the token handle returned by LogonUser.
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
//Duplicate token to set Impersonation Level to Delegate (3)
IntPtr tokenDuplicate = IntPtr.Zero;
DuplicateToken(newId.Token, 3, ref tokenDuplicate);
using (WindowsIdentity newId2 = new WindowsIdentity(tokenDuplicate))
{
using (WindowsImpersonationContext impersonatedUser = newId2.Impersonate())
{
var token = newId2.ImpersonationLevel;
Console.WriteLine("Running as: " + WindowsIdentity.GetCurrent().Name);
Console.WriteLine("Impersonation level: " + token.ToString());
var sc = new ServiceController
{
MachineName = remote,
ServiceName = myService
};
if (sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Stop();
while (!sc.Status.Equals(ServiceControllerStatus.Stopped))
{
sc.Refresh();
System.Threading.Thread.Sleep(100);
}
}
sc.Refresh();
if (sc.Status.Equals(ServiceControllerStatus.Stopped))
{
sc.Start();
while (!sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Refresh();
System.Threading.Thread.Sleep(100);
}
}
}
}
}
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
我使用了 DuplicateToken,因为没有它,ImpersonationLevel 是 "None" 所以我认为这可能是原因。唉,这并没有改变什么。
模拟上下文中使用的用户凭据在远程计算机上具有本地管理员权限,可以重新启动服务。在使用此应用程序的 6 个用户中,其中 4 个用户的服务正确重启,而另外 2 个用户则失败
Unable to start Service Control Manager on [remote]. You may have
insufficient rights...
运行 应用程序面向所有用户时的控制台输出:
Running as: [correct user]
Impersonation level: Delegation
运行 作为管理员的应用程序也没有改变任何东西。
它是一个普通的独立控制台应用程序,不是 WCF 服务或任何依赖 IIS 的应用程序。
所有用户都是 运行 Windows 7 域内专业人士,直接连接到网络,不通过 VPN。
知道这可能是什么原因吗?我错过了什么吗?失败的用户会不会遗漏了什么?框架版本?可再发行?有什么吗?
成功了,虽然不是我真正喜欢的方式(因为它没有告诉我为什么其他代码对这两个用户失败)。
=> 使用 PowerShell 的强大功能:
using System.Management.Automation;
var command = new PSCommand();
command.AddScript("$pass = convertto-securestring \"thePassword" -asplaintext -force");
command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\user\",$pass");
command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
command.AddScript("$obj.StopService()");
using (var ps = PowerShell.Create())
{
ps.Commands = command;
ps.Invoke();
if (ps.HadErrors)
{
Console.WriteLine(ps.InvocationStateInfo.Reason);
}
while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
System.Threading.Thread.Sleep(10);
//Script completed but service not yet stopped.
//Wait 5 seconds, show countdown to user
for (var i = 10; i > 5; i--)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
ps.Commands.Clear();
command = new PSCommand();
command.AddScript("$pass = convertto-securestring \"thePassword\" -asplaintext -force");
command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\user\",$pass");
command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
command.AddScript("$obj.StartService()");
ps.Commands = command;
ps.Invoke();
if (ps.HadErrors)
{
Console.WriteLine(ps.InvocationStateInfo.Reason);
}
while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
System.Threading.Thread.Sleep(10);
//Script completed but service not yet fully started.
//Wait 5 seconds, show countdown to user
for (var i = 5; i > 0; i--)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
}
如您所见,这并未优化;我在服务开始时重复自己。
但它有效,因此我很高兴。
仍然:如果有人知道什么可能导致某些用户使用 ServiceController 的初始代码失败,请分享您的知识!
附录:
即使是 PowerShell 方法一开始也行不通;我不得不在其中一台出现故障的计算机上下载 Windows Management Framework 4.0。这似乎加强了两台机器上确实缺少某些组件的情况。唉,我不知道,哪个。
我知道这个问题已经以多种形式被问到,但我目前已经无计可施了。我已经编写了一个控制台应用程序来重新启动远程机器上的服务。
我们域中的其他用户也需要能够为此目的使用此应用程序,即使他们的凭据不足以重新启动该服务。不过,他们确实拥有对该机器的普通用户访问(读取)权限。因此,我正在使用模拟。不幸的是,该应用程序仅适用于某些用户,而对其他用户则无效。
所有这些用户都属于同一个 AD 组,都拥有他们正在操作的计算机的本地管理员权限,并激活了 UAC。
代码如下:
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
static void Main(string[] args)
{
SafeTokenHandle safeTokenHandle;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_SERVICE = 5;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
// Use the token handle returned by LogonUser.
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
//Duplicate token to set Impersonation Level to Delegate (3)
IntPtr tokenDuplicate = IntPtr.Zero;
DuplicateToken(newId.Token, 3, ref tokenDuplicate);
using (WindowsIdentity newId2 = new WindowsIdentity(tokenDuplicate))
{
using (WindowsImpersonationContext impersonatedUser = newId2.Impersonate())
{
var token = newId2.ImpersonationLevel;
Console.WriteLine("Running as: " + WindowsIdentity.GetCurrent().Name);
Console.WriteLine("Impersonation level: " + token.ToString());
var sc = new ServiceController
{
MachineName = remote,
ServiceName = myService
};
if (sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Stop();
while (!sc.Status.Equals(ServiceControllerStatus.Stopped))
{
sc.Refresh();
System.Threading.Thread.Sleep(100);
}
}
sc.Refresh();
if (sc.Status.Equals(ServiceControllerStatus.Stopped))
{
sc.Start();
while (!sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Refresh();
System.Threading.Thread.Sleep(100);
}
}
}
}
}
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
我使用了 DuplicateToken,因为没有它,ImpersonationLevel 是 "None" 所以我认为这可能是原因。唉,这并没有改变什么。
模拟上下文中使用的用户凭据在远程计算机上具有本地管理员权限,可以重新启动服务。在使用此应用程序的 6 个用户中,其中 4 个用户的服务正确重启,而另外 2 个用户则失败
Unable to start Service Control Manager on [remote]. You may have insufficient rights...
运行 应用程序面向所有用户时的控制台输出:
Running as: [correct user]
Impersonation level: Delegation
运行 作为管理员的应用程序也没有改变任何东西。 它是一个普通的独立控制台应用程序,不是 WCF 服务或任何依赖 IIS 的应用程序。
所有用户都是 运行 Windows 7 域内专业人士,直接连接到网络,不通过 VPN。
知道这可能是什么原因吗?我错过了什么吗?失败的用户会不会遗漏了什么?框架版本?可再发行?有什么吗?
成功了,虽然不是我真正喜欢的方式(因为它没有告诉我为什么其他代码对这两个用户失败)。
=> 使用 PowerShell 的强大功能:
using System.Management.Automation;
var command = new PSCommand();
command.AddScript("$pass = convertto-securestring \"thePassword" -asplaintext -force");
command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\user\",$pass");
command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
command.AddScript("$obj.StopService()");
using (var ps = PowerShell.Create())
{
ps.Commands = command;
ps.Invoke();
if (ps.HadErrors)
{
Console.WriteLine(ps.InvocationStateInfo.Reason);
}
while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
System.Threading.Thread.Sleep(10);
//Script completed but service not yet stopped.
//Wait 5 seconds, show countdown to user
for (var i = 10; i > 5; i--)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
ps.Commands.Clear();
command = new PSCommand();
command.AddScript("$pass = convertto-securestring \"thePassword\" -asplaintext -force");
command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\user\",$pass");
command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
command.AddScript("$obj.StartService()");
ps.Commands = command;
ps.Invoke();
if (ps.HadErrors)
{
Console.WriteLine(ps.InvocationStateInfo.Reason);
}
while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
System.Threading.Thread.Sleep(10);
//Script completed but service not yet fully started.
//Wait 5 seconds, show countdown to user
for (var i = 5; i > 0; i--)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
}
如您所见,这并未优化;我在服务开始时重复自己。
但它有效,因此我很高兴。
仍然:如果有人知道什么可能导致某些用户使用 ServiceController 的初始代码失败,请分享您的知识!
附录: 即使是 PowerShell 方法一开始也行不通;我不得不在其中一台出现故障的计算机上下载 Windows Management Framework 4.0。这似乎加强了两台机器上确实缺少某些组件的情况。唉,我不知道,哪个。