// build a basic read-only filesystem void fs_basic_fs(void) { if (arg_debug) printf("Mounting read-only /bin, /sbin, /lib, /lib32, /lib64, /usr, /etc, /var\n"); fs_rdonly("/bin"); fs_rdonly("/sbin"); fs_rdonly("/lib"); fs_rdonly("/lib64"); fs_rdonly("/lib32"); fs_rdonly("/libx32"); fs_rdonly("/usr"); fs_rdonly("/etc"); fs_rdonly("/var"); // update /var directory in order to support multiple sandboxes running on the same root directory if (!arg_private_dev) fs_dev_shm(); fs_var_lock(); fs_var_tmp(); fs_var_log(); fs_var_lib(); fs_var_cache(); fs_var_utmp(); // don't leak user information restrict_users(); // when starting as root, firejail config is not disabled; // this mode could be used to install and test new software by chaining // firejail sandboxes (firejail --force) if (getuid() != 0) disable_firejail_config(); else fprintf(stderr, "Warning: masking /etc/firejail disabled when starting the sandbox as root\n"); }
// build a basic read-only filesystem void fs_basic_fs(void) { if (arg_debug) printf("Mounting read-only /bin, /sbin, /lib, /lib32, /lib64, /usr, /etc, /var\n"); fs_rdonly("/bin"); fs_rdonly("/sbin"); fs_rdonly("/lib"); fs_rdonly("/lib64"); fs_rdonly("/lib32"); fs_rdonly("/usr"); fs_rdonly("/etc"); fs_rdonly("/var"); // update /var directory in order to support multiple sandboxes running on the same root directory if (!arg_private_dev) fs_dev_shm(); fs_var_lock(); fs_var_tmp(); fs_var_log(); fs_var_lib(); fs_var_cache(); fs_var_utmp(); // don't leak user information restrict_users(); disable_firejail_config(); }
// build a basic read-only filesystem void fs_basic_fs(void) { if (arg_debug) printf("Mounting read-only /bin, /sbin, /lib, /lib64, /usr, /etc, /var\n"); fs_rdonly("/bin"); fs_rdonly("/sbin"); fs_rdonly("/lib"); fs_rdonly("/lib64"); fs_rdonly("/usr"); fs_rdonly("/etc"); fs_rdonly("/var"); // update /var directory in order to support multiple sandboxes running on the same root directory if (!arg_private_dev) fs_dev_shm(); fs_var_lock(); fs_var_tmp(); fs_var_log(); fs_var_lib(); fs_var_cache(); fs_var_utmp(); // only in user mode if (getuid()) sanitize_home(); }
static void disable_file(OPERATION op, const char *filename) { assert(filename); assert(op <OPERATION_MAX); last_disable = UNSUCCESSFUL; // rebuild /run/firejail directory in case tmpfs was mounted on top of /run fs_build_firejail_dir(); // Resolve all symlinks char* fname = realpath(filename, NULL); if (fname == NULL && errno != EACCES) { if (arg_debug) printf("Warning (realpath): %s is an invalid file, skipping...\n", filename); return; } if (fname == NULL && errno == EACCES) { if (arg_debug) printf("Debug: no access to file %s, forcing mount\n", filename); // realpath and stat funtions will fail on FUSE filesystems // they don't seem to like a uid of 0 // force mounting int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); if (rv == 0) last_disable = SUCCESSFUL; else { rv = mount(RUN_RO_FILE, filename, "none", MS_BIND, "mode=400,gid=0"); if (rv == 0) last_disable = SUCCESSFUL; } if (last_disable == SUCCESSFUL) { if (arg_debug) printf("Disable %s\n", filename); if (op == BLACKLIST_FILE) fs_logger2("blacklist", filename); else fs_logger2("blacklist-nolog", filename); } else { if (arg_debug) printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename); } return; } // if the file is not present, do nothing struct stat s; if (fname == NULL) return; if (stat(fname, &s) == -1) { if (arg_debug) printf("Warning: %s does not exist, skipping...\n", fname); free(fname); return; } // modify the file if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { // some distros put all executables under /usr/bin and make /bin a symbolic link if ((strcmp(fname, "/bin") == 0 || strcmp(fname, "/usr/bin") == 0) && is_link(filename) && S_ISDIR(s.st_mode)) fprintf(stderr, "Warning: %s directory link was not blacklisted\n", filename); else { if (arg_debug) printf("Disable %s\n", fname); else if (arg_debug_blacklists) { printf("Disable %s", fname); if (op == BLACKLIST_FILE) printf("\n"); else printf(" - no logging\n"); } if (S_ISDIR(s.st_mode)) { if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } else { if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } last_disable = SUCCESSFUL; if (op == BLACKLIST_FILE) fs_logger2("blacklist", fname); else fs_logger2("blacklist-nolog", fname); } } else if (op == MOUNT_READONLY) { if (arg_debug) printf("Mounting read-only %s\n", fname); fs_rdonly(fname); // todo: last_disable = SUCCESSFUL; } else if (op == MOUNT_TMPFS) { if (S_ISDIR(s.st_mode)) { if (arg_debug) printf("Mounting tmpfs on %s\n", fname); // preserve owner and mode for the directory if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0) errExit("mounting tmpfs"); /* coverity[toctou] */ if (chown(fname, s.st_uid, s.st_gid) == -1) errExit("mounting tmpfs chmod"); last_disable = SUCCESSFUL; fs_logger2("mount tmpfs on", fname); } else printf("Warning: %s is not a directory; cannot mount a tmpfs on top of it.\n", fname); } else assert(0); free(fname); }
static void disable_file(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) { assert(fname); assert(emptydir); assert(emptyfile); assert(op <OPERATION_MAX); // if the file is a link, follow the link char *lnk = NULL; if (is_link(fname)) { lnk = get_link(fname); if (lnk) fname = lnk; else fprintf(stderr, "Warning: cannot follow link %s, skipping...\n", fname); } // if the file is not present, do nothing struct stat s; if (stat(fname, &s) == -1) { if (lnk) free(lnk); return; } // modify the file if (op == BLACKLIST_FILE) { if (arg_debug) printf("Disable %s\n", fname); if (S_ISDIR(s.st_mode)) { if (mount(emptydir, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } else { if (mount(emptyfile, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } } else if (op == MOUNT_READONLY) { if (arg_debug) printf("Mounting read-only %s\n", fname); fs_rdonly(fname); } else if (op == MOUNT_TMPFS) { if (S_ISDIR(s.st_mode)) { if (arg_debug) printf("Mounting tmpfs on %s\n", fname); // preserve owner and mode for the directory if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0) errExit("mounting tmpfs"); /* coverity[toctou] */ if (chown(fname, s.st_uid, s.st_gid) == -1) errExit("mounting tmpfs chmod"); } else printf("Warning: %s is not a directory; cannot mount a tmpfs on top of it.\n", fname); } else assert(0); if (lnk) free(lnk); }
static void disable_file(OPERATION op, const char *filename, const char *emptydir, const char *emptyfile) { assert(filename); assert(emptydir); assert(emptyfile); assert(op <OPERATION_MAX); // Resolve all symlinks char* fname = realpath(filename, NULL); if (fname == NULL) { if (arg_debug) printf("Warning: %s is an invalid file, skipping...\n", filename); return; } // if the file is not present, do nothing struct stat s; if (stat(fname, &s) == -1) { if (arg_debug) printf("Warning: %s does not exist, skipping...\n", fname); free(fname); return; } // modify the file if (op == BLACKLIST_FILE) { // some distros put all executables under /usr/bin and make /bin a symbolic link if ((strcmp(fname, "/bin") == 0 || strcmp(fname, "/usr/bin") == 0) && is_link(filename) && S_ISDIR(s.st_mode)) fprintf(stderr, "Warning: %s directory link was not blacklisted\n", filename); else { if (arg_debug) printf("Disable %s\n", fname); if (S_ISDIR(s.st_mode)) { if (mount(emptydir, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } else { if (mount(emptyfile, fname, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable file"); } } } else if (op == MOUNT_READONLY) { if (arg_debug) printf("Mounting read-only %s\n", fname); fs_rdonly(fname); } else if (op == MOUNT_TMPFS) { if (S_ISDIR(s.st_mode)) { if (arg_debug) printf("Mounting tmpfs on %s\n", fname); // preserve owner and mode for the directory if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0) errExit("mounting tmpfs"); /* coverity[toctou] */ if (chown(fname, s.st_uid, s.st_gid) == -1) errExit("mounting tmpfs chmod"); } else printf("Warning: %s is not a directory; cannot mount a tmpfs on top of it.\n", fname); } else assert(0); free(fname); }
// disable shm in pulseaudio void pulseaudio_init(void) { struct stat s; // do we have pulseaudio in the system? if (stat("/etc/pulse/client.conf", &s) == -1) { if (arg_debug) printf("/etc/pulse/client.conf not found\n"); return; } // create the new user pulseaudio directory if (mkdir(RUN_PULSE_DIR, 0700) == -1) errExit("mkdir"); // mount it nosuid, noexec, nodev fs_noexec(RUN_PULSE_DIR); // create the new client.conf file char *pulsecfg = NULL; if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) errExit("asprintf"); if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) // root needed errExit("copy_file"); FILE *fp = fopen(pulsecfg, "a"); if (!fp) errExit("fopen"); fprintf(fp, "%s", "\nenable-shm = no\n"); SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); fclose(fp); // hand over the directory to the user if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) errExit("set_perms"); // create ~/.config/pulse directory if not present char *homeusercfg; if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1) errExit("asprintf"); if (lstat(homeusercfg, &s) == -1) { if (create_empty_dir_as_user(homeusercfg, 0700)) fs_logger2("create", homeusercfg); } else if (!S_ISDIR(s.st_mode)) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: %s is a symbolic link\n", homeusercfg); else fprintf(stderr, "Error: %s is not a directory\n", homeusercfg); exit(1); } free(homeusercfg); if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) errExit("asprintf"); if (lstat(homeusercfg, &s) == -1) { if (create_empty_dir_as_user(homeusercfg, 0700)) fs_logger2("create", homeusercfg); } else if (!S_ISDIR(s.st_mode)) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: %s is a symbolic link\n", homeusercfg); else fprintf(stderr, "Error: %s is not a directory\n", homeusercfg); exit(1); } // if we have ~/.config/pulse mount the new directory, else set environment variable. if (stat(homeusercfg, &s) == 0) { // get a file descriptor for ~/.config/pulse, fails if there is any symlink int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); if (fd == -1) errExit("safe_fd"); // confirm the actual mount destination is owned by the user if (fstat(fd, &s) == -1) errExit("fstat"); if (s.st_uid != getuid()) { fprintf(stderr, "Error: %s is not owned by the current user\n", homeusercfg); exit(1); } // preserve a read-only mount struct statvfs vfs; if (fstatvfs(fd, &vfs) == -1) errExit("fstatvfs"); if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) fs_rdonly(RUN_PULSE_DIR); // mount via the link in /proc/self/fd char *proc; if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) errExit("asprintf"); if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) errExit("mount pulseaudio"); fs_logger2("tmpfs", homeusercfg); free(proc); close(fd); // check /proc/self/mountinfo to confirm the mount is ok MountData *mptr = get_last_mount(); if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) errLogExit("invalid pulseaudio mount"); char *p; if (asprintf(&p, "%s/client.conf", homeusercfg) == -1) errExit("asprintf"); fs_logger2("create", p); free(p); } else { // set environment if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) errExit("setenv"); } free(pulsecfg); free(homeusercfg); }