aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-03-08 16:22:51 +0100
committerdec05eba <dec05eba@protonmail.com>2025-03-08 16:22:51 +0100
commit49c4d673661e968831390fabfe0ce69716e5e1c7 (patch)
tree427c1ea7db5af5d8705b609ed28fb3c449508b8f
parent616b174b818f26f41a3e114cfe3deb38259398b7 (diff)
Fix global hotkeys not working on some systems (filesystem is mounted with nosuid
-rw-r--r--main.c201
1 files changed, 163 insertions, 38 deletions
diff --git a/main.c b/main.c
index 314d274..68bb4c2 100644
--- a/main.c
+++ b/main.c
@@ -32,16 +32,23 @@ static bool readlink_realpath(const char *filepath, char *buffer) {
return true;
}
-static bool file_has_sys_admin_capability(const char *filepath) {
+static bool file_has_capabilities(const char *filepath, const cap_value_t *caps, int num_caps) {
cap_t cap = cap_get_file(filepath);
if(!cap)
return false;
- cap_flag_value_t res = CAP_CLEAR;
- cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &res);
- bool cap_set = res == CAP_SET;
+ bool has_caps = true;
+ for(int i = 0; i < num_caps; ++i) {
+ cap_flag_value_t res = CAP_CLEAR;
+ cap_get_flag(cap, caps[i], CAP_PERMITTED, &res);
+ if(res != CAP_SET) {
+ has_caps = false;
+ break;
+ }
+ }
+
cap_free(cap);
- return cap_set;
+ return has_caps;
}
static bool file_set_capabilities(const char *filepath, const cap_value_t *caps_to_set, int num_caps_to_set) {
@@ -64,6 +71,89 @@ static bool file_set_capabilities(const char *filepath, const cap_value_t *caps_
return res == 0;
}
+static const char* get_column(const char *str, int size, int *column_size, int column) {
+ *column_size = 0;
+ int current_column = 0;
+ int i = 0;
+ for(; column > 0 && i < size; ++i) {
+ if(str[i] == ' ') {
+ ++current_column;
+ if(current_column == column) {
+ ++i;
+ break;
+ }
+ }
+ }
+
+ const int column_start = i;
+ for(; i < size; ++i) {
+ if(str[i] == ' ') {
+ *column_size = i - column_start;
+ return str + column_start;
+ }
+ }
+
+ *column_size = size;
+ return str;
+}
+
+static bool mount_has_flag(const char *flags, int flags_size, const char *flag_to_find) {
+ if(flags_size == 0)
+ return false;
+
+ const int flag_to_find_size = strlen(flag_to_find);
+ int offset = 0;
+ for(;;) {
+ const char *p = memmem(flags + offset, flags_size - offset, flag_to_find, flag_to_find_size);
+ if(!p)
+ return false;
+
+ offset = p - flags;
+ const char prev_char = offset > 0 ? flags[offset - 1] : ',';
+ const char next_char = offset + flag_to_find_size < flags_size ? flags[offset + flag_to_find_size] : ',';
+ if((prev_char == ',' || prev_char == '\n') && (next_char == ',' || next_char == '\n'))
+ return true;
+ }
+
+ return false;
+}
+
+static bool mount_has_setuid_permission(const char *path) {
+ FILE *f = fopen("/proc/mounts", "rb");
+ if(!f) {
+ fprintf(stderr, "Error: failed to open /proc/mounts\n");
+ // Assume it's ok and just continue
+ return true;
+ }
+
+ // Assume there is permission if it can't be found
+ bool has_permission = true;
+ const int path_size = strlen(path);
+ char line[1024];
+ while(fgets(line, sizeof(line), f)) {
+ const int line_size = strlen(line);
+ int mount_point_size = 0;
+ const char *mount_point = get_column(line, line_size, &mount_point_size, 1);
+ if(!mount_point)
+ continue;
+
+ if(mount_point && mount_point_size == path_size && memcmp(mount_point, path, mount_point_size) == 0) {
+ int flags_size = 0;
+ const char *flags = get_column(line, line_size, &flags_size, 3);
+ if(!flags)
+ continue;
+
+ const bool has_rw = mount_has_flag(flags, flags_size, "rw");
+ const bool has_nosuid = mount_has_flag(flags, flags_size, "nosuid");
+ has_permission = has_rw && !has_nosuid;
+ break;
+ }
+ }
+
+ fclose(f);
+ return has_permission;
+}
+
static bool create_local_kms_server_proxy_directory(const char *home) {
char path[PATH_MAX];
int err;
@@ -139,14 +229,7 @@ static bool copy_file_atomic(const char *source_path, const char *dest_path) {
return res;
}
-static void usage(void) {
- fprintf(stderr, "usage alt.1: kms-server-proxy <initial_socket_path> <card_path> <user_homepath>\n");
- fprintf(stderr, "usage alt.2: kms-server-proxy setup-gsr-ui [user_homepath]\n");
- fprintf(stderr, "usage alt.3: kms-server-proxy launch-gsr-global-hotkeys <user_homepath> [args...]\n");
- exit(1);
-}
-
-static bool set_gsr_files_set_permissions_and_capabilities(const char *kms_server_proxy_local_filepath) {
+static bool gsr_files_set_permissions_and_capabilities(const char *kms_server_proxy_local_filepath) {
/* owner: read/write/execute, group: read/execute, public: read/execute */
if(chmod(kms_server_proxy_local_filepath, 0755) != 0) {
fprintf(stderr, "Error: failed to set %s permissions\n", kms_server_proxy_local_filepath);
@@ -158,7 +241,7 @@ static bool set_gsr_files_set_permissions_and_capabilities(const char *kms_serve
return false;
}
- if(!file_set_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETFCAP, CAP_SETUID }, 3)) {
+ if(!file_set_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETUID }, 2)) {
fprintf(stderr, "Error: failed to set kms-server-proxy capabilities\n");
return false;
}
@@ -167,17 +250,13 @@ static bool set_gsr_files_set_permissions_and_capabilities(const char *kms_serve
}
/* |gsr_global_hotkeys_local_filepath| can be NULL */
-static bool setup_local_gsr_files(const char *user_homepath, const char *kms_server_proxy_local_filepath, const char *kms_server_proxy_home) {
+static bool setup_local_gsr_files(const char *user_homepath, const char *kms_server_proxy_home) {
if(!create_local_kms_server_proxy_directory(user_homepath)) {
fprintf(stderr, "Error: failed to create ~/.local/share/gpu-screen-recorder directory\n");
return false;
}
- if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath)) {
- fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_local_filepath);
- return false;
- }
-
+ remove(kms_server_proxy_home);
if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_home)) {
fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_home);
return false;
@@ -186,13 +265,20 @@ static bool setup_local_gsr_files(const char *user_homepath, const char *kms_ser
return true;
}
-static int setup_gsr_ui(const char *user_homepath) {
- char self_path[PATH_MAX];
- if(!readlink_realpath("/proc/self/exe", self_path)) {
- fprintf(stderr, "failed to resolve /proc/self/exe\n");
- return 1;
+static bool get_local_kms_server_proxy_filepath(char *output, size_t output_size, const char *user_homepath) {
+ if(mount_has_setuid_permission(user_homepath)) {
+ snprintf(output, output_size, "%s/.local/share/gpu-screen-recorder/kms-server-proxy-2", user_homepath);
+ return true;
+ } else if(mount_has_setuid_permission("/")) {
+ snprintf(output, output_size, "/usr/bin/kms-server-proxy-2");
+ return true;
+ } else {
+ fprintf(stderr, "Error: neither %s nor / has both read-write permission and setuid permission\n", user_homepath);
+ return false;
}
+}
+static int setup_gsr_ui(const char *user_homepath) {
if(!user_homepath) {
user_homepath = getenv("HOME");
if(!user_homepath)
@@ -200,8 +286,7 @@ static int setup_gsr_ui(const char *user_homepath) {
}
char kms_server_proxy_local_filepath[PATH_MAX];
- /* Update kms-server-proxy-N to kms-server-proxy-N+1 on update (update that needs to run before this program launches itself) */
- snprintf(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-2", user_homepath);
+ get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath);
/* Weird-ass distros like openSUSE only allow pkexec for files in $HOME!!! */
char kms_server_proxy_home[PATH_MAX];
@@ -209,12 +294,18 @@ static int setup_gsr_ui(const char *user_homepath) {
if(geteuid() == 0) { /* is current user root? */
remove(kms_server_proxy_home);
- if(!set_gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
+ remove(kms_server_proxy_local_filepath);
+ if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath)) {
+ fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
return 1;
return 0;
} else {
- if(!setup_local_gsr_files(user_homepath, kms_server_proxy_local_filepath, kms_server_proxy_home))
+ if(!setup_local_gsr_files(user_homepath, kms_server_proxy_home))
return 1;
const char *args[] = { "pkexec", kms_server_proxy_home, "setup-gsr-ui", user_homepath, NULL };
@@ -225,6 +316,24 @@ static int setup_gsr_ui(const char *user_homepath) {
}
}
+static bool is_setup(const char *user_homepath) {
+ if(!user_homepath) {
+ user_homepath = getenv("HOME");
+ if(!user_homepath)
+ user_homepath = "/tmp";
+ }
+
+ char kms_server_proxy_local_filepath[PATH_MAX];
+ get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath);
+
+ if(!file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETUID }, 2)) {
+ fprintf(stderr, "Error: kms-server-proxy (%s) is either not installed or is missing capabilities\n", kms_server_proxy_local_filepath);
+ return false;
+ }
+
+ return true;
+}
+
static int launch_gsr_kms_server(const char *initial_socket_path, const char *card_path, const char *user_homepath) {
char self_path[PATH_MAX];
if(!readlink_realpath("/proc/self/exe", self_path)) {
@@ -233,14 +342,13 @@ static int launch_gsr_kms_server(const char *initial_socket_path, const char *ca
}
char kms_server_proxy_local_filepath[PATH_MAX];
- /* Update kms-server-proxy-N to kms-server-proxy-N+1 on update (update that needs to run before this program launches itself) */
- snprintf(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-2", user_homepath);
+ get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath);
/* Weird-ass distros like openSUSE only allow pkexec for files in $HOME!!! */
char kms_server_proxy_home[PATH_MAX];
snprintf(kms_server_proxy_home, sizeof(kms_server_proxy_home), "%s/kms-server-proxy", user_homepath);
- if(file_has_sys_admin_capability(kms_server_proxy_local_filepath)) {
+ if(file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN }, 1)) {
/* Need to resolve kms_server_proxy_local_filepath because /home can be a symlink to another location */
char kms_server_proxy_local_filepath_full[PATH_MAX];
if(!readlink_realpath(kms_server_proxy_local_filepath, kms_server_proxy_local_filepath_full)) {
@@ -266,13 +374,19 @@ static int launch_gsr_kms_server(const char *initial_socket_path, const char *ca
return execvp(args[0], (char *const*)args);
} else if(geteuid() == 0) { /* is current user root? */
remove(kms_server_proxy_home);
- if(!set_gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
+ remove(kms_server_proxy_local_filepath);
+ if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath)) {
+ fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
return 1;
const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
return execv(args[0], (char *const*)args);
} else {
- if(!setup_local_gsr_files(user_homepath, kms_server_proxy_local_filepath, kms_server_proxy_home))
+ if(!setup_local_gsr_files(user_homepath, kms_server_proxy_home))
return 1;
const char *args[] = { "pkexec", kms_server_proxy_home, initial_socket_path, card_path, user_homepath, NULL };
@@ -293,11 +407,10 @@ static int launch_gsr_global_hotkeys(char **argv) {
const char *user_homepath = argv[2];
char kms_server_proxy_local_filepath[PATH_MAX];
- /* Update kms-server-proxy-N to kms-server-proxy-N+1 on update (update that needs to run before this program launches itself) */
- snprintf(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-2", user_homepath);
+ get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath);
- if(!file_has_sys_admin_capability(kms_server_proxy_local_filepath)) {
- fprintf(stderr, "Error: kms-server-proxy is missing cap sys admin capability\n");
+ if(!file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SETUID }, 1)) {
+ fprintf(stderr, "Error: kms-server-proxy (%s) is either not installed or is missing capabilities\n", kms_server_proxy_local_filepath);
return 1;
}
@@ -328,6 +441,14 @@ static int launch_gsr_global_hotkeys(char **argv) {
return result;
}
+static void usage(void) {
+ fprintf(stderr, "usage alt.1: kms-server-proxy <initial_socket_path> <card_path> <user_homepath>\n");
+ fprintf(stderr, "usage alt.2: kms-server-proxy setup-gsr-ui [user_homepath]\n");
+ fprintf(stderr, "usage alt.3: kms-server-proxy launch-gsr-global-hotkeys <user_homepath> [args...]\n");
+ fprintf(stderr, "usage alt.4: kms-server-proxy is-setup [user_homepath]\n");
+ exit(1);
+}
+
int main(int argc, char **argv) {
if(argc < 2)
usage();
@@ -342,6 +463,10 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error: missing user_homepath argument to launch-gsr-global-hotkeys\n");
usage();
}
+ } else if(strcmp(argv[1], "is-setup") == 0) {
+ const char *user_homepath = argc == 3 ? argv[2] : NULL;
+ const bool s = is_setup(user_homepath);
+ return s ? 0 : 1;
} else if(argc == 4) {
return launch_gsr_kms_server(argv[1], argv[2], argv[3]);
} else {