Skip to content

Commit ecba5c3

Browse files
authored
fix(linux): security: drop CAP_SYS_ADMIN when possible, retain CAP_SYS_NICE (#5075)
1 parent dd30d05 commit ecba5c3

4 files changed

Lines changed: 37 additions & 28 deletions

File tree

src/platform/common.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -895,13 +895,15 @@ namespace platf {
895895

896896
/**
897897
* @brief Check is the current process is running with elevated privileges (e.g. system admin/etc.)
898-
* @return True if system admin capabilities are present.
898+
* @param all_caps Bool that specifies whether to check all caps or only CAP_SYS_ADMIN
899+
* @return True if capabilities specified to be checked are present.
899900
*/
900-
bool has_elevated_privileges();
901+
bool has_elevated_privileges(bool all_caps);
901902

902903
/**
903-
* @brief Drop elevated privileges (e.g. system admin/etc.)
904+
* @brief Drop elevated privileges (e.g. system admin/nice etc.)
905+
* @param all_caps Bool that specifies whether to drop all caps or only CAP_SYS_ADMIN
904906
*/
905-
void drop_elevated_privileges();
907+
void drop_elevated_privileges(bool all_caps);
906908

907909
} // namespace platf

src/platform/linux/kwingrab.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -674,11 +674,6 @@ namespace platf {
674674
return nullptr;
675675
}
676676

677-
// Drop CAP_SYS_ADMIN so KWin's permission check (if active) can see and match the executable
678-
if (!kwin::screencast_permission_helper_t::is_permission_system_deactivated() && has_elevated_privileges()) {
679-
drop_elevated_privileges();
680-
}
681-
682677
auto display = std::make_shared<kwin::kwin_t>();
683678
if (display->init(hwdevice_type, display_name, config)) {
684679
return nullptr;
@@ -688,7 +683,7 @@ namespace platf {
688683
}
689684

690685
std::vector<std::string> kwin_display_names() {
691-
if (!kwin::screencast_permission_helper_t::is_permission_system_deactivated() && has_elevated_privileges()) {
686+
if (has_elevated_privileges(false)) {
692687
// We're still in the probing phase of Sunshine startup. Dropping portal security early will break KMS.
693688
// Just return a dummy screen for now. Display re-enumeration after encoder probing will yield full result.
694689
std::vector<std::string> display_names;

src/platform/linux/misc.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,19 @@ namespace platf {
10761076
}
10771077

10781078
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
1079+
// Keep KMS as first element to check before dropping CAP_SYS_ADMIN
1080+
#ifdef SUNSHINE_BUILD_DRM
1081+
if (sources[source::KMS]) {
1082+
BOOST_LOG(info) << "Screencasting with KMS"sv;
1083+
return kms_display(hwdevice_type, display_name, config);
1084+
}
1085+
#endif
1086+
1087+
// KMS capture was passed; drop CAP_SYS_ADMIN only.
1088+
if (has_elevated_privileges(false)) {
1089+
drop_elevated_privileges(false);
1090+
}
1091+
10791092
#ifdef SUNSHINE_BUILD_CUDA
10801093
if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) {
10811094
BOOST_LOG(info) << "Screencasting with NvFBC"sv;
@@ -1088,12 +1101,6 @@ namespace platf {
10881101
return wl_display(hwdevice_type, display_name, config);
10891102
}
10901103
#endif
1091-
#ifdef SUNSHINE_BUILD_DRM
1092-
if (sources[source::KMS]) {
1093-
BOOST_LOG(info) << "Screencasting with KMS"sv;
1094-
return kms_display(hwdevice_type, display_name, config);
1095-
}
1096-
#endif
10971104
#ifdef SUNSHINE_BUILD_X11
10981105
if (sources[source::X11]) {
10991106
BOOST_LOG(info) << "Screencasting with X11"sv;
@@ -1261,26 +1268,30 @@ namespace platf {
12611268
}
12621269

12631270
#if !defined(__FreeBSD__)
1264-
constexpr std::array<cap_value_t, 2> ELEVATED_PRIVILEGES_EFFECTIVE {CAP_SYS_ADMIN, CAP_SYS_NICE};
1265-
constexpr std::array<cap_value_t, 2> ELEVATED_PRIVILEGES_PERMITTED {CAP_SYS_ADMIN, CAP_SYS_NICE};
1271+
static constexpr cap_value_t FULL_CAPS[] = {CAP_SYS_ADMIN, CAP_SYS_NICE};
1272+
static constexpr cap_value_t ADMIN_CAPS[] = {CAP_SYS_ADMIN};
1273+
1274+
constexpr std::span<const cap_value_t> ELEVATED_PRIVILEGES_FULL {FULL_CAPS};
1275+
constexpr std::span<const cap_value_t> ELEVATED_PRIVILEGES_ADMIN {ADMIN_CAPS};
12661276
#endif
12671277

1268-
bool has_elevated_privileges() {
1278+
bool has_elevated_privileges(bool all_caps) {
12691279
#if !defined(__FreeBSD__)
1280+
const auto caps_to_check = all_caps ? ELEVATED_PRIVILEGES_FULL : ELEVATED_PRIVILEGES_ADMIN;
12701281
const cap_t caps = cap_get_proc();
12711282
if (!caps) {
12721283
BOOST_LOG(error) << "[misc] has_elevated_privileges failed to get process capabilities."sv;
12731284
return false;
12741285
}
1275-
for (const auto c : ELEVATED_PRIVILEGES_EFFECTIVE) {
1286+
for (const auto c : caps_to_check) {
12761287
cap_flag_value_t cap_flags_value;
12771288
cap_get_flag(caps, c, CAP_EFFECTIVE, &cap_flags_value);
12781289
if (cap_flags_value == CAP_SET) {
12791290
BOOST_LOG(debug) << "[misc] has_elevated_privileges found effective cap:"sv << c;
12801291
return true;
12811292
}
12821293
}
1283-
for (const auto c : ELEVATED_PRIVILEGES_PERMITTED) {
1294+
for (const auto c : caps_to_check) {
12841295
cap_flag_value_t cap_flags_value;
12851296
cap_get_flag(caps, c, CAP_PERMITTED, &cap_flags_value);
12861297
if (cap_flags_value == CAP_SET) {
@@ -1293,17 +1304,18 @@ namespace platf {
12931304
return false;
12941305
}
12951306

1296-
void drop_elevated_privileges() {
1307+
void drop_elevated_privileges(bool all_caps) {
12971308
#if !defined(__FreeBSD__)
12981309
bool failed = false;
1310+
const auto caps_to_drop = all_caps ? ELEVATED_PRIVILEGES_FULL : ELEVATED_PRIVILEGES_ADMIN;
12991311
const cap_t caps = cap_get_proc();
13001312
if (!caps) {
13011313
BOOST_LOG(error) << "[misc] drop_elevated_privileges failed to get process capabilities"sv;
13021314
return;
13031315
}
13041316

1305-
cap_set_flag(caps, CAP_EFFECTIVE, ELEVATED_PRIVILEGES_EFFECTIVE.size(), ELEVATED_PRIVILEGES_EFFECTIVE.data(), CAP_CLEAR);
1306-
cap_set_flag(caps, CAP_PERMITTED, ELEVATED_PRIVILEGES_PERMITTED.size(), ELEVATED_PRIVILEGES_PERMITTED.data(), CAP_CLEAR);
1317+
cap_set_flag(caps, CAP_EFFECTIVE, caps_to_drop.size(), caps_to_drop.data(), CAP_CLEAR);
1318+
cap_set_flag(caps, CAP_PERMITTED, caps_to_drop.size(), caps_to_drop.data(), CAP_CLEAR);
13071319

13081320
if (cap_set_proc(caps) != 0) {
13091321
BOOST_LOG(error) << "[misc] drop_elevated_privileges failed to prune capabilities: "sv << std::strerror(errno);

src/platform/linux/portalgrab.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,9 @@ namespace platf {
705705
return nullptr;
706706
}
707707

708-
// Drop CAP_SYS_ADMIN and set DUMPABLE flag to allow XDG /root access
709-
if (has_elevated_privileges()) {
710-
drop_elevated_privileges();
708+
// Drop CAP_SYS_ADMIN, CAP_SYS_NICE and set DUMPABLE flag to allow XDG /root access
709+
if (has_elevated_privileges(true)) {
710+
drop_elevated_privileges(true);
711711
}
712712

713713
auto portal = std::make_shared<portal::portal_t>();
@@ -727,7 +727,7 @@ namespace platf {
727727
return {};
728728
}
729729

730-
if (has_elevated_privileges()) {
730+
if (has_elevated_privileges(true)) {
731731
// We're still in the probing phase of Sunshine startup. Dropping portal security early will break KMS.
732732
// Just return a dummy screen for now. Display re-enumeration after encoder probing will yield full result.
733733
display_names.emplace_back("init");

0 commit comments

Comments
 (0)