void network_set_run_file(pid_t pid) { char *fname; if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1) errExit("asprintf"); // create an empty file and set mod and ownership FILE *fp = fopen(fname, "w"); if (fp) { if (cfg.bridge0.configured) fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox); if (cfg.bridge1.configured) fprintf(fp, "%s:%s\n", cfg.bridge1.dev, cfg.bridge1.devsandbox); if (cfg.bridge2.configured) fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox); if (cfg.bridge3.configured) fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox); SET_PERMS_STREAM(fp, 0, 0, 0644); fclose(fp); } else { fprintf(stderr, "Error: cannot create network map file\n"); exit(1); } free(fname); }
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"); }
//*********************************** // run file handling //*********************************** static void bandwidth_create_run_file(pid_t pid) { char *fname; if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) errExit("asprintf"); // if the file already exists, do nothing struct stat s; if (stat(fname, &s) == 0) { free(fname); return; } // create an empty file and set mod and ownership /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (fp) { SET_PERMS_STREAM(fp, 0, 0, 0644); fclose(fp); } else { fprintf(stderr, "Error: cannot create bandwidth file\n"); exit(1); } free(fname); }
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 }
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_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"); SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); fclose(fp); 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 save_cpu(void) { if (cfg.cpus == 0) return; FILE *fp = fopen(RUN_CPU_CFG, "w"); if (fp) { fprintf(fp, "%x\n", cfg.cpus); SET_PERMS_STREAM(fp, 0, 0, 0600); fclose(fp); } else { fprintf(stderr, "Error: cannot save cpu affinity mask\n"); exit(1); } }
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 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 { fwarning("cannot find /var/run/utmp\n"); return; } // 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 int rv = fwrite(&u_boot, sizeof(u_boot), 1, fp); (void) rv; SET_PERMS_STREAM(fp, 0, utmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); fclose(fp); // 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 deventry_mount(void) { int i = 0; while (dev[i].dev_fname != NULL) { struct stat s; if (stat(dev[i].run_fname, &s) == 0) { // check device type and subsystem configuration if ((dev[i].type == DEV_SOUND && arg_nosound == 0) || (dev[i].type == DEV_3D && arg_no3d == 0) || (dev[i].type == DEV_VIDEO && arg_novideo == 0) || (dev[i].type == DEV_TV && arg_notv == 0) || (dev[i].type == DEV_DVD && arg_nodvd == 0) || (dev[i].type == DEV_U2F && arg_nou2f == 0)) { int dir = is_dir(dev[i].run_fname); if (arg_debug) printf("mounting %s %s\n", dev[i].run_fname, (dir)? "directory": "file"); if (dir) { mkdir_attr(dev[i].dev_fname, 0755, 0, 0); } else { struct stat s; if (stat(dev[i].run_fname, &s) == -1) { if (arg_debug) fwarning("cannot stat %s file\n", dev[i].run_fname); i++; continue; } FILE *fp = fopen(dev[i].dev_fname, "w"); if (fp) { fprintf(fp, "\n"); SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); fclose(fp); } } if (mount(dev[i].run_fname, dev[i].dev_fname, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting dev file"); fs_logger2("whitelist", dev[i].dev_fname); } } i++; } }
void create_empty_file_as_root(const char *fname, mode_t mode) { assert(fname); mode &= 07777; struct stat s; if (stat(fname, &s)) { if (arg_debug) printf("Creating empty %s file\n", fname); /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (!fp) errExit("fopen"); SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); fclose(fp); if (chmod(fname, mode) == -1) errExit("chmod"); } }
void save_cgroup(void) { if (cfg.cgroup == NULL) return; FILE *fp = fopen(RUN_CGROUP_CFG, "w"); if (fp) { fprintf(fp, "%s", cfg.cgroup); fflush(0); SET_PERMS_STREAM(fp, 0, 0, 0644); if (fclose(fp)) goto errout; } else goto errout; return; errout: fprintf(stderr, "Error: cannot save cgroup\n"); exit(1); }
// return -1 if error, 0 if no error void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) { pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop privileges drop_privs(0); FILE *fp = fopen(fname, "w"); if (fp) { fprintf(fp, "\n"); SET_PERMS_STREAM(fp, uid, gid, mode); fclose(fp); } #ifdef HAVE_GCOV __gcov_flush(); #endif _exit(0); } // wait for the child to finish waitpid(child, NULL, 0); }
static void deventry_mount(void) { int i = 0; while (dev[i].dev_fname != NULL) { struct stat s; if (stat(dev[i].run_fname, &s) == 0) { int dir = is_dir(dev[i].run_fname); if (arg_debug) printf("mounting %s %s\n", dev[i].run_fname, (dir)? "directory": "file"); if (dir) { if (mkdir(dev[i].dev_fname, 0755) == -1) errExit("mkdir"); if (chmod(dev[i].dev_fname, 0755) == -1) errExit("chmod"); ASSERT_PERMS(dev[i].dev_fname, 0, 0, 0755); } else { struct stat s; if (stat(dev[i].run_fname, &s) == -1) { if (arg_debug) printf("Warning: cannot stat %s file\n", dev[i].run_fname); i++; continue; } FILE *fp = fopen(dev[i].dev_fname, "w"); if (fp) { fprintf(fp, "\n"); SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); fclose(fp); } } if (mount(dev[i].run_fname, dev[i].dev_fname, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting dev file"); fs_logger2("whitelist", dev[i].dev_fname); } i++; } }
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); } }
void fs_hostname(const char *hostname) { struct stat s; // create a new /etc/hostname if (stat("/etc/hostname", &s) == 0) { if (arg_debug) printf("Creating a new /etc/hostname file\n"); create_empty_file_as_root(RUN_HOSTNAME_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); // bind-mount the file on top of /etc/hostname if (mount(RUN_HOSTNAME_FILE, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind /etc/hostname"); fs_logger("create /etc/hostname"); } // create a new /etc/hosts if (stat("/etc/hosts", &s) == 0) { if (arg_debug) printf("Creating a new /etc/hosts file\n"); // copy /etc/host into our new file, and modify it on the fly /* coverity[toctou] */ FILE *fp1 = fopen("/etc/hosts", "r"); if (!fp1) goto errexit; FILE *fp2 = fopen(RUN_HOSTS_FILE, "w"); if (!fp2) { fclose(fp1); goto errexit; } char buf[4096]; int done = 0; while (fgets(buf, sizeof(buf), fp1)) { // remove '\n' char *ptr = strchr(buf, '\n'); if (ptr) *ptr = '\0'; // copy line if (strstr(buf, "127.0.0.1") && done == 0) { done = 1; fprintf(fp2, "%s %s\n", buf, hostname); } else fprintf(fp2, "%s\n", buf); } fclose(fp1); // mode and owner SET_PERMS_STREAM(fp2, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); fclose(fp2); // bind-mount the file on top of /etc/hostname if (mount(RUN_HOSTS_FILE, "/etc/hosts", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind /etc/hosts"); fs_logger("create /etc/hosts"); } return; errexit: fprintf(stderr, "Error: cannot create hostname file\n"); exit(1); }
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); }
// 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"); // make it a mount point and add mount flags if (mount(RUN_PULSE_DIR, RUN_PULSE_DIR, NULL, MS_BIND, NULL) < 0 || mount(NULL, RUN_PULSE_DIR, NULL, MS_NOEXEC|MS_NODEV|MS_NOSUID|MS_BIND|MS_REMOUNT, NULL) < 0) errExit("mount 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 *dir1; if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) errExit("asprintf"); if (lstat(dir1, &s) == -1) { pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop privileges drop_privs(0); int rv = mkdir(dir1, 0755); if (rv == 0) { if (set_perms(dir1, getuid(), getgid(), 0755)) {;} // do nothing } #ifdef HAVE_GCOV __gcov_flush(); #endif _exit(0); } // wait for the child to finish waitpid(child, NULL, 0); fs_logger2("create", dir1); } else { // we expect a user owned directory if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: user .config is a symbolic link\n"); else fprintf(stderr, "Error: user .config is not a directory owned by the current user\n"); exit(1); } } free(dir1); if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) errExit("asprintf"); if (lstat(dir1, &s) == -1) { pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop privileges drop_privs(0); int rv = mkdir(dir1, 0700); if (rv == 0) { if (set_perms(dir1, getuid(), getgid(), 0700)) {;} // do nothing } #ifdef HAVE_GCOV __gcov_flush(); #endif _exit(0); } // wait for the child to finish waitpid(child, NULL, 0); fs_logger2("create", dir1); } else { // we expect a user owned directory if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: user .config/pulse is a symbolic link\n"); else fprintf(stderr, "Error: user .config/pulse is not a directory owned by the current user\n"); exit(1); } } free(dir1); // if we have ~/.config/pulse mount the new directory, else set environment variable. char *homeusercfg; if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) errExit("asprintf"); 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 || s.st_uid != getuid()) errExit("fstat"); // 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); }
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); } }
// 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); }
static void whitelist_path(ProfileEntry *entry) { assert(entry); char *path = entry->data + 10; assert(path); const char *fname; char *wfile = NULL; if (entry->home_dir) { if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { fname = path + strlen(cfg.homedir); if (*fname == '\0') goto errexit; } else fname = path; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) errExit("asprintf"); } else if (entry->tmp_dir) { fname = path + 4; // strlen("/tmp") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) errExit("asprintf"); } else if (entry->media_dir) { fname = path + 6; // strlen("/media") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) errExit("asprintf"); } else if (entry->mnt_dir) { fname = path + 4; // strlen("/mnt") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) errExit("asprintf"); } else if (entry->var_dir) { fname = path + 4; // strlen("/var") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) errExit("asprintf"); } else if (entry->dev_dir) { fname = path + 4; // strlen("/dev") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) errExit("asprintf"); } else if (entry->opt_dir) { fname = path + 4; // strlen("/opt") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) errExit("asprintf"); } else if (entry->srv_dir) { fname = path + 4; // strlen("/srv") if (*fname == '\0') goto errexit; if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) errExit("asprintf"); } // check if the file exists struct stat s; if (wfile && stat(wfile, &s) == 0) { if (arg_debug || arg_debug_whitelists) printf("Whitelisting %s\n", path); } else { return; } // create the path if necessary mkpath(path, s.st_mode); fs_logger2("whitelist", path); // process directory if (S_ISDIR(s.st_mode)) { // create directory int rv = mkdir(path, 0755); (void) rv; } // process regular file else { if (access(path, R_OK)) { // create an empty file FILE *fp = fopen(path, "w"); if (!fp) { fprintf(stderr, "Error: cannot create empty file in home directory\n"); exit(1); } // set file properties SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); fclose(fp); } else return; // the file is already present } // mount if (mount(wfile, path, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); free(wfile); return; errexit: fprintf(stderr, "Error: file %s is not in the whitelisted directory\n", path); exit(1); }