diff options
author | Kristóf Marussy <kris7topher@gmail.com> | 2020-02-27 19:55:52 +0100 |
---|---|---|
committer | Kristóf Marussy <kris7topher@gmail.com> | 2020-04-06 21:26:41 +0200 |
commit | 0afb43a5607574fa946fdfd65f3a4cfa25cfa018 (patch) | |
tree | a09eb41b57cbecfccf6e284817d0043f0bbe0ab4 | |
parent | Add sbox_exec_v and SBOX_KEEP_FDS (diff) | |
download | firejail-0afb43a5607574fa946fdfd65f3a4cfa25cfa018.tar.gz firejail-0afb43a5607574fa946fdfd65f3a4cfa25cfa018.tar.zst firejail-0afb43a5607574fa946fdfd65f3a4cfa25cfa018.zip |
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 <parent pid>-user and <parent pid>-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.
-rw-r--r-- | src/firejail/dbus.c | 225 | ||||
-rw-r--r-- | src/firejail/firejail.h | 2 | ||||
-rw-r--r-- | src/firejail/main.c | 10 | ||||
-rw-r--r-- | src/firejail/preproc.c | 4 | ||||
-rw-r--r-- | 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 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/mount.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <sys/wait.h> | ||
25 | #include <unistd.h> | ||
26 | #include <fcntl.h> | ||
21 | 27 | ||
22 | static void dbus_block_user(void) { | 28 | #define DBUS_SOCKET_PATH_PREFIX "unix:path=" |
23 | char *path; | 29 | #define DBUS_USER_SOCKET_FORMAT "/run/user/%d/bus" |
24 | if (asprintf(&path, "/run/user/%d/bus", getuid()) == -1) | 30 | #define DBUS_USER_SOCKET_PATH_FORMAT DBUS_SOCKET_PATH_PREFIX DBUS_USER_SOCKET_FORMAT |
25 | errExit("asprintf"); | 31 | #define DBUS_SYSTEM_SOCKET "/run/dbus/system_bus_socket" |
26 | char *env_var; | 32 | #define DBUS_SYSTEM_SOCKET_PATH DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET |
27 | if (asprintf(&env_var, "unix:path=%s", path) == -1) | 33 | #define DBUS_SESSION_BUS_ADDRESS_ENV "DBUS_SESSION_BUS_ADDRESS" |
28 | errExit("asprintf"); | 34 | #define DBUS_USER_PROXY_SOCKET_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d-user" |
35 | #define DBUS_SYSTEM_PROXY_SOCKET_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d-system" | ||
36 | |||
37 | static pid_t dbus_proxy_pid = 0; | ||
38 | static int dbus_proxy_status_fd = -1; | ||
39 | static char *dbus_user_proxy_socket = NULL; | ||
40 | static char *dbus_system_proxy_socket = NULL; | ||
41 | |||
42 | static void write_arg(int fd, char const *format, ...) { | ||
43 | va_list ap; | ||
44 | va_start(ap, format); | ||
45 | char *arg; | ||
46 | int length = vasprintf(&arg, format, ap); | ||
47 | va_end(ap); | ||
48 | if (length == -1) | ||
49 | errExit("vasprintf"); | ||
50 | length++; | ||
51 | if (arg_debug) | ||
52 | printf("xdg-dbus-proxy arg: %s\n", arg); | ||
53 | if (write(fd, arg, (size_t) length) != (ssize_t) length) | ||
54 | errExit("write"); | ||
55 | free(arg); | ||
56 | } | ||
57 | |||
58 | void dbus_proxy_start(void) { | ||
59 | int status_pipe[2]; | ||
60 | if (pipe(status_pipe) == -1) | ||
61 | errExit("pipe"); | ||
62 | dbus_proxy_status_fd = status_pipe[0]; | ||
63 | |||
64 | int args_pipe[2]; | ||
65 | if (pipe(args_pipe) == -1) | ||
66 | errExit("pipe"); | ||
29 | 67 | ||
30 | // set a new environment variable: DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<UID>/bus | 68 | dbus_proxy_pid = fork(); |
31 | if (setenv("DBUS_SESSION_BUS_ADDRESS", env_var, 1) == -1) { | 69 | if (dbus_proxy_pid == -1) |
32 | fprintf(stderr, "Error: cannot modify DBUS_SESSION_BUS_ADDRESS required by --nodbus\n"); | 70 | errExit("fork"); |
33 | exit(1); | 71 | if (dbus_proxy_pid == 0) { |
72 | int i; | ||
73 | for (i = 3; i < FIREJAIL_MAX_FD; i++) { | ||
74 | if (i != status_pipe[1] && i != args_pipe[0]) | ||
75 | close(i); // close open files | ||
76 | } | ||
77 | char *args[4] = {"/usr/bin/xdg-dbus-proxy", NULL, NULL, NULL}; | ||
78 | if (asprintf(&args[1], "--fd=%d", status_pipe[1]) == -1 | ||
79 | || asprintf(&args[2], "--args=%d", args_pipe[0]) == -1) | ||
80 | errExit("asprintf"); | ||
81 | if (arg_debug) | ||
82 | printf("starting xdg-dbus-proxy\n"); | ||
83 | sbox_exec_v(SBOX_USER | SBOX_SECCOMP | SBOX_CAPS_NONE | SBOX_KEEP_FDS, args); | ||
84 | } else { | ||
85 | if (close(status_pipe[1]) == -1 || close(args_pipe[0]) == -1) | ||
86 | errExit("close"); | ||
87 | |||
88 | if (arg_dbus_user == DBUS_POLICY_FILTER) { | ||
89 | char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); | ||
90 | if (dbus_user_path_env == NULL) { | ||
91 | write_arg(args_pipe[1], DBUS_USER_SOCKET_PATH_FORMAT, getuid()); | ||
92 | } else { | ||
93 | write_arg(args_pipe[1], dbus_user_path_env); | ||
94 | } | ||
95 | if (asprintf(&dbus_user_proxy_socket, DBUS_USER_PROXY_SOCKET_FORMAT, (int) getpid()) == -1) | ||
96 | errExit("asprintf"); | ||
97 | write_arg(args_pipe[1], dbus_user_proxy_socket); | ||
98 | write_arg(args_pipe[1], "--filter"); | ||
99 | // TODO Write filter rules to pipe | ||
100 | } | ||
101 | |||
102 | if (arg_dbus_system == DBUS_POLICY_FILTER) { | ||
103 | write_arg(args_pipe[1], DBUS_SYSTEM_SOCKET_PATH); | ||
104 | if (asprintf(&dbus_system_proxy_socket, DBUS_SYSTEM_PROXY_SOCKET_FORMAT, (int) getpid()) == -1) | ||
105 | errExit("asprintf"); | ||
106 | write_arg(args_pipe[1], dbus_system_proxy_socket); | ||
107 | write_arg(args_pipe[1], "--filter"); | ||
108 | // TODO Write filter rules to pipe | ||
109 | } | ||
110 | |||
111 | if (close(args_pipe[1]) == -1) | ||
112 | errExit("close"); | ||
113 | char buf[1]; | ||
114 | ssize_t read_bytes = read(status_pipe[0], buf, 1); | ||
115 | switch (read_bytes) { | ||
116 | case -1: | ||
117 | errExit("read"); | ||
118 | break; | ||
119 | case 0: | ||
120 | fprintf(stderr, "xdg-dbus-proxy closed pipe unexpectedly\n"); | ||
121 | // Wait for the subordinate process to write any errors to stderr and exit. | ||
122 | waitpid(dbus_proxy_pid, NULL, 0); | ||
123 | exit(-1); | ||
124 | break; | ||
125 | case 1: | ||
126 | if (arg_debug) | ||
127 | printf("xdg-dbus-proxy initialized\n"); | ||
128 | break; | ||
129 | default: | ||
130 | assert(0); | ||
131 | } | ||
34 | } | 132 | } |
133 | } | ||
35 | 134 | ||
36 | // blacklist the path | 135 | void dbus_proxy_stop(void) { |
37 | disable_file_or_dir(path); | 136 | if (dbus_proxy_pid == 0) |
38 | free(path); | 137 | return; |
39 | free(env_var); | 138 | assert(dbus_proxy_status_fd >= 0); |
139 | if (close(dbus_proxy_status_fd) == -1) | ||
140 | errExit("close"); | ||
141 | int status; | ||
142 | if (waitpid(dbus_proxy_pid, &status, 0) == -1) | ||
143 | errExit("waitpid"); | ||
144 | if (WIFEXITED(status) && WEXITSTATUS(status) != 0) | ||
145 | fwarning("xdg-dbus-proxy returned %s\n", WEXITSTATUS(status)); | ||
146 | dbus_proxy_pid = 0; | ||
147 | dbus_proxy_status_fd = -1; | ||
148 | if (dbus_user_proxy_socket != NULL) { | ||
149 | free(dbus_user_proxy_socket); | ||
150 | dbus_user_proxy_socket = NULL; | ||
151 | } | ||
152 | if (dbus_system_proxy_socket != NULL) { | ||
153 | free(dbus_system_proxy_socket); | ||
154 | dbus_system_proxy_socket = NULL; | ||
155 | } | ||
156 | } | ||
40 | 157 | ||
41 | // blacklist the dbus-launch user directory | 158 | static void socket_overlay(char *socket_path, char *proxy_path) { |
42 | if (asprintf(&path, "%s/.dbus", cfg.homedir) == -1) | 159 | if (mount(proxy_path, socket_path, NULL, MS_BIND, NULL) == -1) |
43 | errExit("asprintf"); | 160 | errExit("mount"); |
44 | disable_file_or_dir(path); | ||
45 | free(path); | ||
46 | } | 161 | } |
47 | 162 | ||
48 | static void dbus_block_system() { | 163 | static void disable_socket_dir(void) { |
49 | // blacklist also system D-Bus socket | 164 | struct stat s; |
50 | disable_file_or_dir("/run/dbus/system_bus_socket"); | 165 | if (stat(RUN_FIREJAIL_DBUS_DIR, &s) == 0) |
166 | disable_file_or_dir(RUN_FIREJAIL_DBUS_DIR); | ||
51 | } | 167 | } |
52 | 168 | ||
53 | void dbus_apply_policy(void) { | 169 | void dbus_apply_policy(void) { |
54 | if (arg_dbus_user == DBUS_POLICY_ALLOW && arg_dbus_system == DBUS_POLICY_ALLOW) | 170 | EUID_ROOT(); |
171 | |||
172 | if (arg_dbus_user == DBUS_POLICY_ALLOW && arg_dbus_system == DBUS_POLICY_ALLOW) { | ||
173 | disable_socket_dir(); | ||
55 | return; | 174 | return; |
175 | } | ||
56 | 176 | ||
57 | if (!checkcfg(CFG_DBUS)) { | 177 | if (!checkcfg(CFG_DBUS)) { |
178 | disable_socket_dir(); | ||
58 | fwarning("D-Bus handling is disabled in Firejail configuration file\n"); | 179 | fwarning("D-Bus handling is disabled in Firejail configuration file\n"); |
59 | return; | 180 | return; |
60 | } | 181 | } |
61 | 182 | ||
62 | if (arg_dbus_user != DBUS_POLICY_ALLOW) | 183 | char *dbus_new_user_socket_path; |
63 | dbus_block_user(); | 184 | if (asprintf(&dbus_new_user_socket_path, DBUS_USER_SOCKET_PATH_FORMAT, getuid()) == -1) |
185 | errExit("asprintf"); | ||
186 | char *dbus_new_user_socket = dbus_new_user_socket_path + strlen(DBUS_SOCKET_PATH_PREFIX); | ||
187 | char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); | ||
188 | char *dbus_orig_user_socket_path; | ||
189 | if (dbus_user_path_env != NULL | ||
190 | && strncmp(DBUS_SOCKET_PATH_PREFIX, dbus_user_path_env, strlen(DBUS_SOCKET_PATH_PREFIX)) == 0) { | ||
191 | dbus_orig_user_socket_path = dbus_user_path_env; | ||
192 | } else { | ||
193 | dbus_orig_user_socket_path = dbus_new_user_socket_path; | ||
194 | } | ||
195 | char *dbus_orig_user_socket = dbus_user_path_env + strlen(DBUS_SOCKET_PATH_PREFIX); | ||
196 | |||
197 | if (arg_dbus_user != DBUS_POLICY_ALLOW) { | ||
198 | if (arg_dbus_user == DBUS_POLICY_FILTER) { | ||
199 | assert(dbus_user_proxy_socket != NULL); | ||
200 | socket_overlay(dbus_new_user_socket, dbus_user_proxy_socket); | ||
201 | free(dbus_user_proxy_socket); | ||
202 | } else { // arg_dbus_user == DBUS_POLICY_BLOCK | ||
203 | disable_file_or_dir(dbus_new_user_socket); | ||
204 | } | ||
205 | |||
206 | if (strcmp(dbus_orig_user_socket, dbus_new_user_socket) != 0) | ||
207 | disable_file_or_dir(dbus_orig_user_socket); | ||
208 | |||
209 | // set a new environment variable: | ||
210 | // DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<UID>/bus | ||
211 | if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV, dbus_new_user_socket_path, 1) == -1) { | ||
212 | fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV " required by --dbus-user\n"); | ||
213 | exit(1); | ||
214 | } | ||
215 | |||
216 | // blacklist the dbus-launch user directory | ||
217 | char *path; | ||
218 | if (asprintf(&path, "%s/.dbus", cfg.homedir) == -1) | ||
219 | errExit("asprintf"); | ||
220 | disable_file_or_dir(path); | ||
221 | free(path); | ||
222 | } | ||
223 | |||
224 | free(dbus_new_user_socket_path); | ||
225 | |||
226 | if (arg_dbus_system == DBUS_POLICY_FILTER) { | ||
227 | assert(dbus_system_proxy_socket != NULL); | ||
228 | socket_overlay(DBUS_SYSTEM_SOCKET, dbus_system_proxy_socket); | ||
229 | free(dbus_system_proxy_socket); | ||
230 | } else if (arg_dbus_system == DBUS_POLICY_BLOCK) { | ||
231 | disable_file_or_dir(DBUS_SYSTEM_SOCKET); | ||
232 | } | ||
64 | 233 | ||
65 | if (arg_dbus_system != DBUS_POLICY_ALLOW) | 234 | // Only disable access to /run/firejail/dbus here, when the sockets have been bind-mounted. |
66 | dbus_block_system(); | 235 | disable_socket_dir(); |
67 | 236 | ||
68 | // look for a possible abstract unix socket | 237 | // look for a possible abstract unix socket |
69 | 238 | ||
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); | |||
846 | void set_profile_run_file(pid_t pid, const char *fname); | 846 | void set_profile_run_file(pid_t pid, const char *fname); |
847 | 847 | ||
848 | // dbus.c | 848 | // dbus.c |
849 | void dbus_proxy_start(void); | ||
850 | void dbus_proxy_stop(void); | ||
849 | void dbus_apply_policy(void); | 851 | void dbus_apply_policy(void); |
850 | 852 | ||
851 | // dhcp.c | 853 | // 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) { | |||
2767 | } | 2767 | } |
2768 | EUID_USER(); | 2768 | EUID_USER(); |
2769 | 2769 | ||
2770 | if (checkcfg(CFG_DBUS) && | ||
2771 | (arg_dbus_user == DBUS_POLICY_FILTER || arg_dbus_system == DBUS_POLICY_FILTER)) { | ||
2772 | EUID_ROOT(); | ||
2773 | dbus_proxy_start(); | ||
2774 | EUID_USER(); | ||
2775 | } | ||
2776 | |||
2770 | // clone environment | 2777 | // clone environment |
2771 | int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; | 2778 | int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; |
2772 | 2779 | ||
@@ -2977,6 +2984,9 @@ printf("**********************************\n"); | |||
2977 | // end of signal-safe code | 2984 | // end of signal-safe code |
2978 | //***************************** | 2985 | //***************************** |
2979 | 2986 | ||
2987 | // stop dbus proxy (if any) | ||
2988 | dbus_proxy_stop(); | ||
2989 | |||
2980 | // free globals | 2990 | // free globals |
2981 | if (cfg.profile) { | 2991 | if (cfg.profile) { |
2982 | ProfileEntry *prf = cfg.profile; | 2992 | 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) { | |||
58 | create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); | 58 | create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); |
59 | } | 59 | } |
60 | 60 | ||
61 | if (stat(RUN_FIREJAIL_DBUS_DIR, &s)) { | ||
62 | create_empty_dir_as_root(RUN_FIREJAIL_DBUS_DIR, 01777); | ||
63 | } | ||
64 | |||
61 | if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { | 65 | if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { |
62 | create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); | 66 | create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); |
63 | } | 67 | } |
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 @@ | |||
30 | #define RUN_FIREJAIL_NETWORK_DIR RUN_FIREJAIL_DIR "/network" | 30 | #define RUN_FIREJAIL_NETWORK_DIR RUN_FIREJAIL_DIR "/network" |
31 | #define RUN_FIREJAIL_BANDWIDTH_DIR RUN_FIREJAIL_DIR "/bandwidth" | 31 | #define RUN_FIREJAIL_BANDWIDTH_DIR RUN_FIREJAIL_DIR "/bandwidth" |
32 | #define RUN_FIREJAIL_PROFILE_DIR RUN_FIREJAIL_DIR "/profile" | 32 | #define RUN_FIREJAIL_PROFILE_DIR RUN_FIREJAIL_DIR "/profile" |
33 | #define RUN_FIREJAIL_DBUS_DIR RUN_FIREJAIL_DIR "/dbus" | ||
33 | #define RUN_NETWORK_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-network.lock" | 34 | #define RUN_NETWORK_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-network.lock" |
34 | #define RUN_DIRECTORY_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-run.lock" | 35 | #define RUN_DIRECTORY_LOCK_FILE RUN_FIREJAIL_DIR "/firejail-run.lock" |
35 | #define RUN_RO_DIR RUN_FIREJAIL_DIR "/firejail.ro.dir" | 36 | #define RUN_RO_DIR RUN_FIREJAIL_DIR "/firejail.ro.dir" |