From ba9c9699e79f2a2e3298b01f65799c96d2b0cd27 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Wed, 14 Jul 2021 11:28:58 -0400 Subject: Removing blacklisted files from top level /etc directory if the filse were blacklisted --- src/firejail/firejail.h | 4 +- src/firejail/fs.c | 13 +++++ src/firejail/fs_etc.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ src/firejail/fs_hostname.c | 103 ------------------------------------ src/firejail/sandbox.c | 2 +- 5 files changed, 143 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 6c9d70c0b..545573c08 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -156,6 +156,8 @@ typedef struct config_t { // filesystem ProfileEntry *profile; + ProfileEntry *profile_rebuild_etc; // blacklist files in /etc directory used by fs_rebuild_etc() + #define MAX_PROFILE_IGNORE 32 char *profile_ignore[MAX_PROFILE_IGNORE]; char *chrootdir; // chroot directory @@ -625,7 +627,6 @@ void fs_trace(void); // fs_hostname.c void fs_hostname(const char *hostname); -void fs_resolvconf(void); char *fs_check_hosts_file(const char *fname); void fs_store_hosts_file(void); void fs_mount_hosts_file(void); @@ -668,6 +669,7 @@ void fs_machineid(void); void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); +void fs_rebuild_etc(void); // no_sandbox.c int check_namespace_virt(void); diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 0e26eb505..dd170ad19 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -162,6 +162,19 @@ static void disable_file(OPERATION op, const char *filename) { fs_logger2("blacklist", fname); else fs_logger2("blacklist-nolog", fname); + + // files in /etc will be reprocessed during /etc rebuild + if (strncmp(fname, "/etc/", 5) == 0) { + ProfileEntry *prf = malloc(sizeof(ProfileEntry)); + if (!prf) + errExit("malloc"); + memset(prf, 0, sizeof(ProfileEntry)); + prf->data = strdup(fname); + if (!prf->data) + errExit("strdup"); + prf->next = cfg.profile_rebuild_etc; + cfg.profile_rebuild_etc = prf; + } } } else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index b0e1e1bf1..76054b485 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c @@ -24,6 +24,7 @@ #include #include #include +#include // spoof /etc/machine_id void fs_machineid(void) { @@ -250,3 +251,128 @@ void fs_private_dir_list(const char *private_dir, const char *private_run_dir, c fs_private_dir_mount(private_dir, private_run_dir); fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); } + +void fs_rebuild_etc(void) { + int have_dhcp = 1; + if (cfg.dns1 == NULL && !any_dhcp()) + have_dhcp = 0; + + if (arg_debug) + printf("rebuilding /etc directory\n"); + if (mkdir(RUN_DNS_ETC, 0755)) + errExit("mkdir"); + selinux_relabel_path(RUN_DNS_ETC, "/etc"); + fs_logger("tmpfs /etc"); + + DIR *dir = opendir("/etc"); + if (!dir) + errExit("opendir"); + + struct stat s; + struct dirent *entry; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // skip files in cfg.profile_rebuild_etc list + // these files are already blacklisted + { + ProfileEntry *prf = cfg.profile_rebuild_etc; + int found = 0; + while (prf) { + if (strcmp(entry->d_name, prf->data + 5) == 0) { // 5 is strlen("/etc/") + found = 1; + break; + } + prf = prf->next; + } + if (found) + continue; + } + + // for resolv.conf we might have to create a brand new file later + if (have_dhcp && + (strcmp(entry->d_name, "resolv.conf") == 0 || + strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0)) + continue; +// printf("linking %s\n", entry->d_name); + + char *src; + if (asprintf(&src, "/etc/%s", entry->d_name) == -1) + errExit("asprintf"); + if (stat(src, &s) != 0) { + free(src); + continue; + } + + char *dest; + if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1) + errExit("asprintf"); + + int symlink_done = 0; + if (is_link(src)) { + char *rp =realpath(src, NULL); + if (rp == NULL) { + free(src); + free(dest); + continue; + } + if (symlink(rp, dest)) + errExit("symlink"); + else + symlink_done = 1; + } + else if (S_ISDIR(s.st_mode)) + create_empty_dir_as_root(dest, s.st_mode); + else + create_empty_file_as_root(dest, s.st_mode); + + // bind-mount src on top of dest + if (!symlink_done) { + if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind mirroring /etc"); + } + fs_logger2("clone", src); + + free(src); + free(dest); + } + closedir(dir); + + // mount bind our private etc directory on top of /etc + if (arg_debug) + printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC); + if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind mirroring /etc"); + fs_logger("mount /etc"); + + if (have_dhcp == 0) + return; + + if (arg_debug) + printf("Creating a new /etc/resolv.conf file\n"); + FILE *fp = fopen("/etc/resolv.conf", "wxe"); + if (!fp) { + fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); + exit(1); + } + + if (cfg.dns1) { + if (any_dhcp()) + fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); + fprintf(fp, "nameserver %s\n", cfg.dns1); + } + if (cfg.dns2) + fprintf(fp, "nameserver %s\n", cfg.dns2); + if (cfg.dns3) + fprintf(fp, "nameserver %s\n", cfg.dns3); + if (cfg.dns4) + fprintf(fp, "nameserver %s\n", cfg.dns4); + + // mode and owner + SET_PERMS_STREAM(fp, 0, 0, 0644); + + fclose(fp); + + fs_logger("create /etc/resolv.conf"); +} diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c index 80046f7ae..1a9a78ceb 100644 --- a/src/firejail/fs_hostname.c +++ b/src/firejail/fs_hostname.c @@ -88,109 +88,6 @@ errexit: exit(1); } -void fs_resolvconf(void) { - if (cfg.dns1 == NULL && !any_dhcp()) - return; - - if (arg_debug) - printf("mirroring /etc directory\n"); - if (mkdir(RUN_DNS_ETC, 0755)) - errExit("mkdir"); - selinux_relabel_path(RUN_DNS_ETC, "/etc"); - fs_logger("tmpfs /etc"); - - DIR *dir = opendir("/etc"); - if (!dir) - errExit("opendir"); - - struct stat s; - struct dirent *entry; - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - // for resolv.conf we create a brand new file - if (strcmp(entry->d_name, "resolv.conf") == 0 || - strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0) - continue; -// printf("linking %s\n", entry->d_name); - - char *src; - if (asprintf(&src, "/etc/%s", entry->d_name) == -1) - errExit("asprintf"); - if (stat(src, &s) != 0) { - free(src); - continue; - } - - char *dest; - if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1) - errExit("asprintf"); - - int symlink_done = 0; - if (is_link(src)) { - char *rp =realpath(src, NULL); - if (rp == NULL) { - free(src); - free(dest); - continue; - } - if (symlink(rp, dest)) - errExit("symlink"); - else - symlink_done = 1; - } - else if (S_ISDIR(s.st_mode)) - create_empty_dir_as_root(dest, s.st_mode); - else - create_empty_file_as_root(dest, s.st_mode); - - // bind-mount src on top of dest - if (!symlink_done) { - if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0) - errExit("mount bind mirroring /etc"); - } - fs_logger2("clone", src); - - free(src); - free(dest); - } - closedir(dir); - - // mount bind our private etc directory on top of /etc - if (arg_debug) - printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC); - if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) - errExit("mount bind mirroring /etc"); - fs_logger("mount /etc"); - - if (arg_debug) - printf("Creating a new /etc/resolv.conf file\n"); - FILE *fp = fopen("/etc/resolv.conf", "wxe"); - if (!fp) { - fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); - exit(1); - } - - if (cfg.dns1) { - if (any_dhcp()) - fwarning("network setup uses DHCP, nameservers will likely be overwritten\n"); - fprintf(fp, "nameserver %s\n", cfg.dns1); - } - if (cfg.dns2) - fprintf(fp, "nameserver %s\n", cfg.dns2); - if (cfg.dns3) - fprintf(fp, "nameserver %s\n", cfg.dns3); - if (cfg.dns4) - fprintf(fp, "nameserver %s\n", cfg.dns4); - - // mode and owner - SET_PERMS_STREAM(fp, 0, 0, 0644); - - fclose(fp); - - fs_logger("create /etc/resolv.conf"); -} - char *fs_check_hosts_file(const char *fname) { assert(fname); invalid_filename(fname, 0); // no globbing diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index fd359c39e..59ddfb855 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -1043,7 +1043,7 @@ int sandbox(void* sandbox_arg) { //**************************** // set dns //**************************** - fs_resolvconf(); + fs_rebuild_etc(); //**************************** // start dhcp client -- cgit v1.2.3-70-g09d2