whitelist support for /tmp

This commit is contained in:
netblue30 2015-11-12 08:33:34 -05:00
parent 14c94f18cc
commit da90151010
5 changed files with 146 additions and 58 deletions

View file

@ -8,6 +8,7 @@ blacklist /usr/bin/c9*
blacklist /usr/bin/c8*
blacklist /usr/bin/c++*
blacklist /usr/bin/ld
blacklist /usr/bin/gdb
# Valgrind
blacklist /usr/bin/valgrind*

View file

@ -9,3 +9,4 @@ blacklist ${HOME}/.gnupg
blacklist ${HOME}/.local/share/recently-used.xbel
blacklist ${HOME}/*.kdb
blacklist ${HOME}/*.key
blacklist /etc/shadow

View file

@ -39,7 +39,8 @@
#define DRI_DIR "/run/firejail/mnt/dri"
#define PULSE_DIR "/run/firejail/mnt/pulse"
#define DEVLOG_FILE "/run/firejail/mnt/devlog"
#define WHITELIST_HOME_DIR "/run/firejail/mnt/whome"
#define WHITELIST_HOME_DIR "/run/firejail/mnt/orig-home"
#define WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp"
#define XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority"
#define HOSTNAME_FILE "/run/firejail/mnt/hostname"
#define RESOLVCONF_FILE "/run/firejail/mnt/resolv.conf"
@ -86,8 +87,12 @@ typedef struct interface_t {
typedef struct profile_entry_t {
struct profile_entry_t *next;
char *data; // expanded name of the file
char *data; // command
// whitelist command parameters
char *link; // link name - set if the file is a link
unsigned home_dir:1; // whitelist in /home/user directory
unsigned tmp_dir:1; // whitelist in /tmp directory
}ProfileEntry;
typedef struct config_t {

View file

@ -56,26 +56,40 @@ static int mkpath(const char* path, mode_t mode) {
return 0;
}
static void whitelist_path(const char *path) {
static void whitelist_path(ProfileEntry *entry) {
assert(entry);
char *path = entry->data + 10;
assert(path);
// fname needs to start with /home/username
if (strncmp(path, cfg.homedir, strlen(cfg.homedir))) {
fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path);
exit(1);
}
const char *fname = path + strlen(cfg.homedir);
if (*fname == '\0') {
fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path);
exit(1);
}
const char *fname;
char *wfile;
if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1)
errExit("asprintf");
if (entry->home_dir) {
printf("here %d\n", __LINE__);
fname = path + strlen(cfg.homedir);
if (*fname == '\0') {
fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path);
exit(1);
}
if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1)
errExit("asprintf");
}
else if (entry->tmp_dir) {
printf("here %d\n", __LINE__);
fname = path + 4; // strlen("/tmp")
if (*fname == '\0') {
fprintf(stderr, "Error: file %s is not in /tmp directory, exiting...\n", path);
exit(1);
}
if (asprintf(&wfile, "%s/%s", WHITELIST_TMP_DIR, fname) == -1)
errExit("asprintf");
}
// check if the file exists
printf("here %d %s\n", __LINE__, wfile);
system("ls -l /run/firejail/mnt/orig-tmp");
struct stat s;
if (stat(wfile, &s) == 0) {
if (arg_debug)
@ -132,9 +146,12 @@ void fs_whitelist(void) {
ProfileEntry *entry = cfg.profile;
if (!entry)
return;
// realpath function will fail with ENOENT if the file is not found
// we need to expand the path before installing a new, empty home directory
char *new_name = NULL;
int home_dir = 0; // /home/user directory flag
int tmp_dir = 0; // /tmp directory flag
// verify whitelist files, extract symbolic links, etc.
while (entry) {
// handle only whitelist commands
if (strncmp(entry->data, "whitelist ", 10)) {
@ -142,10 +159,42 @@ void fs_whitelist(void) {
continue;
}
char *new_name = expand_home(entry->data + 10, cfg.homedir);
// replace ~/ or ${HOME} into /home/username
new_name = expand_home(entry->data + 10, cfg.homedir);
assert(new_name);
// extract the absolute path of the file
// realpath function will fail with ENOENT if the file is not found
char *fname = realpath(new_name, NULL);
if (!fname) {
// file not found, blank the entry in the list and continue
if (arg_debug)
printf("Removed whitelist path: %s\n", entry->data);
*entry->data = '\0';
continue;
}
// valid path referenced to filesystem root
if (*new_name != '/')
goto errexit;
// check for home directory or tmp directory
if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) {
entry->home_dir = 1;
home_dir = 1;
// both path and absolute path are under /home
if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0)
goto errexit;
}
else if (strncmp(new_name, "/tmp/", 5) == 0) {
entry->tmp_dir = 1;
tmp_dir = 1;
// both path and absolute path are under /tmp
if (strncmp(fname, "/tmp/", 5) != 0)
goto errexit;
}
else
goto errexit;
// mark symbolic links
if (is_link(new_name))
@ -153,44 +202,60 @@ void fs_whitelist(void) {
else
free(new_name);
if (fname) {
// change file name in entry->data
if (strcmp(fname, entry->data + 10) != 0) {
char *newdata;
if (asprintf(&newdata, "whitelist %s", fname) == -1)
errExit("asprintf");
entry->data = newdata;
if (arg_debug)
printf("Replaced whitelist path: %s\n", entry->data);
}
free(fname);
}
else {
// file not found, blank the entry in the list
// change file name in entry->data
if (strcmp(fname, entry->data + 10) != 0) {
char *newdata;
if (asprintf(&newdata, "whitelist %s", fname) == -1)
errExit("asprintf");
entry->data = newdata;
if (arg_debug)
printf("Removed whitelist path: %s\n", entry->data);
*entry->data = '\0';
printf("Replaced whitelist path: %s\n", entry->data);
}
free(fname);
entry = entry->next;
}
// create /tmp/firejail/mnt/whome directory
// create mount points
fs_build_mnt_dir();
int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
if (rv == -1)
errExit("mkdir");
if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0)
errExit("chown");
if (chmod(WHITELIST_HOME_DIR, 0755) < 0)
errExit("chmod");
// keep a copy of real home dir in /tmp/firejail/mnt/whome
if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
errExit("mount bind");
// start building the new home directory by mounting a tmpfs fielsystem
fs_private();
// /home/user
if (home_dir) {
// keep a copy of real home dir in WHITELIST_HOME_DIR
int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
if (rv == -1)
errExit("mkdir");
if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0)
errExit("chown");
if (chmod(WHITELIST_HOME_DIR, 0755) < 0)
errExit("chmod");
if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
errExit("mount bind");
// mount a tmpfs and initialize /home/user
fs_private();
}
// /tmp mountpoint
if (tmp_dir) {
// keep a copy of real /tmp directory in WHITELIST_TMP_DIR
int rv = mkdir(WHITELIST_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
if (rv == -1)
errExit("mkdir");
if (chown(WHITELIST_TMP_DIR, 0, 0) < 0)
errExit("chown");
if (chmod(WHITELIST_TMP_DIR, 0777) < 0)
errExit("chmod");
if (mount("/tmp", WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
errExit("mount bind");
// mount tmpfs on /tmp
if (arg_debug)
printf("Mounting tmpfs on /tmp directory\n");
if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
errExit("mounting tmpfs on /tmpt");
}
// go through profile rules again, and interpret whitelist commands
entry = cfg.profile;
@ -201,8 +266,9 @@ void fs_whitelist(void) {
continue;
}
//printf("here %d#%s#\n", __LINE__, entry->data);
// whitelist the real file
whitelist_path(entry->data + 10);
whitelist_path(entry);
// create the link if any
if (entry->link) {
@ -220,7 +286,21 @@ void fs_whitelist(void) {
entry = entry->next;
}
// mask the real home directory, currently mounted on /tmp/firejail/mnt/whome
if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
errExit("mount tmpfs");
// mask the real home directory, currently mounted on WHITELIST_HOME_DIR
if (home_dir) {
if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
errExit("mount tmpfs");
}
// mask the real /tmp directory, currently mounted on WHITELIST_TMP_DIR
if (tmp_dir) {
if (mount("tmpfs", WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
errExit("mount tmpfs");
}
return;
errexit:
fprintf(stderr, "Error: invalid whitelist path %s\n", new_name);
exit(1);
}

View file

@ -398,6 +398,7 @@ void profile_add(char *str) {
ProfileEntry *prf = malloc(sizeof(ProfileEntry));
if (!prf)
errExit("malloc");
memset(prf, 0, sizeof(ProfileEntry));
prf->next = NULL;
prf->data = str;