From 46334f0039100403572d7a6144d3a082cb648a42 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Wed, 12 Aug 2015 08:36:41 -0400 Subject: --private-etc option, issue #5 --- src/firejail/firejail.h | 6 ++ src/firejail/fs_etc.c | 146 +++++++++++++++++++++++++++++++++++++++++++ src/firejail/fs_home.c | 2 +- src/firejail/main.c | 7 +++ src/firejail/profile.c | 10 ++- src/firejail/sandbox.c | 2 + src/firejail/usage.c | 5 ++ src/man/firejail-profile.txt | 5 ++ src/man/firejail.txt | 13 ++++ 9 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/firejail/fs_etc.c diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 2ec6e54c9..5adabbcb3 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -28,6 +28,7 @@ #define MNT_DIR "/tmp/firejail/mnt" #define OVERLAY_DIR "/tmp/firejail/overlay" #define HOME_DIR "/tmp/firejail/mnt/home" +#define ETC_DIR "/tmp/firejail/mnt/etc" #define MAX_INCLUDE_LEVEL 6 // main.c @@ -67,6 +68,7 @@ typedef struct config_t { char *chrootdir; // chroot directory 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 *cwd; // current working directory // networking @@ -140,6 +142,7 @@ extern char *arg_netfilter_file; // netfilter file 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_scan; // arp-scan all interfaces extern int parent_to_child_fds[2]; @@ -350,5 +353,8 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in void network_shm_del_file(pid_t pid); void network_shm_set_file(pid_t pid); +// fs_etc.c +void fs_check_etc_list(void); +void fs_private_etc_list(void); #endif \ No newline at end of file diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c new file mode 100644 index 000000000..57b0b4f3e --- /dev/null +++ b/src/firejail/fs_etc.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 void check_dir_or_file(const char *name) { + assert(name); + struct stat s; + char *fname; + if (asprintf(&fname, "/etc/%s", name) == -1) + errExit("asprintf"); + if (arg_debug) + printf("Checking %s\n", fname); + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: file %s not found.\n", fname); + exit(1); + } + + // dir or regular file + if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { + free(fname); + return; + } + + if (!is_link(fname)) { + free(fname); + return; + } + + fprintf(stderr, "Error: invalid file type, %s.\n", fname); + exit(1); +} + +void fs_check_etc_list(void) { + if (strstr(cfg.etc_private_keep, "..")) { + fprintf(stderr, "Error: invalid private etc list\n"); + exit(1); + } + + char *dlist = strdup(cfg.etc_private_keep); + if (!dlist) + errExit("strdup"); + + char *ptr = strtok(dlist, ","); + check_dir_or_file(ptr); + while ((ptr = strtok(NULL, ",")) != NULL) + check_dir_or_file(ptr); + + free(dlist); +} + +static void duplicate(char *fname) { + char *cmd; + + // copy the file + if (asprintf(&cmd, "cp -a --parents /etc/%s %s", fname, MNT_DIR) == -1) + errExit("asprintf"); + if (arg_debug) + printf("%s\n", cmd); + if (system(cmd)) + errExit("system cp -a --parents"); + free(cmd); +} + + +void fs_private_etc_list(void) { + char *private_list = cfg.etc_private_keep; + assert(private_list); + + uid_t u = getuid(); + gid_t g = getgid(); + struct stat s; + if (stat("/etc", &s) == -1) { + fprintf(stderr, "Error: cannot find user /etc directory\n"); + exit(1); + } + + // create /tmp/firejail/mnt/etc directory + fs_build_mnt_dir(); + int rv = mkdir(ETC_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(ETC_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(ETC_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 /etc 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); + + if (arg_debug) + printf("Mount-bind %s on top of /etc\n", ETC_DIR); + if (mount(ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); + +} + diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 853e3930b..ca4691751 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c @@ -273,7 +273,7 @@ static void check_dir_or_file(const char *name) { if (asprintf(&fname, "%s/%s", cfg.homedir, name) == -1) errExit("asprintf"); if (arg_debug) - printf("***************Checking %s\n", fname); + printf("Checking %s\n", fname); if (stat(fname, &s) == -1) { fprintf(stderr, "Error: file %s not found.\n", fname); exit(1); diff --git a/src/firejail/main.c b/src/firejail/main.c index 78971aa86..1f4574c5c 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -81,6 +81,7 @@ char *arg_netfilter_file = NULL; // netfilter file 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_scan = 0; // arp-scan all interfaces int parent_to_child_fds[2]; @@ -699,6 +700,12 @@ int main(int argc, char **argv) { else if (strcmp(argv[i], "--private-dev") == 0) { arg_private_dev = 1; } + else if (strncmp(argv[i], "--private-etc=", 14) == 0) { + // extract private etc dirname + cfg.etc_private_keep = argv[i] + 14; + fs_check_etc_list(); + arg_private_etc = 1; + } diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 877428637..a6843cc6d 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -227,13 +227,21 @@ int profile_check_line(char *ptr, int lineno) { return 0; } - // private list of files and directories + // private home list of files and directories if (strncmp(ptr, "private.keep ", 13) == 0) { cfg.home_private_keep = ptr + 13; fs_check_home_list(); arg_private = 1; return 0; } + + // private /etc list of files and directories + if (strncmp(ptr, "private-etc ", 12) == 0) { + cfg.etc_private_keep = ptr + 12; + fs_check_etc_list(); + arg_private_etc = 1; + return 0; + } // filesystem bind if (strncmp(ptr, "bind ", 5) == 0) { diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3f5fb51fa..2beb31099 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -245,6 +245,8 @@ int sandbox(void* sandbox_arg) { if (arg_private_dev) fs_private_dev(); + if (arg_private_etc) + fs_private_etc_list(); //**************************** // install trace diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 71ae203ff..2beeddb70 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -152,8 +152,13 @@ void usage(void) { printf("\t\tfilesystem, and copy the files and directories in the list in\n"); printf("\t\tthe new home. All modifications are discarded when the sandbox\n"); printf("\t\tis closed.\n\n"); + printf("\t--private-dev - create a new /dev directory. Only null, full, zero, tty,\n"); printf("\t\tpst, ptms, random, urandom and shm devices are available.\n\n"); + + printf("\t--private-etc=file,directory - build a new /etc in a temporary\n"); + printf("\t\tfilesystem, and copy the files and directories in the list.\n"); + printf("\t\tAll modifications are discarded when the sandbox is closed.\n\n"); printf("\t--profile=filename - use a custom profile.\n\n"); printf("\t--read-only=dirname_or_filename - set directory or file read-only.\n\n"); diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index f85e10171..60d9c47c5 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -81,6 +81,11 @@ closed. .TP \f\private-dev Create a new /dev directory. Only null, full, zero, tty, pts, ptmx, random, urandom and shm devices are available. +.TP +\f\private-etc file,directory +Build a new /etc in a temporary +filesystem, and copy the files and directories in the list. +All modifications are discarded when the sandbox is closed. .SH Filters \fBcaps\fR and \fBseccomp\fR enable Linux capabilities and seccomp filters. Examples: diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 4e8d96d31..dbffe68ed 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -670,6 +670,19 @@ full null ptmx pts random shm tty urandom zero .br $ .TP +\fB\-\-private-etc=file,directory +Build a new /etc in a temporary +filesystem, and copy the files and directories in the list. +All modifications are discarded when the sandbox is closed. +.br + +.br +Example: +.br +$ firejail --private-etc=group,hostname,localtime, \\ +.br +nsswitch.conf,passwd,resolv.conf +.TP \fB\-\-profile=filename Load a custom profile from filename. For filename use an absolute path or a path relative to the current path. For more information, see PROFILES section below. -- cgit v1.2.3-54-g00ecf