2008-10-16 4 views
4

У меня есть сайт, на котором есть проверка подлинности Windows. На странице на веб-сайте пользователи имеют возможность запускать службу, которая делает некоторые вещи в базе данных..Net 2.0 ServiceController.GetServices()

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

Мой вопрос:


Кто-нибудь знает способ получить список услуг на указанном компьютере по имени, используя учетную запись различные окна, чем тот, который они в настоящее время вошли в систему с?


Я действительно не хочу, чтобы добавить все пользователь, которые необходимо запустить службу в окне группы и установить их все к локальному администратору на моем сервере IIS .....

Вот некоторые из кода, который у меня есть:

public static ServiceControllerStatus FindService() 
     { 
      ServiceControllerStatus status = ServiceControllerStatus.Stopped; 

      try 
      { 
       string machineName = ConfigurationManager.AppSettings["ServiceMachineName"]; 
       ServiceController[] services = ServiceController.GetServices(machineName); 
       string serviceName = ConfigurationManager.AppSettings["ServiceName"].ToLower(); 

       foreach (ServiceController service in services) 
       { 
        if (service.ServiceName.ToLower() == serviceName) 
        { 
         status = service.Status; 
         break; 
        } 
       } 
      } 
      catch(Exception ex) 
      { 
       status = ServiceControllerStatus.Stopped; 
       SaveError(ex, "Utilities - FindService()"); 
      } 

      return status; 
     } 

Мое исключение относится ко второй строке блока try. Вот ошибка:

System.InvalidOperationException: Cannot open Service Control Manager on computer 'server.domain.com'. This operation might require other privileges. ---> System.ComponentModel.Win32Exception: Access is denied --- End of inner exception stack trace --- at System.ServiceProcess.ServiceController.GetDataBaseHandleWithAccess(String machineName, Int32 serviceControlManaqerAccess) at System.ServiceProcess.ServiceController.GetServicesOfType(String machineName, Int32 serviceType) at TelemarketingWebSite.Utilities.StartService()

Спасибо за помощь/Информация

ответ

5

Примечание: это не относится к перечислению услуг как к другому пользователю, но, учитывая более подробное описание того, что вы делаете, я думаю, что это хороший ответ.

Я думаю, что вы можете упростить это и, возможно, избежать части проблемы безопасности, если вы перейдете непосредственно к интересующей услуге. Вместо вызова GetServices, попробуйте следующее:

string machineName = ConfigurationManager.AppSettings["ServiceMachineName"]; 
string serviceName = ConfigurationManager.AppSettings["ServiceName"]; 
ServiceController service = new ServiceController(serviceName, machineName); 
return service.Status; 

Это подключается непосредственно к службе интерес и обходит шаг перечисления/поиска. Поэтому не требуется, чтобы вызывающий абонент имел SC_MANAGER_ENUMERATE_SERVICE прямо в диспетчере управления службами (SCM), которые по умолчанию не имеют удаленных пользователей. Он по-прежнему требует SC_MANAGER_CONNECT, но according to MSDN, который должен быть предоставлен удаленным пользователям, прошедшим проверку подлинности.

После того, как вы нашли интересующую вас услугу, вам все равно нужно будет остановить и запустить ее, что, возможно, у ваших удаленных пользователей нет прав. Тем не менее, можно изменить дескриптор безопасности (DACL) для отдельных служб, что позволит предоставить вашим удаленным пользователям доступ к остановке и запуску службы, не требуя, чтобы они были локальными администраторами. Это делается с помощью функции API SetNamedSecurityInfo. Права доступа, которые вам необходимо предоставить, - SERVICE_START и SERVICE_STOP. В зависимости от того, к каким группам принадлежат эти пользователи, вам также может потребоваться предоставить их GENERIC_READ. Все эти права: described in MSDN.

Ниже приведен код C++, который будет выполнять эту настройку, предполагая, что заинтересованные пользователи находятся в группе «Remote Service Controllers» (которую вы создадите), а имя службы - «my-service-name». Обратите внимание: если вы хотите предоставить доступ к известной группе, такой как «Пользователи» (не обязательно хорошая идея), а не к группе, которую вы создали, вам необходимо изменить TRUSTEE_IS_GROUP на TRUSTEE_IS_WELL_KNOWN_GROUP.

Код не содержит ошибок, которые вы хотели бы добавить. Все три функции, которые могут выйти из строя (Get/SetNamedSecurityInfo и SetEntriesInAcl), возвращают 0, чтобы указать успех.

Другое примечание. Вы также можете установить дескриптор безопасности службы, используя the SC tool, который можно найти в разделе% WINDIR% \ System32, но это не связано с программированием.

#include "windows.h" 
#include "accctrl.h" 
#include "aclapi.h" 

int main() 
{ 
    char serviceName[] = "my-service-name"; 
    char userGroup[] = "Remote Service Controllers"; 

    // retrieve the security info 
    PACL pDacl = NULL; 
    PSECURITY_DESCRIPTOR pDescriptor = NULL; 
    GetNamedSecurityInfo(serviceName, SE_SERVICE, 
     DACL_SECURITY_INFORMATION, NULL, NULL, 
     &pDacl, NULL, &pDescriptor); 

    // add an entry to allow the users to start and stop the service 
    EXPLICIT_ACCESS access; 
    ZeroMemory(&access, sizeof(access)); 
    access.grfAccessMode = GRANT_ACCESS; 
    access.grfAccessPermissions = SERVICE_START | SERVICE_STOP; 
    access.Trustee.TrusteeForm = TRUSTEE_IS_NAME; 
    access.Trustee.TrusteeType = TRUSTEE_IS_GROUP; 
    access.Trustee.ptstrName = userGroup; 
    PACL pNewDacl; 
    SetEntriesInAcl(1, &access, pDacl, &pNewDacl); 

    // write the changes back to the service 
    SetNamedSecurityInfo(serviceName, SE_SERVICE, 
     DACL_SECURITY_INFORMATION, NULL, NULL, 
     pNewDacl, NULL); 

    LocalFree(pNewDacl); 
    LocalFree(pDescriptor); 
} 

Это также может быть сделано из C# с помощью P/Invoke, но это немного больше работы.

Если вы по-прежнему специально хотите, чтобы иметь возможность перечислять службы как эти пользователи, вам необходимо предоставить их SC_MANAGER_ENUMERATE_SERVICE прямо на SCM. К сожалению, according to MSDN, безопасность SCM может быть изменена только на Windows Server 2003 sp1 или новее.

1

Вы можете попробовать использовать ASP.NET олицетворения в файле web.config и указать учетную запись пользователя, которая имеет соответствующие разрешения:

<system.web> 
     <identity impersonate="true" userName="Username" password="Password" /> 
    </system.web 

Посмотрите на this article on MSDN. Я считаю, что есть другие варианты, которые не требуют хранения пароля в файле web.config, например, вместо этого помещают его в раздел реестра.

Это приведет к тому, что рабочий процесс ASP.NET будет запущен в контексте указанного пользователя вместо пользователя, зарегистрированного в веб-приложении. Однако, это создает проблему безопасности, и я бы решительно переосмыслил ваш дизайн. Возможно, вам захочется подумать о том, чтобы веб-страница ASP.NET отменила запрос на другой процесс, который фактически контролирует службы, даже другую службу Windows или записывает запрос в таблицу базы данных, периодически проверяемую службой Windows.

+0

я должен иметь имя окна, потому что я показываю вещи на веб-сайте на основе их окна входа в систему. Я не хотел, чтобы у пользователя был дополнительный логин. – Miles 2008-10-17 15:30:58

+0

Вы можете продолжать использовать свою учетную запись Windows для аутентификации. Я предполагаю, что вы используете встроенную проверку подлинности в IIS и модели аутентификации ASP.NET. (<режим аутентификации = "Windows" />). Вы можете использовать оба метода вместе, и IIS по-прежнему будет согласовывать учетные данные пользователя без дополнительного входа. – Rich 2008-10-17 22:34:57

1

Спасибо за эту строку кода Charlie. Вот что я в итоге сделал. У меня появилась идея с этого сайта: http://www.codeproject.com/KB/cs/svcmgr.aspx?display=Print

Мне также пришлось добавить учетную запись, к которой я обращаюсь, как к группе «Властные пользователи» на сервере.

public static ServiceControllerStatus FindService() 
     { 
      ServiceControllerStatus status = ServiceControllerStatus.Stopped; 
    try 
      { 
       string machineName = ConfigurationManager.AppSettings["ServiceMachineName"]; 
       string serviceName = ConfigurationManager.AppSettings["ServiceName"].ToLower(); 

       ImpersonationUtil.Impersonate(); 

       ServiceController service = new ServiceController(serviceName, machineName); 
       status = service.Status; 
      } 
      catch(Exception ex) 
      { 
       status = ServiceControllerStatus.Stopped; 
       SaveError(ex, "Utilities - FindService()"); 
      } 

      return status; 
     } 

А вот мой другой класс с ImpersonationUtil.Impersonate():

public static class ImpersonationUtil 
    { 
     public static bool Impersonate() 
     { 
      string logon = ConfigurationManager.AppSettings["ImpersonationUserName"]; 
      string password = ConfigurationManager.AppSettings["ImpersonationPassword"]; 
      string domain = ConfigurationManager.AppSettings["ImpersonationDomain"]; 

      IntPtr token = IntPtr.Zero; 
      IntPtr tokenDuplicate = IntPtr.Zero; 
      WindowsImpersonationContext impersonationContext = null; 

      if (LogonUser(logon, domain, password, 2, 0, ref token) != 0) 
       if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
        impersonationContext = new WindowsIdentity(tokenDuplicate).Impersonate(); 
      // 

      return (impersonationContext != null); 
     } 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto)] 
     public static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); 

     [DllImport("advapi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)] 
     public extern static int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^