From 178374e6cfdea53ca133c4a36c53cf2c042f0992 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Wed, 1 Mar 2017 14:37:45 -0500 Subject: merge #1100 from zackw: xvfb support --- src/firejail/checkcfg.c | 24 ++++++ src/firejail/firejail.h | 2 + src/firejail/main.c | 8 ++ src/firejail/profile.c | 20 +++++ src/firejail/x11.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 254 insertions(+) (limited to 'src') diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index dff892ea3..56ab7c932 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c @@ -27,6 +27,8 @@ static int initialized = 0; static int cfg_val[CFG_MAX]; char *xephyr_screen = "800x600"; char *xephyr_extra_params = ""; +char *xvfb_screen = "800x600x24"; +char *xvfb_extra_params = ""; char *netfilter_default = NULL; int checkcfg(int val) { @@ -234,6 +236,28 @@ int checkcfg(int val) { errExit("strdup"); } + // Xvfb screen size + else if (strncmp(ptr, "xvfb-screen ", 12) == 0) { + // expecting three numbers separated by x's + unsigned int n1; + unsigned int n2; + unsigned int n3; + int rv = sscanf(ptr + 12, "%ux%ux%u", &n1, &n2, &n3); + if (rv != 3) + goto errout; + if (asprintf(&xvfb_screen, "%ux%ux%u", n1, n2, n3) == -1) + errExit("asprintf"); + } + + // Xvfb extra parameters + else if (strncmp(ptr, "xvfb-extra-params ", 18) == 0) { + if (*xvfb_extra_params != '\0') + goto errout; + xvfb_extra_params = strdup(ptr + 18); + if (!xvfb_extra_params) + errExit("strdup"); + } + // quiet by default else if (strncmp(ptr, "quiet-by-default ", 17) == 0) { if (strcmp(ptr + 17, "yes") == 0) diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index cafaf8c4f..aec6f3de4 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -684,6 +684,8 @@ enum { }; extern char *xephyr_screen; extern char *xephyr_extra_params; +extern char *xvfb_screen; +extern char *xvfb_extra_params; extern char *netfilter_default; int checkcfg(int val); void print_compiletime_support(void); diff --git a/src/firejail/main.c b/src/firejail/main.c index 978ca8cd2..5951e5d16 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -349,6 +349,14 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { else exit_err_feature("x11"); } + else if (strcmp(argv[i], "--x11=xvfb") == 0) { + if (checkcfg(CFG_X11)) { + x11_start_xvfb(argc, argv); + exit(0); + } + else + exit_err_feature("x11"); + } #endif #ifdef HAVE_NETWORK else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 271176fcd..9f6688d4a 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -719,6 +719,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)) { @@ -738,6 +739,25 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } + if (strcmp(ptr, "x11 xvfb") == 0) { +#ifdef HAVE_X11 + if (checkcfg(CFG_X11)) { + char *x11env = getenv("FIREJAIL_X11"); + if (x11env && strcmp(x11env, "yes") == 0) { + return 0; + } + else { + // start x11 + x11_start_xvfb(cfg.original_argc, cfg.original_argv); + exit(0); + } + } + else + warning_feature_disabled("x11"); +#endif + return 0; + } + if (strcmp(ptr, "x11") == 0) { #ifdef HAVE_X11 if (checkcfg(CFG_X11)) { diff --git a/src/firejail/x11.c b/src/firejail/x11.c index b668c1c9c..f66848b7c 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c @@ -200,6 +200,206 @@ static int random_display_number(void) { #ifdef HAVE_X11 + +void x11_start_xvfb(int argc, char **argv) { + EUID_ASSERT(); + int i; + struct stat s; + pid_t jail = 0; + pid_t server = 0; + + setenv("FIREJAIL_X11", "yes", 1); + + // mever try to run X servers as root!!! + if (getuid() == 0) { + fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n"); + exit(1); + } + drop_privs(0); + + // check xephyr + if (!program_in_path("Xvfb")) { + fprintf(stderr, "\nError: xvfb program was not found in /usr/bin directory, please install it:\n"); + fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xvfb\n"); + fprintf(stderr, " Arch: sudo pacman -S xorg-server-xvfb\n"); + exit(0); + } + + int display = random_display_number(); + char *display_str; + if (asprintf(&display_str, ":%d", display) == -1) + errExit("asprintf"); + + assert(xvfb_screen); + + char *server_argv[256] = { "Xvfb", display_str, "-screen", "0", xvfb_screen }; // rest initialyzed to NULL + unsigned pos = 0; + while (server_argv[pos] != NULL) pos++; + assert(xvfb_extra_params); // should be "" if empty + + // parse xvfb_extra_params + // very basic quoting support + char *temp = strdup(xephyr_extra_params); + if (*xephyr_extra_params != '\0') { + if (!temp) + errExit("strdup"); + bool dquote = false; + bool squote = false; + for (i = 0; i < (int) strlen(xvfb_extra_params); i++) { + if (temp[i] == '\"') { + dquote = !dquote; + if (dquote) temp[i] = '\0'; // replace closing quote by \0 + } + if (temp[i] == '\'') { + squote = !squote; + if (squote) temp[i] = '\0'; // replace closing quote by \0 + } + if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; + if (dquote && squote) { + fprintf(stderr, "Error: mixed quoting found while parsing xvfb_extra_params\n"); + exit(1); + } + } + if (dquote) { + fprintf(stderr, "Error: unclosed quote found while parsing xephyr_extra_params\n"); + exit(1); + } + + for (i = 0; i < (int) strlen(xvfb_extra_params)-1; i++) { + if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) { + fprintf(stderr, "Error: arg count limit exceeded while parsing xvfb_extra_params\n"); + exit(1); + } + if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) server_argv[pos++] = temp + i + 2; + else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; + } + } + + server_argv[pos++] = NULL; + + assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun + assert(server_argv[pos-1] == NULL); // last element is null + + if (arg_debug) { + size_t i = 0; + printf("xvfb server:"); + while (server_argv[i]!=NULL) { + printf(" \"%s\"", server_argv[i]); + i++; + } + putchar('\n'); + } + + // remove --x11 arg + char *jail_argv[argc+2]; + int j = 0; + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--x11") == 0) + continue; + if (strcmp(argv[i], "--x11=xpra") == 0) + continue; + if (strcmp(argv[i], "--x11=xephyr") == 0) + continue; + if (strcmp(argv[i], "--x11=xvfb") == 0) + continue; + jail_argv[j] = argv[i]; + j++; + } + jail_argv[j] = NULL; + + assert(j < argc+2); // no overrun + + if (arg_debug) { + size_t i = 0; + printf("xvfb client:"); + while (jail_argv[i]!=NULL) { + printf(" \"%s\"", jail_argv[i]); + i++; + } + putchar('\n'); + } + + server = fork(); + if (server < 0) + errExit("fork"); + if (server == 0) { + if (arg_debug) + printf("Starting xvfb...\n"); + + // running without privileges - see drop_privs call above + assert(getenv("LD_PRELOAD") == NULL); + execvp(server_argv[0], server_argv); + perror("execvp"); + _exit(1); + } + + if (arg_debug) + printf("xephyr server pid %d\n", server); + + // check X11 socket + char *fname; + if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1) + errExit("asprintf"); + int n = 0; + // wait for x11 server to start + while (++n < 10) { + sleep(1); + if (stat(fname, &s) == 0) + break; + }; + + if (n == 10) { + fprintf(stderr, "Error: failed to start xephyr\n"); + exit(1); + } + free(fname); + + if (arg_debug) { + printf("X11 sockets: "); fflush(0); + int rv = system("ls /tmp/.X11-unix"); + (void) rv; + } + + setenv("DISPLAY", display_str, 1); + // run attach command + jail = fork(); + if (jail < 0) + errExit("fork"); + if (jail == 0) { + if (!arg_quiet) + printf("\n*** Attaching to Xvfb display %d ***\n\n", display); + + // running without privileges - see drop_privs call above + assert(getenv("LD_PRELOAD") == NULL); + execvp(jail_argv[0], jail_argv); + perror("execvp"); + _exit(1); + } + + // cleanup + free(display_str); + free(temp); + + // wait for either server or jail termination + pid_t pid = wait(NULL); + + // see which process terminated and kill other + if (pid == server) { + kill(jail, SIGTERM); + } else if (pid == jail) { + kill(server, SIGTERM); + } + + // without this closing Xephyr window may mess your terminal: + // "monitoring" process will release terminal before + // jail process ends and releases terminal + wait(NULL); // fulneral + + exit(0); +} + + + //$ Xephyr -ac -br -noreset -screen 800x600 :22 & //$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox void x11_start_xephyr(int argc, char **argv) { -- cgit v1.2.3-70-g09d2