From 84fa03cd77b0afcdee5cc6816596ab5c8a633185 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Tue, 29 Nov 2016 21:38:09 -0500 Subject: private-opt and private-srv --- src/firejail/firejail.h | 8 ++- src/firejail/fs_etc.c | 34 +++++++------ src/firejail/main.c | 20 ++++++++ src/firejail/profile.c | 16 ++++++ src/firejail/sandbox.c | 22 ++++++++- test/root/private.exp | 57 +++++++++++++++++++++ test/root/root.sh | 4 +- test/root/whitelist-mnt.exp | 105 --------------------------------------- test/root/whitelist.exp | 118 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 260 insertions(+), 124 deletions(-) delete mode 100755 test/root/whitelist-mnt.exp create mode 100755 test/root/whitelist.exp diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 61de17bf8..d172efce1 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -43,6 +43,8 @@ #define RUN_PROTOCOL_CFG "/run/firejail/mnt/protocol" #define RUN_HOME_DIR "/run/firejail/mnt/home" #define RUN_ETC_DIR "/run/firejail/mnt/etc" +#define RUN_OPT_DIR "/run/firejail/mnt/opt" +#define RUN_SRV_DIR "/run/firejail/mnt/srv" #define RUN_BIN_DIR "/run/firejail/mnt/bin" #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" @@ -200,6 +202,8 @@ typedef struct config_t { char *home_private; // private home directory char *home_private_keep; // keep list for private home directory char *etc_private_keep; // keep list for private etc directory + char *opt_private_keep; // keep list for private opt directory + char *srv_private_keep; // keep list for private srv directory char *bin_private_keep; // keep list for private bin directory char *cwd; // current working directory char *overlay_dir; @@ -315,6 +319,8 @@ extern int arg_doubledash; // double dash extern int arg_shell_none; // run the program directly without a shell extern int arg_private_dev; // private dev directory extern int arg_private_etc; // private etc directory +extern int arg_private_opt; // private opt directory +extern int arg_private_srv; // private srv directory extern int arg_private_bin; // private bin directory extern int arg_private_tmp; // private tmp directory extern int arg_scan; // arp-scan all interfaces @@ -556,7 +562,7 @@ void network_del_run_file(pid_t pid); void network_set_run_file(pid_t pid); // fs_etc.c -void fs_private_etc_list(void); +void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); // no_sandbox.c int check_namespace_virt(void); diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 80329d5ba..9a28ac601 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c @@ -47,7 +47,7 @@ errexit: exit(1); } -static void duplicate(char *fname) { +static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { if (*fname == '~' || *fname == '/' || strstr(fname, "..")) { fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); exit(1); @@ -55,40 +55,44 @@ static void duplicate(char *fname) { invalid_filename(fname); char *src; - if (asprintf(&src, "/etc/%s", fname) == -1) + if (asprintf(&src, "%s/%s", private_dir, fname) == -1) errExit("asprintf"); if (check_dir_or_file(src) == 0) { if (!arg_quiet) - fprintf(stderr, "Warning: skipping %s for private bin\n", fname); + fprintf(stderr, "Warning: skipping %s for private %s\n", fname, private_dir); free(src); return; } + if (arg_debug) + printf("copying %s to private %s\n", src, private_dir); + struct stat s; if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) { // create the directory in RUN_ETC_DIR char *dirname; - if (asprintf(&dirname, "%s/%s", RUN_ETC_DIR, fname) == -1) + if (asprintf(&dirname, "%s/%s", private_run_dir, fname) == -1) errExit("asprintf"); create_empty_dir_as_root(dirname, s.st_mode); sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, dirname); free(dirname); } else - sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, RUN_ETC_DIR); + sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, src, private_run_dir); fs_logger2("clone", src); free(src); } -void fs_private_etc_list(void) { - char *private_list = cfg.etc_private_keep; +void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list) { + assert(private_dir); + assert(private_run_dir); assert(private_list); // create /run/firejail/mnt/etc directory - mkdir_attr(RUN_ETC_DIR, 0755, 0, 0); - fs_logger("tmpfs /etc"); + mkdir_attr(private_run_dir, 0755, 0, 0); + fs_logger2("tmpfs", private_dir); fs_logger_print(); // save the current log @@ -97,7 +101,7 @@ void fs_private_etc_list(void) { // using a new child process with root privileges if (*private_list != '\0') { if (arg_debug) - printf("Copying files in the new etc directory:\n"); + printf("Copying files in the new %s directory:\n", private_dir); // copy the list of files in the new home directory char *dlist = strdup(private_list); @@ -106,18 +110,18 @@ void fs_private_etc_list(void) { char *ptr = strtok(dlist, ","); - duplicate(ptr); + duplicate(ptr, private_dir, private_run_dir); while ((ptr = strtok(NULL, ",")) != NULL) - duplicate(ptr); + duplicate(ptr, private_dir, private_run_dir); free(dlist); fs_logger_print(); } if (arg_debug) - printf("Mount-bind %s on top of /etc\n", RUN_ETC_DIR); - if (mount(RUN_ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) + printf("Mount-bind %s on top of %s\n", private_run_dir, private_dir); + if (mount(private_run_dir, private_dir, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); - fs_logger("mount /etc"); + fs_logger2("mount", private_dir); } diff --git a/src/firejail/main.c b/src/firejail/main.c index 0929347b7..4ccbb6a86 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -88,6 +88,8 @@ int arg_doubledash = 0; // double dash int arg_shell_none = 0; // run the program directly without a shell int arg_private_dev = 0; // private dev directory int arg_private_etc = 0; // private etc directory +int arg_private_opt = 0; // private opt directory +int arg_private_srv = 0; // private srv directory int arg_private_bin = 0; // private bin directory int arg_private_tmp = 0; // private tmp directory int arg_scan = 0; // arp-scan all interfaces @@ -1624,6 +1626,24 @@ int main(int argc, char **argv) { } arg_private_etc = 1; } + else if (strncmp(argv[i], "--private-opt=", 14) == 0) { + // extract private opt list + cfg.opt_private_keep = argv[i] + 14; + if (*cfg.opt_private_keep == '\0') { + fprintf(stderr, "Error: invalid private-opt option\n"); + exit(1); + } + arg_private_opt = 1; + } + else if (strncmp(argv[i], "--private-srv=", 14) == 0) { + // extract private srv list + cfg.srv_private_keep = argv[i] + 14; + if (*cfg.srv_private_keep == '\0') { + fprintf(stderr, "Error: invalid private-etc option\n"); + exit(1); + } + arg_private_srv = 1; + } else if (strncmp(argv[i], "--private-bin=", 14) == 0) { // extract private bin list cfg.bin_private_keep = argv[i] + 14; diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 9acb1b813..2be6948f0 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -739,6 +739,22 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } + // private /opt list of files and directories + if (strncmp(ptr, "private-opt ", 12) == 0) { + cfg.opt_private_keep = ptr + 12; + arg_private_opt = 1; + + return 0; + } + + // private /srv list of files and directories + if (strncmp(ptr, "private-srv ", 12) == 0) { + cfg.srv_private_keep = ptr + 12; + arg_private_srv = 1; + + return 0; + } + // private /bin list of files if (strncmp(ptr, "private-bin ", 12) == 0) { cfg.bin_private_keep = ptr + 12; diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 0a6777fef..68b8f554d 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -671,13 +671,33 @@ int sandbox(void* sandbox_arg) { else if (arg_overlay) fprintf(stderr, "Warning: private-etc feature is disabled in overlay\n"); else { - fs_private_etc_list(); + fs_private_dir_list("/etc", RUN_ETC_DIR, cfg.etc_private_keep); // create /etc/ld.so.preload file again if (arg_trace || arg_tracelog || mask_x11_abstract_socket) fs_trace_preload(); } } + if (arg_private_opt) { + if (cfg.chrootdir) + fprintf(stderr, "Warning: private-opt feature is disabled in chroot\n"); + else if (arg_overlay) + fprintf(stderr, "Warning: private-opt feature is disabled in overlay\n"); + else { + fs_private_dir_list("/opt", RUN_OPT_DIR, cfg.opt_private_keep); + } + } + + if (arg_private_srv) { + if (cfg.chrootdir) + fprintf(stderr, "Warning: private-srv feature is disabled in chroot\n"); + else if (arg_overlay) + fprintf(stderr, "Warning: private-srv feature is disabled in overlay\n"); + else { + fs_private_dir_list("/srv", RUN_SRV_DIR, cfg.srv_private_keep); + } + } + if (arg_private_bin) { if (cfg.chrootdir) fprintf(stderr, "Warning: private-bin feature is disabled in chroot\n"); diff --git a/test/root/private.exp b/test/root/private.exp index 4040081ee..9ce9716f9 100755 --- a/test/root/private.exp +++ b/test/root/private.exp @@ -29,5 +29,62 @@ expect { after 100 send -- "exit\r" +sleep 1 + + + +send -- "touch /opt/firejail-test-file\r" +after 100 +send -- "mkdir /opt/firejail-test-dir\r" +after 100 +send -- "touch /opt/firejail-test-dir/firejail-test-file\r" +after 100 +send -- "firejail --private-opt=firejail-test-file,firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /opt | wc -l\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "4" +} +after 100 +send -- "exit\r" +sleep 1 + + +send -- "touch /srv/firejail-test-file\r" +after 100 +send -- "mkdir /srv/firejail-test-dir\r" +after 100 +send -- "touch /srv/firejail-test-dir/firejail-test-file\r" after 100 +send -- "firejail --private-srv=firejail-test-file,firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /srv | wc -l\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + "4" +} +after 100 +send -- "exit\r" +sleep 1 + + + + + + + + + + puts "\nall done\n" diff --git a/test/root/root.sh b/test/root/root.sh index 01c372f68..371bccdff 100755 --- a/test/root/root.sh +++ b/test/root/root.sh @@ -53,8 +53,8 @@ fi echo "TESTING: fs private (test/root/private.exp)" ./private.exp -echo "TESTING: fs whitelist mnt, opt, media(test/root/whitelist-mnt.exp)" -./whitelist-mnt.exp +echo "TESTING: fs whitelist mnt, opt, media (test/root/whitelist-mnt.exp)" +./whitelist.exp #******************************** # seccomp diff --git a/test/root/whitelist-mnt.exp b/test/root/whitelist-mnt.exp deleted file mode 100755 index a21446afe..000000000 --- a/test/root/whitelist-mnt.exp +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/expect -f -# This file is part of Firejail project -# Copyright (C) 2014-2016 Firejail Authors -# License GPL v2 - -set timeout 10 -spawn $env(SHELL) -match_max 100000 - -send -- "touch /mnt/firejail-test-file\r" -after 100 -send -- "firejail --whitelist=/mnt/firejail-test-file --debug\r" -expect { - timeout {puts "TESTING ERROR 0\n";exit} - "Child process initialized" -} -sleep 1 - -send -- "find /mnt | wc -l\r" -expect { - timeout {puts "TESTING ERROR 1\n";exit} - "2" -} -after 100 -send -- "exit\r" -sleep 1 - - -send -- "touch /opt/firejail-test-file\r" -after 100 -send -- "firejail --whitelist=/opt/firejail-test-file --debug\r" -expect { - timeout {puts "TESTING ERROR 0\n";exit} - "Child process initialized" -} -sleep 1 - -send -- "find /opt | wc -l\r" -expect { - timeout {puts "TESTING ERROR 1\n";exit} - "2" -} -after 100 -send -- "exit\r" -sleep 1 - -send -- "touch /media/firejail-test-file\r" -after 100 -send -- "firejail --whitelist=/media/firejail-test-file --debug\r" -expect { - timeout {puts "TESTING ERROR 0\n";exit} - "Child process initialized" -} -sleep 1 - -send -- "find /media | wc -l\r" -expect { - timeout {puts "TESTING ERROR 1\n";exit} - "2" -} -after 100 -send -- "exit\r" -sleep 1 - - -send -- "firejail --whitelist=/var/run --whitelist=/var/lock --debug\r" -expect { - timeout {puts "TESTING ERROR 0\n";exit} - "Child process initialized" -} -sleep 1 - -send -- "find /var | wc -l\r" -expect { - timeout {puts "TESTING ERROR 1\n";exit} - "" -} -after 100 -send -- "exit\r" -sleep 1 - -send -- "touch /srv/firejail-test-file\r" -after 100 -send -- "firejail --whitelist=/srv/firejail-test-file --debug\r" -expect { - timeout {puts "TESTING ERROR 0\n";exit} - "Child process initialized" -} -sleep 1 - -send -- "find /srv | wc -l\r" -expect { - timeout {puts "TESTING ERROR 1\n";exit} - "2" -} -after 100 -send -- "exit\r" -sleep 1 - - - - -after 100 -puts "\nall done\n" - diff --git a/test/root/whitelist.exp b/test/root/whitelist.exp new file mode 100755 index 000000000..f6936c048 --- /dev/null +++ b/test/root/whitelist.exp @@ -0,0 +1,118 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "touch /mnt/firejail-test-file\r" +after 100 +send -- "mkdir /mnt/firejail-test-dir\r" +after 100 +send -- "touch /mnt/firejail-test-dir/firejail-test-file\r" +after 100 +send -- "firejail --whitelist=/mnt/firejail-test-file --whitelist=/mnt/firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 0\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /mnt | wc -l\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "4" +} +after 100 +send -- "exit\r" +sleep 1 + + +send -- "touch /opt/firejail-test-file\r" +after 100 +send -- "mkdir /opt/firejail-test-dir\r" +after 100 +send -- "touch /opt/firejail-test-dir/firejail-test-file\r" +after 100 +send -- "firejail --whitelist=/opt/firejail-test-file --whitelist=/opt/firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /opt | wc -l\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "4" +} +after 100 +send -- "exit\r" +sleep 1 + +send -- "touch /media/firejail-test-file\r" +after 100 +send -- "mkdir /media/firejail-test-dir\r" +after 100 +send -- "touch /media/firejail-test-dir/firejail-test-file\r" +after 100 +send -- "firejail --whitelist=/media/firejail-test-file --whitelist=/media/firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /media | wc -l\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + "4" +} +after 100 +send -- "exit\r" +sleep 1 + + +send -- "firejail --whitelist=/var/run --whitelist=/var/lock --debug\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /var | wc -l\r" +expect { + timeout {puts "TESTING ERROR 7\n";exit} + "" +} +after 100 +send -- "exit\r" +sleep 1 + +send -- "touch /srv/firejail-test-file\r" +after 100 +send -- "mkdir /srv/firejail-test-dir\r" +after 100 +send -- "touch /srv/firejail-test-dir/firejail-test-file\r" +after 100 +send -- "firejail --whitelist=/srv/firejail-test-file --whitelist=/srv/firejail-test-dir --debug\r" +expect { + timeout {puts "TESTING ERROR 8\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "find /srv | wc -l\r" +expect { + timeout {puts "TESTING ERROR 9\n";exit} + "4" +} +after 100 +send -- "exit\r" + + +after 100 +puts "\nall done\n" + -- cgit v1.2.3-54-g00ecf