/// <summary> /// Uses P/Invoke to start the process using the login token obtained for the user. /// </summary> /// <param name="token">The login token to use to create the process with.</param> private void StartWithCreateProcessAsUser(IntPtr token) { var startinfo = _process.StartInfo; var path = Path.GetFullPath(startinfo.FileName); var dir = Path.GetDirectoryName(path); bool processCreated = false; Win32NativeMethods.PROCESS_INFORMATION pi = new Win32NativeMethods.PROCESS_INFORMATION(); Win32NativeMethods.SECURITY_ATTRIBUTES saProcess = new Win32NativeMethods.SECURITY_ATTRIBUTES(); saProcess.bInheritHandle = true; saProcess.nLength = (uint)Marshal.SizeOf(saProcess); Win32NativeMethods.SECURITY_ATTRIBUTES saThread = new Win32NativeMethods.SECURITY_ATTRIBUTES(); saThread.bInheritHandle = true; saThread.nLength = (uint)Marshal.SizeOf(saThread); Win32NativeMethods.STARTUPINFO si = new Win32NativeMethods.STARTUPINFO(); si.lpDesktop = string.Empty; si.cb = (uint)Marshal.SizeOf(si); lock (createProcessLock) { _pipeServerToClient = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable); _pipeServerFromClient = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable); _pipeServerErrorFromClient = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable); try { // although these are separate if statements, in reality all standard streams must be // redirected otherwise it just won't work in SACS. if (startinfo.RedirectStandardInput) { _standardInput = new StreamWriter(_pipeServerToClient); _standardInput.AutoFlush = true; } if (startinfo.RedirectStandardOutput) { Encoding encoding = (startinfo.StandardOutputEncoding != null) ? startinfo.StandardOutputEncoding : Console.OutputEncoding; _standardOutput = new StreamReader(_pipeServerFromClient); } if (startinfo.RedirectStandardError) { Encoding encoding2 = (startinfo.StandardErrorEncoding != null) ? startinfo.StandardErrorEncoding : Console.OutputEncoding; _standardError = new StreamReader(_pipeServerErrorFromClient); } ArgumentObject["pipeIn"] = _pipeServerToClient.GetClientHandleAsString(); ArgumentObject["pipeOut"] = _pipeServerFromClient.GetClientHandleAsString(); ArgumentObject["pipeErr"] = _pipeServerErrorFromClient.GetClientHandleAsString(); processCreated = Win32NativeMethods.CreateProcessAsUser( token, path, string.Format("\"{0}\" {1}", startinfo.FileName.Replace("\"", "\"\""), JsonConvert.SerializeObject(this.ArgumentObject)), ref saProcess, ref saThread, true, 0, IntPtr.Zero, dir, ref si, out pi); if (!processCreated) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // For the last step, since we're not using the in-build methods to start the process, // we need to replace the now outdated process _process = Process.GetProcessById((int)pi.dwProcessId); } finally { _pipeServerToClient.DisposeLocalCopyOfClientHandle(); _pipeServerFromClient.DisposeLocalCopyOfClientHandle(); _pipeServerErrorFromClient.DisposeLocalCopyOfClientHandle(); if (pi.hProcess != IntPtr.Zero) { Win32NativeMethods.CloseHandle(pi.hProcess); } if (pi.hThread != IntPtr.Zero) { Win32NativeMethods.CloseHandle(pi.hThread); } } } }
/// <summary> /// Impersonates the specified user account. /// </summary> /// <param name="userName">Name of the user.</param> /// <param name="domainName">Name of the domain.</param> /// <param name="password">The password. <see cref="String"/></param> /// <param name="logonType">Type of the logon.</param> /// <param name="logonProvider">The logon provider.</param> /// /// <param name="impersonationAction">A callback to perform during the impersonation initialization.</param> public virtual void Impersonate(string userName, string domainName, SecureString password, LogonType logonType, LogonProvider logonProvider, Action <IntPtr> impersonationAction = null) { UndoImpersonation(); IntPtr logonToken = IntPtr.Zero; IntPtr logonTokenDuplicate = IntPtr.Zero; IntPtr passwordPtr = new IntPtr(); passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password); bool logonValue = false; try { // revert to the application pool identity, saving the identity of the current requestor this._wic = WindowsIdentity.Impersonate(IntPtr.Zero); // do logon & impersonate logonValue = Win32NativeMethods.LogonUser( userName, domainName, passwordPtr, (int)logonType, (int)logonProvider, ref logonToken); if (logonValue) { Win32NativeMethods.SECURITY_ATTRIBUTES sa = new Win32NativeMethods.SECURITY_ATTRIBUTES(); sa.nLength = (uint)Marshal.SizeOf(sa); if (Win32NativeMethods.DuplicateTokenEx( logonToken, Win32NativeMethods.TOKEN_ASSIGN_PRIMARY | Win32NativeMethods.TOKEN_DUPLICATE | Win32NativeMethods.TOKEN_QUERY, ref sa, (int)Win32NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)Win32NativeMethods.TOKEN_TYPE.TokenPrimary, ref logonTokenDuplicate)) { // Looks like it is working without impersonating. To be revisited. ////var wi = new WindowsIdentity(logonTokenDuplicate); ////wi.Impersonate(); // discard the returned identity context (which is the context of the application) ////this.IsImpersonating = true; if (impersonationAction != null) { impersonationAction(logonTokenDuplicate); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr); if (logonToken != IntPtr.Zero) { Win32NativeMethods.CloseHandle(logonToken); } if (logonTokenDuplicate != IntPtr.Zero) { Win32NativeMethods.CloseHandle(logonTokenDuplicate); } } }