Skip to content

Commit

Permalink
[PowerToys Run] Issues with elevation (#11775)
Browse files Browse the repository at this point in the history
* Delegate creation of non elevated process

* Error handling

* nits

* Fix potential race condition

* Fix spelling

* Fix spelling

* Fix build
  • Loading branch information
mykhailopylyp committed Jun 22, 2021
1 parent 7e79654 commit cf9f0ce
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 109 deletions.
8 changes: 8 additions & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ examplepowertoy
EXCLUDEFILES
EXCLUDEFOLDERS
EXCLUDESUBFOLDERS
exdisp
exe
Executables
executionpolicy
Expand Down Expand Up @@ -1147,6 +1148,7 @@ Lessthan
LEVELID
LExit
lhs
lhwnd
LIBID
LIGHTBLUE
LIGHTGRAY
Expand Down Expand Up @@ -1395,6 +1397,7 @@ NCRBUTTONDOWN
NCRBUTTONUP
NDEBUG
ndp
NEEDDISPATCH
Nefario
neq
NESW
Expand Down Expand Up @@ -1959,6 +1962,7 @@ Soref
SORTDOWN
SOURCECLIENTAREAONLY
SOURCEHEADER
spdisp
spdlog
spdo
spdth
Expand All @@ -1975,6 +1979,7 @@ spsia
spsrif
spsrm
spsrui
spsv
sql
src
SRCCOPY
Expand Down Expand Up @@ -2052,8 +2057,11 @@ surfacehub
sut
SVE
svg
SVGIO
SVGIn
svgpreviewhandler
SWC
SWFO
Switchbetweenvirtualdesktops
SWP
swprintf
Expand Down
145 changes: 145 additions & 0 deletions src/common/utils/elevation.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#include <Windows.h>
#include <shellapi.h>
#include <sddl.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>
#include <comdef.h>

#include <winrt/base.h>
#include <winrt/Windows.Foundation.Collections.h>
Expand All @@ -12,6 +18,119 @@
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>

namespace
{
inline std::wstring GetErrorString(HRESULT handle)
{
_com_error err(handle);
return err.ErrorMessage();
}

inline bool FindDesktopFolderView(REFIID riid, void** ppv)
{
CComPtr<IShellWindows> spShellWindows;
auto result = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
if (result != S_OK)
{
Logger::warn(L"Failed to create instance. {}", GetErrorString(result));
return false;
}

CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
result = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);

if (result != S_OK)
{
Logger::warn(L"Failed to find the window. {}", GetErrorString(result));
return false;
}

CComPtr<IShellBrowser> spBrowser;
result = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser,
IID_PPV_ARGS(&spBrowser));
if (result != S_OK)
{
Logger::warn(L"Failed to query service. {}", GetErrorString(result));
return false;
}

CComPtr<IShellView> spView;
result = spBrowser->QueryActiveShellView(&spView);
if (result != S_OK)
{
Logger::warn(L"Failed to query active shell window. {}", GetErrorString(result));
return false;
}

result = spView->QueryInterface(riid, ppv);
if (result != S_OK)
{
Logger::warn(L"Failed to query interface. {}", GetErrorString(result));
return false;
}

return true;
}

inline bool GetDesktopAutomationObject(REFIID riid, void** ppv)
{
CComPtr<IShellView> spsv;
if (!FindDesktopFolderView(IID_PPV_ARGS(&spsv)))
{
return false;
}

CComPtr<IDispatch> spdispView;
auto result = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
if (result != S_OK)
{
Logger::warn(L"GetItemObject() failed. {}", GetErrorString(result));
return false;
}

result = spdispView->QueryInterface(riid, ppv);
if (result != S_OK)
{
Logger::warn(L"QueryInterface() failed. {}", GetErrorString(result));
return false;
}

return true;
}

inline bool ShellExecuteFromExplorer(
PCWSTR pszFile,
PCWSTR pszParameters = nullptr)
{
CComPtr<IShellFolderViewDual> spFolderView;
if (!GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView)))
{
return false;
}

CComPtr<IDispatch> spdispShell;
auto result = spFolderView->get_Application(&spdispShell);
if (result != S_OK)
{
Logger::warn(L"get_Application() failed. {}", GetErrorString(result));
return false;
}

CComQIPtr<IShellDispatch2>(spdispShell)
->ShellExecute(CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(L""),
CComVariant(L""),
CComVariant(SW_SHOWNORMAL));

return true;
}
}

// Returns true if the current process is running with elevated privileges
inline bool is_process_elevated(const bool use_cached_value = true)
{
Expand Down Expand Up @@ -186,6 +305,32 @@ inline bool run_non_elevated(const std::wstring& file, const std::wstring& param
return succeeded;
}

inline bool RunNonElevatedEx(const std::wstring& file, const std::wstring& params)
{
bool failedToStart = false;
try
{
CoInitialize(nullptr);
if (!ShellExecuteFromExplorer(file.c_str(), params.c_str()))
{
failedToStart = true;
}
}
catch(...)
{
failedToStart = true;
}

if (failedToStart)
{
Logger::warn(L"Failed to delegate process creation. Try a fallback");
DWORD returnPid;
return run_non_elevated(file, params, &returnPid);
}

return true;
}

// Run command with the same elevation, returns true if succeeded
inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid)
{
Expand Down
99 changes: 14 additions & 85 deletions src/modules/launcher/Microsoft.Launcher/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ class Microsoft_Launcher : public PowertoyModuleIface
// Load initial settings from the persisted values.
void init_settings();

// Handle to launch and terminate the launcher
HANDLE m_hProcess = nullptr;
bool processStarted = false;

//contains the name of the powerToys
std::wstring app_name;
Expand Down Expand Up @@ -122,10 +121,6 @@ class Microsoft_Launcher : public PowertoyModuleIface
~Microsoft_Launcher()
{
Logger::info("Launcher object is destroying");
if (m_enabled)
{
terminateProcess();
}
m_enabled = false;
}

Expand Down Expand Up @@ -225,8 +220,8 @@ class Microsoft_Launcher : public PowertoyModuleIface

if (ShellExecuteExW(&sei))
{
m_hProcess = sei.hProcess;
Logger::trace("Started PowerToys Run. Handle {}", m_hProcess);
processStarted = true;
Logger::trace("Started PowerToys Run");
}
else
{
Expand All @@ -236,55 +231,20 @@ class Microsoft_Launcher : public PowertoyModuleIface
else
{
Logger::trace("Starting PowerToys Run from elevated process");
std::wstring action_runner_path = get_module_folderpath();

std::wstring runExecutablePath = get_module_folderpath();
std::wstring params;
params += L"-run-non-elevated ";
params += L"-target modules\\launcher\\PowerLauncher.exe ";
params += L"-pidFile ";
params += POWER_LAUNCHER_PID_SHARED_FILE;
params += L" -powerToysPid " + std::to_wstring(powertoys_pid) + L" ";
params += L"--centralized-kb-hook ";

action_runner_path += L"\\PowerToys.ActionRunner.exe";
// Set up the shared file from which to retrieve the PID of PowerLauncher
HANDLE hMapFile = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(DWORD), POWER_LAUNCHER_PID_SHARED_FILE);
if (!hMapFile)
runExecutablePath += L"\\modules\\launcher\\PowerLauncher.exe";
if (RunNonElevatedEx(runExecutablePath, params))
{
auto err = get_last_error_message(GetLastError());
Logger::error(L"Failed to create FileMapping {}. {}", POWER_LAUNCHER_PID_SHARED_FILE, err.has_value() ? err.value() : L"");
return;
processStarted = true;
Logger::trace(L"The process started successfully");
}

PDWORD pidBuffer = reinterpret_cast<PDWORD>(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(DWORD)));
if (pidBuffer)
else
{
*pidBuffer = 0;
m_hProcess = NULL;

if (run_non_elevated(action_runner_path, params, pidBuffer))
{
Logger::trace("Started PowerToys Run Process");
const int maxRetries = 80;
for (int retry = 0; retry < maxRetries; ++retry)
{
Sleep(50);
DWORD pid = *pidBuffer;
if (pid)
{
m_hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
Logger::trace("Opened PowerToys Run Process. Handle {}", m_hProcess);
break;
}
}
}
else
{
// We expect it to fail if the shell window is not available. It can happen on startup
Logger::warn("Failed to start PowerToys Run");
}
Logger::error(L"Failed to start the process");
}
CloseHandle(hMapFile);
}
}

Expand All @@ -294,9 +254,10 @@ class Microsoft_Launcher : public PowertoyModuleIface
Logger::info("Launcher is disabling");
if (m_enabled)
{
TerminateRunningInstance();
processStarted = false;
ResetEvent(m_hEvent);
ResetEvent(send_telemetry_event);
terminateProcess();
}

m_enabled = false;
Expand Down Expand Up @@ -332,17 +293,13 @@ class Microsoft_Launcher : public PowertoyModuleIface
// For now, hotkeyId will always be zero
if (m_enabled)
{
if (m_hProcess == nullptr)
if (!processStarted)
{
Logger::warn("PowerToys Run hasn't been started. Starting PowerToys Run");
enable();
} else if (WaitForSingleObject(m_hProcess, 0) == WAIT_OBJECT_0)
{
Logger::warn("PowerToys Run has exited unexpectedly, restarting PowerToys Run.");
enable();
}

Logger::trace("Set POWER_LAUNCHER_SHARED_EVENT. Handle {}", m_hProcess);
Logger::trace("Set POWER_LAUNCHER_SHARED_EVENT");
SetEvent(m_hEvent);
return true;
}
Expand All @@ -362,34 +319,6 @@ class Microsoft_Launcher : public PowertoyModuleIface
return true;
}

// Terminate process by sending WM_CLOSE signal and if it fails, force terminate.
void terminateProcess()
{
Logger::trace(L"Terminating PowerToys Run process. Handle {}.", m_hProcess);
if (WaitForSingleObject(m_hProcess, 0) == WAIT_OBJECT_0)
{
Logger::warn("PowerToys Run has exited unexpectedly, so there is no need to terminate it.");
return;
}

DWORD processID = GetProcessId(m_hProcess);
if (TerminateProcess(m_hProcess, 1) == 0)
{
auto err = get_last_error_message(GetLastError());
Logger::error(L"Launcher process was not terminated. {}", err.has_value() ? err.value() : L"");
}

// Temporarily disable sending a message to close
/*
EnumWindows(&requestMainWindowClose, processID);
const DWORD result = WaitForSingleObject(m_hProcess, MAX_WAIT_MILLISEC);
if (result == WAIT_TIMEOUT || result == WAIT_FAILED)
{
TerminateProcess(m_hProcess, 1);
}
*/
}

virtual void send_settings_telemetry() override
{
Logger::info("Send settings telemetry");
Expand Down

0 comments on commit cf9f0ce

Please sign in to comment.