Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Impersonate user in Windows Service

Tags:

I am trying to impersonate a domain user in a windows service with the service logged in as the Local System Account.

So far, I am only able to get this to work by logging the service and set the process using the user credentials, like the following.

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

My goal is to not have the service log in a domain user but rather as local system since the domain accounts passwords get reset.

When I use the local system, I get Access is denied

Any ideas how how to accomplish this?

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  

I have tried wrapping the code in the Impersonate code listed below with no success.

Impersonate Code

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

Update

This works fine if I am just running if I am just executing it. But when it is running as a service, it will not work

Update 2

I am not getting the access denied from the Process.Start when I change the impersonating login to LogonSessionType.NewCredentials and remove the crednetials from the process. But I now see an error when running the robocopy command. When I do have the credentials on the process it does not produce a log file from the robocopy command

Error

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

The copy and move functions are working. But creating sub process is not. I have been playing with CreateProcessAsUser as Hary Johnston has suggested.

like image 758
H20rider Avatar asked Jul 08 '16 20:07

H20rider


People also ask

How do I impersonate a Windows user?

To impersonate another user you must first retrieve the security information of the user you want to impersonate, cache that information in a security context structure, and then later use the information in the security context structure to send the impersonated messages.

What is impersonate user?

User impersonation allows you to temporarily sign in as a different user in your network. Users with full impersonation permissions can impersonate all other users in their network and take any action, regardless of the impersonating user's own permission level.

How do you impersonate someone in ServiceNow?

Open the User menu by clicking your user name in the ServiceNow banner. Select the Impersonate User menu item. In the Impersonate User dialog, enter the name of the user to impersonate in the Search for user field. Select the user from the list.


1 Answers

I was able to get it to work.

For normal impersonating, I used the following code

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

To run it I do the following:

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

For running console commands, I used the following code.

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  } 

and executing it doing the following

string commandLine = "Robocopy " + args;  ProcessId = CreateProcess.LaunchCommand(commandLine, ImperDomain, ImperUsername, ImperPassword); 

I also had to make some changes to the local policy since I want able to copy permissions in robocopy.

Thanks for all the comments and help.

like image 136
H20rider Avatar answered Oct 02 '22 23:10

H20rider