diff options
author | Kristóf Marussy <kris7topher@gmail.com> | 2020-03-26 01:28:57 +0100 |
---|---|---|
committer | Kristóf Marussy <kris7topher@gmail.com> | 2020-04-06 21:26:41 +0200 |
commit | 90facc19c2708b60eb81a2a29993a3f16596bab6 (patch) | |
tree | ef3da7dbfc720b266b1a2ce2b31ceae7cc72ab1a | |
parent | xdg-dbus-proxy hardening (diff) | |
download | firejail-90facc19c2708b60eb81a2a29993a3f16596bab6.tar.gz firejail-90facc19c2708b60eb81a2a29993a3f16596bab6.tar.zst firejail-90facc19c2708b60eb81a2a29993a3f16596bab6.zip |
xdg-dbus-proxy socket finding and mount hardening
To avoid race conditions, the proxy sockets from /run/firejail/dbus/ are
bind-mounted to /run/firejail/mnt/dbus/, which is controlled by root.
Instead of relying on the default locations of the DBus sockets, the environment
variables DBUS_SESSION_BUS_ADDRESS and DBUS_SYSTEM_BUS_ADDRESS are set
accordingly.
User sockets are tried in the following order when starting the proxy:
* DBUS_SESSION_BUS_ADDRES
* /run/user/<pid>/bus
* /run/user/<pid>/dbus/user_bus_socket
These are all blocked (including DBUS_SESSION_BUS_ADDRESS if it points at a
socket in the filesystem) when the filtering or blocking policy is active.
System sockets are tried in the following order:
* DBUS_SYSTEM_BUS_ADDRESS
* /run/dbus/system_bus_socket
These are all blocked (including DBUS_SYSTEM_BUS_ADDRESS if it points at a
socket in the filesystem) when the filtering or blocking policy is active.
-rw-r--r-- | src/firejail/dbus.c | 139 | ||||
-rw-r--r-- | src/include/rundefs.h | 3 |
2 files changed, 105 insertions, 37 deletions
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c index 9efd7bc85..e358aaa49 100644 --- a/src/firejail/dbus.c +++ b/src/firejail/dbus.c | |||
@@ -33,10 +33,10 @@ | |||
33 | 33 | ||
34 | #define DBUS_SOCKET_PATH_PREFIX "unix:path=" | 34 | #define DBUS_SOCKET_PATH_PREFIX "unix:path=" |
35 | #define DBUS_USER_SOCKET_FORMAT "/run/user/%d/bus" | 35 | #define DBUS_USER_SOCKET_FORMAT "/run/user/%d/bus" |
36 | #define DBUS_USER_SOCKET_PATH_FORMAT DBUS_SOCKET_PATH_PREFIX DBUS_USER_SOCKET_FORMAT | 36 | #define DBUS_USER_SOCKET_FORMAT2 "/run/user/%d/dbus/user_bus_socket" |
37 | #define DBUS_SYSTEM_SOCKET "/run/dbus/system_bus_socket" | 37 | #define DBUS_SYSTEM_SOCKET "/run/dbus/system_bus_socket" |
38 | #define DBUS_SYSTEM_SOCKET_PATH DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET | ||
39 | #define DBUS_SESSION_BUS_ADDRESS_ENV "DBUS_SESSION_BUS_ADDRESS" | 38 | #define DBUS_SESSION_BUS_ADDRESS_ENV "DBUS_SESSION_BUS_ADDRESS" |
39 | #define DBUS_SYSTEM_BUS_ADDRESS_ENV "DBUS_SYSTEM_BUS_ADDRESS" | ||
40 | #define DBUS_USER_DIR_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d" | 40 | #define DBUS_USER_DIR_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d" |
41 | #define DBUS_USER_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-user" | 41 | #define DBUS_USER_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-user" |
42 | #define DBUS_SYSTEM_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-system" | 42 | #define DBUS_SYSTEM_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-system" |
@@ -170,6 +170,36 @@ static void dbus_create_user_dir(void) { | |||
170 | free(path); | 170 | free(path); |
171 | } | 171 | } |
172 | 172 | ||
173 | static char *find_user_socket_by_format(char *format) { | ||
174 | char *dbus_user_socket; | ||
175 | if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1) | ||
176 | errExit("asprintf"); | ||
177 | struct stat s; | ||
178 | if (stat(dbus_user_socket, &s) == -1) { | ||
179 | if (errno == ENOENT) | ||
180 | goto fail; | ||
181 | return NULL; | ||
182 | errExit("stat"); | ||
183 | } | ||
184 | if (!S_ISSOCK(s.st_mode)) | ||
185 | goto fail; | ||
186 | return dbus_user_socket; | ||
187 | fail: | ||
188 | free(dbus_user_socket); | ||
189 | return NULL; | ||
190 | } | ||
191 | |||
192 | static char *find_user_socket(void) { | ||
193 | char *socket1 = find_user_socket_by_format(DBUS_USER_SOCKET_FORMAT); | ||
194 | if (socket1 != NULL) | ||
195 | return socket1; | ||
196 | char *socket2 = find_user_socket_by_format(DBUS_USER_SOCKET_FORMAT2); | ||
197 | if (socket2 != NULL) | ||
198 | return socket2; | ||
199 | fprintf(stderr, "DBus user socket was not found.\n"); | ||
200 | exit(1); | ||
201 | } | ||
202 | |||
173 | void dbus_proxy_start(void) { | 203 | void dbus_proxy_start(void) { |
174 | dbus_create_user_dir(); | 204 | dbus_create_user_dir(); |
175 | 205 | ||
@@ -203,26 +233,35 @@ void dbus_proxy_start(void) { | |||
203 | errExit("close"); | 233 | errExit("close"); |
204 | 234 | ||
205 | if (arg_dbus_user == DBUS_POLICY_FILTER) { | 235 | if (arg_dbus_user == DBUS_POLICY_FILTER) { |
206 | char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); | 236 | char *user_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); |
207 | if (dbus_user_path_env == NULL) { | 237 | if (user_env == NULL) { |
208 | write_arg(args_pipe[1], DBUS_USER_SOCKET_PATH_FORMAT, getuid()); | 238 | char *dbus_user_socket = find_user_socket(); |
239 | write_arg(args_pipe[1], DBUS_SOCKET_PATH_PREFIX "%s", | ||
240 | dbus_user_socket); | ||
241 | free(dbus_user_socket); | ||
209 | } else { | 242 | } else { |
210 | write_arg(args_pipe[1], dbus_user_path_env); | 243 | write_arg(args_pipe[1], "%s", user_env); |
211 | } | 244 | } |
212 | if (asprintf(&dbus_user_proxy_socket, DBUS_USER_PROXY_SOCKET_FORMAT, | 245 | if (asprintf(&dbus_user_proxy_socket, DBUS_USER_PROXY_SOCKET_FORMAT, |
213 | (int) getuid(), (int) getpid()) == -1) | 246 | (int) getuid(), (int) getpid()) == -1) |
214 | errExit("asprintf"); | 247 | errExit("asprintf"); |
215 | write_arg(args_pipe[1], dbus_user_proxy_socket); | 248 | write_arg(args_pipe[1], "%s", dbus_user_proxy_socket); |
216 | write_arg(args_pipe[1], "--filter"); | 249 | write_arg(args_pipe[1], "--filter"); |
217 | write_profile(args_pipe[1], "dbus-user."); | 250 | write_profile(args_pipe[1], "dbus-user."); |
218 | } | 251 | } |
219 | 252 | ||
220 | if (arg_dbus_system == DBUS_POLICY_FILTER) { | 253 | if (arg_dbus_system == DBUS_POLICY_FILTER) { |
221 | write_arg(args_pipe[1], DBUS_SYSTEM_SOCKET_PATH); | 254 | char *system_env = getenv(DBUS_SYSTEM_BUS_ADDRESS_ENV); |
255 | if (system_env == NULL) { | ||
256 | write_arg(args_pipe[1], | ||
257 | DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET); | ||
258 | } else { | ||
259 | write_arg(args_pipe[1], "%s", system_env); | ||
260 | } | ||
222 | if (asprintf(&dbus_system_proxy_socket, DBUS_SYSTEM_PROXY_SOCKET_FORMAT, | 261 | if (asprintf(&dbus_system_proxy_socket, DBUS_SYSTEM_PROXY_SOCKET_FORMAT, |
223 | (int) getuid(), (int) getpid()) == -1) | 262 | (int) getuid(), (int) getpid()) == -1) |
224 | errExit("asprintf"); | 263 | errExit("asprintf"); |
225 | write_arg(args_pipe[1], dbus_system_proxy_socket); | 264 | write_arg(args_pipe[1], "%s", dbus_system_proxy_socket); |
226 | write_arg(args_pipe[1], "--filter"); | 265 | write_arg(args_pipe[1], "--filter"); |
227 | write_profile(args_pipe[1], "dbus-system."); | 266 | write_profile(args_pipe[1], "dbus-system."); |
228 | } | 267 | } |
@@ -294,6 +333,16 @@ static void socket_overlay(char *socket_path, char *proxy_path) { | |||
294 | close(fd); | 333 | close(fd); |
295 | } | 334 | } |
296 | 335 | ||
336 | static char *get_socket_env(const char *name) { | ||
337 | char *value = getenv(name); | ||
338 | if (value == NULL) | ||
339 | return NULL; | ||
340 | if (strncmp(value, DBUS_SOCKET_PATH_PREFIX, | ||
341 | strlen(DBUS_SOCKET_PATH_PREFIX)) == 0) | ||
342 | return value + strlen(DBUS_SOCKET_PATH_PREFIX); | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
297 | static void disable_socket_dir(void) { | 346 | static void disable_socket_dir(void) { |
298 | struct stat s; | 347 | struct stat s; |
299 | if (stat(RUN_FIREJAIL_DBUS_DIR, &s) == 0) | 348 | if (stat(RUN_FIREJAIL_DBUS_DIR, &s) == 0) |
@@ -314,36 +363,41 @@ void dbus_apply_policy(void) { | |||
314 | return; | 363 | return; |
315 | } | 364 | } |
316 | 365 | ||
317 | char *dbus_new_user_socket_path; | 366 | create_empty_dir_as_root(RUN_DBUS_DIR, 0755); |
318 | if (asprintf(&dbus_new_user_socket_path, DBUS_USER_SOCKET_PATH_FORMAT, getuid()) == -1) | 367 | create_empty_file_as_root(RUN_DBUS_USER_SOCKET, 0700); |
319 | errExit("asprintf"); | 368 | create_empty_file_as_root(RUN_DBUS_SYSTEM_SOCKET, 0700); |
320 | char *dbus_new_user_socket = dbus_new_user_socket_path + strlen(DBUS_SOCKET_PATH_PREFIX); | ||
321 | char *dbus_user_path_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); | ||
322 | char *dbus_orig_user_socket_path; | ||
323 | if (dbus_user_path_env != NULL | ||
324 | && strncmp(DBUS_SOCKET_PATH_PREFIX, dbus_user_path_env, strlen(DBUS_SOCKET_PATH_PREFIX)) == 0) { | ||
325 | dbus_orig_user_socket_path = dbus_user_path_env; | ||
326 | } else { | ||
327 | dbus_orig_user_socket_path = dbus_new_user_socket_path; | ||
328 | } | ||
329 | char *dbus_orig_user_socket = dbus_orig_user_socket_path + strlen(DBUS_SOCKET_PATH_PREFIX); | ||
330 | 369 | ||
331 | if (arg_dbus_user != DBUS_POLICY_ALLOW) { | 370 | if (arg_dbus_user != DBUS_POLICY_ALLOW) { |
332 | if (arg_dbus_user == DBUS_POLICY_FILTER) { | 371 | if (arg_dbus_user == DBUS_POLICY_FILTER) { |
333 | assert(dbus_user_proxy_socket != NULL); | 372 | assert(dbus_user_proxy_socket != NULL); |
334 | socket_overlay(dbus_new_user_socket, dbus_user_proxy_socket); | 373 | socket_overlay(RUN_DBUS_USER_SOCKET, dbus_user_proxy_socket); |
335 | free(dbus_user_proxy_socket); | 374 | free(dbus_user_proxy_socket); |
336 | } else { // arg_dbus_user == DBUS_POLICY_BLOCK | ||
337 | disable_file_or_dir(dbus_new_user_socket); | ||
338 | } | 375 | } |
339 | 376 | ||
340 | if (strcmp(dbus_orig_user_socket, dbus_new_user_socket) != 0) | 377 | char *dbus_user_socket; |
341 | disable_file_or_dir(dbus_orig_user_socket); | 378 | if (asprintf(&dbus_user_socket, DBUS_USER_SOCKET_FORMAT, |
379 | (int) getuid()) == -1) | ||
380 | errExit("asprintf"); | ||
381 | disable_file_or_dir(dbus_user_socket); | ||
382 | |||
383 | char *dbus_user_socket2; | ||
384 | if (asprintf(&dbus_user_socket2, DBUS_USER_SOCKET_FORMAT2, | ||
385 | (int) getuid()) == -1) | ||
386 | errExit("asprintf"); | ||
387 | disable_file_or_dir(dbus_user_socket2); | ||
342 | 388 | ||
343 | // set a new environment variable: | 389 | char *user_env = get_socket_env(DBUS_SESSION_BUS_ADDRESS_ENV); |
344 | // DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<UID>/bus | 390 | if (user_env != NULL && strcmp(user_env, dbus_user_socket) != 0 && |
345 | if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV, dbus_new_user_socket_path, 1) == -1) { | 391 | strcmp(user_env, dbus_user_socket2) != 0) |
346 | fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV " required by --dbus-user\n"); | 392 | disable_file_or_dir(user_env); |
393 | |||
394 | free(dbus_user_socket); | ||
395 | free(dbus_user_socket2); | ||
396 | |||
397 | if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV, | ||
398 | DBUS_SOCKET_PATH_PREFIX RUN_DBUS_USER_SOCKET, 1) == -1) { | ||
399 | fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV | ||
400 | " required by --dbus-user\n"); | ||
347 | exit(1); | 401 | exit(1); |
348 | } | 402 | } |
349 | 403 | ||
@@ -355,14 +409,25 @@ void dbus_apply_policy(void) { | |||
355 | free(path); | 409 | free(path); |
356 | } | 410 | } |
357 | 411 | ||
358 | free(dbus_new_user_socket_path); | 412 | if (arg_dbus_system != DBUS_POLICY_ALLOW) { |
413 | if (arg_dbus_system == DBUS_POLICY_FILTER) { | ||
414 | assert(dbus_system_proxy_socket != NULL); | ||
415 | socket_overlay(RUN_DBUS_SYSTEM_SOCKET, dbus_system_proxy_socket); | ||
416 | free(dbus_system_proxy_socket); | ||
417 | } | ||
359 | 418 | ||
360 | if (arg_dbus_system == DBUS_POLICY_FILTER) { | ||
361 | assert(dbus_system_proxy_socket != NULL); | ||
362 | socket_overlay(DBUS_SYSTEM_SOCKET, dbus_system_proxy_socket); | ||
363 | free(dbus_system_proxy_socket); | ||
364 | } else if (arg_dbus_system == DBUS_POLICY_BLOCK) { | ||
365 | disable_file_or_dir(DBUS_SYSTEM_SOCKET); | 419 | disable_file_or_dir(DBUS_SYSTEM_SOCKET); |
420 | |||
421 | char *system_env = get_socket_env(DBUS_SYSTEM_BUS_ADDRESS_ENV); | ||
422 | if (system_env != NULL && strcmp(system_env, DBUS_SYSTEM_SOCKET) != 0) | ||
423 | disable_file_or_dir(system_env); | ||
424 | |||
425 | if (setenv(DBUS_SYSTEM_BUS_ADDRESS_ENV, | ||
426 | DBUS_SOCKET_PATH_PREFIX RUN_DBUS_SYSTEM_SOCKET, 1) == -1) { | ||
427 | fprintf(stderr, "Error: cannot modify " DBUS_SYSTEM_BUS_ADDRESS_ENV | ||
428 | " required by --dbus-system\n"); | ||
429 | exit(1); | ||
430 | } | ||
366 | } | 431 | } |
367 | 432 | ||
368 | // Only disable access to /run/firejail/dbus here, when the sockets have been bind-mounted. | 433 | // Only disable access to /run/firejail/dbus here, when the sockets have been bind-mounted. |
diff --git a/src/include/rundefs.h b/src/include/rundefs.h index 528d9e901..f8bcdec52 100644 --- a/src/include/rundefs.h +++ b/src/include/rundefs.h | |||
@@ -57,6 +57,9 @@ | |||
57 | #define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases" | 57 | #define RUN_DHCLIENT_4_LEASES_FILE RUN_DHCLIENT_DIR "/dhclient.leases" |
58 | #define RUN_DHCLIENT_4_PID_FILE RUN_DHCLIENT_DIR "/dhclient.pid" | 58 | #define RUN_DHCLIENT_4_PID_FILE RUN_DHCLIENT_DIR "/dhclient.pid" |
59 | #define RUN_DHCLIENT_6_PID_FILE RUN_DHCLIENT_DIR "/dhclient6.pid" | 59 | #define RUN_DHCLIENT_6_PID_FILE RUN_DHCLIENT_DIR "/dhclient6.pid" |
60 | #define RUN_DBUS_DIR RUN_MNT_DIR "/dbus" | ||
61 | #define RUN_DBUS_USER_SOCKET RUN_DBUS_DIR "/user" | ||
62 | #define RUN_DBUS_SYSTEM_SOCKET RUN_DBUS_DIR "/system" | ||
60 | 63 | ||
61 | #define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp" | 64 | #define RUN_SECCOMP_DIR RUN_MNT_DIR "/seccomp" |
62 | #define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed | 65 | #define RUN_SECCOMP_LIST RUN_SECCOMP_DIR "/seccomp.list" // list of seccomp files installed |