void fs_var_lock(void) { if (is_dir("/var/lock")) { if (arg_debug) printf("Mounting tmpfs on /var/lock\n"); if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting /lock"); fs_logger("tmpfs /var/lock"); } else { char *lnk = realpath("/var/lock", NULL); if (lnk) { if (!is_dir(lnk)) { // create directory if (mkdir(lnk, S_IRWXU|S_IRWXG|S_IRWXO)) errExit("mkdir"); if (chown(lnk, 0, 0)) errExit("chown"); if (chmod(lnk, S_IRWXU|S_IRWXG|S_IRWXO)) errExit("chmod"); } if (arg_debug) printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk); if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting /var/lock"); free(lnk); fs_logger("tmpfs /var/lock"); } else { fprintf(stderr, "Warning: /var/lock not mounted\n"); dbg_test_dir("/var/lock"); } } }
void fs_var_cache(void) { struct stat s; if (stat("/var/cache/apache2", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/cache/apache2\n"); if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/cache/apache2"); fs_logger("tmpfs /var/cache/apache2"); } if (stat("/var/cache/lighttpd", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/cache/lighttpd\n"); if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/cache/lighttpd"); fs_logger("tmpfs /var/cache/lighttpd"); struct passwd *p = getpwnam("www-data"); uid_t uid = 0; gid_t gid = 0; if (p) { uid = p->pw_uid; gid = p->pw_gid; } mkdir_attr("/var/cache/lighttpd/compress", 0755, uid, gid); fs_logger("mkdir /var/cache/lighttpd/compress"); mkdir_attr("/var/cache/lighttpd/uploads", 0755, uid, gid); fs_logger("/var/cache/lighttpd/uploads"); } }
// private mode (--private=homedir): // mount homedir on top of /home/user, // tmpfs on top of /root in nonroot mode, // set skel files, // restore .Xauthority void fs_private_homedir(void) { char *homedir = cfg.homedir; char *private_homedir = cfg.home_private; assert(homedir); assert(private_homedir); int xflag = store_xauthority(); int aflag = store_asoundrc(); uid_t u = getuid(); gid_t g = getgid(); struct stat s; if (stat(homedir, &s) == -1) { fprintf(stderr, "Error: cannot find user home directory\n"); exit(1); } // mount bind private_homedir on top of homedir if (arg_debug) printf("Mount-bind %s on top of %s\n", private_homedir, homedir); if (mount(private_homedir, homedir, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) errExit("mount bind"); fs_logger3("mount-bind", private_homedir, cfg.homedir); fs_logger2("whitelist", cfg.homedir); // preserve mode and ownership // if (chown(homedir, s.st_uid, s.st_gid) == -1) // errExit("mount-bind chown"); // if (chmod(homedir, s.st_mode) == -1) // errExit("mount-bind chmod"); if (u != 0) { // mask /root if (arg_debug) printf("Mounting a new /root directory\n"); if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) errExit("mounting home directory"); fs_logger("tmpfs /root"); } else { // mask /home if (arg_debug) printf("Mounting a new /home directory\n"); if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting home directory"); fs_logger("tmpfs /home"); } skel(homedir, u, g); if (xflag) copy_xauthority(); if (aflag) copy_asoundrc(); }
void fs_var_lib(void) { struct stat s; // ISC DHCP multiserver if (stat("/var/lib/dhcp", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/lib/dhcp\n"); if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/lib/dhcp"); fs_logger("tmpfs /var/lib/dhcp"); // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (chown("/var/lib/dhcp/dhcpd.leases", 0, 0) == -1) errExit("chown"); if (chmod("/var/lib/dhcp/dhcpd.leases", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) errExit("chmod"); fs_logger("touch /var/lib/dhcp/dhcpd.leases"); } } // nginx multiserver if (stat("/var/lib/nginx", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/lib/nginx\n"); if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/lib/nginx"); fs_logger("tmpfs /var/lib/nginx"); } // net-snmp multiserver if (stat("/var/lib/snmp", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/lib/snmp\n"); if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/lib/snmp"); fs_logger("tmpfs /var/lib/snmp"); } // this is where sudo remembers its state if (stat("/var/lib/sudo", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/lib/sudo\n"); if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/lib/sudo"); fs_logger("tmpfs /var/lib/sudo"); } }
void fs_trace(void) { // create the new ld.so.preload file and mount-bind it if (arg_debug) printf("Create the new ld.so.preload file\n"); FILE *fp = fopen(RUN_LDPRELOAD_FILE, "w"); if (!fp) errExit("fopen"); const char *prefix = RUN_FIREJAIL_LIB_DIR; if (arg_trace) { fprintf(fp, "%s/libtrace.so\n", prefix); } else if (arg_tracelog) { fprintf(fp, "%s/libtracelog.so\n", prefix); fmessage("Blacklist violations are logged to syslog\n"); } if (arg_seccomp_postexec) { fprintf(fp, "%s/libpostexecseccomp.so\n", prefix); fmessage("Post-exec seccomp protector enabled\n"); } SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); fclose(fp); // mount the new preload file if (arg_debug) printf("Mount the new ld.so.preload file\n"); if (mount(RUN_LDPRELOAD_FILE, "/etc/ld.so.preload", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind ld.so.preload"); fs_logger("create /etc/ld.so.preload"); }
void fs_private_etc_list(void) { char *private_list = cfg.etc_private_keep; assert(private_list); 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(RUN_ETC_DIR, 0755); if (rv == -1) errExit("mkdir"); if (chown(RUN_ETC_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_ETC_DIR, 0755) < 0) errExit("chmod"); // copy the list of files in the new etc directory // using a new child process without root privileges fs_logger_print(); // save the current log pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { if (arg_debug) printf("Copying files in the new etc directory:\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); fs_logger_print(); exit(0); } // wait for the child to finish waitpid(child, NULL, 0); if (arg_debug) printf("Mount-bind %s on top of /etc\n", RUN_ETC_DIR); if (mount(RUN_ETC_DIR, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); fs_logger("mount /etc"); }
void fs_x11(void) { #ifdef HAVE_X11 int display = x11_display(); if (display <= 0) return; char *x11file; if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) errExit("asprintf"); struct stat s; if (stat(x11file, &s) == -1) return; // keep a copy of real /tmp/.X11-unix directory in WHITELIST_TMP_DIR int rv = mkdir(RUN_WHITELIST_X11_DIR, 1777); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_X11_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_X11_DIR, 1777) < 0) errExit("chmod"); if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /tmp/.X11-unix if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /tmp/.X11-unix directory\n"); if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting tmpfs on /tmp"); fs_logger("tmpfs /tmp/.X11-unix"); // create an empty file FILE *fp = fopen(x11file, "w"); if (!fp) { fprintf(stderr, "Error: cannot create empty file in x11 directory\n"); exit(1); } // set file properties SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); fclose(fp); // mount char *wx11file; if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) errExit("asprintf"); if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); fs_logger2("whitelist", x11file); free(x11file); free(wx11file); // block access to RUN_WHITELIST_X11_DIR if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, "none", MS_BIND, "mode=400,gid=0") == -1) errExit("mount"); fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); #endif }
// private mode (--private): // mount tmpfs over /home/user, // tmpfs on top of /root in nonroot mode, // set skel files, // restore .Xauthority void fs_private(void) { char *homedir = cfg.homedir; assert(homedir); uid_t u = getuid(); gid_t g = getgid(); int xflag = store_xauthority(); int aflag = store_asoundrc(); // mask /home if (arg_debug) printf("Mounting a new /home directory\n"); if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting home directory"); fs_logger("tmpfs /home"); // mask /root if (arg_debug) printf("Mounting a new /root directory\n"); if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) errExit("mounting root directory"); fs_logger("tmpfs /root"); if (u != 0) { // create /home/user if (arg_debug) printf("Create a new user directory\n"); if (mkdir(homedir, S_IRWXU) == -1) { if (mkpath_as_root(homedir) == -1) errExit("mkpath"); if (mkdir(homedir, S_IRWXU) == -1) errExit("mkdir"); } if (chown(homedir, u, g) < 0) errExit("chown"); fs_logger2("mkdir", homedir); } skel(homedir, u, g); if (xflag) copy_xauthority(); if (aflag) copy_asoundrc(); }
void fs_var_log(void) { build_list("/var/log"); // create /var/log if it doesn't exit if (is_dir("/var/log")) { // extract group id for /var/log/wtmp struct stat s; gid_t wtmp_group = 0; if (stat("/var/log/wtmp", &s) == 0) wtmp_group = s.st_gid; // mount a tmpfs on top of /var/log if (arg_debug) printf("Mounting tmpfs on /var/log\n"); if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/log"); fs_logger("tmpfs /var/log"); build_dirs(); release_all(); // create an empty /var/log/wtmp file /* coverity[toctou] */ FILE *fp = fopen("/var/log/wtmp", "w"); if (fp) fclose(fp); if (chown("/var/log/wtmp", 0, wtmp_group) < 0) errExit("chown"); if (chmod("/var/log/wtmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) errExit("chmod"); fs_logger("touch /var/log/wtmp"); // create an empty /var/log/btmp file fp = fopen("/var/log/btmp", "w"); if (fp) fclose(fp); if (chown("/var/log/btmp", 0, wtmp_group) < 0) errExit("chown"); if (chmod("/var/log/btmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP) < 0) errExit("chmod"); fs_logger("touch /var/log/btmp"); } else fprintf(stderr, "Warning: cannot mount tmpfs on top of /var/log\n"); }
void fs_var_log(void) { build_list("/var/log"); // note: /var/log is not created here, if it does not exist, this section fails. // create /var/log if it doesn't exit if (is_dir("/var/log")) { // extract group id for /var/log/wtmp struct stat s; gid_t wtmp_group = 0; if (stat("/var/log/wtmp", &s) == 0) wtmp_group = s.st_gid; // mount a tmpfs on top of /var/log if (arg_debug) printf("Mounting tmpfs on /var/log\n"); if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/log"); fs_logger("tmpfs /var/log"); build_dirs(); release_all(); // create an empty /var/log/wtmp file /* coverity[toctou] */ FILE *fp = fopen("/var/log/wtmp", "w"); if (fp) { SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); fclose(fp); } fs_logger("touch /var/log/wtmp"); // create an empty /var/log/btmp file fp = fopen("/var/log/btmp", "w"); if (fp) { SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP); fclose(fp); } fs_logger("touch /var/log/btmp"); } else fwarning("cannot hide /var/log directory\n"); }
void fs_var_cache(void) { struct stat s; if (stat("/var/cache/apache2", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/cache/apache2\n"); if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/cache/apache2"); fs_logger("mount tmpfs on /var/cache/apache2"); } if (stat("/var/cache/lighttpd", &s) == 0) { if (arg_debug) printf("Mounting tmpfs on /var/cache/lighttpd\n"); if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /var/cache/lighttpd"); fs_logger("mount tmpfs on /var/cache/lighttpd"); struct passwd *p = getpwnam("www-data"); uid_t uid = 0; gid_t gid = 0; if (p) { uid = p->pw_uid; gid = p->pw_gid; } int rv = mkdir("/var/cache/lighttpd/compress", S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown("/var/cache/lighttpd/compress", uid, gid) < 0) errExit("chown"); fs_logger("mkdir /var/cache/lighttpd/compress"); rv = mkdir("/var/cache/lighttpd/uploads", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (rv == -1) errExit("mkdir"); if (chown("/var/cache/lighttpd/uploads", uid, gid) < 0) errExit("chown"); fs_logger("/var/cache/lighttpd/uploads"); } }
static void sanitize_home(void) { assert(getuid() != 0); // this code works only for regular users if (arg_debug) printf("Cleaning /home directory\n"); struct stat s; if (stat(cfg.homedir, &s) == -1) { // cannot find home directory, just return fprintf(stderr, "Warning: cannot find home directory\n"); return; } fs_build_mnt_dir(); if (mkdir(RUN_WHITELIST_HOME_DIR, 0755) == -1) errExit("mkdir"); // keep a copy of the user home directory if (mount(cfg.homedir, RUN_WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs in the new home if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); fs_logger("tmpfs /home"); // create user home directory if (mkdir(cfg.homedir, 0755) == -1) { if (mkpath_as_root(cfg.homedir)) errExit("mkpath"); if (mkdir(cfg.homedir, 0755) == -1) errExit("mkdir"); } fs_logger2("mkdir", cfg.homedir); // set mode and ownership if (chown(cfg.homedir, s.st_uid, s.st_gid) == -1) errExit("chown"); if (chmod(cfg.homedir, s.st_mode) == -1) errExit("chmod"); // mount user home directory if (mount(RUN_WHITELIST_HOME_DIR, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mask home dir under /run if (mount("tmpfs", RUN_WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); fs_logger2("tmpfs", RUN_WHITELIST_HOME_DIR); if (!arg_private) fs_logger2("whitelist", cfg.homedir); }
void fs_var_utmp(void) { struct stat s; // extract utmp group id gid_t utmp_group = 0; if (stat(UTMP_FILE, &s) == 0) utmp_group = s.st_gid; else { fprintf(stderr, "Warning: cannot find /var/run/utmp\n"); return; } // create /tmp/firejail/mnt directory fs_build_mnt_dir(); // create a new utmp file if (arg_debug) printf("Create the new utmp file\n"); /* coverity[toctou] */ FILE *fp = fopen(RUN_UTMP_FILE, "w"); if (!fp) errExit("fopen"); // read current utmp struct utmp *u; struct utmp u_boot; setutent(); while ((u = getutent()) != NULL) { if (u->ut_type == BOOT_TIME) { memcpy(&u_boot, u, sizeof(u_boot)); u_boot.ut_tv.tv_sec = (unsigned) time(NULL); } } endutent(); // save new utmp file fwrite(&u_boot, sizeof(u_boot), 1, fp); fclose(fp); if (chown(RUN_UTMP_FILE, 0, utmp_group) < 0) errExit("chown"); if (chmod(RUN_UTMP_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) errExit("chmod"); // mount the new utmp file if (arg_debug) printf("Mount the new utmp file\n"); if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) errExit("mount bind utmp"); fs_logger("create /var/run/utmp"); }
static void sanitize_run(void) { if (arg_debug) printf("Cleaning /run/user directory\n"); char *runuser; if (asprintf(&runuser, "/run/user/%u", getuid()) == -1) errExit("asprintf"); struct stat s; if (stat(runuser, &s) == -1) { // cannot find /user/run/$UID directory, just return if (arg_debug) printf("Cannot find %s directory\n", runuser); free(runuser); return; } if (mkdir(RUN_WHITELIST_RUN_DIR, 0755) == -1) errExit("mkdir"); // keep a copy of the /run/user/$UID directory if (mount(runuser, RUN_WHITELIST_RUN_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /run/user if (mount("tmpfs", "/run/user", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) errExit("mount tmpfs"); fs_logger("tmpfs /run/user"); // create new user directory if (mkdir(runuser, 0700) == -1) errExit("mkdir"); fs_logger2("mkdir", runuser); // set mode and ownership if (set_perms(runuser, getuid(), getgid(), 0700)) errExit("set_perms"); // mount user home directory if (mount(RUN_WHITELIST_RUN_DIR, runuser, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mask mirrored /run/user/$UID directory if (mount("tmpfs", RUN_WHITELIST_RUN_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) errExit("mount tmpfs"); fs_logger2("tmpfs", RUN_WHITELIST_RUN_DIR); free(runuser); }
void fs_var_tmp(void) { struct stat s; if (stat("/var/tmp", &s) == 0) { if (!is_link("/var/tmp")) { if (arg_debug) printf("Mounting tmpfs on /var/tmp\n"); if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting /var/tmp"); fs_logger("tmpfs /var/tmp"); } } else { fprintf(stderr, "Warning: /var/tmp not mounted\n"); dbg_test_dir("/var/tmp"); } }
void fs_trace_preload(void) { struct stat s; // create an empty /etc/ld.so.preload if (stat("/etc/ld.so.preload", &s)) { if (arg_debug) printf("Creating an empty /etc/ld.so.preload file\n"); /* coverity[toctou] */ FILE *fp = fopen("/etc/ld.so.preload", "w"); if (!fp) errExit("fopen"); SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); fclose(fp); fs_logger("touch /etc/ld.so.preload"); } }
void restrict_users(void) { // only in user mode if (getuid()) { if (strncmp(cfg.homedir, "/home/", 6) == 0) { // user has the home directory under /home sanitize_home(); } else { // user has the home diercotry outside /home // mount tmpfs on top of /home in order to hide it if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); fs_logger("tmpfs /home"); } sanitize_passwd(); sanitize_group(); } }
void fs_dev_shm(void) { uid_t uid = getuid(); // set a new shm only if we started as root if (uid) return; if (is_dir("/dev/shm")) { if (arg_debug) printf("Mounting tmpfs on /dev/shm\n"); if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting /dev/shm"); fs_logger("tmpfs /dev/shm"); } else { char *lnk = realpath("/dev/shm", NULL); if (lnk) { if (!is_dir(lnk)) { // create directory if (mkdir(lnk, 01777)) errExit("mkdir"); // mkdir sets only the file permission bits if (chmod(lnk, 01777)) errExit("chmod"); ASSERT_PERMS(lnk, 0, 0, 01777); } if (arg_debug) printf("Mounting tmpfs on %s on behalf of /dev/shm\n", lnk); if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) errExit("mounting /var/tmp"); fs_logger2("tmpfs", lnk); free(lnk); } else { fprintf(stderr, "Warning: /dev/shm not mounted\n"); dbg_test_dir("/dev/shm"); } } }
void fs_resolvconf(void) { if (cfg.dns1 == 0) return; struct stat s; // create a new /etc/hostname if (stat("/etc/resolv.conf", &s) == 0) { if (arg_debug) printf("Creating a new /etc/resolv.conf file\n"); FILE *fp = fopen(RUN_RESOLVCONF_FILE, "w"); if (!fp) { fprintf(stderr, "Error: cannot create %s\n", RUN_RESOLVCONF_FILE); exit(1); } if (cfg.dns1) fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); if (cfg.dns2) fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); if (cfg.dns3) fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); // mode and owner SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); fclose(fp); // bind-mount the file on top of /etc/hostname if (mount(RUN_RESOLVCONF_FILE, "/etc/resolv.conf", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind /etc/resolv.conf"); fs_logger("create /etc/resolv.conf"); } else { fprintf(stderr, "Error: cannot set DNS servers, /etc/resolv.conf file is missing\n"); exit(1); } }
static void skel(const char *homedir, uid_t u, gid_t g) { char *fname; // zsh if (arg_zsh) { // copy skel files if (asprintf(&fname, "%s/.zshrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.zshrc", &s) == 0) { if (copy_file("/etc/skel/.zshrc", fname) == 0) { if (chown(fname, u, g) == -1) errExit("chown"); fs_logger("clone /etc/skel/.zshrc"); } } else { // FILE *fp = fopen(fname, "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (chown(fname, u, g) == -1) errExit("chown"); if (chmod(fname, S_IRUSR | S_IWUSR) < 0) errExit("chown"); fs_logger2("touch", fname); } } free(fname); } // csh else if (arg_csh) { // copy skel files if (asprintf(&fname, "%s/.cshrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.cshrc", &s) == 0) { if (copy_file("/etc/skel/.cshrc", fname) == 0) { if (chown(fname, u, g) == -1) errExit("chown"); fs_logger("clone /etc/skel/.cshrc"); } } else { // /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (chown(fname, u, g) == -1) errExit("chown"); if (chmod(fname, S_IRUSR | S_IWUSR) < 0) errExit("chown"); fs_logger2("touch", fname); } } free(fname); } // bash etc. else { // copy skel files if (asprintf(&fname, "%s/.bashrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.bashrc", &s) == 0) { if (copy_file("/etc/skel/.bashrc", fname) == 0) { /* coverity[toctou] */ if (chown(fname, u, g) == -1) errExit("chown"); fs_logger("clone /etc/skel/.bashrc"); } } free(fname); } }
void fs_private_dev(void){ // install a new /dev directory if (arg_debug) printf("Mounting tmpfs on /dev\n"); // create DRI_DIR fs_build_mnt_dir(); // keep a copy of dev directory if (mkdir(RUN_DEV_DIR, 0755) == -1) errExit("mkdir"); if (chmod(RUN_DEV_DIR, 0755) == -1) errExit("chmod"); ASSERT_PERMS(RUN_DEV_DIR, 0, 0, 0755); if (mount("/dev", RUN_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/dri"); // create DEVLOG_FILE int have_devlog = 0; struct stat s; if (stat("/dev/log", &s) == 0) { have_devlog = 1; FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); if (!fp) have_devlog = 0; else { fprintf(fp, "\n"); fclose(fp); if (mount("/dev/log", RUN_DEVLOG_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); } } // mount tmpfs on top of /dev if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /dev"); fs_logger("tmpfs /dev"); deventry_mount(); // bring back /dev/log if (have_devlog) { FILE *fp = fopen("/dev/log", "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (mount(RUN_DEVLOG_FILE, "/dev/log", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); fs_logger("clone /dev/log"); } } if (mount(RUN_RO_DIR, RUN_DEV_DIR, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable /dev/snd"); // create /dev/shm if (arg_debug) printf("Create /dev/shm directory\n"); if (mkdir("/dev/shm", 01777) == -1) errExit("mkdir"); // mkdir sets only the file permission bits if (chmod("/dev/shm", 01777) < 0) errExit("chmod"); ASSERT_PERMS("/dev/shm", 0, 0, 01777); fs_logger("mkdir /dev/shm"); // create devices create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5 fs_logger("mknod /dev/zero"); create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3 fs_logger("mknod /dev/null"); create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7 fs_logger("mknod /dev/full"); create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8 fs_logger("mknod /dev/random"); create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9 fs_logger("mknod /dev/urandom"); create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0 fs_logger("mknod /dev/tty"); #if 0 create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0"); create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1"); #endif // pseudo-terminal if (mkdir("/dev/pts", 0755) == -1) errExit("mkdir"); if (chmod("/dev/pts", 0755) == -1) errExit("chmod"); ASSERT_PERMS("/dev/pts", 0, 0, 0755); fs_logger("mkdir /dev/pts"); create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); fs_logger("mknod /dev/pts/ptmx"); create_link("/dev/pts/ptmx", "/dev/ptmx"); // code before github issue #351 // mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts // if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0) // errExit("mounting /dev/pts"); // mount /dev/pts gid_t ttygid = get_tty_gid(); char *data; if (asprintf(&data, "newinstance,gid=%d,mode=620,ptmxmode=0666", (int) ttygid) == -1) errExit("asprintf"); if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, data) < 0) errExit("mounting /dev/pts"); free(data); fs_logger("clone /dev/pts"); #if 0 // stdin, stdout, stderr create_link("/proc/self/fd", "/dev/fd"); create_link("/proc/self/fd/0", "/dev/stdin"); create_link("/proc/self/fd/1", "/dev/stdout"); create_link("/proc/self/fd/2", "/dev/stderr"); #endif }
static void sanitize_group(void) { struct stat s; if (stat("/etc/group", &s) == -1) return; assert(gid_min); if (arg_debug) printf("Sanitizing /etc/group, GID_MIN %d\n", gid_min); if (is_link("/etc/group")) { fprintf(stderr, "Error: invalid /etc/group\n"); exit(1); } FILE *fpin = NULL; FILE *fpout = NULL; // open files /* coverity[toctou] */ fpin = fopen("/etc/group", "r"); if (!fpin) goto errout; fpout = fopen(RUN_GROUP_FILE, "w"); if (!fpout) goto errout; // read the file line by line char buf[MAXBUF]; gid_t mygid = getgid(); while (fgets(buf, MAXBUF, fpin)) { // comments and empty lines if (*buf == '\0' || *buf == '#') continue; // sample line: // pulse:x:115:netblue,bingo // drop lines with uid > 1000 and not the current user group char *ptr = buf; // advance to uid while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; if (*ptr == '\0') goto errout; // process uid int gid; int rv = sscanf(ptr, "%d:", &gid); if (rv == 0 || gid < 0) goto errout; assert(gid_min); if (gid < gid_min || gid == 65534) { // on Debian platforms 65534 is group nogroup if (copy_line(fpout, buf, ptr)) goto errout; continue; } if ((gid_t) gid != mygid) { continue; // skip line } if (copy_line(fpout, buf, ptr)) goto errout; } fclose(fpin); SET_PERMS_STREAM(fpout, 0, 0, 0644); fclose(fpout); // mount-bind tne new group file if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) errExit("mount"); fs_logger("create /etc/group"); return; errout: fwarning("failed to clean up /etc/group\n"); if (fpin) fclose(fpin); if (fpout) fclose(fpout); }
static void sanitize_passwd(void) { struct stat s; if (stat("/etc/passwd", &s) == -1) return; assert(uid_min); if (arg_debug) printf("Sanitizing /etc/passwd, UID_MIN %d\n", uid_min); if (is_link("/etc/passwd")) { fprintf(stderr, "Error: invalid /etc/passwd\n"); exit(1); } FILE *fpin = NULL; FILE *fpout = NULL; // open files /* coverity[toctou] */ fpin = fopen("/etc/passwd", "r"); if (!fpin) goto errout; fpout = fopen(RUN_PASSWD_FILE, "w"); if (!fpout) goto errout; // read the file line by line char buf[MAXBUF]; uid_t myuid = getuid(); while (fgets(buf, MAXBUF, fpin)) { // comments and empty lines if (*buf == '\0' || *buf == '#') continue; // sample line: // www-data:x:33:33:www-data:/var/www:/bin/sh // drop lines with uid > 1000 and not the current user char *ptr = buf; // advance to uid while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; char *ptr1 = ptr; ptr++; while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; if (*ptr == '\0') goto errout; // process uid int uid; int rv = sscanf(ptr, "%d:", &uid); if (rv == 0 || uid < 0) goto errout; assert(uid_min); if (uid < uid_min || uid == 65534) { // on Debian platforms user nobody is 65534 fprintf(fpout, "%s", buf); continue; } if ((uid_t) uid != myuid) { // store user name - necessary to process /etc/group *ptr1 = '\0'; char *user = strdup(buf); if (!user) errExit("malloc"); ulist_add(user); continue; // skip line } fprintf(fpout, "%s", buf); } fclose(fpin); SET_PERMS_STREAM(fpout, 0, 0, 0644); fclose(fpout); // mount-bind tne new password file if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) errExit("mount"); fs_logger("create /etc/passwd"); return; errout: fwarning("failed to clean up /etc/passwd\n"); if (fpin) fclose(fpin); if (fpout) fclose(fpout); }
void fs_private_dev(void){ int rv; // install a new /dev directory if (arg_debug) printf("Mounting tmpfs on /dev\n"); int have_dri = 0; struct stat s; if (stat("/dev/dri", &s) == 0) have_dri = 1; // create DRI_DIR fs_build_mnt_dir(); if (have_dri) { /* coverity[toctou] */ rv = mkdir(RUN_DRI_DIR, 0755); if (rv == -1) errExit("mkdir"); if (chown(RUN_DRI_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_DRI_DIR, 0755) < 0) errExit("chmod"); // keep a copy of /dev/dri under DRI_DIR if (mount("/dev/dri", RUN_DRI_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/dri"); } // restore /dev/log int have_devlog = 0; if (stat("/dev/log", &s) == 0) { have_devlog = 1; FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); if (!fp) have_devlog = 0; else { fprintf(fp, "\n"); fclose(fp); if (mount("/dev/log", RUN_DEVLOG_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); } } // mount tmpfs on top of /dev if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) errExit("mounting /dev"); fs_logger("mount tmpfs on /dev"); // bring back /dev/log if (have_devlog) { FILE *fp = fopen("/dev/log", "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (mount(RUN_DEVLOG_FILE, "/dev/log", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); fs_logger("clone /dev/log"); } } // bring back the /dev/dri directory if (have_dri) { /* coverity[toctou] */ rv = mkdir("/dev/dri", 0755); if (rv == -1) errExit("mkdir"); if (chown("/dev/dri", 0, 0) < 0) errExit("chown"); if (chmod("/dev/dri",0755) < 0) errExit("chmod"); if (mount(RUN_DRI_DIR, "/dev/dri", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/dri"); fs_logger("clone /dev/dri"); } // create /dev/shm if (arg_debug) printf("Create /dev/shm directory\n"); rv = mkdir("/dev/shm", 0777); if (rv == -1) errExit("mkdir"); if (chown("/dev/shm", 0, 0) < 0) errExit("chown"); if (chmod("/dev/shm", 0777) < 0) errExit("chmod"); fs_logger("mkdir /dev/shm"); // create devices create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5 fs_logger("mknod /dev/zero"); create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3 fs_logger("mknod /dev/null"); create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7 fs_logger("mknod /dev/full"); create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8 fs_logger("mknod /dev/random"); create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9 fs_logger("mknod /dev/urandom"); create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0 fs_logger("mknod /dev/tty"); #if 0 create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0"); create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1"); #endif // pseudo-terminal rv = mkdir("/dev/pts", 0755); if (rv == -1) errExit("mkdir"); if (chown("/dev/pts", 0, 0) < 0) errExit("chown"); if (chmod("/dev/pts", 0755) < 0) errExit("chmod"); fs_logger("mkdir /dev/pts"); create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); fs_logger("mknod /dev/pts/ptmx"); create_link("/dev/pts/ptmx", "/dev/ptmx"); // mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0) errExit("mounting /dev/pts"); fs_logger("mount devpts"); #if 0 // stdin, stdout, stderr create_link("/proc/self/fd", "/dev/fd"); create_link("/proc/self/fd/0", "/dev/stdin"); create_link("/proc/self/fd/1", "/dev/stdout"); create_link("/proc/self/fd/2", "/dev/stderr"); #endif }
static void skel(const char *homedir, uid_t u, gid_t g) { char *fname; // zsh if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { // copy skel files if (asprintf(&fname, "%s/.zshrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.zshrc", &s) == 0) { if (copy_file("/etc/skel/.zshrc", fname, u, g, 0644) == 0) { fs_logger("clone /etc/skel/.zshrc"); } } else { // FILE *fp = fopen(fname, "w"); if (fp) { fprintf(fp, "\n"); SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR); fclose(fp); fs_logger2("touch", fname); } } free(fname); } // csh else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { // copy skel files if (asprintf(&fname, "%s/.cshrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.cshrc", &s) == 0) { if (copy_file("/etc/skel/.cshrc", fname, u, g, 0644) == 0) { fs_logger("clone /etc/skel/.cshrc"); } } else { // /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (fp) { fprintf(fp, "\n"); SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR); fclose(fp); fs_logger2("touch", fname); } } free(fname); } // bash etc. else { // copy skel files if (asprintf(&fname, "%s/.bashrc", homedir) == -1) errExit("asprintf"); struct stat s; // don't copy it if we already have the file if (stat(fname, &s) == 0) return; if (stat("/etc/skel/.bashrc", &s) == 0) { if (copy_file("/etc/skel/.bashrc", fname, u, g, 0644) == 0) { fs_logger("clone /etc/skel/.bashrc"); } } free(fname); } }
// whitelist for /home/user directory void fs_whitelist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; char *new_name = NULL; int home_dir = 0; // /home/user directory flag int tmp_dir = 0; // /tmp directory flag int media_dir = 0; // /media directory flag int var_dir = 0; // /var directory flag int dev_dir = 0; // /dev directory flag int opt_dir = 0; // /opt directory flag // verify whitelist files, extract symbolic links, etc. while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } // resolve ${DOWNLOADS} if (strcmp(entry->data + 10, "${DOWNLOADS}") == 0) { char *tmp = resolve_downloads(); if (tmp) entry->data = tmp; else { *entry->data = '\0'; fprintf(stderr, "***\n"); fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n"); fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n"); fprintf(stderr, "***\n"); continue; } } // replace ~/ or ${HOME} into /home/username new_name = expand_home(entry->data + 10, cfg.homedir); assert(new_name); if (arg_debug) fprintf(stderr, "Debug %d: new_name #%s#\n", __LINE__, 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 || arg_debug_whitelists) { printf("Removed whitelist path: %s\n", entry->data); printf("\texpanded: %s\n", new_name); printf("\treal path: (null)\n"); printf("\t");fflush(0); perror("realpath"); } *entry->data = '\0'; continue; } // valid path referenced to filesystem root if (*new_name != '/') { if (arg_debug) fprintf(stderr, "Debug %d: \n", __LINE__); goto errexit; } // check for supported directories if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { // whitelisting home directory is disabled if --private or --private-home option is present if (arg_private) { if (arg_debug || arg_debug_whitelists) printf("Removed whitelist path %s, --private option is present\n", entry->data); *entry->data = '\0'; continue; } entry->home_dir = 1; home_dir = 1; // both path and absolute path are under /home if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", __LINE__, fname, cfg.homedir); 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) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/media/", 7) == 0) { entry->media_dir = 1; media_dir = 1; // both path and absolute path are under /media if (strncmp(fname, "/media/", 7) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/var/", 5) == 0) { entry->var_dir = 1; var_dir = 1; // both path and absolute path are under /var if (strncmp(fname, "/var/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/dev/", 5) == 0) { entry->dev_dir = 1; dev_dir = 1; // both path and absolute path are under /dev if (strncmp(fname, "/dev/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/opt/", 5) == 0) { entry->opt_dir = 1; opt_dir = 1; // both path and absolute path are under /dev if (strncmp(fname, "/opt/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else { if (arg_debug) fprintf(stderr, "Debug %d: \n", __LINE__); goto errexit; } // mark symbolic links if (is_link(new_name)) entry->link = new_name; else { free(new_name); new_name = NULL; } // 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 || arg_debug_whitelists) printf("Replaced whitelist path: %s\n", entry->data); } free(fname); entry = entry->next; } // create mount points fs_build_mnt_dir(); // /home/user if (home_dir) { // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR int rv = mkdir(RUN_WHITELIST_HOME_USER_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_HOME_USER_DIR, getuid(), getgid()) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_HOME_USER_DIR, 0755) < 0) errExit("chmod"); if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_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(RUN_WHITELIST_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_TMP_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_TMP_DIR, 0777) < 0) errExit("chmod"); if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /tmp if (arg_debug || arg_debug_whitelists) 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 /tmp"); fs_logger("mount tmpfs on /tmp"); } // /media mountpoint if (media_dir) { // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR int rv = mkdir(RUN_WHITELIST_MEDIA_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_MEDIA_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_MEDIA_DIR, 0755) < 0) errExit("chmod"); if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /media if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /media directory\n"); if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /media"); fs_logger("mount tmpfs on /media"); } // /var mountpoint if (var_dir) { // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR int rv = mkdir(RUN_WHITELIST_VAR_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_VAR_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_VAR_DIR, 0755) < 0) errExit("chmod"); if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /var if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /var directory\n"); if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /var"); fs_logger("mount tmpfs on /var"); } // /dev mountpoint if (dev_dir) { // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR int rv = mkdir(RUN_WHITELIST_DEV_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_DEV_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_DEV_DIR, 0755) < 0) errExit("chmod"); if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /dev if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /dev directory\n"); if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /dev"); fs_logger("mount tmpfs on /dev"); } // /opt mountpoint if (opt_dir) { // keep a copy of real /opt directory in RUN_WHITELIST_DEV_DIR int rv = mkdir(RUN_WHITELIST_OPT_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_OPT_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_OPT_DIR, 0755) < 0) errExit("chmod"); if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /opt if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /opt directory\n"); if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /opt"); fs_logger("mount tmpfs on /opt"); } // go through profile rules again, and interpret whitelist commands entry = cfg.profile; while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } //printf("here %d#%s#\n", __LINE__, entry->data); // whitelist the real file whitelist_path(entry); // create the link if any if (entry->link) { // if the link is already there, do not bother struct stat s; if (stat(entry->link, &s) != 0) { // create the path if necessary mkpath(entry->link, s.st_mode); int rv = symlink(entry->data + 10, entry->link); if (rv) fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); else if (arg_debug || arg_debug_whitelists) printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); } } entry = entry->next; } // mask the real home directory, currently mounted on RUN_WHITELIST_HOME_DIR if (home_dir) { if (mount("tmpfs", RUN_WHITELIST_HOME_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } // mask the real /tmp directory, currently mounted on RUN_WHITELIST_TMP_DIR if (tmp_dir) { if (mount("tmpfs", RUN_WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } if (new_name) free(new_name); return; errexit: fprintf(stderr, "Error: invalid whitelist path %s\n", new_name); exit(1); }
int sandbox(void* sandbox_arg) { // Get rid of unused parameter warning (void)sandbox_arg; pid_t child_pid = getpid(); if (arg_debug) printf("Initializing child process\n"); // close each end of the unused pipes close(parent_to_child_fds[1]); close(child_to_parent_fds[0]); // wait for parent to do base setup wait_for_other(parent_to_child_fds[0]); if (arg_debug && child_pid == 1) printf("PID namespace installed\n"); //**************************** // set hostname //**************************** if (cfg.hostname) { if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0) errExit("sethostname"); } //**************************** // mount namespace //**************************** // mount events are not forwarded between the host the sandbox if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { chk_chroot(); } //**************************** // log sandbox data //**************************** if (cfg.name) fs_logger2("sandbox name:", cfg.name); fs_logger2int("sandbox pid:", (int) sandbox_pid); if (cfg.chrootdir) fs_logger("sandbox filesystem: chroot"); else if (arg_overlay) fs_logger("sandbox filesystem: overlay"); else fs_logger("sandbox filesystem: local"); fs_logger("install mount namespace"); //**************************** // netfilter etc. //**************************** if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter netfilter(arg_netfilter_file); } if (arg_netfilter6 && any_bridge_configured()) { // assuming by default the client filter netfilter6(arg_netfilter6_file); } // load IBUS env variables if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { // do nothing - there are problems with ibus version 1.5.11 } else env_ibus_load(); // grab a copy of cp command fs_build_cp_command(); // trace pre-install if (arg_trace || arg_tracelog) fs_trace_preload(); //**************************** // configure filesystem //**************************** #ifdef HAVE_SECCOMP int enforce_seccomp = 0; #endif #ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); // redo cp command fs_build_cp_command(); // force caps and seccomp if not started as root if (getuid() != 0) { // force default seccomp inside the chroot, no keep or drop list // the list build on top of the default drop list is kept intact arg_seccomp = 1; #ifdef HAVE_SECCOMP enforce_seccomp = 1; #endif if (cfg.seccomp_list_drop) { free(cfg.seccomp_list_drop); cfg.seccomp_list_drop = NULL; } if (cfg.seccomp_list_keep) { free(cfg.seccomp_list_keep); cfg.seccomp_list_keep = NULL; } // disable all capabilities if (arg_caps_default_filter || arg_caps_list) fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); arg_caps_drop_all = 1; // drop all supplementary groups; /etc/group file inside chroot // is controlled by a regular usr arg_nogroups = 1; if (!arg_quiet) printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); } else arg_seccomp = 1; //**************************** // trace pre-install, this time inside chroot //**************************** if (arg_trace || arg_tracelog) fs_trace_preload(); } else #endif if (arg_overlay) fs_overlayfs(); else fs_basic_fs(); //**************************** // set hostname in /etc/hostname //**************************** if (cfg.hostname) { fs_hostname(cfg.hostname); } //**************************** // private mode //**************************** if (arg_private) { if (cfg.home_private) // --private= fs_private_homedir(); else // --private fs_private(); } if (arg_private_dev) fs_private_dev(); if (arg_private_etc) { fs_private_etc_list(); // create /etc/ld.so.preload file again if (arg_trace || arg_tracelog) fs_trace_preload(); } if (arg_private_bin) fs_private_bin_list(); if (arg_private_tmp) fs_private_tmp(); //**************************** // apply the profile file //**************************** if (cfg.profile) { // apply all whitelist commands ... fs_whitelist(); // ... followed by blacklist commands fs_blacklist(); } //**************************** // install trace //**************************** if (arg_trace || arg_tracelog) fs_trace(); //**************************** // update /proc, /dev, /boot directorymy //**************************** fs_proc_sys_dev_boot(); //**************************** // --nosound and fix for pulseaudio 7.0 //**************************** if (arg_nosound) pulseaudio_disable(); else pulseaudio_init(); //**************************** // networking //**************************** if (arg_nonetwork) { net_if_up("lo"); if (arg_debug) printf("Network namespace enabled, only loopback interface available\n"); } else if (any_bridge_configured() || any_interface_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); if (mac_not_zero(cfg.bridge0.macsandbox)) net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); sandbox_if_up(&cfg.bridge0); if (mac_not_zero(cfg.bridge1.macsandbox)) net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); sandbox_if_up(&cfg.bridge1); if (mac_not_zero(cfg.bridge2.macsandbox)) net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); sandbox_if_up(&cfg.bridge2); if (mac_not_zero(cfg.bridge3.macsandbox)) net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); sandbox_if_up(&cfg.bridge3); // add a default route if (cfg.defaultgw) { // set the default route if (net_add_route(0, 0, cfg.defaultgw)) fprintf(stderr, "Warning: cannot configure default route\n"); } // enable interfaces if (cfg.interface0.configured && cfg.interface0.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); net_if_up(cfg.interface0.dev); } if (cfg.interface1.configured && cfg.interface1.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); net_if_up(cfg.interface1.dev); } if (cfg.interface2.configured && cfg.interface2.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); net_if_up(cfg.interface2.dev); } if (cfg.interface3.configured && cfg.interface3.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); net_if_up(cfg.interface3.dev); } if (arg_debug) printf("Network namespace enabled\n"); } // if any dns server is configured, it is time to set it now fs_resolvconf(); fs_logger_print(); fs_logger_change_owner(); // print network configuration if (!arg_quiet) { if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { printf("\n"); if (any_bridge_configured() || any_interface_configured()) net_ifprint(); if (cfg.defaultgw != 0) printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); if (cfg.dns1 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); if (cfg.dns2 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); if (cfg.dns3 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); printf("\n"); } } fs_delete_cp_command(); //**************************** // set application environment //**************************** prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } // set environment env_defaults(); // set user-supplied environment variables env_apply(); // set nice if (arg_nice) { errno = 0; int rv = nice(cfg.nice); (void) rv; if (errno) { fprintf(stderr, "Warning: cannot set nice value\n"); errno = 0; } } // clean /tmp/.X11-unix sockets fs_x11(); //**************************** // set security filters //**************************** // set capabilities if (!arg_noroot) set_caps(); // set rlimits set_rlimits(); // set seccomp #ifdef HAVE_SECCOMP // install protocol filter if (cfg.protocol) { protocol_filter(); // install filter protocol_filter_save(); // save filter in PROTOCOL_CFG } // if a keep list is available, disregard the drop list if (arg_seccomp == 1) { if (cfg.seccomp_list_keep) seccomp_filter_keep(); else if (cfg.seccomp_list_errno) seccomp_filter_errno(); else seccomp_filter_drop(enforce_seccomp); } #endif // set cpu affinity if (cfg.cpus) { save_cpu(); // save cpu affinity mask to CPU_CFG file set_cpu_affinity(); } // save cgroup in CGROUP_CFG file if (cfg.cgroup) save_cgroup(); //**************************************** // drop privileges or create a new user namespace //**************************************** save_nogroups(); if (arg_noroot) { int rv = unshare(CLONE_NEWUSER); if (rv == -1) { fprintf(stderr, "Error: cannot mount a new user namespace\n"); perror("unshare"); drop_privs(arg_nogroups); } } else drop_privs(arg_nogroups); // notify parent that new user namespace has been created so a proper // UID/GID map can be setup notify_other(child_to_parent_fds[1]); close(child_to_parent_fds[1]); // wait for parent to finish setting up a proper UID/GID map wait_for_other(parent_to_child_fds[0]); close(parent_to_child_fds[0]); // somehow, the new user namespace resets capabilities; // we need to do them again if (arg_noroot) { set_caps(); if (arg_debug) printf("noroot user namespace installed\n"); } //**************************************** // fork the application and monitor it //**************************************** pid_t app_pid = fork(); if (app_pid == -1) errExit("fork"); if (app_pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died start_application(); // start app } monitor_application(app_pid); // monitor application return 0; }
static void empty_dev_shm(void) { // create an empty /dev/shm directory mkdir_attr("/dev/shm", 01777, 0, 0); fs_logger("mkdir /dev/shm"); fs_logger("create /dev/shm"); }
// 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"); } } }
void fs_private_dev(void){ // install a new /dev directory if (arg_debug) printf("Mounting tmpfs on /dev\n"); // create DRI_DIR // keep a copy of dev directory mkdir_attr(RUN_DEV_DIR, 0755, 0, 0); if (mount("/dev", RUN_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev"); // create DEVLOG_FILE int have_devlog = 0; struct stat s; if (stat("/dev/log", &s) == 0) { have_devlog = 1; FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); if (!fp) have_devlog = 0; else { fprintf(fp, "\n"); fclose(fp); if (mount("/dev/log", RUN_DEVLOG_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); } } // mount tmpfs on top of /dev if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /dev"); fs_logger("tmpfs /dev"); // optional devices: sound, video cards etc... deventry_mount(); // bring back /dev/log if (have_devlog) { FILE *fp = fopen("/dev/log", "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (mount(RUN_DEVLOG_FILE, "/dev/log", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); fs_logger("clone /dev/log"); } } // bring forward the current /dev/shm directory if necessary if (arg_debug) printf("Process /dev/shm directory\n"); process_dev_shm(); if (mount(RUN_RO_DIR, RUN_DEV_DIR, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable run dev directory"); // create default devices create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5 fs_logger("mknod /dev/zero"); create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3 fs_logger("mknod /dev/null"); create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7 fs_logger("mknod /dev/full"); create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8 fs_logger("mknod /dev/random"); create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9 fs_logger("mknod /dev/urandom"); create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0 fs_logger("mknod /dev/tty"); #if 0 create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0"); create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1"); #endif // pseudo-terminal mkdir_attr("/dev/pts", 0755, 0, 0); fs_logger("mkdir /dev/pts"); fs_logger("create /dev/pts"); create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); fs_logger("mknod /dev/pts/ptmx"); create_link("/dev/pts/ptmx", "/dev/ptmx"); // code before github issue #351 // mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts // if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0) // errExit("mounting /dev/pts"); // mount /dev/pts gid_t ttygid = get_group_id("tty"); char *data; if (asprintf(&data, "newinstance,gid=%d,mode=620,ptmxmode=0666", (int) ttygid) == -1) errExit("asprintf"); if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, data) < 0) errExit("mounting /dev/pts"); free(data); fs_logger("clone /dev/pts"); // stdin, stdout, stderr #if 0 create_link("/proc/self/fd", "/dev/fd"); create_link("/proc/self/fd/0", "/dev/stdin"); create_link("/proc/self/fd/1", "/dev/stdout"); create_link("/proc/self/fd/2", "/dev/stderr"); #endif // symlinks for DVD/CD players if (stat("/dev/sr0", &s) == 0) { create_link("/dev/sr0", "/dev/cdrom"); create_link("/dev/sr0", "/dev/cdrw"); create_link("/dev/sr0", "/dev/dvd"); create_link("/dev/sr0", "/dev/dvdrw"); } }