2008-10-16 3 views
4

Windows 인증을 사용할 수있는 웹 사이트가 있습니다. 웹 사이트의 페이지에서 사용자는 데이터베이스로 일부 작업을 수행하는 서비스를 시작할 수 있습니다..Net 2.0 ServiceController.GetServices()

저는 서버의 로컬 관리자이기 때문에 서비스를 시작하는 것이 좋습니다. 하지만 사용자가 테스트를 해본 결과 서비스를 시작할 수 없습니다.

내 질문은 :


사람은 다른 창들이 현재 로그인되어있는 것보다는 계정 사용 이름으로 지정된 컴퓨터에서 서비스 목록을 얻을 수있는 방법을 알고 있나요?


정말 Windows 그룹에 서비스를 시작하고 내 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; 

. 따라서 호출자는 원격 사용자가 기본적으로 갖고 있지 않은 서비스 제어 관리자 (SCM)에 바로 SC_MANAGER_ENUMERATE_SERVICE을 보유 할 필요가 없습니다. 원격 인증 된 사용자에게 부여되어야하는 SC_MANAGER_CONNECT이 여전히 필요하지만 according to MSDN이 필요합니다.

관심있는 서비스를 찾았 으면 원격 사용자가 수행 할 권한이없는이 서비스를 중지하고 시작할 수 있어야합니다. 그러나 개별 서비스에서 DACL (보안 설명자)을 수정할 수 있으므로 로컬 사용자가 아니더라도 원격 사용자가 서비스를 중지하고 시작할 수있는 권한을 부여 할 수 있습니다. 이 작업은 SetNamedSecurityInfo API 함수를 통해 수행됩니다. 부여해야하는 액세스 권한은 SERVICE_STARTSERVICE_STOP입니다. 사용자가 속한 그룹에 따라 GENERIC_READ을 부여해야 할 수도 있습니다. 이러한 모든 권한은 described in MSDN입니다.

관심있는 사용자가 "원격 서비스 컨트롤러"그룹 (생성하려는)에 있고 서비스 이름이 "my-service-name"이라고 가정하면이 설정을 수행하는 일부 C++ 코드가 있습니다. 사용자와 같이 잘 알려진 그룹 (사용자가 만든 그룹이 아닌)과 같은 잘 알려진 그룹에 액세스 권한을 부여하려면 TRUSTEE_IS_GROUPTRUSTEE_IS_WELL_KNOWN_GROUP으로 변경해야합니다.

코드에 추가하려는 오류 검사가 없습니다. 실패 할 수있는 세 가지 함수 (Get/SetNamedSecurityInfo 및 SetEntriesInAcl)는 성공을 나타 내기 위해 0을 반환합니다.

다른 참고 사항 : 서비스의 보안 설명자를 % WINDIR % \ System32 아래에서 찾을 수 있지만 프로그래밍을 포함하지 않는 the SC tool을 사용하여 설정할 수도 있습니다.

#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); 
} 

또한 P/호출을 사용하여 C#을에서 수행 할 수 있지만, 조금 더 많은 작업입니다.

이 사용자로 서비스를 열거 할 수 있도록하려면 해당 사용자에게 SC_MANAGER_ENUMERATE_SERVICE 권한을 SCM에 부여해야합니다. 불행하게도 according to MSDN 인 경우 SCM의 보안은 Windows Server 2003 sp1 이상에서만 수정할 수 있습니다.

1

당신은 당신의 web.config 파일에서 ASP.NET 가장을 사용하여 시도하고 적절한 권한이있는 사용자 계정을 지정할 수 있습니다

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

this article on MSDN을보세요. 그 대신에 레지스트리 키에 배치하는 것과 같이 web.config 파일에 암호를 저장하지 않아도되는 다른 옵션이 있다고 생각합니다.

이렇게하면 ASP.NET 작업자 프로세스가 웹 응용 프로그램에 로그인 한 사용자 대신 지정된 사용자 컨텍스트에서 실행됩니다. 그러나 인 경우 보안 문제가 발생하며 디자인을 다시 생각해 보겠습니다. ASP.NET 웹 페이지에서 서비스를 제어하는 ​​다른 프로세스 (심지어 다른 Windows 서비스)에 대한 요청을 실행하거나 Windows 서비스가 주기적으로 폴링하는 데이터베이스 테이블에 요청을 작성하는 것이 좋습니다.

+0

나는 그들의 창 로그인을 기반으로 웹 사이트에 물건을 보여주는거야 때문에 Windows 사용자 이름을 가지고 있습니다. 나는 사용자가 추가 로그인을 갖도록하고 싶지 않았다. – Miles

+0

Windows 계정을 계속해서 인증에 사용할 수 있습니다. IIS 및 ASP.NET 인증 모델에서 통합 인증을 사용한다고 가정합니다. (<인증 모드 = "Windows"/>). 두 가지를 함께 사용할 수 있으며 IIS는 추가 로그인없이 사용자 자격 증명을 협상합니다. – Rich

1

해당 코드 줄을 제공해 주셔서 감사합니다. 내가 끝내 준 일이 여기있다. 이 웹 사이트에서 아이디어를 얻었습니다. http://www.codeproject.com/KB/cs/svcmgr.aspx?display=Print

또한 서버의 Power Users 그룹에 액세스하는 계정을 추가해야했습니다.

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); 
    } 
+0

멋진, 행운을 빈다. 완료되면 가장을 올바르게 되돌 리도록하는 것이 매우 중요합니다. 그렇게하려면 가장에 의해 반환 된 가장 컨텍스트를 처리해야합니다. 그렇게하지 않으면 가장을 유출하여 문제를 일으킬 수 있습니다. – Charlie

+0

사용자가 가장을 되돌리지 않으면 해당 스레드가 지정된 계정을 무기한으로 계속 가장합니다. 이것은 버그의 원인 일 가능성이 높으며 최악의 경우에는 보안상의 결함입니다. 그걸 조심하는 것이 가장 좋습니다. – Charlie

+0

이것은 나를 위해 일했습니다. –