Add Support for Safely Elevating Administrator Privileges (#1036)
This commit is contained in:
parent
6a914f7016
commit
c2fba6f651
4 changed files with 120 additions and 5 deletions
|
|
@ -690,6 +690,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||||
install(TARGETS dxgi-info RUNTIME DESTINATION "tools" COMPONENT dxgi)
|
install(TARGETS dxgi-info RUNTIME DESTINATION "tools" COMPONENT dxgi)
|
||||||
install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio)
|
install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio)
|
||||||
install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT sunshinesvc)
|
install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT sunshinesvc)
|
||||||
|
install(TARGETS elevator RUNTIME DESTINATION "tools" COMPONENT elevator)
|
||||||
|
|
||||||
# Mandatory tools
|
# Mandatory tools
|
||||||
install(TARGETS ddprobe RUNTIME DESTINATION "tools" COMPONENT application)
|
install(TARGETS ddprobe RUNTIME DESTINATION "tools" COMPONENT application)
|
||||||
|
|
@ -801,6 +802,12 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||||
set(CPACK_COMPONENT_AUDIO_DESCRIPTION "CLI tool providing information about sound devices.")
|
set(CPACK_COMPONENT_AUDIO_DESCRIPTION "CLI tool providing information about sound devices.")
|
||||||
set(CPACK_COMPONENT_AUDIO_GROUP "tools")
|
set(CPACK_COMPONENT_AUDIO_GROUP "tools")
|
||||||
|
|
||||||
|
# elevation tool
|
||||||
|
set(CPACK_COMPONENT_ELEVATION_DISPLAY_NAME "elevator")
|
||||||
|
set(CPACK_COMPONENT_ELEVATION_DESCRIPTION "CLI tool that assists with elevating \
|
||||||
|
commands when permissions have been denied.")
|
||||||
|
set(CPACK_COMPONENT_ELEVATION_GROUP "tools")
|
||||||
|
|
||||||
# display tool
|
# display tool
|
||||||
set(CPACK_COMPONENT_DXGI_DISPLAY_NAME "dxgi-info")
|
set(CPACK_COMPONENT_DXGI_DISPLAY_NAME "dxgi-info")
|
||||||
set(CPACK_COMPONENT_DXGI_DESCRIPTION "CLI tool providing information about graphics cards and displays.")
|
set(CPACK_COMPONENT_DXGI_DESCRIPTION "CLI tool providing information about graphics cards and displays.")
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,22 @@
|
||||||
|
|
||||||
// prevent clang format from "optimizing" the header include order
|
// prevent clang format from "optimizing" the header include order
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <winsock2.h>
|
#include <dwmapi.h>
|
||||||
#include <iphlpapi.h>
|
#include <iphlpapi.h>
|
||||||
|
#include <iterator>
|
||||||
|
#include <timeapi.h>
|
||||||
|
#include <userenv.h>
|
||||||
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winuser.h>
|
#include <winuser.h>
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <userenv.h>
|
|
||||||
#include <dwmapi.h>
|
|
||||||
#include <timeapi.h>
|
|
||||||
#include <wlanapi.h>
|
#include <wlanapi.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#include "src/main.h"
|
#include "src/main.h"
|
||||||
#include "src/platform/common.h"
|
#include "src/platform/common.h"
|
||||||
#include "src/utility.h"
|
#include "src/utility.h"
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||||
#ifndef UDP_SEND_MSG_SIZE
|
#ifndef UDP_SEND_MSG_SIZE
|
||||||
|
|
@ -464,6 +466,34 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
||||||
(LPSTARTUPINFOW)&startup_info,
|
(LPSTARTUPINFOW)&startup_info,
|
||||||
&process_info);
|
&process_info);
|
||||||
|
|
||||||
|
if(!ret) {
|
||||||
|
auto error = GetLastError();
|
||||||
|
|
||||||
|
if(error == 740) {
|
||||||
|
BOOST_LOG(info) << "Could not execute previous command because it required elevation. Running the command again with elevation, for security reasons this will prompt user interaction."sv;
|
||||||
|
startup_info.StartupInfo.wShowWindow = SW_HIDE;
|
||||||
|
startup_info.StartupInfo.dwFlags = startup_info.StartupInfo.dwFlags | STARTF_USESHOWWINDOW;
|
||||||
|
std::wstring elevated_command = L"tools\\elevator.exe ";
|
||||||
|
elevated_command += wcmd;
|
||||||
|
|
||||||
|
// For security reasons, Windows enforces that an application can have only one "interactive thread" responsible for processing user input and managing the user interface (UI).
|
||||||
|
// Since UAC prompts are interactive, we cannot have a UAC prompt while Sunshine is already running because it would block the thread.
|
||||||
|
// To work around this issue, we will launch a separate process that will elevate the command, which will prompt the user to confirm the elevation.
|
||||||
|
// This is our intended behavior: to require interaction before elevating the command.
|
||||||
|
ret = CreateProcessAsUserW(shell_token,
|
||||||
|
nullptr,
|
||||||
|
(LPWSTR)elevated_command.c_str(),
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||||
|
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB,
|
||||||
|
env_block.data(),
|
||||||
|
start_dir.empty() ? nullptr : start_dir.c_str(),
|
||||||
|
(LPSTARTUPINFOW)&startup_info,
|
||||||
|
&process_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// End impersonation of the logged on user. If this fails (which is extremely unlikely),
|
// End impersonation of the logged on user. If this fails (which is extremely unlikely),
|
||||||
// we will be running with an unknown user token. The only safe thing to do in that case
|
// we will be running with an unknown user token. The only safe thing to do in that case
|
||||||
// is terminate ourselves.
|
// is terminate ourselves.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ target_link_libraries(dxgi-info
|
||||||
${PLATFORM_LIBRARIES})
|
${PLATFORM_LIBRARIES})
|
||||||
target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||||
|
|
||||||
|
add_executable(elevator elevator.cpp)
|
||||||
|
set_target_properties(elevator PROPERTIES CXX_STANDARD 17)
|
||||||
|
target_link_libraries(elevator
|
||||||
|
shell32
|
||||||
|
${PLATFORM_LIBRARIES})
|
||||||
|
target_compile_options(elevator PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||||
|
|
||||||
add_executable(audio-info audio.cpp)
|
add_executable(audio-info audio.cpp)
|
||||||
set_target_properties(audio-info PROPERTIES CXX_STANDARD 17)
|
set_target_properties(audio-info PROPERTIES CXX_STANDARD 17)
|
||||||
target_link_libraries(audio-info
|
target_link_libraries(audio-info
|
||||||
|
|
@ -36,3 +43,4 @@ target_link_libraries(ddprobe
|
||||||
d3d11
|
d3d11
|
||||||
${PLATFORM_LIBRARIES})
|
${PLATFORM_LIBRARIES})
|
||||||
target_compile_options(ddprobe PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
target_compile_options(ddprobe PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||||
|
|
||||||
|
|
|
||||||
70
tools/elevator.cpp
Normal file
70
tools/elevator.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file elevator.cpp
|
||||||
|
* @brief A simple command line utility to run a given command with administrative privileges.
|
||||||
|
*
|
||||||
|
* This utility helps run a command with administrative privileges on Windows
|
||||||
|
* by leveraging the ShellExecuteExW function. The program accepts a command
|
||||||
|
* and optional arguments, then attempts to run the command with elevated
|
||||||
|
* privileges. If successful, it waits for the process to complete and
|
||||||
|
* returns the exit code of the launched process.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* To run the command prompt with administrative privileges, execute the following command:
|
||||||
|
* elevator.exe cmd
|
||||||
|
*
|
||||||
|
* To run a command, such as 'ipconfig /flushdns', with administrative privileges, execute:
|
||||||
|
* elevator.exe cmd /C "ipconfig /flushdns"
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Check if the user provided at least one argument (the command to run)
|
||||||
|
if(argc < 2) {
|
||||||
|
std::cout << "Usage: " << argv[0] << " <command> [arguments]" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the command and arguments from char* to wstring for use with ShellExecuteExW
|
||||||
|
std::wstring command = std::wstring(argv[1], argv[1] + strlen(argv[1]));
|
||||||
|
std::wstring arguments;
|
||||||
|
|
||||||
|
// Concatenate the remaining arguments (if any) into a single wstring
|
||||||
|
for(int i = 2; i < argc; ++i) {
|
||||||
|
arguments += std::wstring(argv[i], argv[i] + strlen(argv[i]));
|
||||||
|
if(i < argc - 1) {
|
||||||
|
arguments += L" ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the SHELLEXECUTEINFOW structure with the necessary information
|
||||||
|
SHELLEXECUTEINFOW info = { sizeof(SHELLEXECUTEINFOW) };
|
||||||
|
info.lpVerb = L"runas"; // Request elevation
|
||||||
|
info.lpFile = command.c_str();
|
||||||
|
info.lpParameters = arguments.empty() ? nullptr : arguments.c_str();
|
||||||
|
info.nShow = SW_SHOW;
|
||||||
|
info.fMask = SEE_MASK_NOCLOSEPROCESS; // So we can wait for the process to finish
|
||||||
|
|
||||||
|
// Attempt to execute the command with elevation
|
||||||
|
if(!ShellExecuteExW(&info)) {
|
||||||
|
std::cout << "Error: ShellExecuteExW failed with code " << GetLastError() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the launched process to finish
|
||||||
|
WaitForSingleObject(info.hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exitCode = 0;
|
||||||
|
|
||||||
|
// Retrieve the exit code of the launched process
|
||||||
|
if(!GetExitCodeProcess(info.hProcess, &exitCode)) {
|
||||||
|
std::cout << "Error: GetExitCodeProcess failed with code " << GetLastError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the process handle
|
||||||
|
CloseHandle(info.hProcess);
|
||||||
|
|
||||||
|
// Return the exit code of the launched process
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue