From 1151a724ef53c0842d6f1aa3f6714d1024deed15 Mon Sep 17 00:00:00 2001 From: Sven Barth Date: Mon, 28 Apr 2025 16:52:15 +0200 Subject: [PATCH] If the system supports the syscalls to get and set Linux capabilities then apply currently effective Linux capabilities to the Inheritable set and then apply all the capabilities in both the Effective and Inheritable sets to the Ambient set, so that the child process (which can be unaware of capabilities) can have its capabilities set accordingly. Note: a failure to set these capabilities will not be considered as a total failure as the application might still be able to work for the intended use case (otherwise the user simply needs to start it as root). --- src/runtime/runtime.c | 110 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 9846786..3b073f5 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -64,6 +64,16 @@ extern int sqfs_opt_proc(void* data, const char* arg, int key, struct fuse_args* #include #include #include +#include + +#if defined(SYS_capget) && defined(SYS_capset) +#define USE_CAPABILITIES +#endif + +#ifdef USE_CAPABILITIES +#include +#include +#endif const char* fusermountPath = NULL; @@ -1459,6 +1469,97 @@ void set_portable_home_and_config(char* basepath) { } } +#ifdef USE_CAPABILITIES +int appimage_adjust_capabilities() +{ + struct __user_cap_header_struct hdr; + struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3]; + int res, datacnt; + + hdr.version = 0; + hdr.pid = 0; + res = syscall(SYS_capget, &hdr, NULL); + if (res) + { + if (errno != EINVAL || hdr.version == 0) + { + fprintf(stderr, "SYS_capget syscall failed with %d\n", errno); + return -1; + } + } + else if (hdr.version == 0) + { + fprintf(stderr, "Failed to query capability version\n"); + return -1; + } + + //fprintf(stderr, "Linux capability version: %x\n", hdr.version); + + switch (hdr.version) + { + case _LINUX_CAPABILITY_VERSION_1: + datacnt = _LINUX_CAPABILITY_U32S_1; + break; + case _LINUX_CAPABILITY_VERSION_2: + datacnt = _LINUX_CAPABILITY_U32S_2; + break; + case _LINUX_CAPABILITY_VERSION_3: + datacnt = _LINUX_CAPABILITY_U32S_3; + break; + default: + fprintf(stderr, "Unknown Linux capability version: %x; forcing version 3\n", hdr.version); + datacnt = _LINUX_CAPABILITY_U32S_3; + hdr.version = _LINUX_CAPABILITY_VERSION_3; + } + + res = syscall(SYS_capget, &hdr, &data); + if (res) + { + fprintf(stderr, "Failed to query capabilities; error: %d\n", errno); + return -1; + } + + //for (int i = 0; i < datacnt; ++i) + //{ + // fprintf(stderr, "Set %d: eff = %08x, perm = %08x, inh = %08x\n", i, data[i].effective, data[i].permitted, data[i].inheritable); + //} + + for (int i = 0; i < datacnt; ++i) + { + data[i].inheritable |= data[i].permitted; + } + + res = syscall(SYS_capset, &hdr, &data); + if (res) + { + fprintf(stderr, "Failed to set capabilities; error: %d\n", errno); + return -1; + } + + /* now we can set up the ambient set */ + for (int i = 0; i < datacnt; ++i) + { + for (int j = 0; j < 32; ++j) + { + int capval = i * 32 + j; + int capbit = 1 << j; + + if ((data[i].permitted & capbit) && (data[i].inheritable & capbit)) + { + res = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, capval, 0, 0); + if (res) + { + fprintf(stderr, "Failed to set ambient capability %d; error: %d\n", capval, errno); + return -1; + } + } + } + } + + return 0; +} +#endif + int main(int argc, char* argv[]) { const bool verbose = (getenv("VERBOSE") != NULL); if (verbose) { @@ -1832,6 +1933,15 @@ int main(int argc, char* argv[]) { set_portable_home_and_config(fullpath); +#ifdef USE_CAPABILITIES + /* Ensure that capabilities for the AppImage are applied to the children (Note: we won't consider + this failing as an error as the application might still work for the usecase intended by the user) */ + if (appimage_adjust_capabilities()) + { + fprintf(stderr, "Warning: failed to forward capabilities\n"); + } +#endif + /* Original working directory */ char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) {