2014-02-11 3 views
0

Я потратил несколько дней на эту проблему, и даже если в Интернете есть несколько разных примеров, это сложная проблема, и я не могу заставить их работать в моем сценарии.Запуск процессов под конкретные учетные данные из службы Windows

У меня есть служба Windows, которая работает под учетной записью Local System. Он имеет конечную точку WCF, слушая запросы API. Когда он передается через API, служба должна начать новый процесс в системном сеансе (0) и с учетными данными учетной записи «Работник». Этот процесс является рабочим, который проверяет работу в очереди и делает это. Если он не найдет работу, он немного поспит и снова проверит. Если он найдет работу, он начнет новый процесс в том же сеансе и с теми же учетными данными и выполнит эту работу. После завершения работы он закрывается.

«Рабочий» - это учетная запись домена и член группы локальных администраторов на компьютере, которая имеет разрешения на выполнение для исполняемого файла. Машина находится в том же домене, что и учетная запись.

Проблема в том, что когда служба пытается запустить процесс, она получает код ошибки ERROR_ACCESS_DENIED (5) из метода CreateProcessAsUser.

Я попытался запустить тот же код на машине Windows 7 с теми же учетными данными, и он прекрасно работает, но он получает этот код ошибки при работе в Windows Server 2008.

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

ProcessHelper: http://pastie.org/private/y7idu3nw4xv1fxzeizbn9g

служба называет StartAsUserFromService метод, чтобы начать процесс, который внутренне вызывает CreateProcessAsUser после установления сеанса. Процесс вызывает метод StartAsUserFromApplication, чтобы запустить его преемника, который внутренне вызывает CreateProcessWithLogonW.

ImpersonationContext: http://pastie.org/private/xppc7wnoidajmpq8h8sg

Сервис должен получить маркер пользователя, чтобы начать процесс в них. Для этого процесс не нужен, чтобы запустить своего преемника. Насколько я могу судить о том, что олицетворение удалось на сервере 2008, но у него нет некоторых разрешений, и я не могу понять, какой.

EDIT:

Я попробовал как локальную учетную запись администратора и учетная запись домена на компьютере Windows 7, и они прекрасно работают. Но ни один из них не работает на машине Server 2008. Там где-то должно быть разрешение, но я не знаю, где; сообщение об ошибке не помогает.

Я также попытался отметить флажок «запустить как администратор» на вкладке совместимости исполняемого файла, но это не имело никакого значения.

EDIT:

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

Date & Time: 12/02/2014 11:44:03 
Event Class: File System 
Operation: CreateFile 
Result: ACCESS DENIED 
Path: D:\..\executable.exe 
TID: 6244 
Duration: 0.0000450 
Desired Access: Read Data/List Directory, Execute/Traverse, Read Attributes, Synchronize 
Disposition: Open 
Options: Synchronous IO Non-Alert, Non-Directory File 
Attributes: n/a 
ShareMode: Read, Delete 
AllocationSize: n/a 
Impersonating: Domain\Worker 

и

Date & Time: 12/02/2014 11:44:03 
Event Class: File System 
Operation: CreateFile 
Result: ACCESS DENIED 
Path: D:\..\executable.exe 
TID: 6244 
Duration: 0.0000480 
Desired Access: Execute/Traverse, Synchronize 
Disposition: Open 
Options: Synchronous IO Non-Alert, Non-Directory File 
Attributes: n/a 
ShareMode: Read, Delete 
AllocationSize: n/a 
Impersonating: Domain\Worker 
+1

ли пользователь домена у входа в систему в качестве прав обслуживания? http://technet.microsoft.com/en-us/library/cc739424(v=ws.10).aspx –

+0

@ Давид Да, это так. Несмотря на то, что они не нуждаются в них, потому что я не запускаю этот процесс как службу с помощью LogonType.Service. Я начинаю с 'LogonType.Interactive'. – Edgar

+0

О, глупо меня. Это был тупой вопрос, который я задал. Сожалею. –

ответ

0

мне удалось сделать процессы начинаются с этим кодом:

ProcessHelper: http://pastie.org/private/dlkytj8rbigs8ixwtg

TokenImpersonationContext: http://pastie.org/private/nu3pvpghoea6pwwlvjuq

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

Я использую LogonType.Batch в вызове LogonUser, потому что процессу необходимо поговорить с другой службой WCF и выполнить проверку подлинности. LogonType.Network и LogonType.NetworkClearText, но вызвали проблемы с разрешением в службе обмена портами Net.Tcp с учетной записью пользователя Worker.

Этот ответ был полезен: Using Process.Start() to start a process as a different user from within a Windows Service

1

Некоторые советы:
How to Impersonate
Impersonation code in C#
Impersonation Libraries (Class & Com Class)
WindowsIdentity.Impersonate Method

Попробуйте использовать этот образец (найти это где-то):

 
using System; 
using System.Runtime.InteropServices; 
using System.Security.Principal; 
using System.Security.Permissions; 

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,UnmanagedCode=true)] 
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name ="FullTrust")] 
public class ImpersonationDemo 
{ 

    [StructLayout(LayoutKind.Sequential)] 
    public struct SECURITY_ATTRIBUTES 
    { 
     public int Length; 
     public IntPtr lpSecurityDescriptor; 
     public bool bInheritHandle; 
    } 

    public enum SECURITY_IMPERSONATION_LEVEL 
    { 
     SecurityAnonymous, 
     SecurityIdentification, 
     SecurityImpersonation, 
     SecurityDelegation 
    } 

    public enum TOKEN_TYPE 
    { 
     TokenPrimary = 1, 
     TokenImpersonation 
    } 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public extern static bool CloseHandle(IntPtr handle); 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError=true)] 
    public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public extern static bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes, 
     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 
     TOKEN_TYPE TokenType, 
     out IntPtr phNewToken); 

    // GetErrorMessage formats and returns an error message 
    // corresponding to the input errorCode. 
    public unsafe static string GetErrorMessage(int errorCode) 
    { 
     int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; 
     int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; 
     int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 

     int messageSize = 255; 
     String lpMsgBuf = ""; 
     int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; 

     IntPtr ptrlpSource = IntPtr.Zero; 
     IntPtr prtArguments = IntPtr.Zero; 

     int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments); 
     if (0 == retVal) 
     { 
      throw new Exception("Failed to format message for error code " + errorCode + ". "); 
     } 

    return lpMsgBuf; 
    } 

    // Test harness. 
    // If you incorporate this code into a DLL, be sure to demand FullTrust. 
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] 
    public static void Main(string[] args) 
    { 
     IntPtr tokenHandle = new IntPtr(0); 
     IntPtr dupeTokenHandle = new IntPtr(0); 
     try 
     { 
      string UserName, MachineName; 

      // Get the user token for the specified user, machine, and password using the 
      // unmanaged LogonUser method. 

      Console.Write("Enter the name of a machine on which to log on: "); 
      MachineName = Console.ReadLine(); 

      Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", MachineName); 
      UserName = Console.ReadLine(); 

      Console.Write("Enter the password for {0}: ", UserName); 

      const int LOGON32_PROVIDER_DEFAULT = 3; 
      //This parameter causes LogonUser to create a primary token. 
      const int LOGON32_LOGON_INTERACTIVE = 8; 

      tokenHandle = IntPtr.Zero; 
      dupeTokenHandle = IntPtr.Zero; 

      // Call LogonUser to obtain a handle to an access token. 
      bool returnValue = LogonUser(UserName, MachineName, "mm4geata", 
       LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle); 

      Console.WriteLine("LogonUser called."); 

      if (false == returnValue) 
      { 
       int ret = Marshal.GetLastWin32Error(); 
       Console.WriteLine("LogonUser failed with error code : {0}", ret); 
       Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret)); 

       return; 
      } 

      Console.WriteLine("Did LogonUser Succeed? " + (returnValue? "Yes" : "No")); 
      Console.WriteLine("Value of Windows NT token: " + tokenHandle); 

      // Check the identity. 
      Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name); 

      //bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle); 

      SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); 
      sa.bInheritHandle = true; 
      sa.Length = Marshal.SizeOf(sa); 
      sa.lpSecurityDescriptor = (IntPtr)0; 

      bool retVal = DuplicateTokenEx(tokenHandle, 0x10000000, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out dupeTokenHandle); 


      if (false == retVal) 
      { 
       CloseHandle(tokenHandle); 
       Console.WriteLine("Exception thrown in trying to duplicate token."); 
       return; 
      } 


      // The token that is passed to the following constructor must 
      // be a primary token in order to use it for impersonation. 
      WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle); 
      WindowsImpersonationContext impersonatedUser = newId.Impersonate(); 

      // Check the identity. 
      Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name); 

      // Stop impersonating the user. 
      impersonatedUser.Undo(); 

      // Check the identity. 
      Console.WriteLine("After Undo: " + WindowsIdentity.GetCurrent().Name); 

      // Free the tokens. 
      if (tokenHandle != IntPtr.Zero) 
      CloseHandle(tokenHandle); 
      if (dupeTokenHandle != IntPtr.Zero) 
      CloseHandle(dupeTokenHandle); 
     } 
     catch(Exception ex) 
     { 
      Console.WriteLine("Exception occurred. " + ex.Message); 
     } 

    } 
} 
+0

Единственное отличие от моего кода, который я вижу, это то, что 'SECURITY_ATTRIBUTES.bInheritHandle' установлен в значение' true', и вместо 'TokenPrimary' используется' TOKEN_TYPE.TokenImpersonation'. Я попробую это позже, но я не думаю, что это поможет. Мне кажется, мне нужен первичный токен, чтобы начать процесс. – Edgar

+0

Нет, ни один из них не изменил ситуацию. – Edgar

+0

См. Ссылки, которые я добавил, особенно в примере WindowsIdentity.Impersonate Method – lsalamon

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