// disable firejail configuration in /etc/firejail and in ~/.config/firejail static void disable_firejail_config(void) { struct stat s; if (stat("/etc/firejail", &s) == 0) disable_file(BLACKLIST_FILE, "/etc/firejail"); char *fname; if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) errExit("asprintf"); if (stat(fname, &s) == 0) disable_file(BLACKLIST_FILE, fname); if (stat("/usr/local/etc/firejail", &s) == 0) disable_file(BLACKLIST_FILE, "/usr/local/etc/firejail"); if (strcmp(PREFIX, "/usr/local")) { if (asprintf(&fname, "%s/etc/firejail", PREFIX) == -1) errExit("asprintf"); if (stat(fname, &s) == 0) disable_file(BLACKLIST_FILE, fname); } free(fname); }
// disable pulseaudio socket void pulseaudio_disable(void) { // blacklist user config directory disable_file(cfg.homedir, ".config/pulse"); // blacklist any pulse* file in /tmp directory DIR *dir; if (!(dir = opendir("/tmp"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/tmp"))) { fprintf(stderr, "Warning: cannot open /tmp directory. PulseAudio sockets are not disabled\n"); return; } } struct dirent *entry; while ((entry = readdir(dir))) { if (strncmp(entry->d_name, "pulse-", 6) == 0) { disable_file("/tmp", entry->d_name); } } closedir(dir); // blacklist XDG_RUNTIME_DIR char *name = getenv("XDG_RUNTIME_DIR"); if (name) disable_file(name, "pulse/native"); }
// disable firejail configuration in /etc/firejail and in ~/.config/firejail static void disable_firejail_config(void) { struct stat s; if (stat("/etc/firejail", &s) == 0) disable_file(BLACKLIST_FILE, "/etc/firejail"); char *fname; if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) errExit("asprintf"); if (stat(fname, &s) == 0) disable_file(BLACKLIST_FILE, fname); if (stat("/usr/local/etc/firejail", &s) == 0) disable_file(BLACKLIST_FILE, "/usr/local/etc/firejail"); if (strcmp(PREFIX, "/usr/local")) { if (asprintf(&fname, "%s/etc/firejail", PREFIX) == -1) errExit("asprintf"); if (stat(fname, &s) == 0) disable_file(BLACKLIST_FILE, fname); } free(fname); // disable run time information if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0) disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); }
static void globbing(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) { assert(fname); assert(emptydir); assert(emptyfile); // filename globbing: expand * macro and continue processing for every single file if (strchr(fname, '*')) { glob_t globbuf; globbuf.gl_offs = 0; glob(fname, GLOB_DOOFFS, NULL, &globbuf); int i; for (i = 0; i < globbuf.gl_pathc; i++) { assert(globbuf.gl_pathv[i]); disable_file(op, globbuf.gl_pathv[i], emptydir, emptyfile); } } else disable_file(op, fname, emptydir, emptyfile); }
// Treat pattern as a shell glob pattern and blacklist matching files static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { assert(pattern); glob_t globbuf; // Profiles contain blacklists for files that might not exist on a user's machine. // GLOB_NOCHECK makes that okay. int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf); if (globerr) { fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); exit(1); } size_t i, j; for (i = 0; i < globbuf.gl_pathc; i++) { char *path = globbuf.gl_pathv[i]; assert(path); // /home/me/.* can glob to /home/me/.. which would blacklist /home/ const char *base = gnu_basename(path); if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) continue; // noblacklist is expected to be short in normal cases, so stupid and correct brute force is okay bool okay_to_blacklist = true; for (j = 0; j < noblacklist_len; j++) { int result = fnmatch(noblacklist[j], path, FNM_PATHNAME); if (result == FNM_NOMATCH) continue; else if (result == 0) { okay_to_blacklist = false; break; } else { fprintf(stderr, "Error: failed to compare path %s with pattern %s\n", path, noblacklist[j]); exit(1); } } if (okay_to_blacklist) disable_file(op, path); else if (arg_debug) printf("Not blacklist %s\n", path); } globfree(&globbuf); }
static void sanitize_home(void) { // extract current /home directory data struct dirent *dir; DIR *d = opendir("/home"); if (d == NULL) return; char *emptydir = create_empty_dir(); while ((dir = readdir(d))) { if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0) continue; if (dir->d_type == DT_DIR ) { // get properties struct stat s; char *name; if (asprintf(&name, "/home/%s", dir->d_name) == -1) continue; if (stat(name, &s) == -1) continue; if (S_ISLNK(s.st_mode)) { free(name); continue; } if (strcmp(name, cfg.homedir) == 0) continue; // printf("directory %u %u:%u #%s#\n", // s.st_mode, // s.st_uid, // s.st_gid, // name); // disable directory disable_file(BLACKLIST_FILE, name, emptydir, "not used"); free(name); } } closedir(d); }
// mount /proc and /sys directories void fs_proc_sys_dev_boot(void) { struct stat s; if (arg_debug) printf("Remounting /proc and /proc/sys filesystems\n"); if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) errExit("mounting /proc"); fs_logger("remount /proc"); // remount /proc/sys readonly if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0) errExit("mounting /proc/sys"); if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0) errExit("mounting /proc/sys"); fs_logger("read-only /proc/sys"); /* Mount a version of /sys that describes the network namespace */ if (arg_debug) printf("Remounting /sys directory\n"); if (umount2("/sys", MNT_DETACH) < 0) fprintf(stderr, "Warning: failed to unmount /sys\n"); else { if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) fprintf(stderr, "Warning: failed to mount /sys\n"); else fs_logger("remount /sys"); } if (stat("/sys/firmware", &s) == 0) { disable_file(BLACKLIST_FILE, "/sys/firmware"); } if (stat("/sys/hypervisor", &s) == 0) { disable_file(BLACKLIST_FILE, "/sys/hypervisor"); } if (stat("/sys/fs", &s) == 0) { disable_file(BLACKLIST_FILE, "/sys/fs"); } if (stat("/sys/module", &s) == 0) { disable_file(BLACKLIST_FILE, "/sys/module"); } if (stat("/sys/power", &s) == 0) { disable_file(BLACKLIST_FILE, "/sys/power"); } // if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) // errExit("mounting /sys"); // Disable SysRq // a linux box can be shut down easily using the following commands (as root): // # echo 1 > /proc/sys/kernel/sysrq // #echo b > /proc/sysrq-trigger // for more information see https://www.kernel.org/doc/Documentation/sysrq.txt if (arg_debug) printf("Disable /proc/sysrq-trigger\n"); fs_rdonly_noexit("/proc/sysrq-trigger"); // disable hotplug and uevent_helper if (arg_debug) printf("Disable /proc/sys/kernel/hotplug\n"); fs_rdonly_noexit("/proc/sys/kernel/hotplug"); if (arg_debug) printf("Disable /sys/kernel/uevent_helper\n"); fs_rdonly_noexit("/sys/kernel/uevent_helper"); // read-only /proc/irq and /proc/bus if (arg_debug) printf("Disable /proc/irq\n"); fs_rdonly_noexit("/proc/irq"); if (arg_debug) printf("Disable /proc/bus\n"); fs_rdonly_noexit("/proc/bus"); // disable /proc/kcore disable_file(BLACKLIST_FILE, "/proc/kcore"); // disable /proc/kallsyms disable_file(BLACKLIST_FILE, "/proc/kallsyms"); // disable /boot if (stat("/boot", &s) == 0) { if (arg_debug) printf("Disable /boot directory\n"); disable_file(BLACKLIST_FILE, "/boot"); } // disable /selinux if (stat("/selinux", &s) == 0) { if (arg_debug) printf("Disable /selinux directory\n"); disable_file(BLACKLIST_FILE, "/selinux"); } // disable /dev/port if (stat("/dev/port", &s) == 0) { disable_file(BLACKLIST_FILE, "/dev/port"); } if (getuid() != 0) { // disable /dev/kmsg if (stat("/dev/kmsg", &s) == 0) { disable_file(BLACKLIST_FILE, "/dev/kmsg"); } // disable /proc/kmsg if (stat("/proc/kmsg", &s) == 0) { disable_file(BLACKLIST_FILE, "/proc/kmsg"); } } }
// mount /proc and /sys directories void fs_proc_sys_dev_boot(void) { struct stat s; if (arg_debug) printf("Remounting /proc and /proc/sys filesystems\n"); if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) errExit("mounting /proc"); // remount /proc/sys readonly if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0) errExit("mounting /proc/sys"); if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0) errExit("mounting /proc/sys"); /* Mount a version of /sys that describes the network namespace */ if (arg_debug) printf("Remounting /sys directory\n"); if (umount2("/sys", MNT_DETACH) < 0) fprintf(stderr, "Warning: failed to unmount /sys\n"); if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) fprintf(stderr, "Warning: failed to mount /sys\n"); // if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) // errExit("mounting /sys"); // mounting firejail kernel module files if (stat("/proc/firejail-uptime", &s) == 0) { errno = 0; FILE *fp = fopen("/proc/firejail", "w"); int cnt = 0; while (errno == EBUSY && cnt < 10) { if (!fp) { int s = random(); s /= 200000; usleep(s); fp = fopen("/proc/firejail", "w"); } else break; } if (!fp) { fprintf(stderr, "Error: cannot register sandbox with firejail-lkm\n"); exit(1); } if (fp) { // registration fprintf(fp, "register\n"); fflush(0); // filtering x11 connect calls if (arg_nox11) { fprintf(fp, "no connect unix /tmp/.X11\n"); fflush(0); printf("X11 access disabled\n"); } if (arg_nodbus) { fprintf(fp, "no connect unix /var/run/dbus/system_bus_socket\n"); fflush(0); fprintf(fp, "no connect unix /tmp/dbus\n"); fflush(0); printf("D-Bus access disabled\n"); } fclose(fp); if (mount("/proc/firejail-uptime", "/proc/uptime", NULL, MS_BIND|MS_REC, NULL) < 0) fprintf(stderr, "Warning: cannot mount /proc/firejail-uptime\n"); } } // Disable SysRq // a linux box can be shut down easily using the following commands (as root): // # echo 1 > /proc/sys/kernel/sysrq // #echo b > /proc/sysrq-trigger // for more information see https://www.kernel.org/doc/Documentation/sysrq.txt if (arg_debug) printf("Disable /proc/sysrq-trigger\n"); fs_rdonly_noexit("/proc/sysrq-trigger"); // disable hotplug and uevent_helper if (arg_debug) printf("Disable /proc/sys/kernel/hotplug\n"); fs_rdonly_noexit("/proc/sys/kernel/hotplug"); if (arg_debug) printf("Disable /sys/kernel/uevent_helper\n"); fs_rdonly_noexit("/sys/kernel/uevent_helper"); // read-only /proc/irq and /proc/bus if (arg_debug) printf("Disable /proc/irq\n"); fs_rdonly_noexit("/proc/irq"); if (arg_debug) printf("Disable /proc/bus\n"); fs_rdonly_noexit("/proc/bus"); // disable /proc/kcore disable_file(BLACKLIST_FILE, "/proc/kcore", "not used", "/dev/null"); // disable /proc/kallsyms disable_file(BLACKLIST_FILE, "/proc/kallsyms", "not used", "/dev/null"); // disable /boot if (stat("/boot", &s) == 0) { if (arg_debug) printf("Mounting a new /boot directory\n"); if (mount("tmpfs", "/boot", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) errExit("mounting /boot directory"); } // disable /dev/port if (stat("/dev/port", &s) == 0) { disable_file(BLACKLIST_FILE, "/dev/port", "not used", "/dev/null"); } }