mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-08-07 10:20:46 +00:00
refactor(linux/xdgportal): Move elevated privilege check/drop to misc.cpp (#5026)
This commit is contained in:
@@ -896,4 +896,15 @@ namespace platf {
|
||||
*/
|
||||
std::unique_ptr<high_precision_timer> create_high_precision_timer();
|
||||
|
||||
/**
|
||||
* @brief Check is the current process is running with elevated privileges (e.g. system admin/etc.)
|
||||
* @return True if system admin capabilities are present.
|
||||
*/
|
||||
bool has_elevated_privileges();
|
||||
|
||||
/**
|
||||
* @brief Drop elevated privileges (e.g. system admin/etc.)
|
||||
*/
|
||||
void drop_elevated_privileges();
|
||||
|
||||
} // namespace platf
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <sys/resource.h> // For setpriority
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if !defined(__FreeBSD__)
|
||||
#include <sys/capability.h>
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
#include <net/if_dl.h> // For sockaddr_dl, LLADDR, and AF_LINK
|
||||
#include <sys/syscall.h> // For syscall: SYS_thr_self
|
||||
@@ -1226,4 +1230,65 @@ namespace platf {
|
||||
return detected.empty() ? "/dev/dri/renderD128" : detected;
|
||||
}
|
||||
|
||||
#if !defined(__FreeBSD__)
|
||||
constexpr std::array<cap_value_t, 2> ELEVATED_PRIVILEGES_EFFECTIVE {CAP_SYS_ADMIN, CAP_SYS_NICE};
|
||||
constexpr std::array<cap_value_t, 2> ELEVATED_PRIVILEGES_PERMITTED {CAP_SYS_ADMIN, CAP_SYS_NICE};
|
||||
#endif
|
||||
|
||||
bool has_elevated_privileges() {
|
||||
#if !defined(__FreeBSD__)
|
||||
const cap_t caps = cap_get_proc();
|
||||
if (!caps) {
|
||||
BOOST_LOG(error) << "[misc] has_elevated_privileges failed to get process capabilities."sv;
|
||||
return false;
|
||||
}
|
||||
for (const auto c : ELEVATED_PRIVILEGES_EFFECTIVE) {
|
||||
cap_flag_value_t cap_flags_value;
|
||||
cap_get_flag(caps, c, CAP_EFFECTIVE, &cap_flags_value);
|
||||
if (cap_flags_value == CAP_SET) {
|
||||
BOOST_LOG(debug) << "[misc] has_elevated_privileges found effective cap:"sv << c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto c : ELEVATED_PRIVILEGES_PERMITTED) {
|
||||
cap_flag_value_t cap_flags_value;
|
||||
cap_get_flag(caps, c, CAP_PERMITTED, &cap_flags_value);
|
||||
if (cap_flags_value == CAP_SET) {
|
||||
BOOST_LOG(debug) << "[misc] has_elevated_privileges found permitted cap:"sv << c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cap_free(caps);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void drop_elevated_privileges() {
|
||||
#if !defined(__FreeBSD__)
|
||||
bool failed = false;
|
||||
const cap_t caps = cap_get_proc();
|
||||
if (!caps) {
|
||||
BOOST_LOG(error) << "[misc] drop_elevated_privileges failed to get process capabilities"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, ELEVATED_PRIVILEGES_EFFECTIVE.size(), ELEVATED_PRIVILEGES_EFFECTIVE.data(), CAP_CLEAR);
|
||||
cap_set_flag(caps, CAP_PERMITTED, ELEVATED_PRIVILEGES_PERMITTED.size(), ELEVATED_PRIVILEGES_PERMITTED.data(), CAP_CLEAR);
|
||||
|
||||
if (cap_set_proc(caps) != 0) {
|
||||
BOOST_LOG(error) << "[misc] drop_elevated_privileges failed to prune capabilities: "sv << std::strerror(errno);
|
||||
failed = true;
|
||||
}
|
||||
cap_free(caps);
|
||||
|
||||
// Reset dumpable AFTER the caps have been pruned to ensure /proc/pid/root is accessible.
|
||||
if (prctl(PR_SET_DUMPABLE, 1) != 0) {
|
||||
BOOST_LOG(error) << "[misc] drop_elevated_privileges failed to set PR_SET_DUMPABLE: "sv << std::strerror(errno);
|
||||
failed = true;
|
||||
}
|
||||
if (!failed) {
|
||||
BOOST_LOG(info) << "[misc] drop_elevated_privileges succeeded in dropping capabilities"sv;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@@ -33,12 +33,6 @@
|
||||
#include "vulkan_encode.h"
|
||||
#include "wayland.h"
|
||||
|
||||
#if !defined(__FreeBSD__)
|
||||
// platform includes
|
||||
#include <sys/capability.h>
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// Portal configuration constants
|
||||
constexpr uint32_t SOURCE_TYPE_MONITOR = 1;
|
||||
@@ -728,64 +722,6 @@ namespace portal {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Singleton for portalgrab stuff persistent during an application run.
|
||||
*
|
||||
*/
|
||||
class runtime_t {
|
||||
public:
|
||||
static runtime_t &instance();
|
||||
|
||||
bool is_portal_secured() const {
|
||||
return is_portal_secured_;
|
||||
}
|
||||
|
||||
void finalize_portal_security() {
|
||||
#if !defined(__FreeBSD__)
|
||||
BOOST_LOG(debug) << "[portalgrab] Finalizing Portal security: dropping capabilities and resetting dumpable"sv;
|
||||
|
||||
cap_t caps = cap_get_proc();
|
||||
if (!caps) {
|
||||
BOOST_LOG(error) << "[portalgrab] Failed to get process capabilities"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<cap_value_t, 2> effective_list {CAP_SYS_ADMIN, CAP_SYS_NICE};
|
||||
std::array<cap_value_t, 2> permitted_list {CAP_SYS_ADMIN, CAP_SYS_NICE};
|
||||
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, effective_list.size(), effective_list.data(), CAP_CLEAR);
|
||||
cap_set_flag(caps, CAP_PERMITTED, permitted_list.size(), permitted_list.data(), CAP_CLEAR);
|
||||
|
||||
if (cap_set_proc(caps) != 0) {
|
||||
BOOST_LOG(error) << "[portalgrab] Failed to prune capabilities: "sv << std::strerror(errno);
|
||||
}
|
||||
cap_free(caps);
|
||||
|
||||
// Reset dumpable AFTER the caps have been pruned to ensure the Portal can
|
||||
// access /proc/pid/root.
|
||||
if (prctl(PR_SET_DUMPABLE, 1) != 0) {
|
||||
BOOST_LOG(error) << "[portalgrab] Failed to set PR_SET_DUMPABLE: "sv << std::strerror(errno);
|
||||
}
|
||||
#endif
|
||||
is_portal_secured_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
runtime_t() = default;
|
||||
|
||||
// Prevent copying
|
||||
runtime_t(const runtime_t &) = delete;
|
||||
runtime_t &operator=(const runtime_t &) = delete;
|
||||
|
||||
bool is_portal_secured_ = false;
|
||||
};
|
||||
|
||||
runtime_t &runtime_t::instance() {
|
||||
alignas(runtime_t) static std::array<std::byte, sizeof(runtime_t)> storage;
|
||||
static auto instance_ = new (storage.data()) runtime_t();
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
class portal_t: public pipewire::pipewire_display_t {
|
||||
public:
|
||||
int configure_stream(const std::string &display_name, int &out_pipewire_fd, int &out_pipewire_node, int &out_pos_x, int &out_pos_y, int &out_width, int &out_height) override {
|
||||
@@ -870,7 +806,9 @@ namespace platf {
|
||||
}
|
||||
|
||||
// Drop CAP_SYS_ADMIN and set DUMPABLE flag to allow XDG /root access
|
||||
portal::runtime_t::instance().finalize_portal_security();
|
||||
if (has_elevated_privileges()) {
|
||||
drop_elevated_privileges();
|
||||
}
|
||||
|
||||
auto portal = std::make_shared<portal::portal_t>();
|
||||
if (portal->init(hwdevice_type, display_name, config)) {
|
||||
@@ -889,7 +827,7 @@ namespace platf {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!portal::runtime_t::instance().is_portal_secured()) {
|
||||
if (has_elevated_privileges()) {
|
||||
// We're still in the probing phase of Sunshine startup. Dropping portal security early will break KMS.
|
||||
// Just return a dummy screen for now. Display re-enumeration after encoder probing will yield full result.
|
||||
display_names.emplace_back("init");
|
||||
|
||||
Reference in New Issue
Block a user