Запуск удаленных служб Windows с помощью ServiceController и олицетворения - PullRequest
5 голосов
/ 21 декабря 2011

У меня есть приложение .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();
}

Возможно, стоит отметить, что я могу перечислить службы и получить статус отдельной службы (как я делаю здесь) просто отлично - только когда я иду, чтобы запустить или остановить службу, у меня возникают проблемы.

Ответы [ 2 ]

3 голосов
/ 19 сентября 2013

Возможно, я нашел ответ:

В способе олицетворения LogonUserA

используйте LOGON32_LOGON_SERVICE (= 5) вместо LOGON32_LOGON_INTERACTIVE .

Убедитесь, что пользователь, под которым вы выполняете олицетворение, является тем же пользователем, который запускает службу.

В моем случае пользователь включен в локальные политики -> Начать сессию как сервис.Я не проверял пользователя, не включенного в эту локальную политику.

Надеюсь, это поможет!

1 голос
/ 01 октября 2013

Что ж, это было правильно, если пользователь, который вы хотите выдать себя за другого, является администратором компьютера. Если это не так, вы должны предоставить пользователю определенные права для запуска службы.

Поскольку я не являюсь сетевым администратором, я не знал, как это сделать, но нашел ссылку, объясняющую, как это сделать с помощью инструмента subinacl:

Для загрузки

И, наконец, командная строка будет выглядеть так:

  • subinacl / service SERVICENAME / grant = DOMAINNAME \ USERNAME Users = TO

Users = TO предоставляет пользователю права на запуск / остановку

В этом случае вы должны выдавать себя за пользователя в режиме INTERACTIVE , а не в режиме SERVICE

...