From cfbcbf2c95455373aa2570827c52b7b87d80cfef Mon Sep 17 00:00:00 2001 From: Fred Barclay Date: Mon, 22 May 2017 01:48:27 -0500 Subject: --novideo option Still a work in progress. Code needs cleanup and improvement, but it does block /dev/video* in all of my tests so far. --- src/firejail/firejail.h | 22 ++-- src/firejail/fs_dev.c | 36 ++++-- src/firejail/main.c | 286 ++++++++++++++++++++++--------------------- src/firejail/profile.c | 130 ++++++++++---------- src/firejail/sandbox.c | 144 ++++++++++++---------- src/firejail/usage.c | 41 ++++--- src/man/firejail-profile.txt | 13 +- src/man/firejail.txt | 78 ++++++------ 8 files changed, 389 insertions(+), 361 deletions(-) diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index fba4c4fe2..91b9c7be7 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -147,9 +147,9 @@ typedef struct bridge_t { uint32_t mask; // interface device mask uint8_t mac[6]; // interface mac address int mtu; // interface mtu - + char *veth_name; // veth name for the device connected to the bridge - + // inside the sandbox char *devsandbox; // name of the device inside the sandbox uint32_t ipsandbox; // ip address inside the sandbox @@ -157,7 +157,7 @@ typedef struct bridge_t { uint8_t macsandbox[6]; // mac address inside the sandbox uint32_t iprange_start;// iprange arp scan start range uint32_t iprange_end; // iprange arp scan end range - + // flags uint8_t arg_ip_none; // --ip=none uint8_t macvlan; // set by --net=eth0 (or eth1, ...); reset by --net=br0 (or br1, ...) @@ -171,14 +171,14 @@ typedef struct interface_t { uint32_t mask; uint8_t mac[6]; int mtu; - + uint8_t configured; } Interface; typedef struct profile_entry_t { struct profile_entry_t *next; char *data; // command - + // whitelist command parameters char *link; // link name - set if the file is a link unsigned home_dir:1; // whitelist in /home/user directory @@ -195,10 +195,10 @@ typedef struct config_t { // user data char *username; char *homedir; - + // filesystem ProfileEntry *profile; -#define MAX_PROFILE_IGNORE 32 +#define MAX_PROFILE_IGNORE 32 char *profile_ignore[MAX_PROFILE_IGNORE]; char *chrootdir; // chroot directory char *home_private; // private home directory @@ -239,12 +239,12 @@ typedef struct config_t { long long unsigned rlimit_nproc; long long unsigned rlimit_fsize; long long unsigned rlimit_sigpending; - + // cpu affinity, nice and control groups uint32_t cpus; int nice; char *cgroup; - + // command line char *command_line; @@ -331,6 +331,7 @@ extern int arg_private_tmp; // private tmp directory extern int arg_scan; // arp-scan all interfaces extern int arg_whitelist; // whitelist commad extern int arg_nosound; // disable sound +extern int arg_novideo; //disable video devices in /dev extern int arg_no3d; // disable 3d hardware acceleration extern int arg_quiet; // no output for scripting extern int arg_join_network; // join only the network namespace @@ -724,7 +725,7 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc, // bitmapped filters for sbox_run #define SBOX_ROOT (1 << 0) // run the sandbox as root #define SBOX_USER (1 << 1) // run the sandbox as a regular user -#define SBOX_SECCOMP (1 << 2) // install seccomp +#define SBOX_SECCOMP (1 << 2) // install seccomp #define SBOX_CAPS_NONE (1 << 3) // drop all capabilities #define SBOX_CAPS_NETWORK (1 << 4) // caps filter for programs running network programs #define SBOX_ALLOW_STDIN (1 << 5) // don't close stdin @@ -739,4 +740,3 @@ void git_install(); void git_uninstall(); #endif - diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c index 9b73ac9fc..159c8e654 100644 --- a/src/firejail/fs_dev.c +++ b/src/firejail/fs_dev.c @@ -26,7 +26,7 @@ #include #include #ifndef _BSD_SOURCE -#define _BSD_SOURCE +#define _BSD_SOURCE #endif #include #include @@ -35,6 +35,7 @@ typedef struct { const char *dev_fname; const char *run_fname; int sound; + int video; int hw3d; } DevEntry; @@ -93,16 +94,16 @@ static void deventry_mount(void) { fclose(fp); } } - + if (mount(dev[i].run_fname, dev[i].dev_fname, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting dev file"); fs_logger2("whitelist", dev[i].dev_fname); } - - i++; + + i++; } } - + static void create_char_dev(const char *path, mode_t mode, int major, int minor) { dev_t dev = makedev(major, minor); if (mknod(path, S_IFCHR | mode, dev) == -1) @@ -112,7 +113,7 @@ static void create_char_dev(const char *path, mode_t mode, int major, int minor) ASSERT_PERMS(path, 0, 0, mode); return; - + errexit: fprintf(stderr, "Error: cannot create %s device\n", path); exit(1); @@ -161,7 +162,7 @@ void fs_private_dev(void){ if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /dev"); fs_logger("tmpfs /dev"); - + deventry_mount(); // bring back /dev/log @@ -174,11 +175,11 @@ void fs_private_dev(void){ errExit("mounting /dev/log"); fs_logger("clone /dev/log"); } - } + } if (mount(RUN_RO_DIR, RUN_DEV_DIR, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable /dev/snd"); - + // create /dev/shm if (arg_debug) printf("Create /dev/shm directory\n"); @@ -267,24 +268,24 @@ void fs_dev_shm(void) { fwarning("/dev/shm not mounted\n"); dbg_test_dir("/dev/shm"); } - + } } -#endif +#endif static void disable_file_or_dir(const char *fname) { if (arg_debug) printf("disable %s\n", fname); struct stat s; if (stat(fname, &s) != -1) { - if (is_dir(fname)) { + if (is_dir(fname)) { if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable directory"); } else { if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); - } + } } fs_logger2("blacklist", fname); @@ -299,6 +300,15 @@ void fs_dev_disable_sound(void) { } } +void fs_dev_disable_video(void) { + int i = 0; + while (dev[i].dev_fname != NULL) { + if (dev[i].video) + disable_file_or_dir(dev[i].dev_fname); + i++; + } +} + void fs_dev_disable_3d(void) { int i = 0; while (dev[i].dev_fname != NULL) { diff --git a/src/firejail/main.c b/src/firejail/main.c index 86ca422ae..012b2f230 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -88,6 +88,7 @@ int arg_private_tmp = 0; // private tmp directory int arg_scan = 0; // arp-scan all interfaces int arg_whitelist = 0; // whitelist commad int arg_nosound = 0; // disable sound +int arg_novideo = 0; //disable video devices in /dev int arg_no3d; // disable 3d hardware acceleration int arg_quiet = 0; // no output for scripting int arg_join_network = 0; // join only the network namespace @@ -143,7 +144,7 @@ static void myexit(int rv) { clear_run_files(sandbox_pid); appimage_clear(); flush_stdin(); - exit(rv); + exit(rv); } static void my_handler(int s){ @@ -163,7 +164,7 @@ static pid_t extract_pid(const char *name) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); } - + pid_t pid; EUID_ROOT(); if (name2pid(name, &pid)) { @@ -201,7 +202,7 @@ static void init_cfg(int argc, char **argv) { cfg.bridge1.devsandbox = "eth1"; cfg.bridge2.devsandbox = "eth2"; cfg.bridge3.devsandbox = "eth3"; - + // extract user data EUID_ROOT(); // rise permissions for grsecurity struct passwd *pw = getpwuid(getuid()); @@ -249,7 +250,7 @@ void check_user_namespace(void) { EUID_ASSERT(); if (getuid() == 0) goto errout; - + // test user namespaces available in the kernel struct stat s1; struct stat s2; @@ -280,7 +281,7 @@ static void exit_err_feature(const char *feature) { // this function handles command line options such as --version and --help static void run_cmd_and_exit(int i, int argc, char **argv) { EUID_ASSERT(); - + //************************************* // basic arguments //************************************* @@ -303,7 +304,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) errExit("asprintf"); EUID_ROOT(); - if (setreuid(0, 0) < 0 || + if (setreuid(0, 0) < 0 || setregid(0, 0) < 0) errExit("setreuid/setregid"); errno = 0; @@ -349,11 +350,11 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit_err_feature("x11"); } #endif -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { if (checkcfg(CFG_NETWORK)) { logargs(argc, argv); - + // extract the command if ((i + 1) == argc) { fprintf(stderr, "Error: command expected after --bandwidth option\n"); @@ -364,7 +365,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: invalid --bandwidth command.\nValid commands: set, clear, status.\n"); exit(1); } - + // extract network name char *dev = NULL; int down = 0; @@ -376,20 +377,20 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(1); } dev = argv[i + 2]; - + // check device name if (if_nametoindex(dev) == 0) { fprintf(stderr, "Error: network device %s not found\n", dev); exit(1); } - + // extract bandwidth if (strcmp(cmd, "set") == 0) { if ((i + 4) >= argc) { fprintf(stderr, "Error: invalid --bandwidth set command\n"); exit(1); } - + down = atoi(argv[i + 3]); if (down < 0) { fprintf(stderr, "Error: invalid download speed\n"); @@ -401,8 +402,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(1); } } - } - + } + // extract pid or sandbox name pid_t pid = read_pid(argv[i] + 12); bandwidth_pid(pid, cmd, dev, down, up); @@ -450,7 +451,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { if (checkcfg(CFG_SECCOMP)) { // print seccomp filter for a sandbox specified by pid or by name - pid_t pid = read_pid(argv[i] + 17); + pid_t pid = read_pid(argv[i] + 17); protocol_print_filter(pid); } else @@ -509,7 +510,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { 2, PATH_FIREMON, "--top"); exit(0); } -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK else if (strcmp(argv[i], "--netstats") == 0) { if (checkcfg(CFG_NETWORK)) { struct stat s; @@ -524,12 +525,12 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else exit_err_feature("networking"); } -#endif +#endif #ifdef HAVE_FILE_TRANSFER else if (strncmp(argv[i], "--get=", 6) == 0) { if (checkcfg(CFG_FILE_TRANSFER)) { logargs(argc, argv); - + // verify path if ((i + 2) != argc) { fprintf(stderr, "Error: invalid --get option, path expected\n"); @@ -541,7 +542,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: invalid file name %s\n", path); exit(1); } - + // get file pid_t pid = read_pid(argv[i] + 6); sandboxfs(SANDBOX_FS_GET, pid, path, NULL); @@ -553,7 +554,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--put=", 6) == 0) { if (checkcfg(CFG_FILE_TRANSFER)) { logargs(argc, argv); - + // verify path if ((i + 3) != argc) { fprintf(stderr, "Error: invalid --put option, 2 paths expected\n"); @@ -571,7 +572,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: invalid file name %s\n", path2); exit(1); } - + // get file pid_t pid = read_pid(argv[i] + 6); sandboxfs(SANDBOX_FS_PUT, pid, path1, path2); @@ -583,7 +584,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--ls=", 5) == 0) { if (checkcfg(CFG_FILE_TRANSFER)) { logargs(argc, argv); - + // verify path if ((i + 2) != argc) { fprintf(stderr, "Error: invalid --ls option, path expected\n"); @@ -595,7 +596,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: invalid file name %s\n", path); exit(1); } - + // list directory contents pid_t pid = read_pid(argv[i] + 5); sandboxfs(SANDBOX_FS_LS, pid, path, NULL); @@ -608,7 +609,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else if (strncmp(argv[i], "--join=", 7) == 0) { if (checkcfg(CFG_JOIN) || getuid() == 0) { logargs(argc, argv); - + if (arg_shell_none) { if (argc <= (i+1)) { fprintf(stderr, "Error: --shell=none set, but no command specified\n"); @@ -616,10 +617,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { } cfg.original_program_index = i + 1; } - + if (!cfg.shell && !arg_shell_none) cfg.shell = guess_shell(); - + // join sandbox by pid or by name pid_t pid = read_pid(argv[i] + 7); join(pid, argc, argv, i + 1); @@ -652,10 +653,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { join(pid, argc, argv, i + 1); exit(0); } -#endif +#endif // if there no such sandbox continue argument processing } -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK else if (strncmp(argv[i], "--join-network=", 15) == 0) { if (checkcfg(CFG_NETWORK)) { logargs(argc, argv); @@ -664,7 +665,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: --join-network is only available to root user\n"); exit(1); } - + if (!cfg.shell && !arg_shell_none) cfg.shell = guess_shell(); @@ -684,7 +685,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { fprintf(stderr, "Error: --join-filesystem is only available to root user\n"); exit(1); } - + if (!cfg.shell && !arg_shell_none) cfg.shell = guess_shell(); @@ -695,7 +696,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { } else if (strncmp(argv[i], "--shutdown=", 11) == 0) { logargs(argc, argv); - + // shutdown sandbox by pid or by name pid_t pid = read_pid(argv[i] + 11); shut(pid); @@ -801,7 +802,7 @@ static int check_arg(int argc, char **argv, const char *argument) { if (strncmp(argv[i], "--", 2) != 0) break; } - + return found; } @@ -825,7 +826,7 @@ int main(int argc, char **argv) { // build /run/firejail directory structure preproc_build_firejail_dir(); - + if (check_arg(argc, argv, "--quiet")) arg_quiet = 1; if (check_arg(argc, argv, "--allow-debuggers")) { @@ -870,7 +871,7 @@ int main(int argc, char **argv) { // If LXC is detected, start firejail sandbox // otherwise try to detect a PID namespace by looking under /proc for specific kernel processes and: // - if --force flag is set, start firejail sandbox - // -- if --force flag is not set, start the application in a /bin/bash shell + // -- if --force flag is not set, start the application in a /bin/bash shell if (check_namespace_virt() == 0) { EUID_ROOT(); int rv = check_kernel_procs(); @@ -884,7 +885,7 @@ int main(int argc, char **argv) { printf("firejail version %s\n", VERSION); exit(0); } - + // start the program directly without sandboxing run_no_sandbox(argc, argv); // it will never get here! @@ -916,9 +917,9 @@ int main(int argc, char **argv) { network_del_run_file(sandbox_pid); delete_name_file(sandbox_pid); delete_x11_file(sandbox_pid); - + EUID_USER(); - + //check if the parent is sshd daemon int parent_sshd = 0; { @@ -959,7 +960,7 @@ int main(int argc, char **argv) { } EUID_USER();} #endif - + drop_privs(1); int rv = system(argv[2]); exit(rv); @@ -970,14 +971,14 @@ int main(int argc, char **argv) { free(comm); } } - + // is this a login shell, or a command passed by sshd, insert command line options from /etc/firejail/login.users if (*argv[0] == '-' || parent_sshd) { if (argc == 1) login_shell = 1; fullargc = restricted_shell(cfg.username); if (fullargc) { - + #ifdef DEBUG_RESTRICTED_SHELL {EUID_ROOT(); FILE *fp = fopen("/firelog", "a"); @@ -991,7 +992,7 @@ int main(int argc, char **argv) { } EUID_USER();} #endif - + int j; for (i = 1, j = fullargc; i < argc && j < MAX_ARGS; i++, j++, fullargc++) fullargv[j] = argv[i]; @@ -1019,23 +1020,23 @@ int main(int argc, char **argv) { // check --output option and execute it; check_output(argc, argv); // the function will not return if --output option was found } - - + + // check for force-nonewprivs in /etc/firejail/firejail.config file if (checkcfg(CFG_FORCE_NONEWPRIVS)) arg_nonewprivs = 1; - + if (arg_allow_debuggers) { char *cmd = strdup("noblacklist ${PATH}/strace"); if (!cmd) errExit("strdup"); profile_add(cmd); } - + // parse arguments for (i = 1; i < argc; i++) { run_cmd_and_exit(i, argc, argv); // will exit if the command is recognized - + if (strcmp(argv[i], "--debug") == 0) { if (!arg_quiet) { arg_debug = 1; @@ -1065,7 +1066,7 @@ int main(int argc, char **argv) { #ifdef HAVE_APPARMOR else if (strcmp(argv[i], "--apparmor") == 0) arg_apparmor = 1; -#endif +#endif #ifdef HAVE_SECCOMP else if (strncmp(argv[i], "--protocol=", 11) == 0) { if (checkcfg(CFG_SECCOMP)) { @@ -1079,7 +1080,7 @@ int main(int argc, char **argv) { errExit("strdup"); } } - else + else exit_err_feature("seccomp"); } else if (strcmp(argv[i], "--seccomp") == 0) { @@ -1129,7 +1130,7 @@ int main(int argc, char **argv) { else exit_err_feature("seccomp"); } -#endif +#endif else if (strcmp(argv[i], "--caps") == 0) arg_caps_default_filter = 1; else if (strcmp(argv[i], "--caps.drop=all") == 0) @@ -1160,22 +1161,22 @@ int main(int argc, char **argv) { check_unsigned(argv[i] + 16, "Error: invalid rlimit"); sscanf(argv[i] + 16, "%llu", &cfg.rlimit_nofile); arg_rlimit_nofile = 1; - } + } else if (strncmp(argv[i], "--rlimit-nproc=", 15) == 0) { check_unsigned(argv[i] + 15, "Error: invalid rlimit"); sscanf(argv[i] + 15, "%llu", &cfg.rlimit_nproc); arg_rlimit_nproc = 1; - } + } else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) { check_unsigned(argv[i] + 15, "Error: invalid rlimit"); sscanf(argv[i] + 15, "%llu", &cfg.rlimit_fsize); arg_rlimit_fsize = 1; - } + } else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) { check_unsigned(argv[i] + 20, "Error: invalid rlimit"); sscanf(argv[i] + 20, "%llu", &cfg.rlimit_sigpending); arg_rlimit_sigpending = 1; - } + } else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) arg_ipc = 1; else if (strncmp(argv[i], "--cpu=", 6) == 0) @@ -1191,26 +1192,26 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: only a cgroup can be defined\n"); exit(1); } - + option_cgroup = 1; cfg.cgroup = strdup(argv[i] + 9); if (!cfg.cgroup) errExit("strdup"); set_cgroup(cfg.cgroup); } - + //************************************* // filesystem //************************************* else if (strcmp(argv[i], "--allusers") == 0) arg_allusers = 1; -#ifdef HAVE_BIND +#ifdef HAVE_BIND else if (strncmp(argv[i], "--bind=", 7) == 0) { if (checkcfg(CFG_BIND)) { char *line; if (asprintf(&line, "bind %s", argv[i] + 7) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1222,7 +1223,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "tmpfs %s", argv[i] + 8) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1230,7 +1231,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1238,7 +1239,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "noblacklist %s", argv[i] + 14) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1249,7 +1250,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1260,17 +1261,17 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "nowhitelist %s", argv[i] + 14) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } -#endif +#endif else if (strncmp(argv[i], "--read-only=", 12) == 0) { char *line; if (asprintf(&line, "read-only %s", argv[i] + 12) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1278,7 +1279,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "noexec %s", argv[i] + 9) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1286,7 +1287,7 @@ int main(int argc, char **argv) { char *line; if (asprintf(&line, "read-write %s", argv[i] + 13) == -1) errExit("asprintf"); - + profile_check_line(line, 0, NULL); // will exit if something wrong profile_add(line); } @@ -1300,16 +1301,16 @@ int main(int argc, char **argv) { struct stat s; if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); - exit(1); + exit(1); } arg_overlay = 1; arg_overlay_keep = 1; - + char *subdirname; if (asprintf(&subdirname, "%d", getpid()) == -1) errExit("asprintf"); cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse); - + free(subdirname); } else @@ -1329,13 +1330,13 @@ int main(int argc, char **argv) { arg_overlay = 1; arg_overlay_keep = 1; arg_overlay_reuse = 1; - + char *subdirname = argv[i] + 16; if (subdirname == '\0') { fprintf(stderr, "Error: invalid overlay option\n"); exit(1); } - + // check name invalid_filename(subdirname); if (strstr(subdirname, "..") || strstr(subdirname, "/")) { @@ -1356,7 +1357,7 @@ int main(int argc, char **argv) { struct stat s; if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); - exit(1); + exit(1); } arg_overlay = 1; } @@ -1371,7 +1372,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: --noprofile and --profile options are mutually exclusive\n"); exit(1); } - + char *ppath = expand_home(argv[i] + 10, cfg.homedir); if (!ppath) errExit("strdup"); @@ -1391,7 +1392,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: invalid profile path\n"); exit(1); } - + // access call checks as real UID/GID, not as effective UID/GID if (access(custom_profile_dir, R_OK)) { fprintf(stderr, "Error: cannot access profile directory\n"); @@ -1415,11 +1416,11 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: invalid ignore option\n"); exit(1); } - + // find an empty entry in profile_ignore array int j; for (j = 0; j < MAX_PROFILE_IGNORE; j++) { - if (cfg.profile_ignore[j] == NULL) + if (cfg.profile_ignore[j] == NULL) break; } if (j >= MAX_PROFILE_IGNORE) { @@ -1430,23 +1431,23 @@ int main(int argc, char **argv) { else cfg.profile_ignore[j] = argv[i] + 9; } -#ifdef HAVE_CHROOT +#ifdef HAVE_CHROOT else if (strncmp(argv[i], "--chroot=", 9) == 0) { if (checkcfg(CFG_CHROOT)) { if (arg_overlay) { fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); exit(1); } - + struct stat s; if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { fprintf(stderr, "Error: --chroot option is not available on Grsecurity systems\n"); - exit(1); + exit(1); } - - + + invalid_filename(argv[i] + 9); - + // extract chroot dirname cfg.chrootdir = argv[i] + 9; // if the directory starts with ~, expand the home directory @@ -1456,13 +1457,13 @@ int main(int argc, char **argv) { errExit("asprintf"); cfg.chrootdir = tmp; } - + // check chroot dirname exists if (strstr(cfg.chrootdir, "..") || !is_dir(cfg.chrootdir) || is_link(cfg.chrootdir)) { fprintf(stderr, "Error: invalid directory %s\n", cfg.chrootdir); return 1; } - + // don't allow "--chroot=/" char *rpath = realpath(cfg.chrootdir, NULL); if (rpath == NULL || strcmp(rpath, "/") == 0) { @@ -1470,7 +1471,7 @@ int main(int argc, char **argv) { exit(1); } cfg.chrootdir = rpath; - + // check chroot directory structure fs_check_chroot_dir(cfg.chrootdir); } @@ -1528,7 +1529,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: a private home directory was already defined with --private option.\n"); exit(1); } - + // extract private home dirname if (*(argv[i] + 15) == '\0') { fprintf(stderr, "Error: invalid private-home option\n"); @@ -1544,7 +1545,7 @@ int main(int argc, char **argv) { else exit_err_feature("private-home"); } -#endif +#endif else if (strcmp(argv[i], "--private-dev") == 0) { arg_private_dev = 1; } @@ -1553,7 +1554,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); exit(1); } - + // extract private etc list if (*(argv[i] + 14) == '\0') { fprintf(stderr, "Error: invalid private-etc option\n"); @@ -1646,14 +1647,17 @@ int main(int argc, char **argv) { else if (strcmp(argv[i], "--nosound") == 0) { arg_nosound = 1; } + else if (strcmp(argv[i], "--novideo") == 0) { + arg_novideo = 1; + } else if (strcmp(argv[i], "--no3d") == 0) { arg_no3d = 1; } - + //************************************* // network //************************************* -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK else if (strncmp(argv[i], "--interface=", 12) == 0) { if (checkcfg(CFG_NETWORK)) { #ifdef HAVE_NETWORK_RESTRICTED @@ -1668,7 +1672,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: --interface is allowed only to root user\n"); exit(1); } - + // checks if (arg_nonetwork) { fprintf(stderr, "Error: --network=none and --interface are incompatible\n"); @@ -1683,7 +1687,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot find interface %s\n", argv[i] + 12); exit(1); } - + Interface *intf; if (cfg.interface0.configured == 0) intf = &cfg.interface0; @@ -1697,11 +1701,11 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: maximum 4 interfaces are allowed\n"); return 1; } - + intf->dev = strdup(argv[i] + 12); if (!intf->dev) errExit("strdup"); - + if (net_get_if_addr(intf->dev, &intf->ip, &intf->mask, intf->mac, &intf->mtu)) { fwarning("interface %s is not configured\n", intf->dev); } @@ -1742,7 +1746,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot attach to lo device\n"); exit(1); } - + Bridge *br; if (cfg.bridge0.configured == 0) br = &cfg.bridge0; @@ -1799,7 +1803,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot configure the IP range twice for the same interface\n"); return 1; } - + // parse option arguments char *firstip = argv[i] + 10; char *secondip = firstip; @@ -1814,7 +1818,7 @@ int main(int argc, char **argv) { } *secondip = '\0'; secondip++; - + // check addresses if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) || br->iprange_start >= br->iprange_end) { @@ -1841,7 +1845,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); exit(1); } - + // read the address if (atomac(argv[i] + 6, br->macsandbox)) { fprintf(stderr, "Error: invalid MAC address\n"); @@ -1859,7 +1863,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } - + if (sscanf(argv[i] + 6, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { fprintf(stderr, "Error: invalid mtu value\n"); exit(1); @@ -1880,7 +1884,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); exit(1); } - + // configure this IP address for the last bridge defined if (strcmp(argv[i] + 5, "none") == 0) br->arg_ip_none = 1; @@ -1906,7 +1910,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); exit(1); } - + // configure this IP address for the last bridge defined // todo: verify ipv6 syntax br->ip6sandbox = argv[i] + 6; @@ -1915,7 +1919,7 @@ int main(int argc, char **argv) { // exit(1); // } } - else + else exit_err_feature("networking"); } @@ -1930,14 +1934,14 @@ int main(int argc, char **argv) { else exit_err_feature("networking"); } -#endif +#endif else if (strncmp(argv[i], "--dns=", 6) == 0) { uint32_t dns; if (atoip(argv[i] + 6, &dns)) { fprintf(stderr, "Error: invalid DNS server IP address\n"); return 1; } - + if (cfg.dns1 == 0) cfg.dns1 = dns; else if (cfg.dns2 == 0) @@ -1949,7 +1953,7 @@ int main(int argc, char **argv) { return 1; } } - + else if (strncmp(argv[i], "--hosts-file=", 13) == 0) cfg.hosts_file = fs_check_hosts_file(argv[i] + 13); @@ -2042,7 +2046,7 @@ int main(int argc, char **argv) { arg_appimage = 1; else if (strcmp(argv[i], "--csh") == 0) { if (arg_shell_none) { - + fprintf(stderr, "Error: --shell=none was already specified.\n"); return 1; } @@ -2076,7 +2080,7 @@ int main(int argc, char **argv) { return 1; } invalid_filename(argv[i] + 8); - + if (cfg.shell) { fprintf(stderr, "Error: only one user shell can be specified\n"); return 1; @@ -2110,7 +2114,7 @@ int main(int argc, char **argv) { return 1; } } - + // unlike all other x11 features, this is available always else if (strcmp(argv[i], "--x11=none") == 0) { arg_x11_block = 1; @@ -2119,7 +2123,7 @@ int main(int argc, char **argv) { else if (strcmp(argv[i], "--x11=xorg") == 0) { if (checkcfg(CFG_X11)) arg_x11_xorg = 1; - else + else exit_err_feature("x11"); } #endif @@ -2139,7 +2143,7 @@ int main(int argc, char **argv) { fprintf(stderr, "This feature is not enabled in the current build\n"); exit(1); } - + else if (strcmp(argv[i], "--") == 0) { // double dash - positional params to follow arg_doubledash = 1; @@ -2158,7 +2162,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: invalid %s command line option\n", argv[i]); return 1; } - + // we have a program name coming if (arg_appimage) { cfg.command_name = strdup(argv[i]); @@ -2171,7 +2175,7 @@ int main(int argc, char **argv) { break; } } - + // prog_index could still be -1 if no program was specified if (prog_index == -1 && arg_shell_none) { fprintf(stderr, "Error: shell=none configured, but no program specified\n"); @@ -2182,7 +2186,7 @@ int main(int argc, char **argv) { if (arg_trace && arg_tracelog) { fwarning("--trace and --tracelog are mutually exclusive; --tracelog disabled\n"); } - + // check user namespace (--noroot) options if (arg_noroot) { if (arg_overlay) { @@ -2235,12 +2239,12 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: command must be specified when --shell=none used.\n"); exit(1); }*/ - + assert(cfg.command_name); if (arg_debug) printf("Command name #%s#\n", cfg.command_name); - - + + // load the profile if (!arg_noprofile) { if (!custom_profile) { @@ -2278,14 +2282,14 @@ int main(int argc, char **argv) { profile_name = DEFAULT_ROOT_PROFILE; if (arg_debug) printf("Attempting to find %s.profile...\n", profile_name); - + // look for the profile in ~/.config/firejail directory char *usercfgdir; if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1) errExit("asprintf"); custom_profile = profile_find(profile_name, usercfgdir); free(usercfgdir); - + if (!custom_profile) { // look for the profile in /etc/firejail directory if (custom_profile_dir) @@ -2297,7 +2301,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: no default.profile installed\n"); exit(1); } - + if (custom_profile && !arg_quiet) printf("\n** Note: you can use --noprofile to disable %s.profile **\n\n", profile_name); } @@ -2309,7 +2313,7 @@ int main(int argc, char **argv) { // check network configuration options - it will exit if anything went wrong net_check_cfg(); - + // check and assign an IP address - for macvlan it will be done again in the sandbox! if (any_bridge_configured()) { EUID_ROOT(); @@ -2319,12 +2323,12 @@ int main(int argc, char **argv) { (void) rv; flock(lockfd, LOCK_EX); } - + check_network(&cfg.bridge0); check_network(&cfg.bridge1); check_network(&cfg.bridge2); check_network(&cfg.bridge3); - + // save network mapping in shared memory network_set_run_file(sandbox_pid); EUID_USER(); @@ -2354,10 +2358,10 @@ int main(int argc, char **argv) { if (display > 0) set_x11_file(sandbox_pid, display); EUID_USER(); - + // clone environment int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; - + // in root mode also enable CLONE_NEWIPC // in user mode CLONE_NEWIPC will break MIT Shared Memory Extension (MIT-SHM) if (getuid() == 0 || arg_ipc) { @@ -2365,7 +2369,7 @@ int main(int argc, char **argv) { if (arg_debug) printf("Enabling IPC namespace\n"); } - + if (any_bridge_configured() || any_interface_configured() || arg_nonetwork) { flags |= CLONE_NEWNET; } @@ -2387,9 +2391,9 @@ int main(int argc, char **argv) { if (getuid() == 0) // only for root printf("The new log directory is /proc/%d/root/var/log\n", child); } - + if (!arg_nonetwork) { - EUID_ROOT(); + EUID_ROOT(); pid_t net_child = fork(); if (net_child < 0) errExit("fork"); @@ -2401,11 +2405,11 @@ int main(int argc, char **argv) { errExit("setregid"); network_main(child); if (arg_debug) - printf("Host network configured\n"); + printf("Host network configured\n"); #ifdef HAVE_GCOV __gcov_flush(); #endif - _exit(0); + _exit(0); } // wait for the child to finish @@ -2416,10 +2420,10 @@ int main(int argc, char **argv) { // close each end of the unused pipes close(parent_to_child_fds[0]); close(child_to_parent_fds[1]); - + // notify child that base setup is complete notify_other(parent_to_child_fds[1]); - + // wait for child to create new user namespace with CLONE_NEWUSER wait_for_other(child_to_parent_fds[0]); close(child_to_parent_fds[0]); @@ -2440,7 +2444,7 @@ int main(int argc, char **argv) { EUID_USER(); free(map); free(map_path); - + // gid file if (asprintf(&map_path, "/proc/%d/gid_map", child) == -1) errExit("asprintf"); @@ -2452,7 +2456,7 @@ int main(int argc, char **argv) { gid_t gid = getgid(); sprintf(ptr, "%d %d 1\n", gid, gid); ptr += strlen(ptr); - + if (!arg_nogroups) { // add tty group gid_t g = get_group_id("tty"); @@ -2460,38 +2464,38 @@ int main(int argc, char **argv) { sprintf(ptr, "%d %d 1\n", g, g); ptr += strlen(ptr); } - + // add audio group g = get_group_id("audio"); if (g) { sprintf(ptr, "%d %d 1\n", g, g); ptr += strlen(ptr); } - + // add video group g = get_group_id("video"); if (g) { sprintf(ptr, "%d %d 1\n", g, g); ptr += strlen(ptr); } - + // add games group g = get_group_id("games"); if (g) { sprintf(ptr, "%d %d 1\n", g, g); } } - + EUID_ROOT(); update_map(gidmap, map_path); EUID_USER(); free(map_path); } - + // notify child that UID/GID mapping is complete notify_other(parent_to_child_fds[1]); close(parent_to_child_fds[1]); - + EUID_ROOT(); if (lockfd != -1) { flock(lockfd, LOCK_UN); @@ -2499,13 +2503,13 @@ int main(int argc, char **argv) { } // create name file under /run/firejail - + // handle CTRL-C in parent signal (SIGINT, my_handler); signal (SIGTERM, my_handler); - + // wait for the child to finish EUID_USER(); int status = 0; diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 9ae2aa5b4..11258892e 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -28,8 +28,8 @@ int profile_find(const char *name, const char *dir) { EUID_ASSERT(); assert(name); assert(dir); - - int rv = 0; + + int rv = 0; DIR *dp; char *pname; if (asprintf(&pname, "%s.profile", name) == -1) @@ -74,17 +74,17 @@ static void warning_feature_disabled(const char *feature) { // return 0 if the command was already executed inside the function int profile_check_line(char *ptr, int lineno, const char *fname) { EUID_ASSERT(); - + // check ignore list int i; for (i = 0; i < MAX_PROFILE_IGNORE; i++) { if (cfg.profile_ignore[i] == NULL) break; - + if (strncmp(ptr, cfg.profile_ignore[i], strlen(cfg.profile_ignore[i])) == 0) return 0; // ignore line } - + if (strncmp(ptr, "ignore ", 7) == 0) { char *str = strdup(ptr + 7); if (*str == '\0') { @@ -94,7 +94,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { // find an empty entry in profile_ignore array int j; for (j = 0; j < MAX_PROFILE_IGNORE; j++) { - if (cfg.profile_ignore[j] == NULL) + if (cfg.profile_ignore[j] == NULL) break; } if (j >= MAX_PROFILE_IGNORE) { @@ -102,18 +102,18 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { exit(1); } // ... and configure it - else + else cfg.profile_ignore[j] = str; return 0; } - // mkdir + // mkdir if (strncmp(ptr, "mkdir ", 6) == 0) { fs_mkdir(ptr + 6); return 1; // process mkdir again while applying blacklists } - // mkfile + // mkfile if (strncmp(ptr, "mkfile ", 7) == 0) { fs_mkfile(ptr + 7); return 1; // process mkfile again while applying blacklists @@ -166,7 +166,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { else if (strcmp(ptr, "shell none") == 0) { arg_shell_none = 1; return 0; - } + } else if (strcmp(ptr, "tracelog") == 0) { arg_tracelog = 1; return 0; @@ -210,6 +210,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { arg_nosound = 1; return 0; } + else if (strcmp(ptr, "novideo") == 0) { + arg_novideo = 1; + return 0; + } else if (strcmp(ptr, "no3d") == 0) { arg_no3d = 1; return 0; @@ -217,7 +221,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { else if (strcmp(ptr, "allow-private-blacklist") == 0) { arg_allow_private_blacklist = 1; return 0; - } + } else if (strcmp(ptr, "netfilter") == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) @@ -288,7 +292,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n"); exit(1); } - + if (strcmp(ptr + 4, "lo") == 0) { fprintf(stderr, "Error: cannot attach to lo device\n"); exit(1); @@ -314,7 +318,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #endif return 0; } - + else if (strncmp(ptr, "veth-name ", 10) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { @@ -365,7 +369,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { } *secondip = '\0'; secondip++; - + // check addresses if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) || br->iprange_start >= br->iprange_end) { @@ -392,7 +396,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } - + if (mac_not_zero(br->macsandbox)) { fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); exit(1); @@ -418,7 +422,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } - + if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { fprintf(stderr, "Error: invalid mtu value\n"); exit(1); @@ -479,7 +483,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { // fprintf(stderr, "Error: invalid IP address\n"); // exit(1); // } - + } else warning_feature_disabled("networking"); @@ -502,7 +506,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { } if (strcmp(ptr, "apparmor") == 0) { -#ifdef HAVE_APPARMOR +#ifdef HAVE_APPARMOR arg_apparmor = 1; #endif return 0; @@ -515,7 +519,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fwarning("a protocol list is present, the new list \"%s\" will not be installed\n", ptr + 9); return 0; } - + // store list cfg.protocol = strdup(ptr + 9); if (!cfg.protocol) @@ -526,7 +530,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #endif return 0; } - + if (strncmp(ptr, "env ", 4) == 0) { env_store(ptr + 4, SETENV); return 0; @@ -535,7 +539,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { env_store(ptr + 6, RMENV); return 0; } - + // seccomp drop list on top of default list if (strncmp(ptr, "seccomp ", 8) == 0) { #ifdef HAVE_SECCOMP @@ -549,7 +553,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } - + // seccomp drop list without default list if (strncmp(ptr, "seccomp.drop ", 13) == 0) { #ifdef HAVE_SECCOMP @@ -559,7 +563,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { } else warning_feature_disabled("seccomp"); -#endif +#endif return 0; } @@ -572,10 +576,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { } else warning_feature_disabled("seccomp"); -#endif +#endif return 0; } - + // caps drop list if (strncmp(ptr, "caps.drop ", 10) == 0) { arg_caps_drop = 1; @@ -586,7 +590,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { caps_check_list(arg_caps_list, NULL); return 0; } - + // caps keep list if (strncmp(ptr, "caps.keep ", 10) == 0) { arg_caps_keep = 1; @@ -603,13 +607,13 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { cfg.hostname = ptr + 9; return 0; } - + // hosts-file if (strncmp(ptr, "hosts-file ", 11) == 0) { cfg.hosts_file = fs_check_hosts_file(ptr + 11); return 0; } - + // dns if (strncmp(ptr, "dns ", 4) == 0) { uint32_t dns; @@ -617,7 +621,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Error: invalid DNS server IP address\n"); return 1; } - + if (cfg.dns1 == 0) cfg.dns1 = dns; else if (cfg.dns2 == 0) @@ -630,13 +634,13 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { } return 0; } - + // cpu affinity if (strncmp(ptr, "cpu ", 4) == 0) { read_cpu_list(ptr + 4); return 0; } - + // nice value if (strncmp(ptr, "nice ", 4) == 0) { cfg.nice = atoi(ptr + 5); @@ -651,7 +655,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { set_cgroup(ptr + 7); return 0; } - + // writable-etc if (strcmp(ptr, "writable-etc") == 0) { if (cfg.etc_private_keep) { @@ -661,7 +665,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { arg_writable_etc = 1; return 0; } - + if (strcmp(ptr, "machine-id") == 0) { arg_machineid = 1; return 0; @@ -675,7 +679,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { arg_writable_var_log = 1; return 0; } - + // private directory if (strncmp(ptr, "private ", 8) == 0) { cfg.home_private = ptr + 8; @@ -717,7 +721,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #endif return 0; } - + if (strcmp(ptr, "x11 xpra") == 0) { #ifdef HAVE_X11 if (checkcfg(CFG_X11)) { @@ -736,7 +740,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #endif return 0; } - + if (strcmp(ptr, "x11 xvfb") == 0) { #ifdef HAVE_X11 if (checkcfg(CFG_X11)) { @@ -766,15 +770,15 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { else { // start x11 x11_start(cfg.original_argc, cfg.original_argv); - exit(0); + exit(0); } } else warning_feature_disabled("x11"); -#endif +#endif return 0; } - + // private /etc list of files and directories if (strncmp(ptr, "private-etc ", 12) == 0) { if (arg_writable_etc) { @@ -788,7 +792,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { cfg.etc_private_keep = ptr + 12; } arg_private_etc = 1; - + return 0; } @@ -801,7 +805,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { cfg.opt_private_keep = ptr + 12; } arg_private_opt = 1; - + return 0; } @@ -814,7 +818,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { cfg.srv_private_keep = ptr + 12; } arg_private_srv = 1; - + return 0; } @@ -906,13 +910,13 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { // filesystem bind if (strncmp(ptr, "bind ", 5) == 0) { -#ifdef HAVE_BIND +#ifdef HAVE_BIND if (checkcfg(CFG_BIND)) { if (getuid() != 0) { fprintf(stderr, "Error: --bind option is available only if running as root\n"); exit(1); } - + // extract two directories char *dname1 = ptr + 5; char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories @@ -920,7 +924,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Error: missing second directory for bind\n"); exit(1); } - + // check directories invalid_filename(dname1); invalid_filename(dname2); @@ -932,14 +936,14 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Symbolic links are not allowed for bind command\n"); exit(1); } - + // insert comma back *(dname2 - 1) = ','; return 1; } else warning_feature_disabled("bind"); -#endif +#endif return 0; } @@ -969,8 +973,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } - - return 0; + + return 0; } if (strncmp(ptr, "join-or-start ", 14) == 0) { @@ -1005,14 +1009,14 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { else if (strncmp(ptr, "noblacklist ", 12) == 0) ptr += 12; else if (strncmp(ptr, "whitelist ", 10) == 0) { -#ifdef HAVE_WHITELIST +#ifdef HAVE_WHITELIST if (checkcfg(CFG_WHITELIST)) { arg_whitelist = 1; ptr += 10; } else return 0; -#else +#else return 0; #endif } @@ -1058,13 +1062,13 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { // add a profile entry in cfg.profile list; use str to populate the list void profile_add(char *str) { EUID_ASSERT(); - + ProfileEntry *prf = malloc(sizeof(ProfileEntry)); if (!prf) errExit("malloc"); memset(prf, 0, sizeof(ProfileEntry)); prf->next = NULL; - prf->data = str; + prf->data = str; // add prf to the list if (cfg.profile == NULL) { @@ -1081,11 +1085,11 @@ void profile_add(char *str) { static int include_level = 0; void profile_read(const char *fname) { EUID_ASSERT(); - + // exit program if maximum include level was reached if (include_level > MAX_INCLUDE_LEVEL) { fprintf(stderr, "Error: maximum profile include level was reached\n"); - exit(1); + exit(1); } // check file @@ -1100,7 +1104,7 @@ void profile_read(const char *fname) { char *ptr = strstr(base, ".local"); if (ptr && strlen(ptr) == 6) return; - + fprintf(stderr, "Error: cannot access profile file\n"); exit(1); } @@ -1114,7 +1118,7 @@ void profile_read(const char *fname) { return; } } - + // open profile file: FILE *fp = fopen(fname, "r"); if (fp == NULL) { @@ -1133,13 +1137,13 @@ void profile_read(const char *fname) { char *ptr = line_remove_spaces(buf); if (ptr == NULL) continue; - + // comments if (*ptr == '#' || *ptr == '\0') { free(ptr); continue; } - + // process quiet if (strcmp(ptr, "quiet") == 0) { arg_quiet = 1; @@ -1155,13 +1159,13 @@ void profile_read(const char *fname) { // process include if (strncmp(ptr, "include ", 8) == 0) { include_level++; - + // extract profile filename and new skip params char *newprofile = ptr + 8; // profile name - + // expand ${HOME}/ in front of the new profile file char *newprofile2 = expand_home(newprofile, cfg.homedir); - + // recursivity profile_read((newprofile2)? newprofile2:newprofile); include_level--; @@ -1170,7 +1174,7 @@ void profile_read(const char *fname) { free(ptr); continue; } - + // verify syntax, exit in case of error if (profile_check_line(ptr, lineno, fname)) profile_add(ptr); diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3ff104d26..0b4d63c1b 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -17,7 +17,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - + #include "firejail.h" #include #include @@ -65,7 +65,7 @@ static void sandbox_handler(int sig){ FILE *fp = fopen(monfile, "r"); if (!fp) break; - + char c; size_t count = fread(&c, 1, 1, fp); fclose(fp); @@ -78,7 +78,7 @@ static void sandbox_handler(int sig){ monsec--; } free(monfile); - + } @@ -115,7 +115,7 @@ void save_nogroups(void) { fprintf(stderr, "Error: cannot save nogroups state\n"); exit(1); } - + } static void sandbox_if_up(Bridge *br) { @@ -132,7 +132,7 @@ static void sandbox_if_up(Bridge *br) { fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); exit(1); } - + // just assign the address assert(br->ipsandbox); if (arg_debug) @@ -149,19 +149,19 @@ static void sandbox_if_up(Bridge *br) { fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); exit(1); } - + uint32_t rv = arp_check(dev, br->ipsandbox, br->ip); if (rv) { fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox)); exit(1); } } - + if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); } - + if (br->ip6sandbox) net_if_ip6(dev, br->ip6sandbox); } @@ -171,14 +171,14 @@ static void chk_chroot(void) { char *mycont = getenv("container"); if (mycont) return; - + // check if this is a regular chroot struct stat s; if (stat("/", &s) == 0) { if (s.st_ino != 2) return; } - + fprintf(stderr, "Error: cannot mount filesystem as slave\n"); exit(1); } @@ -238,7 +238,7 @@ static int monitor_application(pid_t app_pid) { continue; if (pid == 1) continue; - + // todo: make this generic // Dillo browser leaves a dpid process running, we need to shut it down int found = 0; @@ -268,7 +268,7 @@ void start_audit(void) { char *audit_prog; if (asprintf(&audit_prog, "%s/firejail/faudit", LIBDIR) == -1) errExit("asprintf"); - assert(getenv("LD_PRELOAD") == NULL); + assert(getenv("LD_PRELOAD") == NULL); execl(audit_prog, audit_prog, NULL); perror("execl"); exit(1); @@ -281,7 +281,7 @@ static void print_time(void) { usleep(1000); unsigned long long onems = getticks() - end_timestamp; if (onems) { - printf("Child process initialized in %.02f ms\n", + printf("Child process initialized in %.02f ms\n", (float) (end_timestamp - start_timestamp) / (float) onems); return; } @@ -301,7 +301,7 @@ void start_application(void) { printf("starting application\n"); printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); } - + //**************************************** // audit //**************************************** @@ -405,12 +405,12 @@ static void enforce_filters(void) { free(cfg.seccomp_list_keep); cfg.seccomp_list_keep = NULL; } - + // disable all capabilities if (arg_caps_default_filter || arg_caps_list) fwarning("all capabilities disabled for a regular user in chroot\n"); arg_caps_drop_all = 1; - + // drop all supplementary groups; /etc/group file inside chroot // is controlled by a regular usr arg_nogroups = 1; @@ -424,12 +424,12 @@ int sandbox(void* sandbox_arg) { pid_t child_pid = getpid(); if (arg_debug) - printf("Initializing child process\n"); + printf("Initializing child process\n"); // close each end of the unused pipes close(parent_to_child_fds[1]); close(child_to_parent_fds[0]); - + // wait for parent to do base setup wait_for_other(parent_to_child_fds[0]); @@ -454,7 +454,7 @@ int sandbox(void* sandbox_arg) { } // ... and mount a tmpfs on top of /run/firejail/mnt directory preproc_mount_mnt_dir(); - + //**************************** // log sandbox data //**************************** @@ -463,12 +463,12 @@ int sandbox(void* sandbox_arg) { fs_logger2int("sandbox pid:", (int) sandbox_pid); if (cfg.chrootdir) fs_logger("sandbox filesystem: chroot"); - else if (arg_overlay) + else if (arg_overlay) fs_logger("sandbox filesystem: overlay"); else fs_logger("sandbox filesystem: local"); fs_logger("install mount namespace"); - + //**************************** // netfilter //**************************** @@ -496,23 +496,23 @@ int sandbox(void* sandbox_arg) { else if (any_bridge_configured() || any_interface_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); - + if (mac_not_zero(cfg.bridge0.macsandbox)) net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); sandbox_if_up(&cfg.bridge0); - + if (mac_not_zero(cfg.bridge1.macsandbox)) net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); sandbox_if_up(&cfg.bridge1); - + if (mac_not_zero(cfg.bridge2.macsandbox)) net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); sandbox_if_up(&cfg.bridge2); - + if (mac_not_zero(cfg.bridge3.macsandbox)) net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); sandbox_if_up(&cfg.bridge3); - + // moving an interface in a namespace using --interface will reset the interface configuration; // we need to put the configuration back @@ -520,23 +520,23 @@ int sandbox(void* sandbox_arg) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); net_config_interface(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); - } + } if (cfg.interface1.configured && cfg.interface1.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); net_config_interface(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); - } + } if (cfg.interface2.configured && cfg.interface2.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); net_config_interface(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); - } + } if (cfg.interface3.configured && cfg.interface3.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); net_config_interface(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); - } - + } + // add a default route if (cfg.defaultgw) { // set the default route @@ -549,7 +549,7 @@ int sandbox(void* sandbox_arg) { if (arg_debug) printf("Network namespace enabled\n"); } - + // print network configuration if (!arg_quiet) { @@ -561,7 +561,7 @@ int sandbox(void* sandbox_arg) { sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "printif", "scan"); else sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, PATH_FNET, "printif", "scan"); - + } if (cfg.defaultgw != 0) { if (gw_cfg_failed) @@ -585,7 +585,7 @@ int sandbox(void* sandbox_arg) { } else env_ibus_load(); - + //**************************** // fs pre-processing: // - build seccomp filters @@ -602,7 +602,7 @@ int sandbox(void* sandbox_arg) { if (rv) exit(rv); } -#endif +#endif // trace pre-install if (arg_trace || arg_tracelog) @@ -622,13 +622,13 @@ int sandbox(void* sandbox_arg) { enforce_filters(); #ifdef HAVE_SECCOMP enforce_seccomp = 1; -#endif +#endif } -#ifdef HAVE_CHROOT +#ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); - + // force caps and seccomp if not started as root if (getuid() != 0) { enforce_filters(); @@ -638,14 +638,14 @@ int sandbox(void* sandbox_arg) { } else arg_seccomp = 1; - + //**************************** // trace pre-install, this time inside chroot //**************************** if (arg_trace || arg_tracelog) fs_trace_preload(); } - else + else #endif #ifdef HAVE_OVERLAYFS if (arg_overlay) { @@ -663,7 +663,7 @@ int sandbox(void* sandbox_arg) { else #endif fs_basic_fs(); - + //**************************** // private mode //**************************** @@ -696,7 +696,7 @@ int sandbox(void* sandbox_arg) { else fs_private_dev(); } - + if (arg_private_etc) { if (cfg.chrootdir) fwarning("private-etc feature is disabled in chroot\n"); @@ -709,7 +709,7 @@ int sandbox(void* sandbox_arg) { fs_trace_preload(); } } - + if (arg_private_opt) { if (cfg.chrootdir) fwarning("private-opt feature is disabled in chroot\n"); @@ -719,7 +719,7 @@ int sandbox(void* sandbox_arg) { fs_private_dir_list("/opt", RUN_OPT_DIR, cfg.opt_private_keep); } } - + if (arg_private_srv) { if (cfg.chrootdir) fwarning("private-srv feature is disabled in chroot\n"); @@ -729,7 +729,7 @@ int sandbox(void* sandbox_arg) { fs_private_dir_list("/srv", RUN_SRV_DIR, cfg.srv_private_keep); } } - + if (arg_private_bin) { if (cfg.chrootdir) fwarning("private-bin feature is disabled in chroot\n"); @@ -748,7 +748,7 @@ int sandbox(void* sandbox_arg) { fs_private_bin_list(); } } - + if (arg_private_tmp) { if (cfg.chrootdir) fwarning("private-tmp feature is disabled in chroot\n"); @@ -762,7 +762,7 @@ int sandbox(void* sandbox_arg) { } } - + //**************************** // hosts and hostname //**************************** @@ -777,19 +777,19 @@ int sandbox(void* sandbox_arg) { //**************************** if (arg_netns) netns_mounts(arg_netns); - + //**************************** // update /proc, /sys, /dev, /boot directory //**************************** if (checkcfg(CFG_REMOUNT_PROC_SYS)) fs_proc_sys_dev_boot(); - + //**************************** // handle /mnt and /media //**************************** if (checkcfg(CFG_DISABLE_MNT)) fs_mnt(); - + //**************************** // nosound/no3d and fix for pulseaudio 7.0 //**************************** @@ -802,35 +802,43 @@ int sandbox(void* sandbox_arg) { } else pulseaudio_init(); - + if (arg_no3d) fs_dev_disable_3d(); - + + //**************************** + // novideo + //**************************** + if (arg_novideo) { + // disable /dev/video* + fs_dev_disable_video(); + } + //**************************** // apply the profile file //**************************** - // apply all whitelist commands ... + // apply all whitelist commands ... if (cfg.chrootdir) fwarning("whitelist feature is disabled in chroot\n"); else if (arg_overlay) fwarning("whitelist feature is disabled in overlay\n"); else fs_whitelist(); - + // ... followed by blacklist commands fs_blacklist(); // mkdir and mkfile are processed all over again - + //**************************** // install trace //**************************** if (arg_trace || arg_tracelog) fs_trace(); - + //**************************** // set dns //**************************** fs_resolvconf(); - + //**************************** // fs post-processing //**************************** @@ -846,7 +854,7 @@ int sandbox(void* sandbox_arg) { if (chdir(cfg.cwd) == 0) cwd = 1; } - + if (!cwd) { if (chdir("/") < 0) errExit("chdir"); @@ -866,8 +874,8 @@ int sandbox(void* sandbox_arg) { free(cpath); } } - - + + // set nice if (arg_nice) { errno = 0; @@ -878,12 +886,12 @@ int sandbox(void* sandbox_arg) { errno = 0; } } - + // clean /tmp/.X11-unix sockets fs_x11(); if (arg_x11_xorg) x11_xorg(); - + //**************************** // set security filters //**************************** @@ -899,7 +907,7 @@ int sandbox(void* sandbox_arg) { save_cpu(); // save cpu affinity mask to CPU_CFG file set_cpu_affinity(); } - + // save cgroup in CGROUP_CFG file if (cfg.cgroup) save_cgroup(); @@ -911,7 +919,7 @@ int sandbox(void* sandbox_arg) { if (cfg.protocol) { if (arg_debug) printf("Install protocol filter: %s\n", cfg.protocol); - seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter + seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG } #endif @@ -939,12 +947,12 @@ int sandbox(void* sandbox_arg) { } else drop_privs(arg_nogroups); - + // notify parent that new user namespace has been created so a proper // UID/GID map can be setup notify_other(child_to_parent_fds[1]); close(child_to_parent_fds[1]); - + // wait for parent to finish setting up a proper UID/GID map wait_for_other(parent_to_child_fds[0]); close(parent_to_child_fds[0]); @@ -956,7 +964,7 @@ int sandbox(void* sandbox_arg) { printf("noroot user namespace installed\n"); set_caps(); } - + //**************************************** // Set NO_NEW_PRIVS if desired //**************************************** @@ -989,7 +997,7 @@ int sandbox(void* sandbox_arg) { else if (arg_debug) printf("AppArmor enabled\n"); } -#endif +#endif prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died start_application(); // start app } diff --git a/src/firejail/usage.c b/src/firejail/usage.c index a21633349..76930e1de 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -36,10 +36,10 @@ void usage(void) { printf(" --apparmor - enable AppArmor confinement.\n"); printf(" --appimage - sandbox an AppImage application.\n"); printf(" --audit[=test-program] - audit the sandbox.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --bandwidth=name|pid - set bandwidth limits.\n"); #endif -#ifdef HAVE_BIND +#ifdef HAVE_BIND printf(" --bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n"); printf(" --bind=filename1,filename2 - mount-bind filename1 on top of filename2.\n"); #endif @@ -51,7 +51,7 @@ void usage(void) { printf(" --caps.keep=capability,capability - whitelist capabilities filter.\n"); printf(" --caps.print=name|pid - print the caps filter.\n"); printf(" --cgroup=tasks-file - place the sandbox in the specified control group.\n"); -#ifdef HAVE_CHROOT +#ifdef HAVE_CHROOT printf(" --chroot=dirname - chroot into directory.\n"); #endif printf(" --cpu=cpu-number,cpu-number - set cpu affinity.\n"); @@ -64,15 +64,15 @@ void usage(void) { printf(" --debug-errnos - print all recognized error numbers.\n"); printf(" --debug-protocols - print all recognized protocols.\n"); printf(" --debug-syscalls - print all recognized system calls.\n"); -#ifdef HAVE_WHITELIST +#ifdef HAVE_WHITELIST printf(" --debug-whitelists - debug whitelisting.\n"); #endif -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --defaultgw=address - configure default gateway.\n"); #endif printf(" --dns=address - set DNS server.\n"); printf(" --dns.print=name|pid - print DNS configuration.\n"); - + printf(" --env=name=value - set environment variable.\n"); printf(" --force - attempt to start a new sandbox inside the existing sandbox.\n"); printf(" --fs.print=name|pid - print the filesystem log.\n"); @@ -86,7 +86,7 @@ void usage(void) { printf(" --hostname=name - set sandbox hostname.\n"); printf(" --hosts-file=file - use file as /etc/hosts.\n"); printf(" --ignore=command - ignore command in profile files.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --interface=name - move interface in sandbox.\n"); printf(" --ip=address - set interface IP address.\n"); printf(" --ip=none - no IP address and no default gateway are configured.\n"); @@ -96,21 +96,21 @@ void usage(void) { printf(" --ipc-namespace - enable a new IPC namespace.\n"); printf(" --join=name|pid - join the sandbox.\n"); printf(" --join-filesystem=name|pid - join the mount namespace.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --join-network=name|pid - join the network namespace.\n"); #endif printf(" --join-or-start=name|pid - join the sandbox or start a new one.\n"); printf(" --list - list all sandboxes.\n"); printf(" --ls=name|pid dir_or_filename - list files in sandbox container.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n"); #endif printf(" --machine-id - preserve /etc/machine-id\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --mtu=number - set interface MTU.\n"); #endif printf(" --name=name - set sandbox name.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --net=bridgename - enable network namespaces and connect to this bridge.\n"); printf(" --net=ethernet_interface - enable network namespaces and connect to this\n"); printf("\tEthernet interface.\n"); @@ -127,17 +127,18 @@ void usage(void) { printf(" --nogroups - disable supplementary groups.\n"); printf(" --nonewprivs - sets the NO_NEW_PRIVS prctl.\n"); printf(" --noprofile - do not use a security profile.\n"); -#ifdef HAVE_USERNS +#ifdef HAVE_USERNS printf(" --noroot - install a user namespace with only the current user.\n"); #endif printf(" --nosound - disable sound system.\n"); + printf(" --novideo - disable video devices.\n"); printf(" --nowhitelist=filename - disable whitelist for file or directory .\n"); printf(" --output=logfile - stdout logging and log rotation.\n"); printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n"); printf(" --overlay-named=name - mount a filesystem overlay on top of the current\n"); printf("\tfilesystem, and store it in name directory.\n"); printf(" --overlay-tmpfs - mount a temporary filesystem overlay on top of the current\n"); - printf("\tfilesystem.\n"); + printf("\tfilesystem.\n"); printf(" --overlay-clean - clean all overlays stored in $HOME/.firejail directory.\n"); printf(" --private - temporary home directory.\n"); printf(" --private=directory - use directory as user home.\n"); @@ -169,9 +170,9 @@ void usage(void) { printf(" --rlimit-sigpending=number - set the maximum number of pending signals\n"); printf("\tfor a process.\n"); printf(" --rmenv=name - remove environment variable in the new sandbox.\n"); -#ifdef HAVE_NETWORK +#ifdef HAVE_NETWORK printf(" --scan - ARP-scan all the networks from inside a network namespace.\n"); -#endif +#endif #ifdef HAVE_SECCOMP printf(" --seccomp - enable seccomp filter and apply the default blacklist.\n"); printf(" --seccomp=syscall,syscall,syscall - enable seccomp filter, blacklist the\n"); @@ -195,12 +196,12 @@ void usage(void) { printf("\tdirectoires blacklisted by the security profile.\n"); printf(" --tree - print a tree of all sandboxed processes.\n"); printf(" --version - print program version and exit.\n"); -#ifdef HAVE_NETWORK - printf(" --veth-name=name - use this name for the interface connected to the bridge.\n"); -#endif -#ifdef HAVE_WHITELIST +#ifdef HAVE_NETWORK + printf(" --veth-name=name - use this name for the interface connected to the bridge.\n"); +#endif +#ifdef HAVE_WHITELIST printf(" --whitelist=filename - whitelist directory or file.\n"); -#endif +#endif printf(" --writable-etc - /etc directory is mounted read-write.\n"); printf(" --writable-var - /var directory is mounted read-write.\n"); printf(" --writable-var-log - use the real /var/log directory, not a clone.\n"); diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index bb1bd86b9..cbffa9ce4 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -50,7 +50,7 @@ Parent pid 8553, child pid 8554 .br Child process initialized .br -[...] +[...] .br .br @@ -92,7 +92,7 @@ Example: "include ${CFG}/firefox.profile" will load "/etc/firejail/firefox.profi System configuration files in ${CFG} are overwritten during software installation. Persistent configuration at system level is handled in ".local" files. For every -profile file in ${CFG} directory, the user can create a corresponding .local file +profile file in ${CFG} directory, the user can create a corresponding .local file storing modifications to the persistent configuration. Persistent .local files are included at the start of regular profile files. @@ -255,7 +255,7 @@ Blacklist violations logged to syslog. \fBwhitelist file_or_directory Whitelist directory or file. A temporary file system is mounted on the top directory, and the whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, -everything else is discarded when the sandbox is closed. The top directory could be +everything else is discarded when the sandbox is closed. The top directory could be user home, /dev, /media, /mnt, /opt, /srv, /var, and /tmp. .br @@ -405,6 +405,8 @@ Enable IPC namespace. \fBnosound Disable sound system. .TP +\fBnovideo +Disable video devices. \fBno3d Disable 3D hardware acceleration. @@ -533,7 +535,7 @@ really need network access. .TP \fBveth-name name -Use this name for the interface connected to the bridge for --net=bridge_interface commands, +Use this name for the interface connected to the bridge for --net=bridge_interface commands, instead of the default one. .SH Other @@ -585,6 +587,3 @@ Homepage: http://firejail.wordpress.com \&\flfiremon\fR\|(1), \&\flfirecfg\fR\|(1), \&\flfirejail-login\fR\|(5) - - - diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 38bb6a19e..de300d47b 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -42,7 +42,7 @@ and it is integrated with Linux Control Groups. .PP Written in C with virtually no dependencies, the software runs on any Linux computer with a 3.x kernel version or newer. -It can sandbox any type of processes: servers, graphical applications, and even user login sessions. +It can sandbox any type of processes: servers, graphical applications, and even user login sessions. .PP Firejail allows the user to manage application security using security profiles. Each profile defines a set of permissions for a specific application or group @@ -52,13 +52,13 @@ Linux programs, such as Mozilla Firefox, Chromium, VLC, Transmission etc. .SH USAGE Without any options, the sandbox consists of a filesystem build in a new mount namespace, and new PID and UTS namespaces. IPC, network and user namespaces can be added using the -command line options. The default Firejail filesystem is based on the host filesystem with the main -system directories mounted read-only. These directories are /etc, /var, /usr, /bin, /sbin, /lib, /lib32, +command line options. The default Firejail filesystem is based on the host filesystem with the main +system directories mounted read-only. These directories are /etc, /var, /usr, /bin, /sbin, /lib, /lib32, /libx32 and /lib64. Only /home and /tmp are writable. .PP As it starts up, Firejail tries to find a security profile based on the name of the application. If an appropriate profile is not found, Firejail will use a default profile. -The default profile is quite restrictive. In case the application doesn't work, use --noprofile option +The default profile is quite restrictive. In case the application doesn't work, use --noprofile option to disable it. For more information, please see \fBSECURITY PROFILES\fR section below. .PP If a program argument is not specified, Firejail starts /bin/bash shell. @@ -657,7 +657,7 @@ $ sudo firejail --join-network=browser ip addr .br Switching to pid 1932, the first child process inside the sandbox .br -1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default .br link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 .br @@ -665,11 +665,11 @@ Switching to pid 1932, the first child process inside the sandbox .br valid_lft forever preferred_lft forever .br - inet6 ::1/128 scope host + inet6 ::1/128 scope host .br valid_lft forever preferred_lft forever .br -2: eth0-1931: mtu 1500 qdisc noqueue state UNKNOWN group default +2: eth0-1931: mtu 1500 qdisc noqueue state UNKNOWN group default .br link/ether 76:58:14:42:78:e4 brd ff:ff:ff:ff:ff:ff .br @@ -677,7 +677,7 @@ Switching to pid 1932, the first child process inside the sandbox .br valid_lft forever preferred_lft forever .br - inet6 fe80::7458:14ff:fe42:78e4/64 scope link + inet6 fe80::7458:14ff:fe42:78e4/64 scope link .br valid_lft forever preferred_lft forever @@ -702,13 +702,13 @@ Example: .br $ firejail \-\-list .br -7015:netblue:firejail firefox +7015:netblue:firejail firefox .br -7056:netblue:firejail \-\-net=eth0 transmission-gtk +7056:netblue:firejail \-\-net=eth0 transmission-gtk .br -7064:netblue:firejail \-\-noroot xterm +7064:netblue:firejail \-\-noroot xterm .br -$ +$ .TP \fB\-\-mac=address Assign MAC addresses to the last network interface defined by a \-\-net option. @@ -998,7 +998,7 @@ $ .TP \fB\-\-noprofile -Do not use a security profile. +Do not use a security profile. .br .br @@ -1012,7 +1012,7 @@ Parent pid 8553, child pid 8554 .br Child process initialized .br -[...] +[...] .br .br @@ -1066,6 +1066,11 @@ Example: .br $ firejail \-\-nosound firefox +.TP +\fB\-\-novideo +Disable video devices. +.br + .TP \fB\-\-nowhitelist=dirname_or_filename Disable whitelist for this directory or file. @@ -1200,7 +1205,7 @@ $ firejail \-\-private-home=.mozilla firefox Build a new /bin in a temporary filesystem, and copy the programs in the list. If no listed file is found, /bin directory will be empty. The same directory is also bind-mounted over /sbin, /usr/bin, /usr/sbin and /usr/local/bin. -All modifications are discarded when the sandbox is closed. +All modifications are discarded when the sandbox is closed. .br .br @@ -1240,7 +1245,7 @@ $ Build a new /etc in a temporary filesystem, and copy the files and directories in the list. If no listed file is found, /etc directory will be empty. -All modifications are discarded when the sandbox is closed. +All modifications are discarded when the sandbox is closed. .br .br @@ -1255,7 +1260,7 @@ nsswitch.conf,passwd,resolv.conf Build a new /opt in a temporary filesystem, and copy the files and directories in the list. If no listed file is found, /opt directory will be empty. -All modifications are discarded when the sandbox is closed. +All modifications are discarded when the sandbox is closed. .br .br @@ -1268,7 +1273,7 @@ $ firejail --private-opt=firefox /opt/firefox/firefox Build a new /srv in a temporary filesystem, and copy the files and directories in the list. If no listed file is found, /srv directory will be empty. -All modifications are discarded when the sandbox is closed. +All modifications are discarded when the sandbox is closed. .br .br @@ -1573,7 +1578,7 @@ SECCOMP Filter: .br RETURN_ALLOW .br -$ +$ .TP \fB\-\-shell=none Run the program directly, without a user shell. @@ -1665,7 +1670,7 @@ parent is shutting down, bye... .TP \fB\-\-tracelog This option enables auditing blacklisted files and directories. A message -is sent to syslog in case the file or the directory is accessed. +is sent to syslog in case the file or the directory is accessed. .br .br @@ -1698,13 +1703,13 @@ $ firejail \-\-tree .br 11903:netblue:firejail iceweasel .br - 11904:netblue:iceweasel + 11904:netblue:iceweasel .br 11957:netblue:/usr/lib/iceweasel/plugin-container .br -11969:netblue:firejail \-\-net=eth0 transmission-gtk +11969:netblue:firejail \-\-net=eth0 transmission-gtk .br - 11970:netblue:transmission-gtk + 11970:netblue:transmission-gtk .TP \fB\-\-version @@ -1720,7 +1725,7 @@ firejail version 0.9.27 .TP \fB\-\-veth-name=name -Use this name for the interface connected to the bridge for --net=bridge_interface commands, +Use this name for the interface connected to the bridge for --net=bridge_interface commands, instead of the default one. .br @@ -1733,7 +1738,7 @@ $ firejail \-\-net=br0 --veth-name=if0 \fB\-\-whitelist=dirname_or_filename Whitelist directory or file. A temporary file system is mounted on the top directory, and the whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, -everything else is discarded when the sandbox is closed. The top directory could be +everything else is discarded when the sandbox is closed. The top directory could be user home, /dev, /media, /mnt, /opt, /srv, /var, and /tmp. .br @@ -1789,7 +1794,7 @@ Sandbox the application using Xpra, Xephyr, Xvfb or Xorg security extension. The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing clients running outside the sandbox. Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. -If all fails, Firejail will not attempt to use Xvfb or X11 security extension. +If all fails, Firejail will not attempt to use Xvfb or X11 security extension. .br .br @@ -1828,7 +1833,7 @@ A security profile for OpenBox is provided. .br Xephyr is developed by Xorg project. On Debian platforms it is installed with the command \fBsudo apt-get install xserver-xephyr\fR. -This feature is not available when running as root. +This feature is not available when running as root. .br .br @@ -1838,9 +1843,9 @@ $ firejail \-\-x11=xephyr --net=eth0 openbox .TP \fB\-\-x11=xorg -Sandbox the application using the untrusted mode implemented by X11 security extension. +Sandbox the application using the untrusted mode implemented by X11 security extension. The extension is available in Xorg package -and it is installed by default on most Linux distributions. It provides support for a simple trusted/untrusted +and it is installed by default on most Linux distributions. It provides support for a simple trusted/untrusted connection model. Untrusted clients are restricted in certain ways to prevent them from reading window contents of other clients, stealing input events, etc. @@ -1875,9 +1880,9 @@ $ firejail \-\-x11=xpra --net=eth0 firefox .TP \fB\-\-x11=xvfb -Start Xvfb X11 server and attach the sandbox to this server. -Xvfb, short for X virtual framebuffer, performs all graphical operations in memory -without showing any screen output. Xvfb is mainly used for remote access and software +Start Xvfb X11 server and attach the sandbox to this server. +Xvfb, short for X virtual framebuffer, performs all graphical operations in memory +without showing any screen output. Xvfb is mainly used for remote access and software testing on headless servers. .br @@ -1992,7 +1997,7 @@ $ firejail --tree .br 1190:netblue:firejail firefox .br - 1220:netblue:/bin/sh -c "/usr/lib/firefox/firefox" + 1220:netblue:/bin/sh -c "/usr/lib/firefox/firefox" .br 1221:netblue:/usr/lib/firefox/firefox .RE @@ -2246,7 +2251,7 @@ Parent pid 8553, child pid 8554 .br Child process initialized .br -[...] +[...] .br .br @@ -2260,7 +2265,7 @@ Child process initialized .RE See man 5 firejail-profile for profile file syntax information. - + .SH RESTRICTED SHELL To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in /etc/passwd file for each user that needs to be restricted. Alternatively, @@ -2307,6 +2312,3 @@ Homepage: http://firejail.wordpress.com \&\flfirecfg\fR\|(1), \&\flfirejail-profile\fR\|(5), \&\flfirejail-login\fR\|(5) - - - -- cgit v1.2.3-54-g00ecf