From 0afb43a5607574fa946fdfd65f3a4cfa25cfa018 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 27 Feb 2020 19:55:52 +0100 Subject: Add xdg-dbus-proxy support * The proxy is forked off outside the sandbox namespace to protect the fds of the original buses from the sandboxed process. * The /run/firejail/dbus directory (with the sticky bit set) holds the proxy sockets. The sockets are -user and -system for the user and system buses, respectively. Each socket is owned by the sandbox user. * The sockets are bind-mounted over their expected locations and the /run/firejail/dbus directory is subsequently hidden from the sandbox. * Upon sandbox exit, the xdg-dbus-proxy instance is terminated and the sockets are cleaned up. * Filter rules will be added in a future commit. --- src/firejail/dbus.c | 225 ++++++++++++++++++++++++++++++++++++++++++------ src/firejail/firejail.h | 2 + src/firejail/main.c | 10 +++ src/firejail/preproc.c | 4 + src/include/rundefs.h | 1 + 5 files changed, 214 insertions(+), 28 deletions(-) diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c index 241b8fc44..5cdd4b372 100644 --- a/src/firejail/dbus.c +++ b/src/firejail/dbus.c @@ -18,52 +18,221 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "firejail.h" +#include +#include +#include +#include +#include +#include -static void dbus_block_user(void) { - char *path; - if (asprintf(&path, "/run/user/%d/bus", getuid()) == -1) - errExit("asprintf"); - char *env_var; - if (asprintf(&env_var, "unix:path=%s", path) == -1) - errExit("asprintf"); +#define DBUS_SOCKET_PATH_PREFIX "unix:path=" +#define DBUS_USER_SOCKET_FORMAT "/run/user/%d/bus" +#define DBUS_USER_SOCKET_PATH_FORMAT DBUS_SOCKET_PATH_PREFIX DBUS_USER_SOCKET_FORMAT +#define DBUS_SYSTEM_SOCKET "/run/dbus/system_bus_socket" +#define DBUS_SYSTEM_SOCKET_PATH DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET +#define DBUS_SESSION_BUS_ADDRESS_ENV "DBUS_SESSION_BUS_ADDRESS" +#define DBUS_USER_PROXY_SOCKET_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d-user" +#define DBUS_SYSTEM_PROXY_SOCKET_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d-system" + +static pid_t dbus_proxy_pid = 0; +static int dbus_proxy_status_fd = -1; +static char *dbus_user_proxy_socket = NULL; +static char *dbus_system_proxy_socket = NULL; + +static void write_arg(int fd, char const *format, ...) { + va_list ap; + va_start(ap, format); + char *arg; + int length = vasprintf(&arg, format, ap); + va_end(ap); + if (length == -1) + errExit("vasprintf"); + length++; + if (arg_debug) + printf("xdg-dbus-proxy arg: %s\n", arg); + if (write(fd, arg, (size_t) length) != (ssize_t) length) + errExit("write"); + free(arg); +} + +void dbus_proxy_start(void) { + int status_pipe[2]; + if (pipe(status_pipe) == -1) + errExit("pipe"); + dbus_proxy_status_fd = status_pipe[0]; + + int args_pipe[2]; + if (pipe(args_pipe) == -1) + errExit("pipe"); - // set a new environment variable: DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user//bus - if (setenv("DBUS_SESSION_BUS_ADDRESS", env_var, 1) == -1) { - fprintf(stderr, "Error: cannot modify DBUS_SESSION_BUS_ADDRESS required by --nodbus\n"); - exit(1); + dbus_proxy_pid = fork(); + if (dbus_proxy_pid == -1) + errExit("fork"); + if (dbus_proxy_pid == 0) { + int i; + for (i = 3; i < FIREJAIL_MAX_FD; i++) { + if (i != status_pipe[1] && i != args_pipe[0]) + close(i); // close open files + } + char *args[4] = {"/usr/bin/xdg-dbus-proxy", NULL, NULL, NULL}; + if (asprintf(&args[1], "--fd=%d", status_pipe[1]) == -1 + || asprintf(&args[2], "--args=%d", args_pipe[0]) == -1) + errExit("asprintf"); + if (arg_debug) + printf("starting xdg-dbus-proxy\n"); + sbox_exec_v(SBOX_USER | SBOX_SECCOMP | SBOX_CAPS_NONE | SBOX_KEEP_FDS, args); + } else { + if (close(status_pipe[1]) == -1 || close(args_pipe[0]) == -1) + errExit("close"); + + if (arg_dbus_user == DBUS_POLICY_FILTER) { + char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); + if (dbus_user_path_env == NULL) { + write_arg(args_pipe[1], DBUS_USER_SOCKET_PATH_FORMAT, getuid()); + } else { + write_arg(args_pipe[1], dbus_user_path_env); + } + if (asprintf(&dbus_user_proxy_socket, DBUS_USER_PROXY_SOCKET_FORMAT, (int) getpid()) == -1) + errExit("asprintf"); + write_arg(args_pipe[1], dbus_user_proxy_socket); + write_arg(args_pipe[1], "--filter"); + // TODO Write filter rules to pipe + } + + if (arg_dbus_system == DBUS_POLICY_FILTER) { + write_arg(args_pipe[1], DBUS_SYSTEM_SOCKET_PATH); + if (asprintf(&dbus_system_proxy_socket, DBUS_SYSTEM_PROXY_SOCKET_FORMAT, (int) getpid()) == -1) + errExit("asprintf"); + write_arg(args_pipe[1], dbus_system_proxy_socket); + write_arg(args_pipe[1], "--filter"); + // TODO Write filter rules to pipe + } + + if (close(args_pipe[1]) == -1) + errExit("close"); + char buf[1]; + ssize_t read_bytes = read(status_pipe[0], buf, 1); + switch (read_bytes) { + case -1: + errExit("read"); + break; + case 0: + fprintf(stderr, "xdg-dbus-proxy closed pipe unexpectedly\n"); + // Wait for the subordinate process to write any errors to stderr and exit. + waitpid(dbus_proxy_pid, NULL, 0); + exit(-1); + break; + case 1: + if (arg_debug) + printf("xdg-dbus-proxy initialized\n"); + break; + default: + assert(0); + } } +} - // blacklist the path - disable_file_or_dir(path); - free(path); - free(env_var); +void dbus_proxy_stop(void) { + if (dbus_proxy_pid == 0) + return; + assert(dbus_proxy_status_fd >= 0); + if (close(dbus_proxy_status_fd) == -1) + errExit("close"); + int status; + if (waitpid(dbus_proxy_pid, &status, 0) == -1) + errExit("waitpid"); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) + fwarning("xdg-dbus-proxy returned %s\n", WEXITSTATUS(status)); + dbus_proxy_pid = 0; + dbus_proxy_status_fd = -1; + if (dbus_user_proxy_socket != NULL) { + free(dbus_user_proxy_socket); + dbus_user_proxy_socket = NULL; + } + if (dbus_system_proxy_socket != NULL) { + free(dbus_system_proxy_socket); + dbus_system_proxy_socket = NULL; + } +} - // blacklist the dbus-launch user directory - if (asprintf(&path, "%s/.dbus", cfg.homedir) == -1) - errExit("asprintf"); - disable_file_or_dir(path); - free(path); +static void socket_overlay(char *socket_path, char *proxy_path) { + if (mount(proxy_path, socket_path, NULL, MS_BIND, NULL) == -1) + errExit("mount"); } -static void dbus_block_system() { - // blacklist also system D-Bus socket - disable_file_or_dir("/run/dbus/system_bus_socket"); +static void disable_socket_dir(void) { + struct stat s; + if (stat(RUN_FIREJAIL_DBUS_DIR, &s) == 0) + disable_file_or_dir(RUN_FIREJAIL_DBUS_DIR); } void dbus_apply_policy(void) { - if (arg_dbus_user == DBUS_POLICY_ALLOW && arg_dbus_system == DBUS_POLICY_ALLOW) + EUID_ROOT(); + + if (arg_dbus_user == DBUS_POLICY_ALLOW && arg_dbus_system == DBUS_POLICY_ALLOW) { + disable_socket_dir(); return; + } if (!checkcfg(CFG_DBUS)) { + disable_socket_dir(); fwarning("D-Bus handling is disabled in Firejail configuration file\n"); return; } - if (arg_dbus_user != DBUS_POLICY_ALLOW) - dbus_block_user(); + char *dbus_new_user_socket_path; + if (asprintf(&dbus_new_user_socket_path, DBUS_USER_SOCKET_PATH_FORMAT, getuid()) == -1) + errExit("asprintf"); + char *dbus_new_user_socket = dbus_new_user_socket_path + strlen(DBUS_SOCKET_PATH_PREFIX); + char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); + char *dbus_orig_user_socket_path; + if (dbus_user_path_env != NULL + && strncmp(DBUS_SOCKET_PATH_PREFIX, dbus_user_path_env, strlen(DBUS_SOCKET_PATH_PREFIX)) == 0) { + dbus_orig_user_socket_path = dbus_user_path_env; + } else { + dbus_orig_user_socket_path = dbus_new_user_socket_path; + } + char *dbus_orig_user_socket = dbus_user_path_env + strlen(DBUS_SOCKET_PATH_PREFIX); + + if (arg_dbus_user != DBUS_POLICY_ALLOW) { + if (arg_dbus_user == DBUS_POLICY_FILTER) { + assert(dbus_user_proxy_socket != NULL); + socket_overlay(dbus_new_user_socket, dbus_user_proxy_socket); + free(dbus_user_proxy_socket); + } else { // arg_dbus_user == DBUS_POLICY_BLOCK + disable_file_or_dir(dbus_new_user_socket); + } + + if (strcmp(dbus_orig_user_socket, dbus_new_user_socket) != 0) + disable_file_or_dir(dbus_orig_user_socket); + + // set a new environment variable: + // DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user//bus + if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV, dbus_new_user_socket_path, 1) == -1) { + fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV " required by --dbus-user\n"); + exit(1); + } + + // blacklist the dbus-launch user directory + char *path; + if (asprintf(&path, "%s/.dbus", cfg.homedir) == -1) + errExit("asprintf"); + disable_file_or_dir(path); + free(path); + } + + free(dbus_new_user_socket_path); + + if (arg_dbus_system == DBUS_POLICY_FILTER) { + assert(dbus_system_proxy_socket != NULL); + socket_overlay(DBUS_SYSTEM_SOCKET, dbus_system_proxy_socket); + free(dbus_system_proxy_socket); + } else if (arg_dbus_system == DBUS_POLICY_BLOCK) { + disable_file_or_dir(DBUS_SYSTEM_SOCKET); + } - if (arg_dbus_system != DBUS_POLICY_ALLOW) - dbus_block_system(); + // Only disable access to /run/firejail/dbus here, when the sockets have been bind-mounted. + disable_socket_dir(); // look for a possible abstract unix socket diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index d35e0d155..36ffd89b6 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -846,6 +846,8 @@ void set_x11_run_file(pid_t pid, int display); void set_profile_run_file(pid_t pid, const char *fname); // dbus.c +void dbus_proxy_start(void); +void dbus_proxy_stop(void); void dbus_apply_policy(void); // dhcp.c diff --git a/src/firejail/main.c b/src/firejail/main.c index fd2c6cb62..9e82a00b5 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -2767,6 +2767,13 @@ int main(int argc, char **argv, char **envp) { } EUID_USER(); + if (checkcfg(CFG_DBUS) && + (arg_dbus_user == DBUS_POLICY_FILTER || arg_dbus_system == DBUS_POLICY_FILTER)) { + EUID_ROOT(); + dbus_proxy_start(); + EUID_USER(); + } + // clone environment int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; @@ -2977,6 +2984,9 @@ printf("**********************************\n"); // end of signal-safe code //***************************** + // stop dbus proxy (if any) + dbus_proxy_stop(); + // free globals if (cfg.profile) { ProfileEntry *prf = cfg.profile; diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index 7f23a9f6f..61573b220 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c @@ -58,6 +58,10 @@ void preproc_build_firejail_dir(void) { create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); } + if (stat(RUN_FIREJAIL_DBUS_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_DBUS_DIR, 01777); + } + if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); } diff --git a/src/include/rundefs.h b/src/include/rundefs.h index 32f5ff12c..528d9e901 100644 --- a/src/include/rundefs.h +++ b/src/include/rundefs.h @@ -30,6 +30,7 @@ #define RUN_FIREJAIL_NETWORK_DIR RUN_FIREJAIL_DIR "/network" #define RUN_FIREJAIL_BANDWIDTH_DIR RUN_FIREJAIL_DIR "/bandwidth" #define RUN_FIREJAIL_PROFILE_DIR RUN_FIREJAIL_DIR "/profile" +#define RUN_FIREJAIL_DBUS_DIR RUN_FIREJAIL_DIR "/dbus" #define RUN_NETWORK_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-network.lock" #define RUN_DIRECTORY_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-run.lock" #define RUN_RO_DIR RUN_FIREJAIL_DIR "/firejail.ro.dir" -- cgit v1.2.3-70-g09d2