From 9be68f1b643c497163842aa1b3fa820c089d8b5f Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sat, 10 Oct 2015 14:57:44 -0400 Subject: --private-bin --- src/firejail/firejail.h | 3 + src/firejail/fs_bin.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ src/firejail/main.c | 9 +- src/firejail/sandbox.c | 2 + 4 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 src/firejail/fs_bin.c diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 2dd70c7f4..27e49ea38 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -28,6 +28,7 @@ #define MNT_DIR "/tmp/firejail/mnt" #define HOME_DIR "/tmp/firejail/mnt/home" #define ETC_DIR "/tmp/firejail/mnt/etc" +#define BIN_DIR "/tmp/firejail/mnt/bin" #define WHITELIST_HOME_DIR "/tmp/firejail/mnt/whome" #define DEFAULT_USER_PROFILE "generic" #define DEFAULT_ROOT_PROFILE "server" @@ -82,6 +83,7 @@ typedef struct config_t { char *home_private; // private home directory char *home_private_keep; // keep list for private home directory char *etc_private_keep; // keep list for private etc directory + char *bin_private_keep; // keep list for private etc directory char *cwd; // current working directory char *overlay_dir; @@ -169,6 +171,7 @@ extern int arg_doubledash; // double dash extern int arg_shell_none; // run the program directly without a shell extern int arg_private_dev; // private dev directory extern int arg_private_etc; // private etc directory +extern int arg_private_bin; // private bin directory extern int arg_scan; // arp-scan all interfaces extern int arg_whitelist; // whitelist commad diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c new file mode 100644 index 000000000..4b3292b6c --- /dev/null +++ b/src/firejail/fs_bin.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2014, 2015 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 + +static char *paths[] = { + "/bin", + "/sbin", + "/usr/bin", + "/usr/sbin", + NULL +}; + +// return 1 if found, 0 if not found +static char *check_dir_or_file(const char *name) { + assert(name); + struct stat s; + char *fname = NULL; + + int i = 0; + while (paths[i]) { + if (asprintf(&fname, "%s/%s", paths[i], name) == -1) + errExit("asprintf"); + if (arg_debug) + printf("Checking %s/%s\n", paths[i], name); + if (stat(fname, &s) == 0) + break; // file found + + free(fname); + fname = NULL; + i++; + } + + if (!fname) + return NULL; + + free(fname); + return paths[i]; +} + +void fs_check_bin_list(void) { + if (strstr(cfg.bin_private_keep, "..")) { + fprintf(stderr, "Error: invalid private bin list\n"); + exit(1); + } + + char *dlist = strdup(cfg.bin_private_keep); + if (!dlist) + errExit("strdup"); + + // create a new list removing files not found + char *newlist = malloc(strlen(dlist) + 1 + 1); // +',' + '\0' + if (!newlist) + errExit("malloc"); + *newlist = '\0'; + char *newlistptr = newlist; + + // check the first file + char *ptr = strtok(dlist, ","); + int notfound = 0; + if (check_dir_or_file(ptr)) { + // file found, copy the name in the new list + strcpy(newlistptr, ptr); + strcat(newlistptr, ","); + newlistptr += strlen(newlistptr); + } + else + notfound = 1; + + // check the rest of the list + while ((ptr = strtok(NULL, ",")) != NULL) { + if (check_dir_or_file(ptr)) { + // file found, copy the name in the new list + strcpy(newlistptr, ptr); + strcat(newlistptr, ","); + newlistptr += strlen(newlistptr); + } + else + notfound = 1; + } +printf("here %d: newlist #%s#\n", __LINE__, newlist); + + if (*newlist == '\0') { + fprintf(stderr, "Warning: no --private-bin list executable found, option disabled\n"); + cfg.bin_private_keep = NULL; + arg_private_bin = 0; + free(newlist); + } + else { + ptr = strrchr(newlist, ','); + assert(ptr); + *ptr = '\0'; + if (notfound) + fprintf(stderr, "Warning: not all executables from --private-bin list were found. The current list is %s\n", newlist); + + cfg.bin_private_keep = newlist; + } + + free(dlist); +} + +static void duplicate(char *fname) { + char *cmd; + char *path = check_dir_or_file(fname); + if (!path) + return; + + // expand path, just in case this is a symbolic link + char *full_path; + if (asprintf(&full_path, "%s/%s", path, fname) == -1) + errExit("asprintf"); + + char *actual_path = realpath(full_path, NULL); + if (actual_path) { + // copy the file + if (asprintf(&cmd, "cp -a %s %s/%s", actual_path, BIN_DIR, fname) == -1) + errExit("asprintf"); + if (arg_debug) + printf("%s\n", cmd); + if (system(cmd)) + errExit("system cp -a"); + free(cmd); + free(actual_path); + } + + free(full_path); +} + + +void fs_private_bin_list(void) { + char *private_list = cfg.bin_private_keep; + assert(private_list); + + // check bin paths + int i = 0; + while (paths[i]) { + struct stat s; + if (stat(paths[i], &s) == -1) { + fprintf(stderr, "Error: cannot find %s directory\n", paths[i]); + exit(1); + } + i++; + } + + // create /tmp/firejail/mnt/bin directory + fs_build_mnt_dir(); + int rv = mkdir(BIN_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(BIN_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(BIN_DIR, 0755) < 0) + errExit("chmod"); + + // copy the list of files in the new etc directory + // using a new child process without root privileges + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + if (arg_debug) + printf("Copying files in the new home:\n"); + + // elevate privileges - files in the new /bin directory belong to root + if (setreuid(0, 0) < 0) + errExit("setreuid"); + if (setregid(0, 0) < 0) + errExit("setregid"); + + // copy the list of files in the new home directory + char *dlist = strdup(private_list); + if (!dlist) + errExit("strdup"); + + char *ptr = strtok(dlist, ","); + duplicate(ptr); + + while ((ptr = strtok(NULL, ",")) != NULL) + duplicate(ptr); + free(dlist); + exit(0); + } + // wait for the child to finish + waitpid(child, NULL, 0); + + // moun-bind + i = 0; + while (paths[i]) { + if (arg_debug) + printf("Mount-bind %s on top of %s\n", BIN_DIR, paths[i]); + if (mount(BIN_DIR, paths[i], NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); + i++; + } +} + diff --git a/src/firejail/main.c b/src/firejail/main.c index a7eda3906..616b26894 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -82,6 +82,7 @@ int arg_doubledash = 0; // double dash int arg_shell_none = 0; // run the program directly without a shell int arg_private_dev = 0; // private dev directory int arg_private_etc = 0; // private etc directory +int arg_private_bin = 0; // private bin directory int arg_scan = 0; // arp-scan all interfaces int arg_whitelist = 0; // whitelist commad @@ -764,6 +765,12 @@ int main(int argc, char **argv) { fs_check_etc_list(); arg_private_etc = 1; } + else if (strncmp(argv[i], "--private-bin=", 14) == 0) { + // extract private etc dirname + cfg.bin_private_keep = argv[i] + 14; + fs_check_bin_list(); + arg_private_bin = 1; + } @@ -1029,7 +1036,7 @@ int main(int argc, char **argv) { } cfg.shell = argv[i] + 8; - if (is_dir(cfg.shell) || is_link(cfg.shell) || strstr(cfg.shell, "..")) { + if (is_dir(cfg.shell) || strstr(cfg.shell, "..")) { fprintf(stderr, "Error: invalid shell\n"); exit(1); } diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index eca4c2282..d3f92e51b 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -257,6 +257,8 @@ int sandbox(void* sandbox_arg) { fs_private_dev(); if (arg_private_etc) fs_private_etc_list(); + if (arg_private_bin) + fs_private_bin_list(); //**************************** // install trace -- cgit v1.2.3-54-g00ecf