From 99a9f8d680ea5a613506d5f0a1804bf97a5d69f3 Mon Sep 17 00:00:00 2001 From: Austin Morton Date: Mon, 20 May 2019 19:44:40 -0400 Subject: Add private-cwd option to control working directory within jail --- src/firejail/firejail.h | 3 +++ src/firejail/fs_home.c | 15 +++++++++++++ src/firejail/main.c | 15 +++++++++++++ src/firejail/profile.c | 14 +++++++++++- src/firejail/sandbox.c | 4 ++++ src/firejail/usage.c | 2 ++ src/man/firejail-profile.txt | 6 +++++ src/man/firejail.txt | 42 +++++++++++++++++++++++++++++++++++ test/fs/fs.sh | 3 +++ test/fs/private-cwd.exp | 52 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 155 insertions(+), 1 deletion(-) create mode 100755 test/fs/private-cwd.exp diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index e0f3a6a16..bbdf279ce 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -283,6 +283,7 @@ 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_private_lib; // private lib directory +extern int arg_private_cwd; // private working directory extern int arg_scan; // arp-scan all interfaces extern int arg_whitelist; // whitelist command extern int arg_nosound; // disable sound @@ -521,6 +522,8 @@ void fs_private(void); void fs_private_homedir(void); // check new private home directory (--private= option) - exit if it fails void fs_check_private_dir(void); +// check new private working directory (--private-cwd= option) - exit if it fails +void fs_check_private_cwd(void); void fs_private_home_list(void); diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index b44d09acc..a1a16841a 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c @@ -370,6 +370,21 @@ void fs_check_private_dir(void) { } } +// check new private working directory (--private-cwd= option) - exit if it fails +void fs_check_private_cwd(void) { + EUID_ASSERT(); + invalid_filename(cfg.cwd, 0); // no globbing + + // Expand the working directory + cfg.cwd = expand_macros(cfg.cwd); + + // realpath/is_dir not used because path may not exist outside of jail + if (!cfg.cwd) { + fprintf(stderr, "Error: invalid private working directory\n"); + exit(1); + } +} + //*********************************************************************************** // --private-home //*********************************************************************************** diff --git a/src/firejail/main.c b/src/firejail/main.c index f3dc72944..2c7290854 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -92,6 +92,7 @@ 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_private_lib = 0; // private lib directory +int arg_private_cwd = 0; // private working directory int arg_scan = 0; // arp-scan all interfaces int arg_whitelist = 0; // whitelist command int arg_nosound = 0; // disable sound @@ -1773,6 +1774,20 @@ int main(int argc, char **argv) { else exit_err_feature("private-cache"); } + else if (strcmp(argv[i], "--private-cwd") == 0) { + cfg.cwd = NULL; + arg_private_cwd = 1; + } + else if (strncmp(argv[i], "--private-cwd=", 14) == 0) { + cfg.cwd = argv[i] + 14; + if (*cfg.cwd == '\0') { + fprintf(stderr, "Error: invalid private-cwd option\n"); + exit(1); + } + + fs_check_private_cwd(); + arg_private_cwd = 1; + } //************************************* // hostname, etc diff --git a/src/firejail/profile.c b/src/firejail/profile.c index c8619f7e2..801c8ba4c 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -338,7 +338,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { arg_private = 1; return 0; } - if (strncmp(ptr, "private-home ", 13) == 0) { + else if (strncmp(ptr, "private-home ", 13) == 0) { #ifdef HAVE_PRIVATE_HOME if (checkcfg(CFG_PRIVATE_HOME)) { if (cfg.home_private_keep) { @@ -353,6 +353,18 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { #endif return 0; } + else if (strcmp(ptr, "private-cwd") == 0) { + cfg.cwd = NULL; + arg_private_cwd = 1; + return 0; + } + else if (strncmp(ptr, "private-cwd ", 12) == 0) { + cfg.cwd = strdup(ptr + 12); + + fs_check_private_cwd(); + arg_private_cwd = 1; + return 0; + } else if (strcmp(ptr, "allusers") == 0) { arg_allusers = 1; return 0; diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 9f0a5f25c..250247f8a 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -1016,6 +1016,10 @@ int sandbox(void* sandbox_arg) { if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; + else if (arg_private_cwd) { + fprintf(stderr, "Error: unabled to enter private working directory: %s: %s\n", cfg.cwd, strerror(errno)); + exit(1); + } } if (!cwd) { diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 7620bba82..a8d5bfdda 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -162,6 +162,8 @@ static char *usage_str = " --private-etc=file,directory - build a new /etc in a temporary\n" "\tfilesystem, and copy the files and directories in the list.\n" " --private-tmp - mount a tmpfs on top of /tmp directory.\n" + " --private-cwd - do not inherit working directory inside jail.\n" + " --private-cwd=directory - set working directory inside jail.\n" " --private-opt=file,directory - build a new /opt in a temporary filesystem.\n" " --private-srv=file,directory - build a new /srv in a temporary filesystem.\n" " --profile=filename|profile_name - use a custom profile.\n" diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 703fac30f..23007725a 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -288,6 +288,12 @@ All modifications are discarded when the sandbox is closed. \fBprivate-tmp Mount an empty temporary filesystem on top of /tmp directory whitelisting /tmp/.X11-unix. .TP +\fBprivate-cwd +Set working directory inside jail to the home directory, and failing that, the root directory. +.TP +\fBprivate-cwd directory +Set working directory inside the jail. +.TP \fBread-only file_or_directory Make directory or file read-only. .TP diff --git a/src/man/firejail.txt b/src/man/firejail.txt index e6826448b..1fa2a6546 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -1568,6 +1568,48 @@ drwx------ 2 nobody nogroup 4096 Apr 30 10:52 pulse-PKdhtXMmr18n drwxrwxrwt 2 nobody nogroup 4096 Apr 30 10:52 .X11-unix .br +.TP +\fB\-\-private-cwd +Set working directory inside jail to the home directory, and failing that, the root directory. +.br +Does not impact working directory of profile include paths. +.br + +.br +Example: +.br +$ pwd +.br +/tmp +.br +$ firejail \-\-private-cwd +.br +$ pwd +.br +/home/user +.br + +.TP +\fB\-\-private-cwd=directory +Set working directory inside the jail. +.br +Does not impact working directory of profile include paths. +.br + +.br +Example: +.br +$ pwd +.br +/tmp +.br +$ firejail \-\-private-cwd=/opt +.br +$ pwd +.br +/opt +.br + .TP \fB\-\-profile=filename_or_profilename diff --git a/test/fs/fs.sh b/test/fs/fs.sh index 0fc216b20..7e1d46f0a 100755 --- a/test/fs/fs.sh +++ b/test/fs/fs.sh @@ -69,6 +69,9 @@ echo "TESTING: empty private-etc (test/fs/private-etc-empty.exp)" echo "TESTING: private-bin (test/fs/private-bin.exp)" ./private-bin.exp +echo "TESTING: private-cwd (test/fs/private-cwd.exp)" +./private-cwd.exp + echo "TESTING: macros (test/fs/macro.exp)" ./macro.exp diff --git a/test/fs/private-cwd.exp b/test/fs/private-cwd.exp new file mode 100755 index 000000000..0fa87a92f --- /dev/null +++ b/test/fs/private-cwd.exp @@ -0,0 +1,52 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2019 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "cd /tmp\r" +after 100 + +# testing profile and private +send -- "firejail --private-cwd\r" +expect { + timeout {puts "TESTING ERROR 0\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "pwd\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "$env(HOME)" +} +after 100 + +send -- "exit\r" +sleep 1 + +send -- "cd /\r" +after 100 + +# testing profile and private +send -- "firejail --private-cwd=/tmp\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "pwd\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "/tmp" +} +after 100 + +send -- "exit\r" +sleep 1 + +puts "all done\n" -- cgit v1.2.3-54-g00ecf