コード例 #1
0
ファイル: bandwidth.c プロジェクト: maces/firejail
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);
}
コード例 #2
0
ファイル: fs_trace.c プロジェクト: Fred-Barclay/firejail
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");
}
コード例 #3
0
ファイル: bandwidth.c プロジェクト: maces/firejail
//***********************************
// 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);
}
コード例 #4
0
ファイル: x11.c プロジェクト: reinerh/firejail
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
}
コード例 #5
0
ファイル: fs_var.c プロジェクト: manevich/firejail
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");
}
コード例 #6
0
ファイル: fs_var.c プロジェクト: manevich/firejail
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");
	}
}
コード例 #7
0
ファイル: cpu.c プロジェクト: reinerh/firejail
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);
	}
}
コード例 #8
0
ファイル: fs_trace.c プロジェクト: Fred-Barclay/firejail
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");
	}
}
コード例 #9
0
ファイル: fs_var.c プロジェクト: manevich/firejail
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");
}
コード例 #10
0
ファイル: fs_dev.c プロジェクト: manevich/firejail
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++;
	}
}
コード例 #11
0
ファイル: util.c プロジェクト: netblue30/firejail
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");
	}
}
コード例 #12
0
ファイル: cgroup.c プロジェクト: maces/firejail
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);
}
コード例 #13
0
ファイル: util.c プロジェクト: maces/firejail
// 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);
}
コード例 #14
0
ファイル: fs_dev.c プロジェクト: reinerh/firejail
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++;	
	}
}
コード例 #15
0
ファイル: fs_hostname.c プロジェクト: maces/firejail
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);
	}
}
コード例 #16
0
ファイル: fs_hostname.c プロジェクト: maces/firejail
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);
}
コード例 #17
0
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);
}
コード例 #18
0
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);
}
コード例 #19
0
ファイル: pulseaudio.c プロジェクト: manevich/firejail
// 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);
}
コード例 #20
0
ファイル: fs_home.c プロジェクト: netblue30/firejail
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);
	}
}
コード例 #21
0
ファイル: pulseaudio.c プロジェクト: Fred-Barclay/firejail
// 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);
}
コード例 #22
0
ファイル: fs_whitelist.c プロジェクト: netblue30/firejail
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);
}