У меня есть приложение .NET MVC3, которое должно включать и выключать удаленный сервис. Для этого я олицетворяю конкретную учетную запись пользователя через WindowsIdentity.Impersonate (). Чтобы проверить права пользователя, я могу войти в систему как пользователь и выполнить sc.exe \\[server] start [service]
из командной строки. Я также знаю, что команда олицетворения работает должным образом, поскольку приложение работает анонимно и поэтому не может управлять службами на моем локальном компьютере (.
) без олицетворения, но может управлять локальными службами с олицетворением. Однако, когда я собираю его вместе и пытаюсь запустить удаленную службу, а не локальную службу, я всегда получаю сообщение об ошибке «Не удается открыть [service]
службу на компьютере '[server]
'»
Кто-нибудь сталкивался с подобной проблемой? Я ожидал, что это будет конфигурация сервера, а не проблема .NET, пока не понял, что sc.exe работает без проблем. Вот сокращенная версия класса, который я использую:
public class Service
{
public string Name;
public bool Running;
private ServiceController serviceController;
public Service(string name, string host)
{
Name = name;
serviceController = new ServiceController(Name, host);
Running = serviceController.Status == ServiceControllerStatus.Running;
}
public bool StartService()
{
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, serviceController.MachineName, Name);
scp.Assert();
serviceController.Start();
serviceController.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 5));
serviceController.Refresh();
Running = serviceController.Status == ServiceControllerStatus.Running;
return Running;
}
}
Еще одно примечание: если вместо сервера я указываю приложение на другой ПК с Windows 7 в домене и изменяю учетные данные пользователя для этого владельца, я фактически могу удаленно управлять их службами без проблем.
Для каждого запроса я добавляю здесь код олицетворения. Это немного дольше, так что терпите меня:
public class Impersonate
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
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)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
public bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
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)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
public void undoImpersonation()
{
impersonationContext.Undo();
}
}
Я вызываю этот код непосредственно перед попыткой запустить или остановить службу:
Service s = new Service(ServiceName, MachineName);
if (Impersonation.impersonateValidUser(Username, Domain, Password))
{
if (s.Running)
s.StopService();
else
s.StartService();
Impersonation.undoImpersonation();
}
Возможно, стоит отметить, что я могу перечислить службы и получить статус отдельной службы (как я делаю здесь) просто отлично - только когда я иду, чтобы запустить или остановить службу, у меня возникают проблемы.