From 0e831c52d8611f42053e39eb8f7b93ee34b698df Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 13 Aug 2022 19:08:52 +0300 Subject: [PATCH] module-access: determine trusted application executable Determine application executable file so that the result can be trusted, and the file exists in the current namespace. Don't use /proc/pid/cmdline, since that contains whatever was specified by the exec() call. --- src/modules/module-access.c | 84 ++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/src/modules/module-access.c b/src/modules/module-access.c index 9948d3953..8db2b2fb5 100644 --- a/src/modules/module-access.c +++ b/src/modules/module-access.c @@ -131,41 +131,57 @@ struct impl { struct spa_hook module_listener; }; -static int check_cmdline(struct pw_impl_client *client, int pid, const char *str) +static int get_exe_name(int pid, char *buf, size_t buf_size) { - char path[2048], key[1024]; - ssize_t len; - int fd, res; + char path[256]; + struct stat s1, s2; + int res; + + /* + * Find executable name, checking it is an existing file + * (in the current namespace). + */ + +#if defined(__linux__) + spa_scnprintf(path, sizeof(path), "/proc/%u/exe", pid); +#elif defined(__FreeBSD__) || defined(__MidnightBSD__) + spa_scnprintf(path, sizeof(path), "/proc/%u/file", pid); +#else + return -ENOTSUP; +#endif + + res = readlink(path, buf, buf_size); + if (res < 0) + return -errno; + if ((size_t)res >= buf_size) + return -E2BIG; + buf[res] = '\0'; + + /* Check the file exists (= not deleted, and is in current namespace) */ + if (stat(path, &s1) != 0 || stat(buf, &s2) != 0) + return -errno; + if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) + return -ENXIO; + + return 0; +} + +static int check_exe(struct pw_impl_client *client, const char *path, const char *str) +{ + char key[1024]; + int res; struct spa_json it[2]; - sprintf(path, "/proc/%u/cmdline", pid); - - fd = open(path, O_RDONLY); - if (fd < 0) { - res = -errno; - goto exit; - } - if ((len = read(fd, path, sizeof(path)-1)) < 0) { - res = -errno; - goto exit_close; - } - path[len] = '\0'; - spa_json_init(&it[0], str, strlen(str)); if ((res = spa_json_enter_array(&it[0], &it[1])) <= 0) - goto exit_close; + return res; while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) { - if (spa_streq(path, key)) { - res = 1; - goto exit_close; - } + if (spa_streq(path, key)) + return 1; } - res = 0; -exit_close: - close(fd); -exit: - return res; + + return 0; } static void @@ -174,6 +190,7 @@ context_check_access(void *data, struct pw_impl_client *client) struct impl *impl = data; struct pw_permission permissions[1]; struct spa_dict_item items[2]; + char exe_path[PATH_MAX]; const struct pw_properties *props; const char *str, *access; char *flatpak_app_id = NULL; @@ -195,10 +212,17 @@ context_check_access(void *data, struct pw_impl_client *client) goto granted; } else { pw_log_info("client %p has trusted pid %d", client, pid); + if ((res = get_exe_name(pid, exe_path, sizeof(exe_path))) >= 0) { + pw_log_info("client %p has trusted exe path '%s'", client, exe_path); + } else { + pw_log_info("client %p has no trusted exe path: %s", + client, spa_strerror(res)); + exe_path[0] = '\0'; + } } if (impl->properties && (str = pw_properties_get(impl->properties, "access.allowed")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p allowed check failed: %s", impl, client, spa_strerror(res)); @@ -209,7 +233,7 @@ context_check_access(void *data, struct pw_impl_client *client) } if (impl->properties && (str = pw_properties_get(impl->properties, "access.rejected")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p rejected check failed: %s", impl, client, spa_strerror(res)); @@ -221,7 +245,7 @@ context_check_access(void *data, struct pw_impl_client *client) } if (impl->properties && (str = pw_properties_get(impl->properties, "access.restricted")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p restricted check failed: %s", impl, client, spa_strerror(res));