From 66c398e34cdf39c5ad9aa06bd022ce8a518bcbc4 Mon Sep 17 00:00:00 2001 From: smitsohu Date: Wed, 19 Aug 2020 00:39:20 +0200 Subject: refactor ls.c and prepare for new --cat option --- src/firejail/firejail.h | 3 + src/firejail/ls.c | 163 +++++++++++++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 63 deletions(-) diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 54a1023ab..56bdd7ec3 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -729,10 +729,13 @@ void x11_xorg(void); // ls.c enum { SANDBOX_FS_LS = 0, + SANDBOX_FS_CAT, SANDBOX_FS_GET, SANDBOX_FS_PUT, SANDBOX_FS_MAX // this should always be the last entry }; +void ls(const char *path); +void cat(const char *path); void sandboxfs(int op, pid_t pid, const char *path1, const char *path2); // checkcfg.c diff --git a/src/firejail/ls.c b/src/firejail/ls.c index aa33d838b..7c7f8b75f 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c @@ -34,18 +34,12 @@ static uid_t c_uid = 0; static char *c_uid_name = NULL; -static void print_file_or_dir(const char *path, const char *fname, int separator) { +static void print_file_or_dir(const char *path, const char *fname) { assert(fname); char *name; - if (separator) { - if (asprintf(&name, "%s/%s", path, fname) == -1) - errExit("asprintf"); - } - else { - if (asprintf(&name, "%s%s", path, fname) == -1) - errExit("asprintf"); - } + if (asprintf(&name, "%s/%s", path, fname) == -1) + errExit("asprintf"); struct stat s; if (stat(name, &s) == -1) { @@ -178,13 +172,75 @@ static void print_directory(const char *path) { errExit("scandir"); else { for (i = 0; i < n; i++) { - print_file_or_dir(path, namelist[i]->d_name, 0); + print_file_or_dir(path, namelist[i]->d_name); free(namelist[i]); } } free(namelist); } +void ls(const char *path) { + EUID_ASSERT(); + assert(path); + + char *rp = realpath(path, NULL); + if (!rp || access(rp, R_OK) == -1) { + fprintf(stderr, "Error: cannot access %s\n", path); + exit(1); + } + if (arg_debug) + printf("ls %s\n", rp); + + // list directory contents + struct stat s; + if (stat(rp, &s) == -1) { + fprintf(stderr, "Error: cannot access %s\n", rp); + exit(1); + } + if (S_ISDIR(s.st_mode)) + print_directory(rp); + else { + char *split = strrchr(rp, '/'); + if (split) { + *split = '\0'; + char *rp2 = split + 1; + if (arg_debug) + printf("path %s, file %s\n", rp, rp2); + print_file_or_dir(rp, rp2); + } + } + free(rp); +} + +void cat(const char *path) { + EUID_ASSERT(); + assert(path); + + if (arg_debug) + printf("cat %s\n", path); + FILE *fp = fopen(path, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot read %s\n", path); + exit(1); + } + int fd = fileno(fp); + if (fd == -1) + errExit("fileno"); + struct stat s; + if (fstat(fd, &s) == -1) + errExit("fstat"); + if (!S_ISREG(s.st_mode)) { + fprintf(stderr, "Error: %s is not a regular file\n", path); + exit(1); + } + + int c; + while ((c = fgetc(fp)) != EOF) + fputc(c, stdout); + fflush(stdout); + fclose(fp); +} + char *expand_path(const char *path) { char *fname = NULL; if (*path == '/') { @@ -219,14 +275,14 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { check_join_permission(pid); // expand paths - char *fname1 = expand_path(path1);; + char *fname1 = expand_path(path1); char *fname2 = NULL; if (path2 != NULL) { fname2 = expand_path(path2); } if (arg_debug) { printf("file1 %s\n", fname1); - printf("file2 %s\n", fname2); + printf("file2 %s\n", fname2 ? fname2 : "(null)"); } // sandbox root directory @@ -234,57 +290,36 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); - if (op == SANDBOX_FS_LS) { - EUID_ROOT(); - // chroot - if (chroot(rootdir) < 0) - errExit("chroot"); - if (chdir("/") < 0) - errExit("chdir"); - - // drop privileges - drop_privs(0); - - // check access - if (access(fname1, R_OK) == -1) { - fprintf(stderr, "Error: Cannot access %s\n", fname1); - exit(1); - } - /* coverity[toctou] */ - char *rp = realpath(fname1, NULL); - if (!rp) { - fprintf(stderr, "Error: Cannot access %s\n", fname1); - exit(1); - } - if (arg_debug) - printf("realpath %s\n", rp); - + if (op == SANDBOX_FS_LS || op == SANDBOX_FS_CAT) { + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + EUID_ROOT(); + // chroot + if (chroot(rootdir) < 0) + errExit("chroot"); + if (chdir("/") < 0) + errExit("chdir"); - // list directory contents - struct stat s; - if (stat(rp, &s) == -1) { - fprintf(stderr, "Error: Cannot access %s\n", rp); - exit(1); - } - if (S_ISDIR(s.st_mode)) { - char *dir; - if (asprintf(&dir, "%s/", rp) == -1) - errExit("asprintf"); + // drop privileges + drop_privs(0); - print_directory(dir); - free(dir); - } - else { - char *split = strrchr(rp, '/'); - if (split) { - *split = '\0'; - char *rp2 = split + 1; - if (arg_debug) - printf("path %s, file %s\n", rp, rp2); - print_file_or_dir(rp, rp2, 1); - } + if (op == SANDBOX_FS_LS) + ls(fname1); + else + cat(fname1); +#ifdef HAVE_GCOV + __gcov_flush(); +#endif + _exit(0); } - free(rp); + // wait for the child to finish + int status = 0; + waitpid(child, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0); + else + exit(1); } // get file from sandbox and store it in the current directory @@ -303,10 +338,12 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) { // create a user-owned temporary file in /run/firejail directory char tmp_fname[] = "/run/firejail/tmpget-XXXXXX"; int fd = mkstemp(tmp_fname); - if (fd != -1) { - SET_PERMS_FD(fd, getuid(), getgid(), 0600); - close(fd); + if (fd == -1) { + fprintf(stderr, "Error: cannot create temporary file %s\n", tmp_fname); + exit(1); } + SET_PERMS_FD(fd, getuid(), getgid(), 0600); + close(fd); // copy the source file into the temporary file - we need to chroot pid_t child = fork(); -- cgit v1.2.3-54-g00ecf From 921b0c95a91d621786346dc6f6e88d8907852bc7 Mon Sep 17 00:00:00 2001 From: smitsohu Date: Wed, 19 Aug 2020 00:41:25 +0200 Subject: drop system(3) calls from sandbox.c --- src/firejail/sandbox.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 2314d5744..df33319f6 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -140,6 +140,20 @@ void set_apparmor(void) { } #endif +#ifdef HAVE_SECCOMP +void seccomp_debug(void) { + if (arg_debug == 0) + return; + + EUID_USER(); + printf("Seccomp directory:\n"); + ls(RUN_SECCOMP_DIR); + printf("Active seccomp files:\n"); + cat(RUN_SECCOMP_LIST); + EUID_ROOT(); +} +#endif + static void save_nogroups(void) { if (arg_nogroups == 0) return; @@ -197,32 +211,6 @@ static FILE *create_ready_for_join_file(void) { } } -#ifdef HAVE_SECCOMP -static void seccomp_debug(void) { - if (arg_debug == 0) - return; - - pid_t child = fork(); - if (child < 0) - errExit("fork"); - if (child == 0) { - // dropping privs before calling system(3) - drop_privs(1); - printf("Seccomp directory:\n"); - int rv = system("ls -l " RUN_SECCOMP_DIR); - (void) rv; - printf("Active seccomp files:\n"); - rv = system("cat " RUN_SECCOMP_LIST); - (void) rv; -#ifdef HAVE_GCOV - __gcov_flush(); -#endif - _exit(0); - } - waitpid(child, NULL, 0); -} -#endif - static void sandbox_if_up(Bridge *br) { assert(br); if (!br->configured) -- cgit v1.2.3-54-g00ecf From f473c959d2549ec0799bc6a7b7609c10b7f7e758 Mon Sep 17 00:00:00 2001 From: smitsohu Date: Wed, 19 Aug 2020 00:55:47 +0200 Subject: cat option --- src/firejail/main.c | 31 +++++++++++++++++++++++++++++++ src/firejail/usage.c | 3 +++ src/man/firejail.txt | 18 +++++++++++++++--- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/firejail/main.c b/src/firejail/main.c index 79e39b669..afd9af91d 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -811,6 +811,8 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { } // list directory contents + if (!arg_debug) + arg_quiet = 1; pid_t pid = require_pid(argv[i] + 5); sandboxfs(SANDBOX_FS_LS, pid, path, NULL); exit(0); @@ -818,6 +820,35 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else exit_err_feature("file transfer"); } + else if (strncmp(argv[i], "--cat=", 6) == 0) { + if (checkcfg(CFG_FILE_TRANSFER)) { + logargs(argc, argv); + if (arg_private_cwd) { + fprintf(stderr, "Error: --cat and --private-cwd options are mutually exclusive\n"); + exit(1); + } + + if ((i + 2) != argc) { + fprintf(stderr, "Error: invalid --cat option, path expected\n"); + exit(1); + } + char *path = argv[i + 1]; + invalid_filename(path, 0); // no globbing + if (strstr(path, "..")) { + fprintf(stderr, "Error: invalid file name %s\n", path); + exit(1); + } + + // write file contents to stdout + if (!arg_debug) + arg_quiet = 1; + pid_t pid = require_pid(argv[i] + 6); + sandboxfs(SANDBOX_FS_CAT, pid, path, NULL); + exit(0); + } + else + exit_err_feature("file transfer"); + } #endif else if (strncmp(argv[i], "--join=", 7) == 0) { if (checkcfg(CFG_JOIN) || getuid() == 0) { diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 4ab464289..be6715df4 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -47,6 +47,9 @@ static char *usage_str = " --caps.drop=capability,capability - blacklist capabilities filter.\n" " --caps.keep=capability,capability - whitelist capabilities filter.\n" " --caps.print=name|pid - print the caps filter.\n" +#ifdef HAVE_FILE_TRANSFER + " --cat=name|pid filename - print content of file from sandbox container.\n" +#endif " --cgroup=tasks-file - place the sandbox in the specified control group.\n" #ifdef HAVE_CHROOT " --chroot=dirname - chroot into directory.\n" diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 69cd4a7bc..f5f092bd9 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -272,6 +272,10 @@ $ firejail \-\-list .br $ firejail \-\-caps.print=3272 +.TP +\fB\-\-cat=name|pid filename +Print content of file from sandbox container, see FILE TRANSFER section for more details. + .TP \fB\-\-cgroup=tasks-file Place the sandbox in the specified control group. tasks-file is the full path of cgroup tasks file. @@ -344,7 +348,7 @@ $ firejail --dbus-system=filter --dbus-system.log --dbus-log=dbus.txt .TP \fB\-\-dbus-system=filter|none -Set system DBus sandboxing policy. +Set system DBus sandboxing policy. .br .br @@ -3028,6 +3032,10 @@ $ firejail --read-only=~/dir[1-4] These features allow the user to inspect the filesystem container of an existing sandbox and transfer files between the container and the host filesystem. +.TP +\fB\-\-cat=name|pid filename +Write content of a container file to standard out. + .TP \fB\-\-get=name|pid filename Retrieve the container file and store it on the host in the current working directory. @@ -3072,6 +3080,10 @@ $ firejail \-\-get=mybrowser ~/Downloads/xpra-clipboard.png $ firejail \-\-put=mybrowser xpra-clipboard.png ~/Downloads/xpra-clipboard.png .br +.br +$ firejail \-\-cat=mybrowser ~/.bashrc +.br + .SH MONITORING Option \-\-list prints a list of all sandboxes. The format for each process entry is as follows: @@ -3259,7 +3271,7 @@ Homepage: https://firejail.wordpress.com \&\flfirejail-profile\fR\|(5), \&\flfirejail-login\fR\|(5), \&\flfirejail-users\fR\|(5), -.UR https://github.com/netblue30/firejail/wiki +.UR https://github.com/netblue30/firejail/wiki .UE , -.UR https://github.com/netblue30/firejail +.UR https://github.com/netblue30/firejail .UE -- cgit v1.2.3-54-g00ecf From 450ae941a52e44a1acf68628fa8c3c9e293a8ea3 Mon Sep 17 00:00:00 2001 From: smitsohu Date: Thu, 20 Aug 2020 23:51:22 +0200 Subject: harden cat option --- src/firejail/ls.c | 8 +++++++- src/man/firejail.txt | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/firejail/ls.c b/src/firejail/ls.c index 7c7f8b75f..4d0a001b6 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c @@ -233,10 +233,16 @@ void cat(const char *path) { fprintf(stderr, "Error: %s is not a regular file\n", path); exit(1); } + bool tty = isatty(STDOUT_FILENO); int c; - while ((c = fgetc(fp)) != EOF) + while ((c = fgetc(fp)) != EOF) { + // file is untrusted + // replace control characters when printing to a terminal + if (tty && c != '\t' && c != '\n' && iscntrl((unsigned char) c)) + c = '?'; fputc(c, stdout); + } fflush(stdout); fclose(fp); } diff --git a/src/man/firejail.txt b/src/man/firejail.txt index abb73b5e2..3b7ba4e3d 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -3036,7 +3036,9 @@ and transfer files between the container and the host filesystem. .TP \fB\-\-cat=name|pid filename -Write content of a container file to standard out. +Write content of a container file to standard out. The container is specified by name or PID. +If standard out is a terminal, all ASCII control characters except new line and horizontal tab +are replaced. .TP \fB\-\-get=name|pid filename -- cgit v1.2.3-54-g00ecf