2016-07-08 89 views
12

Sto tentando di impersonare un utente del dominio in un servizio Windows con il servizio connesso come account di sistema locale.Impersonare l'utente nel servizio Windows

Finora, sono in grado di farlo funzionare solo registrando il servizio e impostando il processo utilizzando le credenziali dell'utente, come nel seguito.

 ProcessStartInfo startInfo = new ProcessStartInfo(); 
     startInfo.FileName = CommandDetails.Command; 
     startInfo.WorkingDirectory = Settings.RoboCopyWorkingDirectory; 
     startInfo.Arguments = commandLine; 

     startInfo.UseShellExecute = false; 
     startInfo.CreateNoWindow = true; 
     startInfo.RedirectStandardError = true; 
     startInfo.RedirectStandardOutput = true; 

     // Credentials 
     startInfo.Domain = ImperDomain; 
     startInfo.UserName = ImperUsername; 
     startInfo.Password = ImperPasswordSecure; 

     process = Process.Start(startInfo); 

Il mio obiettivo è quello di non avere il registro servizio in un utente di dominio, ma piuttosto come sistema locale dal momento che rappresenta il dominio password viene reimpostato.

Quando uso il sistema locale, vengo Accesso negato

Tutte le idee come come per ottenere questo risultato?

StackTace

Access is denied 

    at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) 
    at System.Diagnostics.Process.Start() 
    at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) 
    at Ace.WindowsService.ProcessCmd.ProcessCommand.StartProcess(ProcessStartInfo startInfo) in 

ho cercato avvolgendo il codice nel codice Rappresenta elencato di seguito senza successo.

Rappresenta Codice

public class Impersonation2 : IDisposable 
{ 
    private WindowsImpersonationContext _impersonatedUserContext; 

    // Declare signatures for Win32 LogonUser and CloseHandle APIs 
    [DllImport("advapi32.dll", SetLastError = true)] 
    static extern bool LogonUser(
     string principal, 
     string authority, 
     string password, 
     LogonSessionType logonType, 
     LogonProvider logonProvider, 
     out IntPtr token); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern bool CloseHandle(IntPtr handle); 

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

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern bool RevertToSelf(); 

    // ReSharper disable UnusedMember.Local 
    enum LogonSessionType : uint 
    { 
     Interactive = 2, 
     Network, 
     Batch, 
     Service, 
     NetworkCleartext = 8, 
     NewCredentials 
    } 
    // ReSharper disable InconsistentNaming 
    enum LogonProvider : uint 
    { 
     Default = 0, // default for platform (use this!) 
     WinNT35,  // sends smoke signals to authority 
     WinNT40,  // uses NTLM 
     WinNT50  // negotiates Kerb or NTLM 
    } 
    // ReSharper restore InconsistentNaming 
    // ReSharper restore UnusedMember.Local 

    /// <summary> 
    /// Class to allow running a segment of code under a given user login context 
    /// </summary> 
    /// <param name="user">domain\user</param> 
    /// <param name="password">user's domain password</param> 
    public Impersonation2(string domain, string username, string password) 
    { 
     var token = ValidateParametersAndGetFirstLoginToken(username, domain, password); 

     var duplicateToken = IntPtr.Zero; 
     try 
     { 
      if (DuplicateToken(token, 2, ref duplicateToken) == 0) 
      { 
       throw new Exception("DuplicateToken call to reset permissions for this token failed"); 
      } 

      var identityForLoggedOnUser = new WindowsIdentity(duplicateToken); 
      _impersonatedUserContext = identityForLoggedOnUser.Impersonate(); 
      if (_impersonatedUserContext == null) 
      { 
       throw new Exception("WindowsIdentity.Impersonate() failed"); 
      } 
     } 
     finally 
     { 
      if (token != IntPtr.Zero) 
       CloseHandle(token); 
      if (duplicateToken != IntPtr.Zero) 
       CloseHandle(duplicateToken); 
     } 
    } 

    private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password) 
    { 


     if (!RevertToSelf()) 
     { 
      throw new Exception("RevertToSelf call to remove any prior impersonations failed"); 
      ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, ""); 

     } 

     IntPtr token; 

     var result = LogonUser(domain, username, 
           password, 
           LogonSessionType.Interactive, 
           LogonProvider.Default, 
           out token); 
     if (!result) 
     { 
      var errorCode = Marshal.GetLastWin32Error(); 
      ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, ""); 
      throw new Exception("Logon for user " + username + " failed."); 
     } 
     return token; 
    } 

    public void Dispose() 
    { 
     // Stop impersonation and revert to the process identity 
     if (_impersonatedUserContext != null) 
     { 
      _impersonatedUserContext.Undo(); 
      _impersonatedUserContext = null; 
     } 
    } 

Aggiornamento

Questo funziona bene se io sono solo in esecuzione se sto solo eseguirlo. Ma quando è in esecuzione come servizio, non funzionerà

Update 2

Non ricevo l'accesso negato dalla Process.Start quando cambio la login si rappresenta a LogonSessionType.NewCredentials e rimuovere la crednetials dal processo. Ma ora vedo un errore durante l'esecuzione del comando robocopy. Quando io ho le credenziali sul processo non produce un file di registro dal comando robocopy

errore

2016/07/16 09:19:12 ERROR 5 (0x00000005) 
Accessing Source Directory \\[server]\[path]\ 
Access is denied. 

Change

var result = LogonUser(domain, username, 
    password, 
    LogonSessionType.NewCredentials, 
    LogonProvider.Default, 
    out token); 

Update 3

Le funzioni di copia e spostamento funzionano. Ma la creazione di sub process non lo è. Ho giocato con CreateProcessAsUser come suggerito da Hary Johnston.

+0

Stai dicendo che il tuo servizio è in esecuzione come "Sistema locale" e che il tuo servizio sta avviando un altro processo utilizzando l'account di un utente del dominio? – wablab

+0

In tal caso, questa domanda è probabilmente un duplicato di http://stackoverflow.com/questions/559719/windows-impersonation-from-c-sharp – wablab

+0

Quale riga di codice genera l'errore "accesso negato"? –

risposta

7

Sono riuscito a farlo funzionare.

Per impersonating normale, ho usato il seguente codice

public class Impersonation : IDisposable 
{ 
    private WindowsImpersonationContext _impersonatedUserContext; 

    #region FUNCTIONS (P/INVOKE) 

    // Declare signatures for Win32 LogonUser and CloseHandle APIs 
    [DllImport("advapi32.dll", SetLastError = true)] 
    static extern bool LogonUser(
     string principal, 
     string authority, 
     string password, 
     LogonSessionType logonType, 
     LogonProvider logonProvider, 
     out IntPtr token); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern bool CloseHandle(IntPtr handle); 

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

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern bool RevertToSelf(); 

    #endregion 

    #region ENUMS 

    enum LogonSessionType : uint 
    { 
     Interactive = 2, 
     Network, 
     Batch, 
     Service, 
     NetworkCleartext = 8, 
     NewCredentials 
    } 

    enum LogonProvider : uint 
    { 
     Default = 0, // default for platform (use this!) 
     WinNT35,  // sends smoke signals to authority 
     WinNT40,  // uses NTLM 
     WinNT50  // negotiates Kerb or NTLM 
    } 

    #endregion 


    /// <summary> 
    /// Class to allow running a segment of code under a given user login context 
    /// </summary> 
    /// <param name="user">domain\user</param> 
    /// <param name="password">user's domain password</param> 
    public Impersonation(string domain, string username, string password) 
    { 
     var token = ValidateParametersAndGetFirstLoginToken(username, domain, password); 

     var duplicateToken = IntPtr.Zero; 
     try 
     { 
      if (DuplicateToken(token, 2, ref duplicateToken) == 0) 
      { 


       throw new InvalidOperationException("DuplicateToken call to reset permissions for this token failed"); 
      } 

      var identityForLoggedOnUser = new WindowsIdentity(duplicateToken); 
      _impersonatedUserContext = identityForLoggedOnUser.Impersonate(); 
      if (_impersonatedUserContext == null) 
      { 
       throw new InvalidOperationException("WindowsIdentity.Impersonate() failed"); 
      } 
     } 
     finally 
     { 
      if (token != IntPtr.Zero) 
       CloseHandle(token); 
      if (duplicateToken != IntPtr.Zero) 
       CloseHandle(duplicateToken); 
     } 
    } 

    private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password) 
    { 


     if (!RevertToSelf()) 
     { 
      ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, ""); 

      throw new InvalidOperationException("RevertToSelf call to remove any prior impersonations failed"); 

     } 

     IntPtr token; 

     var result = LogonUser(domain, username, 
           password, 
           LogonSessionType.NewCredentials, 
           LogonProvider.Default, 
           out token); 
     if (!result) 
     { 
      var errorCode = Marshal.GetLastWin32Error(); 
      ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, ""); 
      throw new InvalidOperationException("Logon for user " + username + " failed."); 
     } 
     return token; 
    } 

    public void Dispose() 
    { 
     // Stop impersonation and revert to the process identity 
     if (_impersonatedUserContext != null) 
     { 
      _impersonatedUserContext.Undo(); 
      _impersonatedUserContext = null; 
     } 
    } 
} 

Per eseguirlo faccio la seguente:

  FileInfo fi = new FileInfo(logfile); 
      using (var imp = new Impersonation(Settings.ImpersonateUser.AccountDomain, Settings.ImpersonateUser.AccountName, Settings.ImpersonateUser.AccountPassword)) 
      { 
       if (File.Exists(filename)) 
        File.Delete(filename); 
       fi.MoveTo(filename); 
      } 

Per l'esecuzione di comandi da console, ho usato il seguente codice.

public class CreateProcess 
{ 

    #region Constants 

    const UInt32 INFINITE = 0xFFFFFFFF; 
    const UInt32 WAIT_FAILED = 0xFFFFFFFF; 

    #endregion 


    #region ENUMS 

    [Flags] 
    public enum LogonType 
    { 
     LOGON32_LOGON_INTERACTIVE = 2, 
     LOGON32_LOGON_NETWORK = 3, 
     LOGON32_LOGON_BATCH = 4, 
     LOGON32_LOGON_SERVICE = 5, 
     LOGON32_LOGON_UNLOCK = 7, 
     LOGON32_LOGON_NETWORK_CLEARTEXT = 8, 
     LOGON32_LOGON_NEW_CREDENTIALS = 9 
    } 


    [Flags] 
    public enum LogonProvider 
    { 
     LOGON32_PROVIDER_DEFAULT = 0, 
     LOGON32_PROVIDER_WINNT35, 
     LOGON32_PROVIDER_WINNT40, 
     LOGON32_PROVIDER_WINNT50 
    } 

    #endregion 


    #region Structs 

    [StructLayout(LayoutKind.Sequential)] 
    public struct STARTUPINFO 
    { 
     public Int32 cb; 
     public String lpReserved; 
     public String lpDesktop; 
     public String lpTitle; 
     public Int32 dwX; 
     public Int32 dwY; 
     public Int32 dwXSize; 
     public Int32 dwYSize; 
     public Int32 dwXCountChars; 
     public Int32 dwYCountChars; 
     public Int32 dwFillAttribute; 
     public Int32 dwFlags; 
     public Int16 wShowWindow; 
     public Int16 cbReserved2; 
     public IntPtr lpReserved2; 
     public IntPtr hStdInput; 
     public IntPtr hStdOutput; 
     public IntPtr hStdError; 
    } 


    [StructLayout(LayoutKind.Sequential)] 
    public struct PROCESS_INFORMATION 
    { 
     public IntPtr hProcess; 
     public IntPtr hThread; 
     public Int32 dwProcessId; 
     public Int32 dwThreadId; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct SECURITY_ATTRIBUTES 
    { 
     public int nLength; 
     public unsafe byte* lpSecurityDescriptor; 
     public int bInheritHandle; 
    } 

    public enum TOKEN_TYPE 
    { 
     TokenPrimary = 1, 
     TokenImpersonation 
    } 

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

    #endregion 


    #region FUNCTIONS (P/INVOKE) 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern bool RevertToSelf(); 

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


    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    public static extern Boolean LogonUser 
    (
     String UserName, 
     String Domain, 
     String Password, 
     LogonType dwLogonType, 
     LogonProvider dwLogonProvider, 
     out IntPtr phToken 
    ); 


    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern Boolean CreateProcessAsUser 
    (
     IntPtr hToken, 
     String lpApplicationName, 
     String lpCommandLine, 
     IntPtr lpProcessAttributes, 
     IntPtr lpThreadAttributes, 
     Boolean bInheritHandles, 
     Int32 dwCreationFlags, 
     IntPtr lpEnvironment, 
     String lpCurrentDirectory, 
     ref STARTUPINFO lpStartupInfo, 
     out PROCESS_INFORMATION lpProcessInformation 
    ); 





    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern UInt32 WaitForSingleObject 
    (
     IntPtr hHandle, 
     UInt32 dwMilliseconds 
    ); 

    [DllImport("kernel32", SetLastError = true)] 
    public static extern Boolean CloseHandle(IntPtr handle); 

    #endregion 

    #region Functions 

    public static int LaunchCommand(string command, string domain, string account, string password) 
    { 
     int ProcessId = -1; 
     PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION(); 
     STARTUPINFO startInfo = new STARTUPINFO(); 
     Boolean bResult = false; 

     UInt32 uiResultWait = WAIT_FAILED; 

     var token = ValidateParametersAndGetFirstLoginToken(domain, account, password); 

     var duplicateToken = IntPtr.Zero; 
     try 
     { 

      startInfo.cb = Marshal.SizeOf(startInfo); 
      // startInfo.lpDesktop = "winsta0\\default"; 

      bResult = CreateProcessAsUser(
       token, 
       null, 
       command, 
       IntPtr.Zero, 
       IntPtr.Zero, 
       false, 
       0, 
       IntPtr.Zero, 
       null, 
       ref startInfo, 
       out processInfo 
      ); 

      if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); } 

      // Wait for process to end 
      uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE); 

      ProcessId = processInfo.dwProcessId; 

      if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); } 

     } 
     finally 
     { 
      if (token != IntPtr.Zero) 
       CloseHandle(token); 
      if (duplicateToken != IntPtr.Zero) 
       CloseHandle(duplicateToken); 
      CloseHandle(processInfo.hProcess); 
      CloseHandle(processInfo.hThread); 
     } 

     return ProcessId; 
    } 


    private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password) 
    { 


     if (!RevertToSelf()) 
     { 
      ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, ""); 
      throw new Exception("RevertToSelf call to remove any prior impersonations failed"); 
     } 

     IntPtr token; 

     var result = LogonUser(username, 
           domain, 
           password, 
           LogonType.LOGON32_LOGON_INTERACTIVE, 
           LogonProvider.LOGON32_PROVIDER_DEFAULT, 
           out token); 
     if (!result) 
     { 
      var errorCode = Marshal.GetLastWin32Error(); 
      ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, ""); 
      throw new Exception("Logon for user " + username + " failed."); 
     } 
     return token; 
    } 

    #endregion 

} 

ed eseguirlo facendo quanto segue

string commandLine = "Robocopy " + args; 

ProcessId = CreateProcess.LaunchCommand(commandLine, ImperDomain, ImperUsername, ImperPassword); 

Ho anche dovuto apportare alcune modifiche alla politica locale perché voglio in grado di copiare i permessi in robocopy.

Grazie per tutti i commenti e l'aiuto.

+0

Sei in Windows 10? L'ho usato e continuo a ricevere Accesso negato con il tuo codice. – freedeveloper

+0

Non sto usando Windows 10. Che tipo di comando stai usando. E hai provato a eseguire quel comando sulla macchina di Windows 10 con quell'utente che stai impersonando? – H20rider

-4

Non succederà.

Windows non è * nix. Non c'è sudo. Non puoi semplicemente impersonare un utente senza avere le credenziali di accesso dell'utente. E anche se lo avessi gestito, avresti essenzialmente scoperto una vulnerabilità che sarebbe stata patchata non appena ha funzionato nel processo di aggiornamento di Microsoft.

Che cosa lavoro è quello di creare un account di dominio con i privilegi corretti e senza scadenza e utilizzare quello per il login del servizio.

+0

Non pertinente a questa domanda, poiché l'OP ha le credenziali per l'account utente in questione. –

+0

L'op li ha ma non vuole usarli per qualche motivo. –

+0

Hai frainteso la domanda. L'OP non vuole configurare il * servizio * per l'esecuzione con tali credenziali, presumibilmente perché non ha modo di riconfigurare automaticamente il servizio quando le credenziali cambiano. Non obietta a passare le credenziali a LogonUser, e in effetti questo è esattamente ciò che fa la sua auto-risposta. –