From 4e86686da02b39e650edbb80e2fc837d2ccd3a33 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Tue, 23 Feb 2016 08:43:05 -0500 Subject: x11 work --- src/firejail/firejail.h | 5 + src/firejail/main.c | 4 + src/firejail/sandbox.c | 4 +- src/firejail/x11.c | 217 ++++++++++++++++++++++++++++++++++++++++++ test/chromium-x11.exp | 75 +++++++++++++++ test/firefox-x11.exp | 68 +++++++++++++ test/test-apps-x11.sh | 29 ++++++ test/test.sh | 1 + test/transmission-gtk-x11.exp | 74 ++++++++++++++ 9 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 src/firejail/x11.c create mode 100755 test/chromium-x11.exp create mode 100755 test/firefox-x11.exp create mode 100755 test/test-apps-x11.sh create mode 100755 test/transmission-gtk-x11.exp diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 4babc58e7..acb49d246 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -46,6 +46,7 @@ #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" #define RUN_DEVLOG_FILE "/run/firejail/mnt/devlog" +#define RUN_WHITELIST_X11_DIR "/run/firejail/mnt/orig-x11" #define RUN_WHITELIST_HOME_DIR "/run/firejail/mnt/orig-home" // default home directory masking #define RUN_WHITELIST_HOME_USER_DIR "/run/firejail/mnt/orig-home-user" // home directory whitelisting #define RUN_WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp" @@ -520,5 +521,9 @@ char **build_paths(void); // fs_mkdir.c void fs_mkdir(const char *name); +// x11.c +void fs_x11(void); +void x11_start(int argc, char **argv); + #endif diff --git a/src/firejail/main.c b/src/firejail/main.c index f02da66aa..2a5ded984 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -263,6 +263,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { #endif exit(0); } + else if (strcmp(argv[i], "--x11") == 0) { + x11_start(argc, argv); + exit(0); + } #ifdef HAVE_NETWORK else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { logargs(argc, argv); diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 1ba655301..d43e1dac1 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -588,13 +588,15 @@ int sandbox(void* sandbox_arg) { errno = 0; int rv = nice(cfg.nice); (void) rv; -printf("nice rv %d\n", rv); if (errno) { fprintf(stderr, "Warning: cannot set nice value\n"); errno = 0; } } + // clean /tmp/.X11-unix sockets + fs_x11(); + //**************************** // set security filters //**************************** diff --git a/src/firejail/x11.c b/src/firejail/x11.c new file mode 100644 index 000000000..5e8fb9bbd --- /dev/null +++ b/src/firejail/x11.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2014-2016 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * 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 +#include +#include +#include +#include +#include + +void fs_x11(void) { + // extract display + char *d = getenv("DISPLAY"); + if (!d) + return; + + int display; + int rv = sscanf(d, ":%d", &display); + if (rv != 1) + return; + if (arg_debug) + printf("DISPLAY %s, %d\n", d, display); + + char *x11file; + if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) + errExit("asprintf"); + struct stat s; + if (stat(x11file, &s) == -1) + return; + + // keep a copy of real /tmp/.X11-unix directory in WHITELIST_TMP_DIR + rv = mkdir(RUN_WHITELIST_X11_DIR, 1777); + if (rv == -1) + errExit("mkdir"); + if (chown(RUN_WHITELIST_X11_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(RUN_WHITELIST_X11_DIR, 1777) < 0) + errExit("chmod"); + + if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); + + // mount tmpfs on /tmp/.X11-unix + if (arg_debug || arg_debug_whitelists) + printf("Mounting tmpfs on /tmp/.X11-unix directory\n"); + if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) + errExit("mounting tmpfs on /tmp"); + fs_logger("mount tmpfs on /tmp/.X11-unix"); + + // create an empty file + FILE *fp = fopen(x11file, "w"); + if (!fp) { + fprintf(stderr, "Error: cannot create empty file in x11 directory\n"); + exit(1); + } + fclose(fp); + + // set file properties + if (chown(x11file, s.st_uid, s.st_gid) < 0) + errExit("chown"); + if (chmod(x11file, s.st_mode) < 0) + errExit("chmod"); + + // mount + char *wx11file; + if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) + errExit("asprintf"); + if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); + fs_logger2("whitelist", x11file); + + free(x11file); + free(wx11file); + + // block access to RUN_WHITELIST_X11_DIR + if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, "none", MS_BIND, "mode=400,gid=0") == -1) + errExit("mount"); + fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); +} + + +void x11_start(int argc, char **argv) { + EUID_ASSERT(); + int i; + struct stat s; + pid_t client = 0; + pid_t server = 0; + + // check xpra + if (stat("/usr/bin/xpra", &s) == -1) { + fprintf(stderr, "\nError: Xpra program was not found in /usr/bin directory, please install it:\n"); + fprintf(stderr, " Arch: sudo pacman -S xpra\n"); + fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); + fprintf(stderr, " Fedora/CentOS: sudo yum install xpra\n"); + fprintf(stderr, " Gentoo: sudo emerge xpra\n\n"); + exit(0); + } + + int display; + int found = 1; + for (i = 0; i < 100; i++) { + display = rand() % 1024; + char *fname; + if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) + errExit("asprintf"); + if (stat(fname, &s) == -1) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "Error: cannot pick up a random X11 display number, exiting...\n"); + exit(1); + } + + // build the start command + int len = 50; // xpra start... + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; // + ' ' + } + + char *cmd1 = malloc(len + 1); // + '\0' + if (!cmd1) + errExit("malloc"); + + sprintf(cmd1, "xpra start :%d --exit-with-children --start-child=\"", display); + char *ptr = cmd1 + strlen(cmd1); + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--x11") == 0) + continue; + ptr += sprintf(ptr, "%s ", argv[i]); + } + sprintf(ptr, "\""); + if (arg_debug) + printf("xpra server: %s\n", cmd1); + + // build the attach command + char *cmd2; + if (asprintf(&cmd2, "xpra attach :%d", display) == -1) + errExit("asprintf"); + if (arg_debug) + printf("xpra client: %s\n", cmd2); + + signal(SIGHUP,SIG_IGN); // fix sleep(1`) below + server = fork(); + if (server < 0) + errExit("fork"); + if (server == 0) { + if (arg_debug) + printf("Starting xpra...\n"); + + char *a[4]; + a[0] = "/bin/bash"; + a[1] = "-c"; + a[2] = cmd1; + a[3] = NULL; + + execvp(a[0], a); + perror("execvp"); + exit(1); + } + sleep(1); + if (arg_debug) { + printf("X11 sockets: "); fflush(0); + int rv = system("ls /tmp/.X11-unix"); + (void) rv; + } + + // check X11 socket + char *fname; + if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) + errExit("asprintf"); + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: failed to start xpra\n"); + exit(1); + } + + // run attach command + client = fork(); + if (client < 0) + errExit("fork"); + if (client == 0) { + printf("\n*** Attaching to xpra display %d ***\n\n", display); + char *a[4]; + a[0] = "/bin/bash"; + a[1] = "-c"; + a[2] = cmd2; + a[3] = NULL; + + execvp(a[0], a); + perror("execvp"); + exit(1); + } + sleep(1); + + if (!arg_quiet) + printf("Xpra server pid %d, client pid %d\n", server, client); + exit(0); +} diff --git a/test/chromium-x11.exp b/test/chromium-x11.exp new file mode 100755 index 000000000..0d8a5dfb3 --- /dev/null +++ b/test/chromium-x11.exp @@ -0,0 +1,75 @@ +#!/usr/bin/expect -f + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --x11 --net=br0 chromium www.gentoo.org\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "chromium" +} +sleep 1 + +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 5.0\n";exit} + "chromium" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "Seccomp: 0" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 6.0\n";exit} + "chromium" +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd:" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "fffffffff" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + + +puts "\nall done\n" + diff --git a/test/firefox-x11.exp b/test/firefox-x11.exp new file mode 100755 index 000000000..c82408896 --- /dev/null +++ b/test/firefox-x11.exp @@ -0,0 +1,68 @@ +#!/usr/bin/expect -f + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --x11 --net=br0 firefox www.gentoo.org\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "firefox" {puts "firefox detected\n";} + "iceweasel" {puts "iceweasel detected\n";} +} +sleep 1 +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + " firefox" {puts "firefox detected\n";} + " iceweasel" {puts "iceweasel detected\n";} +} +expect { + timeout {puts "TESTING ERROR 5.1 (seccomp)\n";exit} + "Seccomp: 2" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + " firefox" {puts "firefox detected\n";} + " iceweasel" {puts "iceweasel detected\n";} +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd:" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "0000000000000000" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + +puts "\nall done\n" + diff --git a/test/test-apps-x11.sh b/test/test-apps-x11.sh new file mode 100755 index 000000000..6521fa2b0 --- /dev/null +++ b/test/test-apps-x11.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +which firefox +if [ "$?" -eq 0 ]; +then + echo "TESTING: firefox x11" + ./firefox-x11.exp +else + echo "TESTING: firefox not found" +fi + +which chromium +if [ "$?" -eq 0 ]; +then + echo "TESTING: chromium x11" + ./chromium-x11.exp +else + echo "TESTING: chromium not found" +fi + +which transmission-gtk +if [ "$?" -eq 0 ]; +then + echo "TESTING: transmission-gtk x11" + ./transmission-gtk.exp +else + echo "TESTING: transmission-gtk not found" +fi + diff --git a/test/test.sh b/test/test.sh index ddb8431c8..d7e9e2aed 100755 --- a/test/test.sh +++ b/test/test.sh @@ -166,6 +166,7 @@ else fi ./test-apps.sh +./test-apps-x11.sh echo "TESTING: PID (pid.exp)" ./pid.exp diff --git a/test/transmission-gtk-x11.exp b/test/transmission-gtk-x11.exp new file mode 100755 index 000000000..6192b277c --- /dev/null +++ b/test/transmission-gtk-x11.exp @@ -0,0 +1,74 @@ +#!/usr/bin/expect -f + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --net=br0 --x11 transmission-gtk\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "transmission-gtk" +} +sleep 1 + +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 5.0\n";exit} + "transmission-gtk" +} +expect { + timeout {puts "TESTING ERROR 5.1 (seccomp)\n";exit} + "Seccomp: 2" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 6.0\n";exit} + "transmission-gtk" +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "0000000000000000" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + +puts "\nall done\n" + -- cgit v1.2.3-70-g09d2