aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--main.c364
2 files changed, 273 insertions, 93 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..1dcd973
--- /dev/null
+++ b/TODO
@@ -0,0 +1,2 @@
+Instead of checking if $HOME and / has permissions, check if ~/.local/share/gpu-screen-recorder has permission by going up the tree,
+ checking ~/.local/share/gpu-screen-recorder and then ~/.local/share and so on, until /. \ No newline at end of file
diff --git a/main.c b/main.c
index bca38fc..ff71eed 100644
--- a/main.c
+++ b/main.c
@@ -5,53 +5,61 @@
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/stat.h>
-#include <sys/sendfile.h>
#include <sys/capability.h>
#define KMS_SERVER_PROXY_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy"
#define GSR_KMS_SERVER_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-kms-server"
#define GSR_GLOBAL_HOTKEYS_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-global-hotkeys"
-static int readlink_realpath(const char *filepath, char *buffer) {
+static bool readlink_realpath(const char *filepath, char *buffer) {
char symlinked_path[PATH_MAX];
ssize_t bytes_written = readlink(filepath, symlinked_path, sizeof(symlinked_path) - 1);
if(bytes_written == -1 && errno == EINVAL) {
/* Not a symlink */
strncpy(symlinked_path, filepath, sizeof(symlinked_path));
} else if(bytes_written == -1) {
- return 0;
+ return false;
} else {
symlinked_path[bytes_written] = '\0';
}
if(!realpath(symlinked_path, buffer))
- return 0;
+ return false;
- return 1;
+ return true;
}
-static int 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 0;
+ return false;
+
+ 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_flag_value_t res = CAP_CLEAR;
- cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &res);
- int cap_set = res == CAP_SET;
cap_free(cap);
- return cap_set;
+ return has_caps;
}
-static int file_set_capabilities(const char *filepath, const cap_value_t *caps_to_set, int num_caps_to_set) {
+static bool file_set_capabilities(const char *filepath, const cap_value_t *caps_to_set, int num_caps_to_set) {
cap_t cap = cap_get_file(filepath);
if(!cap && errno != ENODATA)
- return 0;
+ return false;
if(!cap) {
cap = cap_init();
if(!cap)
- return 0;
+ return false;
}
int res = 0;
@@ -63,7 +71,90 @@ static int file_set_capabilities(const char *filepath, const cap_value_t *caps_t
return res == 0;
}
-static int create_local_kms_server_proxy_directory(const char *home) {
+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;
@@ -73,35 +164,53 @@ static int create_local_kms_server_proxy_directory(const char *home) {
snprintf(path, sizeof(path), "%s/%s", home, path_part);
err = mkdir(path, S_IRWXU);
if(err == -1 && errno != EEXIST)
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-static int copy_file_atomic(const char *source_path, const char *dest_path) {
+static bool copy_file_atomic(const char *source_path, const char *dest_path) {
int in_fd = -1;
int out_fd = -1;
- int res = 0;
+ bool res = false;
char tmp_filepath[PATH_MAX];
snprintf(tmp_filepath, sizeof(tmp_filepath), "%s.tmp", dest_path);
in_fd = open(source_path, O_RDONLY);
- if(in_fd == -1)
+ if(in_fd == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to open %s, error: %s\n", source_path, strerror(errno));
goto done;
+ }
- struct stat st;
- if(fstat(in_fd, &st) == -1)
+ out_fd = open(tmp_filepath, O_WRONLY | O_CREAT, 0755);
+ if(out_fd == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to create %s, error: %s\n", tmp_filepath, strerror(errno));
goto done;
+ }
- out_fd = open(tmp_filepath, O_RDWR | O_CREAT | O_TRUNC, 0755);
- if(out_fd == -1)
- goto done;
+ uint8_t buffer[8192];
+ for(;;) {
+ const ssize_t bytes_read = read(in_fd, buffer, sizeof(buffer));
+ if(bytes_read == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to read data from %s, error: %s\n", source_path, strerror(errno));
+ goto done;
+ } else if(bytes_read == 0) {
+ break;
+ }
- if(sendfile(out_fd, in_fd, NULL, st.st_size) != st.st_size)
- goto done;
+ ssize_t bytes_written_total = 0;
+ while(bytes_written_total < bytes_read) {
+ const ssize_t bytes_written = write(out_fd, buffer + bytes_written_total, bytes_read - bytes_written_total);
+ if(bytes_written == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to write data to %s, error: %s\n", tmp_filepath, strerror(errno));
+ goto done;
+ }
+ bytes_written_total += bytes_written;
+ }
+ }
- res = 1;
+ res = true;
done:
if(in_fd)
@@ -109,26 +218,67 @@ static int copy_file_atomic(const char *source_path, const char *dest_path) {
if(out_fd)
close(out_fd);
- if(res)
+ if(res) {
res = rename(tmp_filepath, dest_path) == 0;
+ if(!res)
+ fprintf(stderr, "Error: copy_file_atomic: Failed to rename from %s to %s, error: %s\n", strerror(errno), tmp_filepath, dest_path);
+ } else {
+ remove(tmp_filepath);
+ }
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 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);
+ return false;
+ }
+
+ if(chown(kms_server_proxy_local_filepath, 0, 0) != 0) {
+ fprintf(stderr, "Error: failed to set %s ownership\n", kms_server_proxy_local_filepath);
+ return false;
+ }
+
+ 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;
+ }
+
+ 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;
+/* |gsr_global_hotkeys_local_filepath| can be NULL */
+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;
+ }
+
+ 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;
}
+ return true;
+}
+
+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)
@@ -136,45 +286,56 @@ 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);
-
- char gsr_kms_server_local_filepath[PATH_MAX];
- snprintf(gsr_kms_server_local_filepath, sizeof(gsr_kms_server_local_filepath), "%s/.local/share/gpu-screen-recorder/gsr-kms-server", user_homepath);
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
- char gsr_global_hotkeys_local_filepath[PATH_MAX];
- snprintf(gsr_global_hotkeys_local_filepath, sizeof(gsr_global_hotkeys_local_filepath), "%s/.local/share/gpu-screen-recorder/gsr-global-hotkeys", 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(geteuid() == 0) { /* is current user root? */
- int success = 1;
- success &= (file_set_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETFCAP, CAP_SETUID }, 3) == 1);
- return success ? 0 : 1;
- } else {
- if(create_local_kms_server_proxy_directory(user_homepath) != 1) {
- fprintf(stderr, "Error: failed to create ~/.local/share/gpu-screen-recorder directory\n");
- return 1;
- }
-
- if(copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath) != 1) {
+ remove(kms_server_proxy_home);
+ 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(copy_file_atomic(GSR_KMS_SERVER_FILEPATH, gsr_kms_server_local_filepath) != 1) {
- fprintf(stderr, "Error: failed to copy gsr-kms-server to %s\n", gsr_kms_server_local_filepath);
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
return 1;
- }
- if(copy_file_atomic(GSR_GLOBAL_HOTKEYS_FILEPATH, gsr_global_hotkeys_local_filepath) != 1) {
- fprintf(stderr, "Error: failed to copy gsr-global-hotkeys to %s\n", gsr_global_hotkeys_local_filepath);
+ return 0;
+ } else {
+ if(!setup_local_gsr_files(user_homepath, kms_server_proxy_home))
return 1;
- }
- const char *args[] = { "pkexec", kms_server_proxy_local_filepath, "setup-gsr-ui", user_homepath, NULL };
- return execvp(args[0], (char *const*)args);
+ const char *args[] = { "pkexec", kms_server_proxy_home, "setup-gsr-ui", user_homepath, NULL };
+ const int result = execvp(args[0], (char *const*)args);
+ perror("pkexec");
+ remove(kms_server_proxy_home);
+ return result;
}
}
+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];
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return false;
+
+ 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)) {
@@ -183,13 +344,14 @@ 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);
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
- char gsr_kms_server_local_filepath[PATH_MAX];
- snprintf(gsr_kms_server_local_filepath, sizeof(gsr_kms_server_local_filepath), "%s/.local/share/gpu-screen-recorder/gsr-kms-server", 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, CAP_SETUID }, 2)) {
/* 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)) {
@@ -206,30 +368,35 @@ static int launch_gsr_kms_server(const char *initial_socket_path, const char *ca
return execv(args[0], (char *const*)args);
}
- const char *args[] = { "pkexec", gsr_kms_server_local_filepath, initial_socket_path, card_path, NULL };
- return execvp(args[0], (char *const*)args);
- } else if(geteuid() == 0) { /* is current user root? */
- file_set_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETFCAP, CAP_SETUID }, 3);
- const char *args[] = { gsr_kms_server_local_filepath, initial_socket_path, card_path, NULL };
- return execv(args[0], (char *const*)args);
- } else {
- if(create_local_kms_server_proxy_directory(user_homepath) != 1) {
- fprintf(stderr, "Error: failed to create ~/.local/share/gpu-screen-recorder directory\n");
+ if(setuid(0) == -1) {
+ fprintf(stderr, "Error: failed to switch to root user to launch gsr-kms-server\n");
return 1;
}
- if(copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath) != 1) {
+ const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
+ return execvp(args[0], (char *const*)args);
+ } else if(geteuid() == 0) { /* is current user root? */
+ remove(kms_server_proxy_home);
+ 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(copy_file_atomic(GSR_KMS_SERVER_FILEPATH, gsr_kms_server_local_filepath) != 1) {
- fprintf(stderr, "Error: failed to copy gsr-kms-server to %s\n", gsr_kms_server_local_filepath);
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
return 1;
- }
- const char *args[] = { "pkexec", kms_server_proxy_local_filepath, initial_socket_path, card_path, user_homepath, NULL };
- return execvp(args[0], (char *const*)args);
+ 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_home))
+ return 1;
+
+ const char *args[] = { "pkexec", kms_server_proxy_home, initial_socket_path, card_path, user_homepath, NULL };
+ const int result = execvp(args[0], (char *const*)args);
+ perror("pkexec");
+ remove(kms_server_proxy_home);
+ return result;
}
}
@@ -243,14 +410,11 @@ 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);
-
- char gsr_global_hotkeys_local_filepath[PATH_MAX];
- snprintf(gsr_global_hotkeys_local_filepath, sizeof(gsr_global_hotkeys_local_filepath), "%s/.local/share/gpu-screen-recorder/gsr-global-hotkeys", user_homepath);
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
- 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;
}
@@ -275,8 +439,18 @@ static int launch_gsr_global_hotkeys(char **argv) {
return 1;
}
- argv[2] = gsr_global_hotkeys_local_filepath;
- return execv(argv[2], argv + 2);
+ argv[2] = GSR_GLOBAL_HOTKEYS_FILEPATH;
+ const int result = execv(argv[2], argv + 2);
+ perror(argv[2]);
+ 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) {
@@ -293,6 +467,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 {