diff options
Diffstat (limited to 'src/firejail/sandbox.c')
-rw-r--r-- | src/firejail/sandbox.c | 618 |
1 files changed, 392 insertions, 226 deletions
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3f3564295..109daf552 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -28,12 +28,23 @@ | |||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <dirent.h> | 29 | #include <dirent.h> |
30 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <fcntl.h> | ||
31 | 32 | ||
32 | #include <sched.h> | 33 | #include <sched.h> |
33 | #ifndef CLONE_NEWUSER | 34 | #ifndef CLONE_NEWUSER |
34 | #define CLONE_NEWUSER 0x10000000 | 35 | #define CLONE_NEWUSER 0x10000000 |
35 | #endif | 36 | #endif |
36 | 37 | ||
38 | #include <sys/prctl.h> | ||
39 | #ifndef PR_SET_NO_NEW_PRIVS | ||
40 | # define PR_SET_NO_NEW_PRIVS 38 | ||
41 | #endif | ||
42 | |||
43 | #ifdef HAVE_APPARMOR | ||
44 | #include <sys/apparmor.h> | ||
45 | #endif | ||
46 | |||
47 | |||
37 | static int monitored_pid = 0; | 48 | static int monitored_pid = 0; |
38 | static void sandbox_handler(int sig){ | 49 | static void sandbox_handler(int sig){ |
39 | if (!arg_quiet) { | 50 | if (!arg_quiet) { |
@@ -70,8 +81,11 @@ static void sandbox_handler(int sig){ | |||
70 | 81 | ||
71 | } | 82 | } |
72 | 83 | ||
84 | |||
73 | // broadcast a SIGKILL | 85 | // broadcast a SIGKILL |
74 | kill(-1, SIGKILL); | 86 | kill(-1, SIGKILL); |
87 | flush_stdin(); | ||
88 | |||
75 | exit(sig); | 89 | exit(sig); |
76 | } | 90 | } |
77 | 91 | ||
@@ -94,9 +108,8 @@ void save_nogroups(void) { | |||
94 | FILE *fp = fopen(RUN_GROUPS_CFG, "w"); | 108 | FILE *fp = fopen(RUN_GROUPS_CFG, "w"); |
95 | if (fp) { | 109 | if (fp) { |
96 | fprintf(fp, "\n"); | 110 | fprintf(fp, "\n"); |
111 | SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644 | ||
97 | fclose(fp); | 112 | fclose(fp); |
98 | if (chown(RUN_GROUPS_CFG, 0, 0) < 0) | ||
99 | errExit("chown"); | ||
100 | } | 113 | } |
101 | else { | 114 | else { |
102 | fprintf(stderr, "Error: cannot save nogroups state\n"); | 115 | fprintf(stderr, "Error: cannot save nogroups state\n"); |
@@ -109,7 +122,7 @@ static void sandbox_if_up(Bridge *br) { | |||
109 | assert(br); | 122 | assert(br); |
110 | if (!br->configured) | 123 | if (!br->configured) |
111 | return; | 124 | return; |
112 | 125 | ||
113 | char *dev = br->devsandbox; | 126 | char *dev = br->devsandbox; |
114 | net_if_up(dev); | 127 | net_if_up(dev); |
115 | 128 | ||
@@ -124,8 +137,7 @@ static void sandbox_if_up(Bridge *br) { | |||
124 | assert(br->ipsandbox); | 137 | assert(br->ipsandbox); |
125 | if (arg_debug) | 138 | if (arg_debug) |
126 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); | 139 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); |
127 | net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); | 140 | net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); |
128 | net_if_up(dev); | ||
129 | } | 141 | } |
130 | else if (br->arg_ip_none == 0 && br->macvlan == 1) { | 142 | else if (br->arg_ip_none == 0 && br->macvlan == 1) { |
131 | // reassign the macvlan address | 143 | // reassign the macvlan address |
@@ -147,8 +159,7 @@ static void sandbox_if_up(Bridge *br) { | |||
147 | 159 | ||
148 | if (arg_debug) | 160 | if (arg_debug) |
149 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); | 161 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); |
150 | net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); | 162 | net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); |
151 | net_if_up(dev); | ||
152 | } | 163 | } |
153 | 164 | ||
154 | if (br->ip6sandbox) | 165 | if (br->ip6sandbox) |
@@ -198,6 +209,12 @@ static int monitor_application(pid_t app_pid) { | |||
198 | if (arg_debug) | 209 | if (arg_debug) |
199 | printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); | 210 | printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); |
200 | 211 | ||
212 | // if /proc is not remounted, we cannot check /proc directory, | ||
213 | // for now we just get out of here | ||
214 | // todo: find another way of checking child processes! | ||
215 | if (!checkcfg(CFG_REMOUNT_PROC_SYS)) | ||
216 | break; | ||
217 | |||
201 | DIR *dir; | 218 | DIR *dir; |
202 | if (!(dir = opendir("/proc"))) { | 219 | if (!(dir = opendir("/proc"))) { |
203 | // sleep 2 seconds and try again | 220 | // sleep 2 seconds and try again |
@@ -237,40 +254,46 @@ static int monitor_application(pid_t app_pid) { | |||
237 | 254 | ||
238 | // return the latest exit status. | 255 | // return the latest exit status. |
239 | return status; | 256 | return status; |
257 | } | ||
240 | 258 | ||
241 | #if 0 | 259 | void start_audit(void) { |
242 | // todo: find a way to shut down interfaces before closing the namespace | 260 | char *audit_prog; |
243 | // the problem is we don't have enough privileges to shutdown interfaces in this moment | 261 | if (asprintf(&audit_prog, "%s/firejail/faudit", LIBDIR) == -1) |
244 | // shut down bridge/macvlan interfaces | 262 | errExit("asprintf"); |
245 | if (any_bridge_configured()) { | 263 | assert(getenv("LD_PRELOAD") == NULL); |
246 | 264 | execl(audit_prog, audit_prog, NULL); | |
247 | if (cfg.bridge0.configured) { | 265 | perror("execl"); |
248 | printf("Shutting down %s\n", cfg.bridge0.devsandbox); | 266 | exit(1); |
249 | net_if_down( cfg.bridge0.devsandbox); | ||
250 | } | ||
251 | if (cfg.bridge1.configured) { | ||
252 | printf("Shutting down %s\n", cfg.bridge1.devsandbox); | ||
253 | net_if_down( cfg.bridge1.devsandbox); | ||
254 | } | ||
255 | if (cfg.bridge2.configured) { | ||
256 | printf("Shutting down %s\n", cfg.bridge2.devsandbox); | ||
257 | net_if_down( cfg.bridge2.devsandbox); | ||
258 | } | ||
259 | if (cfg.bridge3.configured) { | ||
260 | printf("Shutting down %s\n", cfg.bridge3.devsandbox); | ||
261 | net_if_down( cfg.bridge3.devsandbox); | ||
262 | } | ||
263 | usleep(20000); // 20 ms sleep | ||
264 | } | ||
265 | #endif | ||
266 | } | 267 | } |
267 | 268 | ||
269 | void start_application(void) { | ||
270 | //if (setsid() == -1) | ||
271 | //errExit("setsid"); | ||
268 | 272 | ||
269 | static void start_application(void) { | 273 | // set environment |
274 | env_defaults(); | ||
275 | env_apply(); | ||
276 | if (arg_debug) { | ||
277 | printf("starting application\n"); | ||
278 | printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); | ||
279 | } | ||
280 | |||
281 | //**************************************** | ||
282 | // audit | ||
283 | //**************************************** | ||
284 | if (arg_audit) { | ||
285 | assert(arg_audit_prog); | ||
286 | struct stat s; | ||
287 | if (stat(arg_audit_prog, &s) != 0) { | ||
288 | fprintf(stderr, "Error: cannot find the audit program\n"); | ||
289 | exit(1); | ||
290 | } | ||
291 | execl(arg_audit_prog, arg_audit_prog, NULL); | ||
292 | } | ||
270 | //**************************************** | 293 | //**************************************** |
271 | // start the program without using a shell | 294 | // start the program without using a shell |
272 | //**************************************** | 295 | //**************************************** |
273 | if (arg_shell_none) { | 296 | else if (arg_shell_none) { |
274 | if (arg_debug) { | 297 | if (arg_debug) { |
275 | int i; | 298 | int i; |
276 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { | 299 | for (i = cfg.original_program_index; i < cfg.original_argc; i++) { |
@@ -289,35 +312,33 @@ static void start_application(void) { | |||
289 | printf("Child process initialized\n"); | 312 | printf("Child process initialized\n"); |
290 | 313 | ||
291 | execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index]); | 314 | execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index]); |
315 | exit(1); | ||
292 | } | 316 | } |
293 | //**************************************** | 317 | //**************************************** |
294 | // start the program using a shell | 318 | // start the program using a shell |
295 | //**************************************** | 319 | //**************************************** |
296 | else { | 320 | else { |
297 | // choose the shell requested by the user, or use bash as default | 321 | assert(cfg.shell); |
298 | char *sh; | 322 | assert(cfg.command_line); |
299 | if (cfg.shell) | 323 | |
300 | sh = cfg.shell; | ||
301 | else if (arg_zsh) | ||
302 | sh = "/usr/bin/zsh"; | ||
303 | else if (arg_csh) | ||
304 | sh = "/bin/csh"; | ||
305 | else | ||
306 | sh = "/bin/bash"; | ||
307 | |||
308 | char *arg[5]; | 324 | char *arg[5]; |
309 | int index = 0; | 325 | int index = 0; |
310 | arg[index++] = sh; | 326 | arg[index++] = cfg.shell; |
311 | arg[index++] = "-c"; | 327 | if (login_shell) { |
312 | assert(cfg.command_line); | 328 | arg[index++] = "-l"; |
313 | if (arg_debug) | 329 | if (arg_debug) |
314 | printf("Starting %s\n", cfg.command_line); | 330 | printf("Starting %s login shell\n", cfg.shell); |
315 | if (arg_doubledash) | 331 | } else { |
316 | arg[index++] = "--"; | 332 | arg[index++] = "-c"; |
317 | arg[index++] = cfg.command_line; | 333 | if (arg_debug) |
334 | printf("Running %s command through %s\n", cfg.command_line, cfg.shell); | ||
335 | if (arg_doubledash) | ||
336 | arg[index++] = "--"; | ||
337 | arg[index++] = cfg.command_line; | ||
338 | } | ||
318 | arg[index] = NULL; | 339 | arg[index] = NULL; |
319 | assert(index < 5); | 340 | assert(index < 5); |
320 | 341 | ||
321 | if (arg_debug) { | 342 | if (arg_debug) { |
322 | char *msg; | 343 | char *msg; |
323 | if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) | 344 | if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) |
@@ -325,7 +346,7 @@ static void start_application(void) { | |||
325 | logmsg(msg); | 346 | logmsg(msg); |
326 | free(msg); | 347 | free(msg); |
327 | } | 348 | } |
328 | 349 | ||
329 | if (arg_debug) { | 350 | if (arg_debug) { |
330 | int i; | 351 | int i; |
331 | for (i = 0; i < 5; i++) { | 352 | for (i = 0; i < 5; i++) { |
@@ -334,17 +355,40 @@ static void start_application(void) { | |||
334 | printf("execvp argument %d: %s\n", i, arg[i]); | 355 | printf("execvp argument %d: %s\n", i, arg[i]); |
335 | } | 356 | } |
336 | } | 357 | } |
337 | 358 | ||
338 | if (!arg_command && !arg_quiet) | 359 | if (!arg_command && !arg_quiet) |
339 | printf("Child process initialized\n"); | 360 | printf("Child process initialized\n"); |
340 | execvp(sh, arg); | 361 | execvp(arg[0], arg); |
341 | } | 362 | } |
342 | 363 | ||
343 | perror("execvp"); | 364 | perror("execvp"); |
344 | exit(1); // it should never get here!!! | 365 | exit(1); // it should never get here!!! |
345 | } | 366 | } |
346 | 367 | ||
347 | 368 | static void enforce_filters(void) { | |
369 | // force default seccomp inside the chroot, no keep or drop list | ||
370 | // the list build on top of the default drop list is kept intact | ||
371 | arg_seccomp = 1; | ||
372 | if (cfg.seccomp_list_drop) { | ||
373 | free(cfg.seccomp_list_drop); | ||
374 | cfg.seccomp_list_drop = NULL; | ||
375 | } | ||
376 | if (cfg.seccomp_list_keep) { | ||
377 | free(cfg.seccomp_list_keep); | ||
378 | cfg.seccomp_list_keep = NULL; | ||
379 | } | ||
380 | |||
381 | // disable all capabilities | ||
382 | if (arg_caps_default_filter || arg_caps_list) | ||
383 | fprintf(stderr, "Warning: all capabilities disabled for a regular user in chroot\n"); | ||
384 | arg_caps_drop_all = 1; | ||
385 | |||
386 | // drop all supplementary groups; /etc/group file inside chroot | ||
387 | // is controlled by a regular usr | ||
388 | arg_nogroups = 1; | ||
389 | if (!arg_quiet) | ||
390 | printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); | ||
391 | } | ||
348 | 392 | ||
349 | int sandbox(void* sandbox_arg) { | 393 | int sandbox(void* sandbox_arg) { |
350 | // Get rid of unused parameter warning | 394 | // Get rid of unused parameter warning |
@@ -364,6 +408,7 @@ int sandbox(void* sandbox_arg) { | |||
364 | if (arg_debug && child_pid == 1) | 408 | if (arg_debug && child_pid == 1) |
365 | printf("PID namespace installed\n"); | 409 | printf("PID namespace installed\n"); |
366 | 410 | ||
411 | |||
367 | //**************************** | 412 | //**************************** |
368 | // set hostname | 413 | // set hostname |
369 | //**************************** | 414 | //**************************** |
@@ -379,7 +424,8 @@ int sandbox(void* sandbox_arg) { | |||
379 | if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { | 424 | if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { |
380 | chk_chroot(); | 425 | chk_chroot(); |
381 | } | 426 | } |
382 | 427 | // ... and mount a tmpfs on top of /run/firejail/mnt directory | |
428 | preproc_mount_mnt_dir(); | ||
383 | 429 | ||
384 | //**************************** | 430 | //**************************** |
385 | // log sandbox data | 431 | // log sandbox data |
@@ -396,7 +442,7 @@ int sandbox(void* sandbox_arg) { | |||
396 | fs_logger("install mount namespace"); | 442 | fs_logger("install mount namespace"); |
397 | 443 | ||
398 | //**************************** | 444 | //**************************** |
399 | // netfilter etc. | 445 | // netfilter |
400 | //**************************** | 446 | //**************************** |
401 | if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter | 447 | if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter |
402 | netfilter(arg_netfilter_file); | 448 | netfilter(arg_netfilter_file); |
@@ -405,6 +451,101 @@ int sandbox(void* sandbox_arg) { | |||
405 | netfilter6(arg_netfilter6_file); | 451 | netfilter6(arg_netfilter6_file); |
406 | } | 452 | } |
407 | 453 | ||
454 | //**************************** | ||
455 | // networking | ||
456 | //**************************** | ||
457 | int gw_cfg_failed = 0; // default gw configuration flag | ||
458 | if (arg_nonetwork) { | ||
459 | net_if_up("lo"); | ||
460 | if (arg_debug) | ||
461 | printf("Network namespace enabled, only loopback interface available\n"); | ||
462 | } | ||
463 | else if (any_bridge_configured() || any_interface_configured()) { | ||
464 | // configure lo and eth0...eth3 | ||
465 | net_if_up("lo"); | ||
466 | |||
467 | if (mac_not_zero(cfg.bridge0.macsandbox)) | ||
468 | net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); | ||
469 | sandbox_if_up(&cfg.bridge0); | ||
470 | |||
471 | if (mac_not_zero(cfg.bridge1.macsandbox)) | ||
472 | net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); | ||
473 | sandbox_if_up(&cfg.bridge1); | ||
474 | |||
475 | if (mac_not_zero(cfg.bridge2.macsandbox)) | ||
476 | net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); | ||
477 | sandbox_if_up(&cfg.bridge2); | ||
478 | |||
479 | if (mac_not_zero(cfg.bridge3.macsandbox)) | ||
480 | net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); | ||
481 | sandbox_if_up(&cfg.bridge3); | ||
482 | |||
483 | |||
484 | // moving an interface in a namespace using --interface will reset the interface configuration; | ||
485 | // we need to put the configuration back | ||
486 | if (cfg.interface0.configured && cfg.interface0.ip) { | ||
487 | if (arg_debug) | ||
488 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); | ||
489 | net_config_interface(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); | ||
490 | } | ||
491 | if (cfg.interface1.configured && cfg.interface1.ip) { | ||
492 | if (arg_debug) | ||
493 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); | ||
494 | net_config_interface(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); | ||
495 | } | ||
496 | if (cfg.interface2.configured && cfg.interface2.ip) { | ||
497 | if (arg_debug) | ||
498 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); | ||
499 | net_config_interface(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); | ||
500 | } | ||
501 | if (cfg.interface3.configured && cfg.interface3.ip) { | ||
502 | if (arg_debug) | ||
503 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); | ||
504 | net_config_interface(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); | ||
505 | } | ||
506 | |||
507 | // add a default route | ||
508 | if (cfg.defaultgw) { | ||
509 | // set the default route | ||
510 | if (net_add_route(0, 0, cfg.defaultgw)) { | ||
511 | fprintf(stderr, "Warning: cannot configure default route\n"); | ||
512 | gw_cfg_failed = 1; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | if (arg_debug) | ||
517 | printf("Network namespace enabled\n"); | ||
518 | } | ||
519 | |||
520 | |||
521 | // print network configuration | ||
522 | if (!arg_quiet) { | ||
523 | if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { | ||
524 | printf("\n"); | ||
525 | if (any_bridge_configured() || any_interface_configured()) { | ||
526 | // net_ifprint(); | ||
527 | if (arg_scan) | ||
528 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "printif", "scan"); | ||
529 | else | ||
530 | sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, PATH_FNET, "printif", "scan"); | ||
531 | |||
532 | } | ||
533 | if (cfg.defaultgw != 0) { | ||
534 | if (gw_cfg_failed) | ||
535 | printf("Default gateway configuration failed\n"); | ||
536 | else | ||
537 | printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); | ||
538 | } | ||
539 | if (cfg.dns1 != 0) | ||
540 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); | ||
541 | if (cfg.dns2 != 0) | ||
542 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); | ||
543 | if (cfg.dns3 != 0) | ||
544 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); | ||
545 | printf("\n"); | ||
546 | } | ||
547 | } | ||
548 | |||
408 | // load IBUS env variables | 549 | // load IBUS env variables |
409 | if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { | 550 | if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { |
410 | // do nothing - there are problems with ibus version 1.5.11 | 551 | // do nothing - there are problems with ibus version 1.5.11 |
@@ -412,11 +553,29 @@ int sandbox(void* sandbox_arg) { | |||
412 | else | 553 | else |
413 | env_ibus_load(); | 554 | env_ibus_load(); |
414 | 555 | ||
415 | // grab a copy of cp command | 556 | //**************************** |
416 | fs_build_cp_command(); | 557 | // fs pre-processing: |
417 | 558 | // - copy some commands under /run | |
559 | // - build seccomp filters | ||
560 | // - create an empty /etc/ld.so.preload | ||
561 | //**************************** | ||
562 | preproc_build_cp_command(); | ||
563 | |||
564 | #ifdef HAVE_SECCOMP | ||
565 | if (cfg.protocol) { | ||
566 | if (arg_debug) | ||
567 | printf("Build protocol filter: %s\n", cfg.protocol); | ||
568 | |||
569 | // build the seccomp filter as a regular user | ||
570 | int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, | ||
571 | PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); | ||
572 | if (rv) | ||
573 | exit(rv); | ||
574 | } | ||
575 | #endif | ||
576 | |||
418 | // trace pre-install | 577 | // trace pre-install |
419 | if (arg_trace || arg_tracelog) | 578 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
420 | fs_trace_preload(); | 579 | fs_trace_preload(); |
421 | 580 | ||
422 | //**************************** | 581 | //**************************** |
@@ -425,39 +584,23 @@ int sandbox(void* sandbox_arg) { | |||
425 | #ifdef HAVE_SECCOMP | 584 | #ifdef HAVE_SECCOMP |
426 | int enforce_seccomp = 0; | 585 | int enforce_seccomp = 0; |
427 | #endif | 586 | #endif |
587 | if (arg_appimage) { | ||
588 | enforce_filters(); | ||
589 | #ifdef HAVE_SECCOMP | ||
590 | enforce_seccomp = 1; | ||
591 | #endif | ||
592 | } | ||
593 | |||
428 | #ifdef HAVE_CHROOT | 594 | #ifdef HAVE_CHROOT |
429 | if (cfg.chrootdir) { | 595 | if (cfg.chrootdir) { |
430 | fs_chroot(cfg.chrootdir); | 596 | fs_chroot(cfg.chrootdir); |
431 | // redo cp command | ||
432 | fs_build_cp_command(); | ||
433 | 597 | ||
434 | // force caps and seccomp if not started as root | 598 | // force caps and seccomp if not started as root |
435 | if (getuid() != 0) { | 599 | if (getuid() != 0) { |
436 | // force default seccomp inside the chroot, no keep or drop list | 600 | enforce_filters(); |
437 | // the list build on top of the default drop list is kept intact | ||
438 | arg_seccomp = 1; | ||
439 | #ifdef HAVE_SECCOMP | 601 | #ifdef HAVE_SECCOMP |
440 | enforce_seccomp = 1; | 602 | enforce_seccomp = 1; |
441 | #endif | 603 | #endif |
442 | if (cfg.seccomp_list_drop) { | ||
443 | free(cfg.seccomp_list_drop); | ||
444 | cfg.seccomp_list_drop = NULL; | ||
445 | } | ||
446 | if (cfg.seccomp_list_keep) { | ||
447 | free(cfg.seccomp_list_keep); | ||
448 | cfg.seccomp_list_keep = NULL; | ||
449 | } | ||
450 | |||
451 | // disable all capabilities | ||
452 | if (arg_caps_default_filter || arg_caps_list) | ||
453 | fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); | ||
454 | arg_caps_drop_all = 1; | ||
455 | |||
456 | // drop all supplementary groups; /etc/group file inside chroot | ||
457 | // is controlled by a regular usr | ||
458 | arg_nogroups = 1; | ||
459 | if (!arg_quiet) | ||
460 | printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); | ||
461 | } | 604 | } |
462 | else | 605 | else |
463 | arg_seccomp = 1; | 606 | arg_seccomp = 1; |
@@ -465,17 +608,28 @@ int sandbox(void* sandbox_arg) { | |||
465 | //**************************** | 608 | //**************************** |
466 | // trace pre-install, this time inside chroot | 609 | // trace pre-install, this time inside chroot |
467 | //**************************** | 610 | //**************************** |
468 | if (arg_trace || arg_tracelog) | 611 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
469 | fs_trace_preload(); | 612 | fs_trace_preload(); |
470 | } | 613 | } |
471 | else | 614 | else |
472 | #endif | 615 | #endif |
473 | if (arg_overlay) | 616 | #ifdef HAVE_OVERLAYFS |
617 | if (arg_overlay) { | ||
474 | fs_overlayfs(); | 618 | fs_overlayfs(); |
619 | // force caps and seccomp if not started as root | ||
620 | if (getuid() != 0) { | ||
621 | enforce_filters(); | ||
622 | #ifdef HAVE_SECCOMP | ||
623 | enforce_seccomp = 1; | ||
624 | #endif | ||
625 | } | ||
626 | else | ||
627 | arg_seccomp = 1; | ||
628 | } | ||
475 | else | 629 | else |
630 | #endif | ||
476 | fs_basic_fs(); | 631 | fs_basic_fs(); |
477 | 632 | ||
478 | |||
479 | //**************************** | 633 | //**************************** |
480 | // set hostname in /etc/hostname | 634 | // set hostname in /etc/hostname |
481 | //**************************** | 635 | //**************************** |
@@ -487,145 +641,135 @@ int sandbox(void* sandbox_arg) { | |||
487 | // private mode | 641 | // private mode |
488 | //**************************** | 642 | //**************************** |
489 | if (arg_private) { | 643 | if (arg_private) { |
490 | if (cfg.home_private) // --private= | 644 | if (cfg.home_private) { // --private= |
491 | fs_private_homedir(); | 645 | if (cfg.chrootdir) |
646 | fprintf(stderr, "Warning: private=directory feature is disabled in chroot\n"); | ||
647 | else if (arg_overlay) | ||
648 | fprintf(stderr, "Warning: private=directory feature is disabled in overlay\n"); | ||
649 | else | ||
650 | fs_private_homedir(); | ||
651 | } | ||
652 | else if (cfg.home_private_keep) { // --private-home= | ||
653 | if (cfg.chrootdir) | ||
654 | fprintf(stderr, "Warning: private-home= feature is disabled in chroot\n"); | ||
655 | else if (arg_overlay) | ||
656 | fprintf(stderr, "Warning: private-home= feature is disabled in overlay\n"); | ||
657 | else | ||
658 | fs_private_home_list(); | ||
659 | } | ||
492 | else // --private | 660 | else // --private |
493 | fs_private(); | 661 | fs_private(); |
494 | } | 662 | } |
495 | 663 | ||
496 | if (arg_private_dev) | 664 | if (arg_private_dev) { |
497 | fs_private_dev(); | 665 | if (cfg.chrootdir) |
666 | fprintf(stderr, "Warning: private-dev feature is disabled in chroot\n"); | ||
667 | else if (arg_overlay) | ||
668 | fprintf(stderr, "Warning: private-dev feature is disabled in overlay\n"); | ||
669 | else | ||
670 | fs_private_dev(); | ||
671 | } | ||
672 | |||
498 | if (arg_private_etc) { | 673 | if (arg_private_etc) { |
499 | fs_private_etc_list(); | 674 | if (cfg.chrootdir) |
500 | // create /etc/ld.so.preload file again | 675 | fprintf(stderr, "Warning: private-etc feature is disabled in chroot\n"); |
501 | if (arg_trace || arg_tracelog) | 676 | else if (arg_overlay) |
502 | fs_trace_preload(); | 677 | fprintf(stderr, "Warning: private-etc feature is disabled in overlay\n"); |
678 | else { | ||
679 | fs_private_etc_list(); | ||
680 | // create /etc/ld.so.preload file again | ||
681 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) | ||
682 | fs_trace_preload(); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | if (arg_private_bin) { | ||
687 | if (cfg.chrootdir) | ||
688 | fprintf(stderr, "Warning: private-bin feature is disabled in chroot\n"); | ||
689 | else if (arg_overlay) | ||
690 | fprintf(stderr, "Warning: private-bin feature is disabled in overlay\n"); | ||
691 | else { | ||
692 | // for --x11=xorg we need to add xauth command | ||
693 | if (arg_x11_xorg) { | ||
694 | EUID_USER(); | ||
695 | char *tmp; | ||
696 | if (asprintf(&tmp, "%s,xauth", cfg.bin_private_keep) == -1) | ||
697 | errExit("asprintf"); | ||
698 | cfg.bin_private_keep = tmp; | ||
699 | fs_check_bin_list(); | ||
700 | EUID_ROOT(); | ||
701 | } | ||
702 | fs_private_bin_list(); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | if (arg_private_tmp) { | ||
707 | if (cfg.chrootdir) | ||
708 | fprintf(stderr, "Warning: private-tmp feature is disabled in chroot\n"); | ||
709 | else if (arg_overlay) | ||
710 | fprintf(stderr, "Warning: private-tmp feature is disabled in overlay\n"); | ||
711 | else { | ||
712 | // private-tmp is implemented as a whitelist | ||
713 | EUID_USER(); | ||
714 | profile_add("whitelist /tmp/.X11-unix"); | ||
715 | EUID_ROOT(); | ||
716 | } | ||
503 | } | 717 | } |
504 | if (arg_private_bin) | 718 | |
505 | fs_private_bin_list(); | 719 | //**************************** |
506 | if (arg_private_tmp) | 720 | // update /proc, /sys, /dev, /boot directorymy |
507 | fs_private_tmp(); | 721 | //**************************** |
722 | if (checkcfg(CFG_REMOUNT_PROC_SYS)) | ||
723 | fs_proc_sys_dev_boot(); | ||
508 | 724 | ||
509 | //**************************** | 725 | //**************************** |
510 | // apply the profile file | 726 | // apply the profile file |
511 | //**************************** | 727 | //**************************** |
512 | if (cfg.profile) { | 728 | // apply all whitelist commands ... |
513 | // apply all whitelist commands ... | 729 | if (cfg.chrootdir) |
730 | fprintf(stderr, "Warning: whitelist feature is disabled in chroot\n"); | ||
731 | else if (arg_overlay) | ||
732 | fprintf(stderr, "Warning: whitelist feature is disabled in overlay\n"); | ||
733 | else | ||
514 | fs_whitelist(); | 734 | fs_whitelist(); |
515 | 735 | ||
516 | // ... followed by blacklist commands | 736 | // ... followed by blacklist commands |
517 | fs_blacklist(); | 737 | fs_blacklist(); // mkdir and mkfile are processed all over again |
518 | } | ||
519 | 738 | ||
520 | //**************************** | 739 | //**************************** |
521 | // install trace | 740 | // install trace |
522 | //**************************** | 741 | //**************************** |
523 | if (arg_trace || arg_tracelog) | 742 | if (arg_trace || arg_tracelog || mask_x11_abstract_socket) |
524 | fs_trace(); | 743 | fs_trace(); |
525 | 744 | ||
526 | //**************************** | 745 | //**************************** |
527 | // update /proc, /dev, /boot directorymy | 746 | // nosound/no3d and fix for pulseaudio 7.0 |
528 | //**************************** | ||
529 | fs_proc_sys_dev_boot(); | ||
530 | |||
531 | //**************************** | ||
532 | // --nosound and fix for pulseaudio 7.0 | ||
533 | //**************************** | 747 | //**************************** |
534 | if (arg_nosound) | 748 | if (arg_nosound) { |
749 | // disable pulseaudio | ||
535 | pulseaudio_disable(); | 750 | pulseaudio_disable(); |
751 | |||
752 | // disable /dev/snd | ||
753 | fs_dev_disable_sound(); | ||
754 | } | ||
536 | else | 755 | else |
537 | pulseaudio_init(); | 756 | pulseaudio_init(); |
538 | 757 | ||
758 | if (arg_no3d) | ||
759 | fs_dev_disable_3d(); | ||
760 | |||
539 | //**************************** | 761 | //**************************** |
540 | // networking | 762 | // set dns |
541 | //**************************** | 763 | //**************************** |
542 | if (arg_nonetwork) { | ||
543 | net_if_up("lo"); | ||
544 | if (arg_debug) | ||
545 | printf("Network namespace enabled, only loopback interface available\n"); | ||
546 | } | ||
547 | else if (any_bridge_configured() || any_interface_configured()) { | ||
548 | // configure lo and eth0...eth3 | ||
549 | net_if_up("lo"); | ||
550 | |||
551 | if (mac_not_zero(cfg.bridge0.macsandbox)) | ||
552 | net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); | ||
553 | sandbox_if_up(&cfg.bridge0); | ||
554 | |||
555 | if (mac_not_zero(cfg.bridge1.macsandbox)) | ||
556 | net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); | ||
557 | sandbox_if_up(&cfg.bridge1); | ||
558 | |||
559 | if (mac_not_zero(cfg.bridge2.macsandbox)) | ||
560 | net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); | ||
561 | sandbox_if_up(&cfg.bridge2); | ||
562 | |||
563 | if (mac_not_zero(cfg.bridge3.macsandbox)) | ||
564 | net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); | ||
565 | sandbox_if_up(&cfg.bridge3); | ||
566 | |||
567 | // add a default route | ||
568 | if (cfg.defaultgw) { | ||
569 | // set the default route | ||
570 | if (net_add_route(0, 0, cfg.defaultgw)) | ||
571 | fprintf(stderr, "Warning: cannot configure default route\n"); | ||
572 | } | ||
573 | |||
574 | // enable interfaces | ||
575 | if (cfg.interface0.configured && cfg.interface0.ip) { | ||
576 | if (arg_debug) | ||
577 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); | ||
578 | net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); | ||
579 | net_if_up(cfg.interface0.dev); | ||
580 | } | ||
581 | if (cfg.interface1.configured && cfg.interface1.ip) { | ||
582 | if (arg_debug) | ||
583 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); | ||
584 | net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); | ||
585 | net_if_up(cfg.interface1.dev); | ||
586 | } | ||
587 | if (cfg.interface2.configured && cfg.interface2.ip) { | ||
588 | if (arg_debug) | ||
589 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); | ||
590 | net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); | ||
591 | net_if_up(cfg.interface2.dev); | ||
592 | } | ||
593 | if (cfg.interface3.configured && cfg.interface3.ip) { | ||
594 | if (arg_debug) | ||
595 | printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); | ||
596 | net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); | ||
597 | net_if_up(cfg.interface3.dev); | ||
598 | } | ||
599 | |||
600 | if (arg_debug) | ||
601 | printf("Network namespace enabled\n"); | ||
602 | } | ||
603 | |||
604 | // if any dns server is configured, it is time to set it now | ||
605 | fs_resolvconf(); | 764 | fs_resolvconf(); |
765 | |||
766 | //**************************** | ||
767 | // fs post-processing | ||
768 | //**************************** | ||
769 | preproc_delete_cp_command(); | ||
606 | fs_logger_print(); | 770 | fs_logger_print(); |
607 | fs_logger_change_owner(); | 771 | fs_logger_change_owner(); |
608 | 772 | ||
609 | // print network configuration | ||
610 | if (!arg_quiet) { | ||
611 | if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { | ||
612 | printf("\n"); | ||
613 | if (any_bridge_configured() || any_interface_configured()) | ||
614 | net_ifprint(); | ||
615 | if (cfg.defaultgw != 0) | ||
616 | printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); | ||
617 | if (cfg.dns1 != 0) | ||
618 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); | ||
619 | if (cfg.dns2 != 0) | ||
620 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); | ||
621 | if (cfg.dns3 != 0) | ||
622 | printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); | ||
623 | printf("\n"); | ||
624 | } | ||
625 | } | ||
626 | |||
627 | fs_delete_cp_command(); | ||
628 | |||
629 | //**************************** | 773 | //**************************** |
630 | // set application environment | 774 | // set application environment |
631 | //**************************** | 775 | //**************************** |
@@ -649,12 +793,6 @@ int sandbox(void* sandbox_arg) { | |||
649 | } | 793 | } |
650 | } | 794 | } |
651 | 795 | ||
652 | // set environment | ||
653 | env_defaults(); | ||
654 | |||
655 | // set user-supplied environment variables | ||
656 | env_apply(); | ||
657 | |||
658 | // set nice | 796 | // set nice |
659 | if (arg_nice) { | 797 | if (arg_nice) { |
660 | errno = 0; | 798 | errno = 0; |
@@ -668,6 +806,8 @@ int sandbox(void* sandbox_arg) { | |||
668 | 806 | ||
669 | // clean /tmp/.X11-unix sockets | 807 | // clean /tmp/.X11-unix sockets |
670 | fs_x11(); | 808 | fs_x11(); |
809 | if (arg_x11_xorg) | ||
810 | x11_xorg(); | ||
671 | 811 | ||
672 | //**************************** | 812 | //**************************** |
673 | // set security filters | 813 | // set security filters |
@@ -679,35 +819,35 @@ int sandbox(void* sandbox_arg) { | |||
679 | // set rlimits | 819 | // set rlimits |
680 | set_rlimits(); | 820 | set_rlimits(); |
681 | 821 | ||
682 | // set seccomp | 822 | // set cpu affinity |
823 | if (cfg.cpus) { | ||
824 | save_cpu(); // save cpu affinity mask to CPU_CFG file | ||
825 | set_cpu_affinity(); | ||
826 | } | ||
827 | |||
828 | // save cgroup in CGROUP_CFG file | ||
829 | if (cfg.cgroup) | ||
830 | save_cgroup(); | ||
831 | |||
832 | // set seccomp //todo: push it down after drop_privs and/or configuring noroot | ||
683 | #ifdef HAVE_SECCOMP | 833 | #ifdef HAVE_SECCOMP |
684 | // install protocol filter | 834 | // install protocol filter |
685 | if (cfg.protocol) { | 835 | if (cfg.protocol) { |
686 | protocol_filter(); // install filter | 836 | if (arg_debug) |
687 | protocol_filter_save(); // save filter in PROTOCOL_CFG | 837 | printf("Install protocol filter: %s\n", cfg.protocol); |
838 | seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter | ||
839 | protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG | ||
688 | } | 840 | } |
689 | 841 | ||
690 | // if a keep list is available, disregard the drop list | 842 | // if a keep list is available, disregard the drop list |
691 | if (arg_seccomp == 1) { | 843 | if (arg_seccomp == 1) { |
692 | if (cfg.seccomp_list_keep) | 844 | if (cfg.seccomp_list_keep) |
693 | seccomp_filter_keep(); | 845 | seccomp_filter_keep(); |
694 | else if (cfg.seccomp_list_errno) | ||
695 | seccomp_filter_errno(); | ||
696 | else | 846 | else |
697 | seccomp_filter_drop(enforce_seccomp); | 847 | seccomp_filter_drop(enforce_seccomp); |
698 | } | 848 | } |
699 | #endif | 849 | #endif |
700 | 850 | ||
701 | // set cpu affinity | ||
702 | if (cfg.cpus) { | ||
703 | save_cpu(); // save cpu affinity mask to CPU_CFG file | ||
704 | set_cpu_affinity(); | ||
705 | } | ||
706 | |||
707 | // save cgroup in CGROUP_CFG file | ||
708 | if (cfg.cgroup) | ||
709 | save_cgroup(); | ||
710 | |||
711 | //**************************************** | 851 | //**************************************** |
712 | // drop privileges or create a new user namespace | 852 | // drop privileges or create a new user namespace |
713 | //**************************************** | 853 | //**************************************** |
@@ -715,7 +855,7 @@ int sandbox(void* sandbox_arg) { | |||
715 | if (arg_noroot) { | 855 | if (arg_noroot) { |
716 | int rv = unshare(CLONE_NEWUSER); | 856 | int rv = unshare(CLONE_NEWUSER); |
717 | if (rv == -1) { | 857 | if (rv == -1) { |
718 | fprintf(stderr, "Warning: cannot mount a new user namespace, going forward without it...\n"); | 858 | fprintf(stderr, "Warning: cannot create a new user namespace, going forward without it...\n"); |
719 | drop_privs(arg_nogroups); | 859 | drop_privs(arg_nogroups); |
720 | arg_noroot = 0; | 860 | arg_noroot = 0; |
721 | } | 861 | } |
@@ -739,6 +879,18 @@ int sandbox(void* sandbox_arg) { | |||
739 | printf("noroot user namespace installed\n"); | 879 | printf("noroot user namespace installed\n"); |
740 | set_caps(); | 880 | set_caps(); |
741 | } | 881 | } |
882 | |||
883 | //**************************************** | ||
884 | // Set NO_NEW_PRIVS if desired | ||
885 | //**************************************** | ||
886 | if (arg_nonewprivs) { | ||
887 | int no_new_privs = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | ||
888 | |||
889 | if(no_new_privs != 0) | ||
890 | fprintf(stderr, "Warning: NO_NEW_PRIVS disabled, it requires a Linux kernel version 3.5 or newer.\n"); | ||
891 | else if (arg_debug) | ||
892 | printf("NO_NEW_PRIVS set\n"); | ||
893 | } | ||
742 | 894 | ||
743 | //**************************************** | 895 | //**************************************** |
744 | // fork the application and monitor it | 896 | // fork the application and monitor it |
@@ -746,13 +898,27 @@ int sandbox(void* sandbox_arg) { | |||
746 | pid_t app_pid = fork(); | 898 | pid_t app_pid = fork(); |
747 | if (app_pid == -1) | 899 | if (app_pid == -1) |
748 | errExit("fork"); | 900 | errExit("fork"); |
749 | 901 | ||
750 | if (app_pid == 0) { | 902 | if (app_pid == 0) { |
903 | #ifdef HAVE_APPARMOR | ||
904 | if (arg_apparmor) { | ||
905 | errno = 0; | ||
906 | if (aa_change_onexec("firejail-default")) { | ||
907 | fprintf(stderr, "Error: cannot confine the application using AppArmor.\n"); | ||
908 | fprintf(stderr, "Maybe firejail-default AppArmor profile is not loaded into the kernel.\n"); | ||
909 | fprintf(stderr, "As root, run \"aa-enforce firejail-default\" to load it.\n"); | ||
910 | exit(1); | ||
911 | } | ||
912 | else if (arg_debug) | ||
913 | printf("AppArmor enabled\n"); | ||
914 | } | ||
915 | #endif | ||
751 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died | 916 | prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died |
752 | start_application(); // start app | 917 | start_application(); // start app |
753 | } | 918 | } |
754 | 919 | ||
755 | int status = monitor_application(app_pid); // monitor application | 920 | int status = monitor_application(app_pid); // monitor application |
921 | flush_stdin(); | ||
756 | 922 | ||
757 | if (WIFEXITED(status)) { | 923 | if (WIFEXITED(status)) { |
758 | // if we had a proper exit, return that exit status | 924 | // if we had a proper exit, return that exit status |