Impersonate user in Windows Service

前端 未结 1 1367
离开以前
离开以前 2020-12-05 07:26

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 l

1条回答
  •  离开以前
    2020-12-05 07:57

    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
    
    
        /// 
        /// Class to allow running a segment of code under a given user login context
        /// 
        /// domain\user
        /// user's domain password
        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.

    0 讨论(0)
提交回复
热议问题