libstore: extract capability raising into raiseAmbientCaps

this will be needed in other places in the future, and splitting it out
also clarifies the surrounding code. the keep-caps dance launchPasta is
doing doesn't have to be moved since it is only needed to allow setuid.

Change-Id: I6baaa138c2b1bca9626971ed3266c1a971a63acc
This commit is contained in:
eldritch horrors
2026-02-02 14:20:54 +00:00
parent 576ff2f598
commit 1d227cbe07
+38 -34
View File
@@ -190,6 +190,40 @@ static auto inVFork(int flags, auto fn)
}
}
static void raiseAmbientCaps(std::span<const unsigned> caps)
{
// we do the capability dance like this to avoid a dependency
// on libcap, which has a rather large build closure and many
// more features that we need for now. maybe some other time.
static constexpr uint32_t LINUX_CAPABILITY_VERSION_3 = 0x20080522;
static constexpr uint32_t LINUX_CAPABILITY_U32S_3 = 2;
struct user_cap_header_struct
{
uint32_t version;
int pid;
} hdr = {LINUX_CAPABILITY_VERSION_3, 0};
struct user_cap_data_struct
{
uint32_t effective;
uint32_t permitted;
uint32_t inheritable;
} data[LINUX_CAPABILITY_U32S_3] = {};
for (auto cap : caps) {
assert(cap / 32 < LINUX_CAPABILITY_U32S_3);
data[cap / 32].permitted |= 1 << (cap % 32);
data[cap / 32].inheritable |= 1 << (cap % 32);
}
if (syscall(SYS_capset, &hdr, data)) {
throw SysError("couldn't set capabilities");
}
for (auto cap : caps) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0) {
throw SysError("couldn't set ambient caps");
}
}
}
static Pid launchPasta(
const AutoCloseFD & logFD,
const Path & pasta,
@@ -215,8 +249,7 @@ static Pid launchPasta(
execArgs.push_back(nullptr);
static constexpr unsigned ROOT_CAPS[] = {CAP_SYS_ADMIN, CAP_NET_BIND_SERVICE};
const std::span<const unsigned> caps =
geteuid() == 0 ? std::span{ROOT_CAPS} : std::span<const unsigned>{};
const bool needsCaps = geteuid() == 0;
auto [pid, result] = asVFork(/* flags */ 0, [&] -> std::tuple<> {
// TODO these redirections are crimes. pasta closes all non-stdio file
@@ -233,7 +266,7 @@ static Pid launchPasta(
}
closeOnExec(1, false);
}
if (!caps.empty() && prctl(PR_SET_KEEPCAPS, 1) < 0) {
if (needsCaps && prctl(PR_SET_KEEPCAPS, 1) < 0) {
throw SysError("setting keep-caps failed");
}
if (gid && syscall(SYS_setgid, *gid) == -1) {
@@ -246,41 +279,12 @@ static Pid launchPasta(
if (uid && syscall(SYS_setuid, *uid) == -1) {
throw SysError("setuid failed");
}
if (!caps.empty()) {
if (needsCaps) {
if (prctl(PR_SET_KEEPCAPS, 0)) {
throw SysError("clearing keep-caps failed");
}
// we do the capability dance like this to avoid a dependency
// on libcap, which has a rather large build closure and many
// more features that we need for now. maybe some other time.
static constexpr uint32_t LINUX_CAPABILITY_VERSION_3 = 0x20080522;
static constexpr uint32_t LINUX_CAPABILITY_U32S_3 = 2;
struct user_cap_header_struct
{
uint32_t version;
int pid;
} hdr = {LINUX_CAPABILITY_VERSION_3, 0};
struct user_cap_data_struct
{
uint32_t effective;
uint32_t permitted;
uint32_t inheritable;
} data[LINUX_CAPABILITY_U32S_3] = {};
for (auto cap : caps) {
assert(cap / 32 < LINUX_CAPABILITY_U32S_3);
data[cap / 32].permitted |= 1 << (cap % 32);
data[cap / 32].inheritable |= 1 << (cap % 32);
}
if (syscall(SYS_capset, &hdr, data)) {
throw SysError("couldn't set capabilities");
}
for (auto cap : caps) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0) {
throw SysError("couldn't set ambient caps");
}
}
raiseAmbientCaps(ROOT_CAPS);
}
restoreProcessContext();