Beispiel #1
0
// default sandbox env variables
void env_defaults(void) {
	// Qt fixes
	if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
		errExit("setenv");
	if (setenv("QML_DISABLE_DISK_CACHE", "1", 1) < 0)
		errExit("setenv");
//	if (setenv("QTWEBENGINE_DISABLE_SANDBOX", "1", 1) < 0)
//		errExit("setenv");
//	if (setenv("MOZ_NO_REMOTE, "1", 1) < 0)
//		errExit("setenv");
	if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
		errExit("setenv");
	if (!cfg.shell)
		cfg.shell = guess_shell();
	if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0)
		errExit("setenv");

	// spawn KIO slaves inside the sandbox
	if (setenv("KDE_FORK_SLAVES", "1", 1) < 0)
		errExit("setenv");

	// set prompt color to green
	int set_prompt = 0;
	if (checkcfg(CFG_FIREJAIL_PROMPT))
		set_prompt = 1;
	else { // check FIREJAIL_PROMPT="yes" environment variable
		char *prompt = getenv("FIREJAIL_PROMPT");
		if (prompt && strcmp(prompt, "yes") == 0)
			set_prompt = 1;
	}

	if (set_prompt) {
		//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
		if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
			errExit("setenv");
	}
	else {
		// remove PROMPT_COMMAND
		if (setenv("PROMPT_COMMAND", ":", 1) < 0) // unsetenv() will not work here, bash still picks it up from somewhere
			errExit("setenv");
	}

	// set the window title
	if (!arg_quiet)
		printf("\033]0;firejail %s\007", cfg.window_title);
	fflush(0);
}
Beispiel #2
0
void dbus_session_disable(void) {
	if (!checkcfg(CFG_DBUS)) {
		fwarning("D-Bus handling is disabled in Firejail configuration file\n");
		return;
	}

	char *path;
	if (asprintf(&path, "/run/user/%d/bus", getuid()) == -1)
		errExit("asprintf");
	char *env_var;
	if (asprintf(&env_var, "DBUS_SESSION_BUS_ADDRESS=unix:path=%s", path) == -1)
		errExit("asprintf");

	// set a new environment variable: DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<UID>/bus
	if (setenv("DBUS_SESSION_BUS_ADDRESS", env_var, 1) == -1) {
		fprintf(stderr, "Error: cannot modify DBUS_SESSION_BUS_ADDRESS required by --nodbus\n");
		exit(1);
	}

	// blacklist the path
	disable_file_or_dir(path);
	free(path);
	free(env_var);

	// look for a possible abstract unix socket

	// --net=none
	if (arg_nonetwork)
		return;

	// --net=eth0
	if (any_bridge_configured())
		return;

	// --protocol=unix
#ifdef HAVE_SECCOMP
	if (cfg.protocol && !strstr(cfg.protocol, "unix"))
		return;
#endif

	fwarning("An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.\n");
}
Beispiel #3
0
// check profile line; if line == 0, this was generated from a command line option
// return 1 if the command is to be added to the linked list of profile commands
// return 0 if the command was already executed inside the function
int profile_check_line(char *ptr, int lineno, const char *fname) {
	EUID_ASSERT();

	// check ignore list
	int i;
	for (i = 0; i < MAX_PROFILE_IGNORE; i++) {
		if (cfg.profile_ignore[i] == NULL)
			break;

		if (strncmp(ptr, cfg.profile_ignore[i], strlen(cfg.profile_ignore[i])) == 0)
			return 0;	// ignore line
	}

	if (strncmp(ptr, "ignore ", 7) == 0) {
		char *str = strdup(ptr + 7);
		if (*str == '\0') {
			fprintf(stderr, "Error: invalid ignore option\n");
			exit(1);
		}
		// find an empty entry in profile_ignore array
		int j;
		for (j = 0; j < MAX_PROFILE_IGNORE; j++) {
			if (cfg.profile_ignore[j] == NULL)
				break;
		}
		if (j >= MAX_PROFILE_IGNORE) {
			fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE);
			exit(1);
		}
		// ... and configure it
		else
			cfg.profile_ignore[j] = str;

		return 0;
	}

	// mkdir
	if (strncmp(ptr, "mkdir ", 6) == 0) {
		fs_mkdir(ptr + 6);
		return 1;	// process mkdir again while applying blacklists
	}
	// mkfile
	if (strncmp(ptr, "mkfile ", 7) == 0) {
		fs_mkfile(ptr + 7);
		return 1;	// process mkfile again while applying blacklists
	}
	// sandbox name
	else if (strncmp(ptr, "name ", 5) == 0) {
		cfg.name = ptr + 5;
		if (strlen(cfg.name) == 0) {
			fprintf(stderr, "Error: invalid sandbox name\n");
			exit(1);
		}
		return 0;
	}
	else if (strcmp(ptr, "ipc-namespace") == 0) {
		arg_ipc = 1;
		return 0;
	}
	// seccomp, caps, private, user namespace
	else if (strcmp(ptr, "noroot") == 0) {
#if HAVE_USERNS
		if (checkcfg(CFG_USERNS))
			check_user_namespace();
		else
			warning_feature_disabled("noroot");
#endif

		return 0;
	}
	else if (strcmp(ptr, "nonewprivs") == 0) {
		arg_nonewprivs = 1;
		return 0;
	}
	else if (strcmp(ptr, "seccomp") == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP))
			arg_seccomp = 1;
		else
			warning_feature_disabled("seccomp");
#endif
		return 0;
	}
	else if (strcmp(ptr, "caps") == 0) {
		arg_caps_default_filter = 1;
		return 0;
	}
	else if (strcmp(ptr, "caps.drop all") == 0) {
		arg_caps_drop_all = 1;
		return 0;
	}
	else if (strcmp(ptr, "shell none") == 0) {
		arg_shell_none = 1;
		return 0;
	}
	else if (strcmp(ptr, "tracelog") == 0) {
		arg_tracelog = 1;
		return 0;
	}
	else if (strcmp(ptr, "private") == 0) {
		arg_private = 1;
		return 0;
	}
	if (strncmp(ptr, "private-home ", 13) == 0) {
#ifdef HAVE_PRIVATE_HOME
		if (checkcfg(CFG_PRIVATE_HOME)) {
			if (cfg.home_private_keep) {
				if ( asprintf(&cfg.home_private_keep, "%s,%s", cfg.home_private_keep, ptr + 13) < 0 )
					errExit("asprintf");
			} else
				cfg.home_private_keep = ptr + 13;
			arg_private = 1;
		}
		else
			warning_feature_disabled("private-home");
#endif
		return 0;
	}
	else if (strcmp(ptr, "allusers") == 0) {
		arg_allusers = 1;
		return 0;
	}
	else if (strcmp(ptr, "private-dev") == 0) {
		arg_private_dev = 1;
		return 0;
	}
	else if (strcmp(ptr, "private-tmp") == 0) {
		arg_private_tmp = 1;
		return 0;
	}
	else if (strcmp(ptr, "nogroups") == 0) {
		arg_nogroups = 1;
		return 0;
	}
	else if (strcmp(ptr, "nosound") == 0) {
		arg_nosound = 1;
		return 0;
	}
	else if (strcmp(ptr, "novideo") == 0) {
		arg_novideo = 1;
		return 0;
	}
	else if (strcmp(ptr, "no3d") == 0) {
		arg_no3d = 1;
		return 0;
	}
	else if (strcmp(ptr, "allow-private-blacklist") == 0) {
		arg_allow_private_blacklist = 1;
		return 0;
	}
	else if (strcmp(ptr, "netfilter") == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK))
			arg_netfilter = 1;
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}
	else if (strncmp(ptr, "netfilter ", 10) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_netfilter = 1;
			arg_netfilter_file = strdup(ptr + 10);
			if (!arg_netfilter_file)
				errExit("strdup");
			check_netfilter_file(arg_netfilter_file);
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}
	else if (strncmp(ptr, "netfilter6 ", 11) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_netfilter6 = 1;
			arg_netfilter6_file = strdup(ptr + 11);
			if (!arg_netfilter6_file)
				errExit("strdup");
			check_netfilter_file(arg_netfilter6_file);
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}
	else if (strcmp(ptr, "net none") == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_nonetwork  = 1;
			cfg.bridge0.configured = 0;
			cfg.bridge1.configured = 0;
			cfg.bridge2.configured = 0;
			cfg.bridge3.configured = 0;
			cfg.interface0.configured = 0;
			cfg.interface1.configured = 0;
			cfg.interface2.configured = 0;
			cfg.interface3.configured = 0;
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}
	else if (strncmp(ptr, "net ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
#ifdef HAVE_NETWORK_RESTRICTED
			// compile time restricted networking
			if (getuid() != 0) {
				fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n");
				exit(1);
			}
#endif
			// run time restricted networking
			if (checkcfg(CFG_RESTRICTED_NETWORK) && getuid() != 0) {
				fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n");
				exit(1);
			}

			if (strcmp(ptr + 4, "lo") == 0) {
				fprintf(stderr, "Error: cannot attach to lo device\n");
				exit(1);
			}

			Bridge *br;
			if (cfg.bridge0.configured == 0)
				br = &cfg.bridge0;
			else if (cfg.bridge1.configured == 0)
				br = &cfg.bridge1;
			else if (cfg.bridge2.configured == 0)
				br = &cfg.bridge2;
			else if (cfg.bridge3.configured == 0)
				br = &cfg.bridge3;
			else {
				fprintf(stderr, "Error: maximum 4 network devices are allowed\n");
				exit(1);
			}
			net_configure_bridge(br, ptr + 4);
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "veth-name ", 10) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}

			br->veth_name = strdup(ptr + 10);
			if (br->veth_name == NULL)
				errExit("strdup");
			if (*br->veth_name == '\0') {
				fprintf(stderr, "Error: no veth-name configured\n");
				exit(1);
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "iprange ", 8) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->iprange_start || br->iprange_end) {
				fprintf(stderr, "Error: cannot configure the IP range twice for the same interface\n");
				exit(1);
			}

			// parse option arguments
			char *firstip = ptr + 8;
			char *secondip = firstip;
			while (*secondip != '\0') {
				if (*secondip == ',')
					break;
				secondip++;
			}
			if (*secondip == '\0') {
				fprintf(stderr, "Error: invalid IP range\n");
				exit(1);
			}
			*secondip = '\0';
			secondip++;

			// check addresses
			if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) ||
			    br->iprange_start >= br->iprange_end) {
				fprintf(stderr, "Error: invalid IP range\n");
				exit(1);
			}
			if (in_netrange(br->iprange_start, br->ip, br->mask) || in_netrange(br->iprange_end, br->ip, br->mask)) {
				fprintf(stderr, "Error: IP range addresses not in network range\n");
				exit(1);
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}


	else if (strncmp(ptr, "mac ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}

			if (mac_not_zero(br->macsandbox)) {
				fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n");
				exit(1);
			}

			// read the address
			if (atomac(ptr + 4, br->macsandbox)) {
				fprintf(stderr, "Error: invalid MAC address\n");
				exit(1);
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "mtu ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}

			if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) {
				fprintf(stderr, "Error: invalid mtu value\n");
				exit(1);
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "ip ", 3) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->arg_ip_none || br->ipsandbox) {
				fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
				exit(1);
			}

			// configure this IP address for the last bridge defined
			if (strcmp(ptr + 3, "none") == 0)
				br->arg_ip_none = 1;
			else {
				if (atoip(ptr + 3, &br->ipsandbox)) {
					fprintf(stderr, "Error: invalid IP address\n");
					exit(1);
				}
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "ip6 ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->arg_ip_none || br->ip6sandbox) {
				fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
				exit(1);
			}

			// configure this IP address for the last bridge defined
			// todo: verify ipv6 syntax
			br->ip6sandbox = ptr + 4;
//			if (atoip(argv[i] + 5, &br->ipsandbox)) {
//				fprintf(stderr, "Error: invalid IP address\n");
//				exit(1);
//			}

		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	else if (strncmp(ptr, "defaultgw ", 10) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			if (atoip(ptr + 10, &cfg.defaultgw)) {
				fprintf(stderr, "Error: invalid IP address\n");
				exit(1);
			}
		}
		else
			warning_feature_disabled("networking");
#endif
		return 0;
	}

	if (strcmp(ptr, "apparmor") == 0) {
#ifdef HAVE_APPARMOR
		arg_apparmor = 1;
#endif
		return 0;
	}

	if (strncmp(ptr, "protocol ", 9) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			if (cfg.protocol) {
				fwarning("a protocol list is present, the new list \"%s\" will not be installed\n", ptr + 9);
				return 0;
			}

			// store list
			cfg.protocol = strdup(ptr + 9);
			if (!cfg.protocol)
				errExit("strdup");
		}
		else
			warning_feature_disabled("seccomp");
#endif
		return 0;
	}

	if (strncmp(ptr, "env ", 4) == 0) {
		env_store(ptr + 4, SETENV);
		return 0;
	}
	if (strncmp(ptr, "rmenv ", 6) == 0) {
		env_store(ptr + 6, RMENV);
		return 0;
	}

	// seccomp drop list on top of default list
	if (strncmp(ptr, "seccomp ", 8) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list = seccomp_check_list(ptr + 8);
		}
		else if (!arg_quiet)
			warning_feature_disabled("seccomp");
#endif

		return 0;
	}

	// seccomp drop list without default list
	if (strncmp(ptr, "seccomp.drop ", 13) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list_drop = seccomp_check_list(ptr + 13);
		}
		else
			warning_feature_disabled("seccomp");
#endif
		return 0;
	}

	// seccomp keep list
	if (strncmp(ptr, "seccomp.keep ", 13) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list_keep= seccomp_check_list(ptr + 13);
		}
		else
			warning_feature_disabled("seccomp");
#endif
		return 0;
	}

	// caps drop list
	if (strncmp(ptr, "caps.drop ", 10) == 0) {
		arg_caps_drop = 1;
		arg_caps_list = strdup(ptr + 10);
		if (!arg_caps_list)
			errExit("strdup");
		// verify caps list and exit if problems
		caps_check_list(arg_caps_list, NULL);
		return 0;
	}

	// caps keep list
	if (strncmp(ptr, "caps.keep ", 10) == 0) {
		arg_caps_keep = 1;
		arg_caps_list = strdup(ptr + 10);
		if (!arg_caps_list)
			errExit("strdup");
		// verify caps list and exit if problems
		caps_check_list(arg_caps_list, NULL);
		return 0;
	}

	// hostname
	if (strncmp(ptr, "hostname ", 9) == 0) {
		cfg.hostname = ptr + 9;
		return 0;
	}

	// hosts-file
	if (strncmp(ptr, "hosts-file ", 11) == 0) {
		cfg.hosts_file = fs_check_hosts_file(ptr + 11);
		return 0;
	}

	// dns
	if (strncmp(ptr, "dns ", 4) == 0) {
		uint32_t dns;
		if (atoip(ptr + 4, &dns)) {
			fprintf(stderr, "Error: invalid DNS server IP address\n");
			return 1;
		}

		if (cfg.dns1 == 0)
			cfg.dns1 = dns;
		else if (cfg.dns2 == 0)
			cfg.dns2 = dns;
		else if (cfg.dns3 == 0)
			cfg.dns3 = dns;
		else {
			fprintf(stderr, "Error: up to 3 DNS servers can be specified\n");
			return 1;
		}
		return 0;
	}

	// cpu affinity
	if (strncmp(ptr, "cpu ", 4) == 0) {
		read_cpu_list(ptr + 4);
		return 0;
	}

	// nice value
	if (strncmp(ptr, "nice ", 4) == 0) {
		cfg.nice = atoi(ptr + 5);
		if (getuid() != 0 &&cfg.nice < 0)
			cfg.nice = 0;
		arg_nice = 1;
		return 0;
	}

	// cgroup
	if (strncmp(ptr, "cgroup ", 7) == 0) {
		set_cgroup(ptr + 7);
		return 0;
	}

	// writable-etc
	if (strcmp(ptr, "writable-etc") == 0) {
		if (cfg.etc_private_keep) {
			fprintf(stderr, "Error: private-etc and writable-etc are mutually exclusive\n");
			exit(1);
		}
		arg_writable_etc = 1;
		return 0;
	}

	if (strcmp(ptr, "machine-id") == 0) {
		arg_machineid = 1;
		return 0;
	}
	// writable-var
	if (strcmp(ptr, "writable-var") == 0) {
		arg_writable_var = 1;
		return 0;
	}
	if (strcmp(ptr, "writable-var-log") == 0) {
		arg_writable_var_log = 1;
		return 0;
	}

	// private directory
	if (strncmp(ptr, "private ", 8) == 0) {
		cfg.home_private = ptr + 8;
		fs_check_private_dir();
		arg_private = 1;
		return 0;
	}

	if (strcmp(ptr, "x11 none") == 0) {
		arg_x11_block = 1;
		return 0;
	}

	if (strcmp(ptr, "x11 xephyr") == 0) {
#ifdef HAVE_X11
		if (checkcfg(CFG_X11)) {
			char *x11env = getenv("FIREJAIL_X11");
			if (x11env && strcmp(x11env, "yes") == 0) {
				return 0;
			}
			else {
				// start x11
				x11_start_xephyr(cfg.original_argc, cfg.original_argv);
				exit(0);
			}
		}
		else
			warning_feature_disabled("x11");
#endif
		return 0;
	}

	if (strcmp(ptr, "x11 xorg") == 0) {
#ifdef HAVE_X11
		if (checkcfg(CFG_X11))
			arg_x11_xorg = 1;
		else
			warning_feature_disabled("x11");
#endif
		return 0;
	}

	if (strcmp(ptr, "x11 xpra") == 0) {
#ifdef HAVE_X11
		if (checkcfg(CFG_X11)) {
			char *x11env = getenv("FIREJAIL_X11");
			if (x11env && strcmp(x11env, "yes") == 0) {
				return 0;
			}
			else {
				// start x11
				x11_start_xpra(cfg.original_argc, cfg.original_argv);
				exit(0);
			}
		}
		else
			warning_feature_disabled("x11");
#endif
		return 0;
	}

	if (strcmp(ptr, "x11 xvfb") == 0) {
#ifdef HAVE_X11
		if (checkcfg(CFG_X11)) {
			char *x11env = getenv("FIREJAIL_X11");
			if (x11env && strcmp(x11env, "yes") == 0) {
				return 0;
			}
			else {
				// start x11
				x11_start_xvfb(cfg.original_argc, cfg.original_argv);
				exit(0);
			}
		}
		else
			warning_feature_disabled("x11");
#endif
		return 0;
	}

	if (strcmp(ptr, "x11") == 0) {
#ifdef HAVE_X11
		if (checkcfg(CFG_X11)) {
			char *x11env = getenv("FIREJAIL_X11");
			if (x11env && strcmp(x11env, "yes") == 0) {
				return 0;
			}
			else {
				// start x11
				x11_start(cfg.original_argc, cfg.original_argv);
				exit(0);
			}
		}
		else
			warning_feature_disabled("x11");
#endif
		return 0;
	}

	// private /etc list of files and directories
	if (strncmp(ptr, "private-etc ", 12) == 0) {
		if (arg_writable_etc) {
			fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
			exit(1);
		}
		if (cfg.etc_private_keep) {
			if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 )
				errExit("asprintf");
		} else {
			cfg.etc_private_keep = ptr + 12;
		}
		arg_private_etc = 1;

		return 0;
	}

	// private /opt list of files and directories
	if (strncmp(ptr, "private-opt ", 12) == 0) {
		if (cfg.opt_private_keep) {
			if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 )
				errExit("asprintf");
		} else {
			cfg.opt_private_keep = ptr + 12;
		}
		arg_private_opt = 1;

		return 0;
	}

	// private /srv list of files and directories
	if (strncmp(ptr, "private-srv ", 12) == 0) {
		if (cfg.srv_private_keep) {
			if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 )
				errExit("asprintf");
		} else {
			cfg.srv_private_keep = ptr + 12;
		}
		arg_private_srv = 1;

		return 0;
	}

	// private /bin list of files
	if (strncmp(ptr, "private-bin ", 12) == 0) {
		if (cfg.bin_private_keep) {
			if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 )
				errExit("asprintf");
		} else {
			cfg.bin_private_keep = ptr + 12;
		}
		arg_private_bin = 1;
		return 0;
	}


#ifdef HAVE_OVERLAYFS
	if (strncmp(ptr, "overlay-named ", 14) == 0) {
		if (checkcfg(CFG_OVERLAYFS)) {
			if (cfg.chrootdir) {
				fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n");
				exit(1);
			}
			struct stat s;
			if (stat("/proc/sys/kernel/grsecurity", &s) == 0) {
				fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n");
				exit(1);
			}
			arg_overlay = 1;
			arg_overlay_keep = 1;
			arg_overlay_reuse = 1;

			char *subdirname = ptr + 14;
			if (subdirname == '\0') {
				fprintf(stderr, "Error: invalid overlay option\n");
				exit(1);
			}

			// check name
			invalid_filename(subdirname);
			if (strstr(subdirname, "..") || strstr(subdirname, "/")) {
				fprintf(stderr, "Error: invalid overlay name\n");
				exit(1);
			}
			cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse);
		}

		return 0;
	} else if (strcmp(ptr, "overlay-tmpfs") == 0) {
		if (checkcfg(CFG_OVERLAYFS)) {
			if (cfg.chrootdir) {
				fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n");
				exit(1);
			}
			struct stat s;
			if (stat("/proc/sys/kernel/grsecurity", &s) == 0) {
				fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n");
				exit(1);
			}
			arg_overlay = 1;

			return 0;
		}
	} else if (strcmp(ptr, "overlay") == 0) {
		if (checkcfg(CFG_OVERLAYFS)) {
			if (cfg.chrootdir) {
				fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n");
				exit(1);
			}
			struct stat s;
			if (stat("/proc/sys/kernel/grsecurity", &s) == 0) {
				fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n");
				exit(1);
			}
			arg_overlay = 1;
			arg_overlay_keep = 1;

			char *subdirname;
			if (asprintf(&subdirname, "%d", getpid()) == -1)
				errExit("asprintf");
			cfg.overlay_dir = fs_check_overlay_dir(subdirname, arg_overlay_reuse);

			free(subdirname);

			return 0;
		}
	}
#endif

	// filesystem bind
	if (strncmp(ptr, "bind ", 5) == 0) {
#ifdef HAVE_BIND
		if (checkcfg(CFG_BIND)) {
			if (getuid() != 0) {
				fprintf(stderr, "Error: --bind option is available only if running as root\n");
				exit(1);
			}

			// extract two directories
			char *dname1 = ptr + 5;
			char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories
			if (dname2 == NULL) {
				fprintf(stderr, "Error: missing second directory for bind\n");
				exit(1);
			}

			// check directories
			invalid_filename(dname1);
			invalid_filename(dname2);
			if (strstr(dname1, "..") || strstr(dname2, "..")) {
				fprintf(stderr, "Error: invalid file name.\n");
				exit(1);
			}
			if (is_link(dname1) || is_link(dname2)) {
				fprintf(stderr, "Symbolic links are not allowed for bind command\n");
				exit(1);
			}

			// insert comma back
			*(dname2 - 1) = ',';
			return 1;
		}
		else
			warning_feature_disabled("bind");
#endif
		return 0;
	}

	// rlimit
	if (strncmp(ptr, "rlimit", 6) == 0) {
		if (strncmp(ptr, "rlimit-nofile ", 14) == 0) {
			check_unsigned(ptr + 14, "Error: invalid rlimit in profile file: ");
			sscanf(ptr + 14, "%llu", &cfg.rlimit_nofile);
			arg_rlimit_nofile = 1;
		}
		else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) {
			check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: ");
			sscanf(ptr + 13, "%llu", &cfg.rlimit_nproc);
			arg_rlimit_nproc = 1;
		}
		else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
			check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: ");
			sscanf(ptr + 13, "%llu", &cfg.rlimit_fsize);
			arg_rlimit_fsize = 1;
		}
		else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
			check_unsigned(ptr + 18, "Error: invalid rlimit in profile file: ");
			sscanf(ptr + 18, "%llu", &cfg.rlimit_sigpending);
			arg_rlimit_sigpending = 1;
		}
		else {
			fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
			exit(1);
		}

		return 0;
	}

	if (strncmp(ptr, "join-or-start ", 14) == 0) {
		// try to join by name only
		pid_t pid;
		if (!name2pid(ptr + 14, &pid)) {
			if (!cfg.shell && !arg_shell_none)
				cfg.shell = guess_shell();

			// find first non-option arg
			int i;
			for (i = 1; i < cfg.original_argc && strncmp(cfg.original_argv[i], "--", 2) != 0; i++);

			join(pid, cfg.original_argc,cfg.original_argv, i + 1);
			exit(0);
		}

		// set sandbox name and start normally
		cfg.name = ptr + 14;
		if (strlen(cfg.name) == 0) {
			fprintf(stderr, "Error: invalid sandbox name\n");
			exit(1);
		}
		return 0;
	}

	// rest of filesystem
	if (strncmp(ptr, "blacklist ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "blacklist-nolog ", 16) == 0)
		ptr += 16;
	else if (strncmp(ptr, "noblacklist ", 12) == 0)
		ptr += 12;
	else if (strncmp(ptr, "whitelist ", 10) == 0) {
#ifdef HAVE_WHITELIST
		if (checkcfg(CFG_WHITELIST)) {
			arg_whitelist = 1;
			ptr += 10;
		}
		else
			return 0;
#else
		return 0;
#endif
	}
	else if (strncmp(ptr, "nowhitelist ", 12) == 0)
		ptr += 12;
	else if (strncmp(ptr, "read-only ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "read-write ", 11) == 0)
		ptr += 11;
	else if (strncmp(ptr, "noexec ", 7) == 0)
		ptr += 7;
	else if (strncmp(ptr, "tmpfs ", 6) == 0) {
		if (getuid() != 0) {
			fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n");
			exit(1);
		}
		ptr += 6;
	}
	else {
		if (lineno == 0)
			fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr);
		else if (fname != NULL)
			fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname);
		else
			fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
		exit(1);
	}

	// some characters just don't belong in filenames
	invalid_filename(ptr);
	if (strstr(ptr, "..")) {
		if (lineno == 0)
			fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
		else if (fname != NULL)
			fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname);
		else
			fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
		exit(1);
	}
	return 1;
}
Beispiel #4
0
// check profile line; if line == 0, this was generated from a command line option
// return 1 if the command is to be added to the linked list of profile commands
// return 0 if the command was already executed inside the function
int profile_check_line(char *ptr, int lineno, const char *fname) {
	EUID_ASSERT();
	
	// check ignore list
	int i;
	for (i = 0; i < MAX_PROFILE_IGNORE; i++) {
		if (cfg.profile_ignore[i] == NULL)
			break;
		
		if (strncmp(ptr, cfg.profile_ignore[i], strlen(cfg.profile_ignore[i])) == 0)
			return 0;	// ignore line
	}
	
	if (strncmp(ptr, "ignore ", 7) == 0) {
		char *str = strdup(ptr + 7);
		if (*str == '\0') {
			fprintf(stderr, "Error: invalid ignore option\n");
			exit(1);
		}
		// find an empty entry in profile_ignore array
		int j;
		for (j = 0; j < MAX_PROFILE_IGNORE; j++) {
			if (cfg.profile_ignore[j] == NULL) 
				break;
		}
		if (j >= MAX_PROFILE_IGNORE) {
			fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE);
			exit(1);
		}
		// ... and configure it
		else 
			cfg.profile_ignore[j] = str;

		return 0;
	}

	// mkdir 
	if (strncmp(ptr, "mkdir ", 6) == 0) {
		fs_mkdir(ptr + 6);
		return 0;
	}
	// sandbox name
	else if (strncmp(ptr, "name ", 5) == 0) {
		cfg.name = ptr + 5;
		if (strlen(cfg.name) == 0) {
			fprintf(stderr, "Error: invalid sandbox name\n");
			exit(1);
		}
		return 0;
	}
	else if (strcmp(ptr, "ipc-namespace") == 0) {
		arg_ipc = 1;
		return 0;
	}
	// seccomp, caps, private, user namespace
	else if (strcmp(ptr, "noroot") == 0) {
#if HAVE_USERNS
		if (checkcfg(CFG_USERNS))
			check_user_namespace();
		else
			fprintf(stderr, "Warning: user namespace feature is disabled in Firejail configuration file\n");
#endif

		return 0;
	}
	else if (strcmp(ptr, "nonewprivs") == 0) {
		arg_nonewprivs = 1;
		return 0;
	}
	else if (strcmp(ptr, "seccomp") == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP))
			arg_seccomp = 1;
		else
			fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	else if (strcmp(ptr, "caps") == 0) {
		arg_caps_default_filter = 1;
		return 0;
	}
	else if (strcmp(ptr, "caps.drop all") == 0) {
		arg_caps_drop_all = 1;
		return 0;
	}
	else if (strcmp(ptr, "shell none") == 0) {
		arg_shell_none = 1;
		return 0;
	}	
	else if (strcmp(ptr, "tracelog") == 0) {
		arg_tracelog = 1;
		return 0;
	}
	else if (strcmp(ptr, "private") == 0) {
		arg_private = 1;
		return 0;
	}
	else if (strcmp(ptr, "private-dev") == 0) {
		arg_private_dev = 1;
		return 0;
	}
	else if (strcmp(ptr, "private-tmp") == 0) {
		arg_private_tmp = 1;
		return 0;
	}
	else if (strcmp(ptr, "nogroups") == 0) {
		arg_nogroups = 1;
		return 0;
	}
	else if (strcmp(ptr, "nosound") == 0) {
		arg_nosound = 1;
		arg_private_dev = 1;
		return 0;
	}
	else if (strcmp(ptr, "netfilter") == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK))
			arg_netfilter = 1;
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	else if (strncmp(ptr, "netfilter ", 10) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_netfilter = 1;
			arg_netfilter_file = strdup(ptr + 10);
			if (!arg_netfilter_file)
				errExit("strdup");
			check_netfilter_file(arg_netfilter_file);
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	else if (strncmp(ptr, "netfilter6 ", 11) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_netfilter6 = 1;
			arg_netfilter6_file = strdup(ptr + 11);
			if (!arg_netfilter6_file)
				errExit("strdup");
			check_netfilter_file(arg_netfilter6_file);
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	else if (strcmp(ptr, "net none") == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			arg_nonetwork  = 1;
			cfg.bridge0.configured = 0;
			cfg.bridge1.configured = 0;
			cfg.bridge2.configured = 0;
			cfg.bridge3.configured = 0;
			cfg.interface0.configured = 0;
			cfg.interface1.configured = 0;
			cfg.interface2.configured = 0;
			cfg.interface3.configured = 0;
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	else if (strncmp(ptr, "net ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
#ifdef HAVE_NETWORK_RESTRICTED
			// compile time restricted networking
			if (getuid() != 0) {
				fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n");
				exit(1);
			}
#endif
			// run time restricted networking
			if (checkcfg(CFG_RESTRICTED_NETWORK) && getuid() != 0) {
				fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n");
				exit(1);
			}
			
			if (strcmp(ptr + 4, "lo") == 0) {
				fprintf(stderr, "Error: cannot attach to lo device\n");
				exit(1);
			}

			Bridge *br;
			if (cfg.bridge0.configured == 0)
				br = &cfg.bridge0;
			else if (cfg.bridge1.configured == 0)
				br = &cfg.bridge1;
			else if (cfg.bridge2.configured == 0)
				br = &cfg.bridge2;
			else if (cfg.bridge3.configured == 0)
				br = &cfg.bridge3;
			else {
				fprintf(stderr, "Error: maximum 4 network devices are allowed\n");
				exit(1);
			}
			net_configure_bridge(br, ptr + 4);
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	
	else if (strncmp(ptr, "iprange ", 8) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->iprange_start || br->iprange_end) {
				fprintf(stderr, "Error: cannot configure the IP range twice for the same interface\n");
				exit(1);
			}

			// parse option arguments
			char *firstip = ptr + 8;
			char *secondip = firstip;
			while (*secondip != '\0') {
				if (*secondip == ',')
					break;
				secondip++;
			}
			if (*secondip == '\0') {
				fprintf(stderr, "Error: invalid IP range\n");
				exit(1);
			}
			*secondip = '\0';
			secondip++;
			
			// check addresses
			if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) ||
			    br->iprange_start >= br->iprange_end) {
				fprintf(stderr, "Error: invalid IP range\n");
				exit(1);
			}
			if (in_netrange(br->iprange_start, br->ip, br->mask) || in_netrange(br->iprange_end, br->ip, br->mask)) {
				fprintf(stderr, "Error: IP range addresses not in network range\n");
				exit(1);
			}
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}


// from here
	else if (strncmp(ptr, "mac ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			
			if (mac_not_zero(br->macsandbox)) {
				fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n");
				exit(1);
			}

			// read the address
			if (atomac(ptr + 4, br->macsandbox)) {
				fprintf(stderr, "Error: invalid MAC address\n");
				exit(1);
			}
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}

	else if (strncmp(ptr, "mtu ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			
			if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) {
				fprintf(stderr, "Error: invalid mtu value\n");
				exit(1);
			}
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}

	else if (strncmp(ptr, "ip ", 3) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->arg_ip_none || br->ipsandbox) {
				fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
				exit(1);
			}

			// configure this IP address for the last bridge defined
			if (strcmp(ptr + 3, "none") == 0)
				br->arg_ip_none = 1;
			else {
				if (atoip(ptr + 3, &br->ipsandbox)) {
					fprintf(stderr, "Error: invalid IP address\n");
					exit(1);
				}
			}
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}

	else if (strncmp(ptr, "ip6 ", 4) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			Bridge *br = last_bridge_configured();
			if (br == NULL) {
				fprintf(stderr, "Error: no network device configured\n");
				exit(1);
			}
			if (br->arg_ip_none || br->ip6sandbox) {
				fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n");
				exit(1);
			}

			// configure this IP address for the last bridge defined
			// todo: verify ipv6 syntax
			br->ip6sandbox = ptr + 4;
//			if (atoip(argv[i] + 5, &br->ipsandbox)) {
//				fprintf(stderr, "Error: invalid IP address\n");
//				exit(1);
//			}
			
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}

	else if (strncmp(ptr, "defaultgw ", 10) == 0) {
#ifdef HAVE_NETWORK
		if (checkcfg(CFG_NETWORK)) {
			if (atoip(ptr + 10, &cfg.defaultgw)) {
				fprintf(stderr, "Error: invalid IP address\n");
				exit(1);
			}
		}
		else
			fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n");
#endif
		return 0;
	}

	if (strncmp(ptr, "protocol ", 9) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP))
			protocol_store(ptr + 9);
		else
			fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
#endif
		return 0;
	}
	
	if (strncmp(ptr, "env ", 4) == 0) {
		env_store(ptr + 4);
		return 0;
	}
	
	// seccomp drop list on top of default list
	if (strncmp(ptr, "seccomp ", 8) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list = strdup(ptr + 8);
			if (!cfg.seccomp_list)
				errExit("strdup");
		}
		else
			fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
#endif

		return 0;
	}
	
	// seccomp drop list without default list
	if (strncmp(ptr, "seccomp.drop ", 13) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list_drop = strdup(ptr + 13);
			if (!cfg.seccomp_list_drop)
				errExit("strdup");
		}
		else
			fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
#endif		
		return 0;
	}

	// seccomp keep list
	if (strncmp(ptr, "seccomp.keep ", 13) == 0) {
#ifdef HAVE_SECCOMP
		if (checkcfg(CFG_SECCOMP)) {
			arg_seccomp = 1;
			cfg.seccomp_list_keep= strdup(ptr + 13);
			if (!cfg.seccomp_list_keep)
				errExit("strdup");
		}
		else
			fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
#endif		
		return 0;
	}
	
	// caps drop list
	if (strncmp(ptr, "caps.drop ", 10) == 0) {
		arg_caps_drop = 1;
		arg_caps_list = strdup(ptr + 10);
		if (!arg_caps_list)
			errExit("strdup");
		// verify seccomp list and exit if problems
		if (caps_check_list(arg_caps_list, NULL))
			exit(1);
		return 0;
	}
	
	// caps keep list
	if (strncmp(ptr, "caps.keep ", 10) == 0) {
		arg_caps_keep = 1;
		arg_caps_list = strdup(ptr + 10);
		if (!arg_caps_list)
			errExit("strdup");
		// verify seccomp list and exit if problems
		if (caps_check_list(arg_caps_list, NULL))
			exit(1);
		return 0;
	}

	// hostname
	if (strncmp(ptr, "hostname ", 9) == 0) {
		cfg.hostname = ptr + 9;
		return 0;
	}
	
	// dns
	if (strncmp(ptr, "dns ", 4) == 0) {
		uint32_t dns;
		if (atoip(ptr + 4, &dns)) {
			fprintf(stderr, "Error: invalid DNS server IP address\n");
			return 1;
		}
		
		if (cfg.dns1 == 0)
			cfg.dns1 = dns;
		else if (cfg.dns2 == 0)
			cfg.dns2 = dns;
		else if (cfg.dns3 == 0)
			cfg.dns3 = dns;
		else {
			fprintf(stderr, "Error: up to 3 DNS servers can be specified\n");
			return 1;
		}
		return 0;
	}
	
	// cpu affinity
	if (strncmp(ptr, "cpu ", 4) == 0) {
		read_cpu_list(ptr + 4);
		return 0;
	}
	
	// nice value
	if (strncmp(ptr, "nice ", 4) == 0) {
		cfg.nice = atoi(ptr + 5);
		if (getuid() != 0 &&cfg.nice < 0)
			cfg.nice = 0;
		arg_nice = 1;
		return 0;
	}

	// cgroup
	if (strncmp(ptr, "cgroup ", 7) == 0) {
		set_cgroup(ptr + 7);
		return 0;
	}
	
	// writable-etc
	if (strcmp(ptr, "writable-etc") == 0) {
		if (cfg.etc_private_keep) {
			fprintf(stderr, "Error: private-etc and writable-etc are mutually exclusive\n");
			exit(1);
		}
		arg_writable_etc = 1;
		return 0;
	}
	
	// writable-var
	if (strcmp(ptr, "writable-var") == 0) {
		arg_writable_var = 1;
		return 0;
	}
	
	// private directory
	if (strncmp(ptr, "private ", 8) == 0) {
		cfg.home_private = ptr + 8;
		fs_check_private_dir();
		arg_private = 1;
		return 0;
	}

	// private /etc list of files and directories
	if (strncmp(ptr, "private-etc ", 12) == 0) {
		if (arg_writable_etc) {
			fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
			exit(1);
		}
		cfg.etc_private_keep = ptr + 12;
		fs_check_etc_list();
		arg_private_etc = 1;
		
		return 0;
	}

	// private /bin list of files
	if (strncmp(ptr, "private-bin ", 12) == 0) {
		cfg.bin_private_keep = ptr + 12;
		arg_private_bin = 1;
		fs_check_bin_list();
		return 0;
	}

	// filesystem bind
	if (strncmp(ptr, "bind ", 5) == 0) {
#ifdef HAVE_BIND		
		if (checkcfg(CFG_BIND)) {
			if (getuid() != 0) {
				fprintf(stderr, "Error: --bind option is available only if running as root\n");
				exit(1);
			}
	
			// extract two directories
			char *dname1 = ptr + 5;
			char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories
			if (dname2 == NULL) {
				fprintf(stderr, "Error: missing second directory for bind\n");
				exit(1);
			}
			
			// check directories
			invalid_filename(dname1);
			invalid_filename(dname2);
			if (strstr(dname1, "..") || strstr(dname2, "..")) {
				fprintf(stderr, "Error: invalid file name.\n");
				exit(1);
			}
			if (is_link(dname1) || is_link(dname2)) {
				fprintf(stderr, "Symbolic links are not allowed for bind command\n");
				exit(1);
			}
			
			// insert comma back
			*(dname2 - 1) = ',';
			return 1;
		}
		else {
			fprintf(stderr, "Warning: bind feature is disabled in Firejail configuration file\n");
			return 0;			
		}
#else
		return 0;
#endif		
	}

	// rlimit
	if (strncmp(ptr, "rlimit", 6) == 0) {
		if (strncmp(ptr, "rlimit-nofile ", 14) == 0) {
			ptr += 14;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_nofile);
			arg_rlimit_nofile = 1;
		}
		else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) {
			ptr += 13;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_nproc);
			arg_rlimit_nproc = 1;
		}
		else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
			ptr += 13;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_fsize);
			arg_rlimit_fsize = 1;
		}
		else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
			ptr += 18;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_sigpending);
			arg_rlimit_sigpending = 1;
		}
		else {
			fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
			exit(1);
		}
		
		return 0;		
	}

	// read-write
	if (strncmp(ptr, "read-write ", 11) == 0) {
		if (getuid() != 0) {
			fprintf(stderr, "Error: read-write command is available only for root user\n");
			exit(1);
		}
		fs_rdwr_add(ptr + 11);
		return 0;
	}

	// rest of filesystem
	if (strncmp(ptr, "blacklist ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "blacklist-nolog ", 16) == 0)
		ptr += 16;
	else if (strncmp(ptr, "noblacklist ", 12) == 0)
		ptr += 12;
	else if (strncmp(ptr, "whitelist ", 10) == 0) {
#ifdef HAVE_WHITELIST		
		if (checkcfg(CFG_WHITELIST)) {
			arg_whitelist = 1;
			ptr += 10;
		}
		else
			return 0;
#else		
		return 0;
#endif
	}
	else if (strncmp(ptr, "read-only ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "tmpfs ", 6) == 0) {
		if (getuid() != 0) {
			fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n");
			exit(1);
		}
		ptr += 6;
	}
	else {
		if (lineno == 0)
			fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr);
		else if (fname != NULL)
			fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname);
		else
			fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
		exit(1);
	}

	// some characters just don't belong in filenames
	invalid_filename(ptr);
	if (strstr(ptr, "..")) {
		if (lineno == 0)
			fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
		else if (fname != NULL)
			fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname);
		else
			fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
		exit(1);
	}
	return 1;
}
//**************************************************************
// Main <smile> All sub-programs/functions should be called 	
// from here. Note: they should also be listed in lciheader.h  
//**************************************************************
int main(int argc, char *argv[])
{
    int retval=0;     // a return value indicator		
    int verbose=0;    // for verbosity			
    // note: switches: 
    //lci -a = advanced help
    //lci -d = perform a diff of the md5output and old md5output
    //lci -v = verbose, verbose = 1
    //lci    = slightly verbose, verbose = 0, 
    // the module name is printed as it performes each module
    //lci -s = silent, verbose = -1, nothing printed at all.
    //lci -r = run checkrpm module.
    //lci -m = force a distribution mode, e.g. lci -m redhat
    //lci -h = give help output
    //lci -o outfile = put output in outfile instead of lci.out	
    //lci -w = print output in html format
    //lci -x module = exclude module from checks
    
    int diff=0; // do we run the md5 diff? 
    int rpmmodule=0; // do we run the rpm module? 
    int inetexists=0; // if inetd.conf exists, this is 1 	
    int distribution=1; // what distro are we on?		
    // -1=user specified a distro, its in man_distro 
    // 1=redhat 
    // 2=debian 
    // 3=solaris 
    // 4=gentoo
    // 5=macosx 
    // 6=slackware
    // default = redhat   
    int html=0; // html output? 
    // html = 0: normal output
    //html = 1: user wants html output
     
    char release[50]; // array for release level		
    char kernel[50];  // what kernel user is running	
    static char *man_distro; // if the user specifies a distribution 
    const char * header   =NULL;   // to print out the header 
    static char *out_file = "lci.out";  // output filename var  
    char xlist[100]; // modules to exclude 
    int xarray[33] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 
    int  somethinginxlist = 0;
    // note if no filename given, default = lci.out	
    FILE *tempptr;    // a temp file pointer		

     uid_t uidme;
     uidme = getuid();

    // we used to set the umask of the user here... 
    // note that does not work all of the time.     
    // instead use open with options below. Also,   
    // warn user at bottom about setting umask to   
    // sensible value before modifying files.       

    // we will again try to set umask here...       
    mode_t old_umask;

    old_umask=(umask(0177));


    // Now do options processing 
    int i;
    for (i=1; i < argc; i++)
    {
	if(argv[i][0] == '-')
	{
	    switch (argv[i][1]) 
	    {
		case 'a': 
			  usage(); 
			  break;
	        case 'h':
			  usage();
			  break;
		case 'd': 
			  diff = 1; 
			  break;
		case 'm': 
			  strcpy(man_distro, argv[i]+3);
			  break;
		case 'o': 
			  strcpy(out_file, argv[i]+3);
			  break;
		case 'r': 
			  rpmmodule = 1; 
			  break;
		case 's': 
			  verbose = -1; 
			  break;
	        case 'v': 
			  verbose = 1; 
			  break;
		case 'w': 
			  html = 1; 
			  out_file="lci.html"; 
			  break;
		case 'x': strcpy(xlist,argv[i]+3);
			  somethinginxlist = 1;
			  break;
	        default : 
			  usage(); 
		break;
	    }
	}
    }

    if (!out_file) // if no filename after -o 
    {
        usage();  // note usage() will exit lci 
    }

    // if an exclude list was given, parse the file and 
    // fill the array of excludes..			
    if (somethinginxlist == 1)
    {
	if (findexclude(xlist, xarray) != 0)
	{
	     // something went wrong 
	     perror("Error reading exclude list...\n");
	     return(-1);
	}
    }

    // check if output exists, if it does, make a 	
    // backup and rm the orig. named one.		
    tempptr = fopen(out_file, "r");
    if (tempptr != 0)
    {
        // output file exists 
        // make a backup copy 
	char *newname = NULL;
	if ((newname = (char *) calloc(2, sizeof(out_file))) == NULL)
	{
	    perror("Can not allocate temp memory in lcimain.\n");
	    perror("Strange. Bailing...\n");
	    exit(-1);
	}
        fclose(tempptr);
	// add .old onto the old outfile name 
        sprintf(newname, "%s.old", out_file);

        // gnu, but should be ok... 
        // whoops, we are now failure checking this...   
        retval = 0;
        retval = rename(out_file, newname);
        if (retval < 0)
        {
            // damn, rename bombed 
            perror("GNU rename bombed in lcimain.\n");
            perror("Very odd, you should have seen errors, report them :) \n");
            exit(-1);
        }
	
	// free up calloc'd mem 
	free(newname);
    }
    
    // now that we have a backup, remove the outfile 
    // don't care about failure here...              
    remove(out_file);

    // not seem to work 100% of the time on my system
    // for making files w/chmod 0600...              
    // reverting to open with options...             
    retval = 0;
    if ((retval = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) 
    {
	perror("Could not make file w/perms 0600...\n");
	perror("Possible link attack while creating/opening file!\n");
	perror("Bailing in lcimain...\n");
        exit(-1);
    }

    // we print this out no matter what verbose is 
    printf("Starting LCI...\n");

    // remove old tempfiles (if lci got killed) 
    // if this fails, we should stop _now_! 
    if (rm() < 0)
    {
	perror("Could not remove tempfiles during startup.\n");
	perror("You may need root permissions to do this.\n");
	return(-1);
    }
    
    // read that in, check kernel version and "release"	
    if (verbose >= 0)
    {
        printf("Getting system information...\n");
    }

    retval = versions(release, kernel, verbose);
    if (retval < 0)
    {
        // something went wrong... 			
        perror("Problem in versions function.\n");
        return(0);
    }

    // note about Mandrake: until I can find vast differences 
    // between RedHat/Mandrake in terms of configs, we assume 
    // that it is redhat and don't check /etc/mandrake_rel... 
    // simple check to see if we are on RedHat or Debian 
    // first, did the user specify a distro? 
    if (distribution != -1)
    {
        if ((system("cat /etc/redhat-release 1>/dev/null 2>/dev/null >/dev/null")) == 0)
        {
            // we are on redhat 
            distribution = 1;
        }
        if ((system("cat /etc/debian_version 1>/dev/null 2>/dev/null >/dev/null")) == 0)
        {
            // we are on debian 
            distribution = 2;
        }
	if ((system("uname |grep SunOS 1>/dev/null 2>/dev/null >/dev/null")) == 0)
	{
	    // we are on Solaris/SunOS 
	    distribution = 3;
	}
        if ((system("uname -a |grep gentoo 1>/dev/null 2>/dev/null >/dev/null")) == 0)
        {
	    // we are on gentoo 
	    distribution = 4;
	} 
        if ((system("uname |grep Darwin 1>/dev/null 2>/dev/null >/dev/null")) == 0)
	{
	    // we are on Mac OSX 
	    distribution = 5;
	}
        if ((system("cat /etc/slackware-version 1>/dev/null 2>/dev/null >/dev/null")) == 0)  // JTO 
        {
            // we are on slack 
            distribution = 6;
        }
    }
    if (distribution == -1)
    {
	// user specified a distribution 
        if ((strcmp(man_distro, "redhat")) == 0)
	{
	    // redhat 
	    distribution = 1;
	}
        if ((strcmp(man_distro, "centos")) == 0)
	{
	    // CentOS, RHEL-alike 
	    distribution = 1;
	}
        if ((strcmp(man_distro, "caos")) == 0)
	{
	    // CaOS, RHL-alike 
	    distribution = 1;
	}
        if ((strcmp(man_distro, "fedora")) == 0)
	{
	    // Post-RHL 
	    distribution = 1;
	}
	if ((strcmp(man_distro, "debian")) == 0)
	{
	    // debian 
	    distribution = 2;
	}
	if ((strcmp(man_distro, "solaris")) == 0)
	{
	    // solaris 
	    distribution = 3;
	}
	if ((strcmp(man_distro, "mandrake")) == 0)
 	{
	    // mandrake, same as redhat, sortof 
	    distribution = 1;
	}
	if ((strcmp(man_distro, "gentoo")) == 0)
	{
	    // gentoo 
	    distribution = 4;
	}
 	if ((strcmp(man_distro, "macosx")) == 0)
	{
	    // Mac OSX 
	    distribution = 5;
	}
        if ((strcmp(man_distro, "slackware")) == 0) // JTO 
        {
            // slackware- JTO 
            distribution = 6;
        }
	// default is redhat 
        else
	{
	   distribution = 1;
        }
    } // end if (distribution == -1) 

    // ********* checks are below *****************//
    // ok, we should have enough info, run the checks 

    if (verbose >= 0)
    {
	printf("Running modules...\n");
    }

    // we need to make a header if html = 1, as this 
    // could confuse the issue by putting it in the  
    // first module... call dostuff here...          
   
    if (html == 1)
    {
	html = 2; // this tells dostuff to write out header html 
	header = "\n";  // can't be NULL 
        if ((dostuff(0, out_file, 0, header, html)) < 0)
        {
            // something went wrong 
            perror(" Creation of list failed.");
            return (-1);
        }
        html = 1;   // change html int back to one 
    }
 

    // run checkpkgs 
    // did user put it in exclude list? 
    if (xarray[0] == 0 )
    {
        retval = 0;
        retval = checkpkgs(distribution, out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checkpkgs module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // run checkrpm if rpmmodule = 1 and distribution =1 
    // did user put it in exclude list? 
    if (xarray[1] == 0)
    {
        retval = 0;
        if ((rpmmodule == 1) && (distribution == 1))
        {
            retval = checkrpm(out_file, verbose, html);
            if (retval < 0)
	    {
	        // something went wrong...                      
                perror("Problem in checkrpm module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
	    }
        }
    }

    // run checkinetd 
    // did user put it in exclude list? 
    if (xarray[2] == 0)
    {

    	// if hosts file exists run checkhostsfiles 
    	inetexists = checkinetd(out_file,distribution, verbose, html);
    	// if inetexists == 1, it does!				
    	// if inetexists == 0, it does not...			
    	// if inetexists == -1, we had some problems.		
    	    if (inetexists == 1)
    	    {
                checkhostsfiles(out_file, verbose, html);
    	    }
    	if (inetexists == 0)
    	{
        	// do nothing... 
        	;
        }
    	if (inetexists == -1)
    	{
            // there was a problem. User should have seen	
            // messages from checkinetd program.		
            // do nothing here and continue...		
            // module bombed, clean up 
            rm();
        }
    }

    // check inittab		
    // did user put it in exclude list? 
    if (xarray[3] == 0)
    {
    	retval = 0;
    	retval = checkinittab(out_file, distribution, verbose, html);
    	if (retval < 0)
    	{
            // something went wrong...   
	    perror("Problem in checkinittab module.\n");
	    perror("You should have seen errors...\n");
	    // module bombed, clean up 
	    rm();
	    return(0);
        }
    }

    // check logging		
    // did user put it in exclude list? 
    if (xarray[4] == 0)
    {
        retval = 0;
        retval = checklogging(out_file, distribution, verbose, html);
    	if (retval < 0)
    	{
            // something went wrong...   
            perror("Problem in checklogging module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }
        

    // check for setuid/setgid files 
    // did user put it in exclude list? 
    if (xarray[5] == 0)
    {
        retval = 0;
        retval = checkset(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checkset module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }


    // check for world writable files/directories 
    // did user put it in exclude list? 
    if (xarray[6] == 0)
    {
        retval = 0;
        retval = checkwrite(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checkwrite module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // check for .exrc and .forward files 
    // did user put it in exclude list? 
    if (xarray[7] == 0)
    {
    	retval = 0;
    	retval = checkdotfiles(out_file, verbose, html);
    	if (retval < 0)
        {	
            // something went wrong...                      
            perror("Problem in checkdotfiles module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }


    // check /etc/passwd for uid 0 other than 
    // root and users not needed on the sys.  
    // did user put it in exclude list? 
    if (xarray[8] == 0)
    {
    	retval = 0;
    	retval = checkpasswd(out_file, distribution, verbose, html);
    	if (retval < 0)
    	{
            // something went wrong...                      
            perror("Problem in checkpasswd module.\n");
            perror("You should have seen errors...\n");
	   
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // check to see if sticky bits are set 
    // on /tmp & /var/tmp. I will add more 
    // to this later on...			
    // did user put it in exclude list? 
    if (xarray[9] == 0)
    {
        retval = 0;
        retval = checkfiles(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...			
            perror("Problem in checkfiles module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[10] == 0)
    {
        retval = 0;
        retval = checkumask(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in chumask module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[11] == 0)
    {
        retval = 0;
        retval = checkftpusers(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checkftpusers module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[12] == 0)
    {
        retval = 0;
        retval = checkrc(release, kernel, distribution, out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...			
            perror("Problem in checkrc module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[13] == 0)
    {
        retval = 0;
        retval = checkkbd(release, out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checkkbd module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[14] == 0)
    {
        if (distribution != 6) // need to fix for slack, JTO 
        {
        retval = 0;
        retval = checklimits(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checklimits module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
        } // end if its not slack if statement 
    }

    // did user put it in exclude list? 
    if (xarray[15] == 0)
    {
        retval = 0;
    	retval = checkssh(out_file, distribution, verbose, html);
    	if (retval < 0)
        {
	    // something went wrong..	
	    perror("Problem in checkssh module.\n");
	    perror("You should have seen errors...\n");
	    rm();
	    return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[16] == 0)
    {
        retval = 0;
        retval = checkopenfiles(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checkopenfiles module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[17] == 0)
    {
        retval = 0;
        retval = checkissue(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checksissue module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[18] == 0)
    {
        retval = 0;
        retval = checkwww(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checkwww module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[19] == 0)
    {
        retval = 0;
        retval = checkmd5(out_file, distribution, verbose, html, diff);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checkmd5 module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[20] == 0)
    {
        if  ((distribution != 3) && (distribution !=5))
    	// we are not on Solaris, so we can run this module 
    	{
            retval = 0;
            retval = checkmodules(out_file, verbose, html);
            if (retval < 0)
            {
                // something went wrong..       
                perror("Problem in checkmodules module.\n");
                perror("You should have seen errors...\n");
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[21] == 0)
    {
        retval = 0;
        retval = checksecuretty(out_file, verbose, html);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checksecuretty module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[22] == 0)
    {
        retval = 0;
        retval = checkrcperms(out_file, distribution, verbose, html);
        if (retval < 0)
        {
	    // something went wrong...	
	    perror("Problem in checkrcperms module.\n");
	    perror("You should have seen errors...\n");
	    rm();
	    return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[23] == 0)
    {
        retval = 0;
        retval = checknet(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checknet module.\n");
            perror("You should have seen errors...\n");
	    // module bombed, clean up 
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[24] == 0)
    {
        retval = 0;
        retval = checknetforward(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong...                      
            perror("Problem in checknetforward module.\n");
            perror("You should have seen errors...\n");
            // module bombed, clean up 
            rm();
            return(0);
        }
    }


    // did user put it in exclude list? 
    if (xarray[25] == 0)
    {
        if ((distribution !=3) && (distribution != 5))
        {
            // we are on linux... 
            retval = 0;
            retval = checknetp(kernel, out_file, distribution, verbose, html);
            if (retval < 0)
            {
                perror("Problem in checknetpromisc module.\n");
                perror("You should have seen errors...\n");
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[26] == 0)
    {
        if (distribution == 1) // redhat (or derived) specific 
        {
            retval = 0;
            retval = checkcfg(out_file, verbose, html);
            if (retval < 0)
            {
                // something went wrong...                      
                perror("Problem in checkcfg module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[27] == 0)
    {
        if ((distribution !=3) && (distribution != 5)) // we are on linux 
        {
            retval = 0;
            retval = checkbpass(distribution, out_file, verbose, html);
            if (retval < 0)
            {
                // something went wrong...                      
                perror("Problem in checkbpass module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[28] == 0)
    {
        if ((distribution != 3) && (distribution != 5)) // we are on linux 
        {
            retval = 0;
            retval = checkipv4(out_file, verbose, html);
            if (retval < 0)
            {
                // something went wrong...                      
                perror("Problem in checkipv4 module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[29] == 0)
    {
        if ((distribution !=3) && (distribution !=5)) // we are on linux 
        {
            retval = 0;
            retval = checkx(out_file, verbose, html);
            if (retval < 0)
            {
                // something went wrong...                      
                perror("Problem in checkx module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[30] == 0)
    {
        if ((distribution !=3) && (distribution !=5)) // we are on linux 
        {
            retval = 0;
            retval = checkftp(out_file, distribution, verbose, html);
            if (retval < 0)
            {
                // something went wrong...                      
                perror("Problem in checkftp module.\n");
                perror("You should have seen errors...\n");
                // module bombed, clean up 
                rm();
                return(0);
            }
        }
    }

    // did user put it in exclude list? 
    if (xarray[31] == 0)
    {
        retval = 0;
        retval = checklistening(out_file, distribution, verbose, html);
        if (retval < 0)
        {
            // something went wrong..       
            perror("Problem in checklistening module.\n");
            perror("You should have seen errors...\n");
            rm();
            return(0);
        }
    }

    // did user put it in exclude list? 
    if (xarray[32] == 0)
    {
        retval = 0;
	retval = checkdisk(out_file, verbose, html);
	if (retval < 0)
	{
	    // something went wrong...      
	    perror("Problem in checkdisk module.\n");
	    perror("You should have seen errors...\n");
	    rm();
	    return(0);
	}
    }	


    // this is to put a footer html at the end of the output 
    // file if the user wanted html output. 		     
    if (html == 1)
    {
        html = 3; // tells dostuff to do an html footer 
        header = "\n"; // can't be NULL 
        if ((dostuff(0, out_file, 0, header, html)) < 0)
        {
            // something went wrong 
            perror(" Creation of list failed.");
            return (-1);
        }
        html = 1; // set html int back... habit 
    }
    else
    {
        // print a general footer 
	header = "\n";
	if ((dostuff(0, out_file, 0, header, 0)) < 0)
	{
	    // something went wrong 
	    perror(" Creation of footer failed.");
	    return(-1);
	}
    }
    

    // tell the user we are finished and where the output is 
    // we print this out no matter what verbose is 
    printf("Finished.\n");
    printf("Check %s for details.\n", out_file);
    // in silent mode we don't say much 
    if (verbose >= 0)
    {
        printf("Don't forget to check your umask or file perms\n");
        printf("when modifying files on the system.\n");
    }

    // set the umask back... 
    umask(old_umask);
    return(0);
}
Beispiel #6
0
//$ Xephyr -ac -br -noreset -screen 800x600 :22 &
//$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox
void x11_start_xephyr(int argc, char **argv) {
	EUID_ASSERT();
	int i;
	struct stat s;
	pid_t jail = 0;
	pid_t server = 0;
	
	setenv("FIREJAIL_X11", "yes", 1);

	// unfortunately, xephyr does a number of weird things when started by root user!!!
	if (getuid() == 0) {
		fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n");
		exit(1);
	}
	drop_privs(0);

	// check xephyr
	if (x11_check_xephyr() == 0) {
		fprintf(stderr, "\nError: Xephyr program was not found in /usr/bin directory, please install it:\n");
		fprintf(stderr, "   Debian/Ubuntu/Mint: sudo apt-get install xserver-xephyr\n");
		fprintf(stderr, "   Arch: sudo pacman -S xorg-server-xephyr\n");
		exit(0);
	}
	
	int display = random_display_number();
	char *display_str;
	if (asprintf(&display_str, ":%d", display) == -1)
		errExit("asprintf");

	assert(xephyr_screen);
	char *server_argv[256] = { "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen }; // rest initialyzed to NULL
	unsigned pos = 0;
	while (server_argv[pos] != NULL) pos++;
	if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) {
		server_argv[pos++] = "-title";
		server_argv[pos++] = "firejail x11 sandbox";
	}

	assert(xephyr_extra_params); // should be "" if empty

	// parse xephyr_extra_params
	// very basic quoting support
	char *temp = strdup(xephyr_extra_params);
	if (*xephyr_extra_params != '\0') {
		if (!temp)
			errExit("strdup");
		bool dquote = false;
		bool squote = false;
		for (i = 0; i < (int) strlen(xephyr_extra_params); i++) {
			if (temp[i] == '\"') {
				dquote = !dquote;
				if (dquote) temp[i] = '\0'; // replace closing quote by \0
			}
			if (temp[i] == '\'') {
				squote = !squote;
				if (squote) temp[i] = '\0'; // replace closing quote by \0
			}
			if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0';
			if (dquote && squote) {
				fprintf(stderr, "Error: mixed quoting found while parsing xephyr_extra_params\n");
				exit(1);
			}
		}
		if (dquote) {
			fprintf(stderr, "Error: unclosed quote found while parsing xephyr_extra_params\n");
			exit(1);
		}

		for (i = 0; i < (int) strlen(xephyr_extra_params)-1; i++) {
			if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) {
				fprintf(stderr, "Error: arg count limit exceeded while parsing xephyr_extra_params\n");
				exit(1);
			}
			if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) server_argv[pos++] = temp + i + 2;
			else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1;
		}
	}
	
	server_argv[pos++] = display_str;
	server_argv[pos++] = NULL;

	assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun
	assert(server_argv[pos-1] == NULL); // last element is null
	
	if (arg_debug) {
		size_t i = 0;
		printf("xephyr server:");
		while (server_argv[i]!=NULL) {
			printf(" \"%s\"", server_argv[i]);
			i++;
		}
		putchar('\n');
	}

	// remove --x11 arg
	char *jail_argv[argc+2];
	int j = 0;
	for (i = 0; i < argc; i++) {
		if (strcmp(argv[i], "--x11") == 0)
			continue;
		if (strcmp(argv[i], "--x11=xpra") == 0)
			continue;
		if (strcmp(argv[i], "--x11=xephyr") == 0)
			continue;
		jail_argv[j] = argv[i];
		j++;
	}
	jail_argv[j] = NULL;

	assert(j < argc+2); // no overrun

	if (arg_debug) {
		size_t i = 0;
		printf("xephyr client:");
		while (jail_argv[i]!=NULL) {
			printf(" \"%s\"", jail_argv[i]);
			i++;
		}
		putchar('\n');
	}
	
	server = fork();
	if (server < 0)
		errExit("fork");
	if (server == 0) {
		if (arg_debug)
			printf("Starting xephyr...\n");

		// running without privileges - see drop_privs call above
		assert(getenv("LD_PRELOAD") == NULL);	
		execvp(server_argv[0], server_argv);
		perror("execvp");
		_exit(1);
	}

	if (arg_debug)
		printf("xephyr server pid %d\n", server);

	// check X11 socket
	char *fname;
	if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1)
		errExit("asprintf");
	int n = 0;
	// wait for x11 server to start
	while (++n < 10) {
		sleep(1);
		if (stat(fname, &s) == 0)
			break;
	};
	
	if (n == 10) {
		fprintf(stderr, "Error: failed to start xephyr\n");
		exit(1);
	}
	free(fname);
	
	if (arg_debug) {
            	printf("X11 sockets: "); fflush(0);
            	int rv = system("ls /tmp/.X11-unix");
            	(void) rv;
	}

	setenv("DISPLAY", display_str, 1);
	// run attach command
	jail = fork();
	if (jail < 0)
		errExit("fork");
	if (jail == 0) {
		if (!arg_quiet)
			printf("\n*** Attaching to Xephyr display %d ***\n\n", display);

		// running without privileges - see drop_privs call above
		assert(getenv("LD_PRELOAD") == NULL);	
		execvp(jail_argv[0], jail_argv);
		perror("execvp");
		_exit(1);
	}

	// cleanup
	free(display_str);
	free(temp);

	// wait for either server or jail termination
	pid_t pid = wait(NULL);

	// see which process terminated and kill other
	if (pid == server) {
		kill(jail, SIGTERM);
	} else if (pid == jail) {
		kill(server, SIGTERM);
	}

	// without this closing Xephyr window may mess your terminal:
	// "monitoring" process will release terminal before
	// jail process ends and releases terminal
	wait(NULL); // fulneral

	exit(0);
}
Beispiel #7
0
void clientmgr (void *arg)
{
  int status;
  BINKD_CONFIG *config = NULL;
#if defined(WITH_PERL) && defined(HAVE_THREADS)
  void *cperl = NULL;
#endif

  UNUSED_ARG(arg);

#ifdef HAVE_FORK
  pidcmgr = 0;
  pidCmgr = (int) getpid();
  blocksig();
  signal (SIGCHLD, sighandler);
#elif HAVE_THREADS
  pidcmgr = PID();
#endif

  config = lock_current_config();
#if defined(WITH_PERL) && defined(HAVE_THREADS)
  if (server_flag)
    cperl = perl_init_clone(config);
#endif

  setproctitle ("client manager");
  Log (4, "clientmgr started");

  for (;;)
  {
    if (config != current_config)
    {
#if defined(WITH_PERL) && defined(HAVE_THREADS)
      if (server_flag && cperl)
        perl_done_clone(cperl);
#endif
      if (config)
        unlock_config_structure(config, 0);
      config = lock_current_config();
#if defined(WITH_PERL) && defined(HAVE_THREADS)
      if (server_flag)
        cperl = perl_init_clone(config);
#endif
    }
    status = do_client(config);

    if (status != 0 || binkd_exit) break;

    if (
#ifdef HAVE_THREADS
        !server_flag && 
#endif
        !poll_flag)
      checkcfg();
  }

  Log (5, "downing clientmgr...");

#if defined(WITH_PERL) && defined(HAVE_THREADS)
  if (server_flag && cperl)
    perl_done_clone(cperl);
#endif
  unlock_config_structure(config, 0);

  unblocksig();
#ifdef HAVE_THREADS
  pidcmgr = 0;
  PostSem(&eothread);
  if (binkd_exit)
    _endthread();
#endif
  exit (0);
}
Beispiel #8
0
static int do_server(BINKD_CONFIG *config)
{
  struct addrinfo *ai, *aiHead, hints;
  int aiErr;
  SOCKET new_sockfd;
  int pid;
  socklen_t client_addr_len;
  struct sockaddr_storage client_addr;
  int opt = 1;
  int save_errno;
  struct listenchain *listen_list;

  /* setup hints for getaddrinfo */
  memset((void *)&hints, 0, sizeof(hints));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;

  for (listen_list = config->listen.first; listen_list; listen_list = listen_list->next)
  {
    if ((aiErr = getaddrinfo(listen_list->addr[0] ? listen_list->addr : NULL, 
                             listen_list->port, &hints, &aiHead)) != 0)
    {
      Log(0, "servmgr getaddrinfo: %s (%d)", gai_strerror(aiErr), aiErr);
      return -1;
    }

    for (ai = aiHead; ai != NULL && sockfd_used < MAX_LISTENSOCK; ai = ai->ai_next)
    {
      sockfd[sockfd_used] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
      if (sockfd[sockfd_used] < 0)
      {
        Log (0, "servmgr socket(): %s", TCPERR ());
        continue;
      }
#ifdef UNIX /* Not sure how to set NOINHERIT flag for socket on Windows and OS/2 */
      if (fcntl(sockfd[sockfd_used], F_SETFD, FD_CLOEXEC) != 0)
        Log(1, "servmgr fcntl set FD_CLOEXEC error: %s", strerror(errno));
#endif
#ifdef IPV6_V6ONLY
      if (ai->ai_family == PF_INET6)
      {
        int v6only = 1;
        if (setsockopt(sockfd[sockfd_used], IPPROTO_IPV6, IPV6_V6ONLY, 
                 (char *) &v6only, sizeof(v6only)) == SOCKET_ERROR)
          Log(1, "servmgr setsockopt (IPV6_V6ONLY): %s", TCPERR());
      }
#endif
      if (setsockopt (sockfd[sockfd_used], SOL_SOCKET, SO_REUSEADDR,
                    (char *) &opt, sizeof opt) == SOCKET_ERROR)
        Log (1, "servmgr setsockopt (SO_REUSEADDR): %s", TCPERR ());
    
      if (bind (sockfd[sockfd_used], ai->ai_addr, ai->ai_addrlen) != 0)
      {
        Log (0, "servmgr bind(): %s", TCPERR ());
        soclose(sockfd[sockfd_used]);
        continue;
      }
      if (listen (sockfd[sockfd_used], 5) != 0)
      {
        Log(0, "servmgr listen(): %s", TCPERR ());
        soclose(sockfd[sockfd_used]);
        continue;
      }

      sockfd_used++;
    }

    Log (3, "servmgr listen on %s:%s", listen_list->addr[0] ? listen_list->addr : "*", listen_list->port);
  
    freeaddrinfo(aiHead);
  }

  if (sockfd_used == 0) {
    Log(0, "servmgr: No listen socket open");
    return -1;
  }

  setproctitle ("server manager (listen %s)", config->listen.first->port);

  for (;;)
  {
    struct timeval tv;
    int n;
    int curfd, maxfd = 0;
    fd_set r;

    FD_ZERO (&r);
    for (curfd=0; curfd<sockfd_used; curfd++)
    {
      FD_SET (sockfd[curfd], &r);
      if (sockfd[curfd] > maxfd)
        maxfd = sockfd[curfd];
    }
    tv.tv_usec = 0;
    tv.tv_sec  = CHECKCFG_INTERVAL;
    unblocksig();
    check_child(&n_servers);
    n = select(maxfd+1, &r, NULL, NULL, &tv);
    blocksig();
    switch (n)
    { case 0: /* timeout */
        if (checkcfg()) 
        {
          for (curfd=0; curfd<sockfd_used; curfd++)
            soclose(sockfd[curfd]);
          sockfd_used = 0;
          return 0;
        }
        unblocksig();
        check_child(&n_servers);
        blocksig();
        continue;
      case -1:
        save_errno = TCPERRNO;
        if (binkd_exit)
          goto accepterr;
        if (TCPERRNO == EINTR)
        {
          unblocksig();
          check_child(&n_servers);
          blocksig();
          if (checkcfg())
          {
            for (curfd=0; curfd<sockfd_used; curfd++)
              soclose(sockfd[curfd]);
            sockfd_used = 0;
            return 0;
          }
          continue;
        }
        Log (1, "servmgr select(): %s", TCPERR ());
        goto accepterr;
    }
 
    for (curfd=0; curfd<sockfd_used; curfd++)
    {
      if (!FD_ISSET(sockfd[curfd], &r))
        continue;

      client_addr_len = sizeof (client_addr);
      if ((new_sockfd = accept (sockfd[curfd], (struct sockaddr *)&client_addr,
                                &client_addr_len)) == INVALID_SOCKET)
      {
        save_errno = TCPERRNO;
        if (save_errno != EINVAL && save_errno != EINTR)
        {
          if (!binkd_exit)
            Log (1, "servmgr accept(): %s", TCPERR ());
#ifdef UNIX
          if (save_errno == ECONNRESET ||
              save_errno == ETIMEDOUT ||
              save_errno == ECONNABORTED ||
              save_errno == EHOSTUNREACH)
            continue;
#endif
        accepterr:
#ifdef OS2
          /* Buggy external process closed our socket? Or OS/2 bug? */
          if (save_errno == ENOTSOCK)
            return 0;  /* will force socket re-creation */
#endif
          return -1;
        }
      }
      else
      {
        char host[BINKD_FQDNLEN + 1];
        char service[MAXSERVNAME + 1];
        int aiErr;
  
        add_socket(new_sockfd);
        /* Was the socket created after close_sockets loop in exitfunc()? */
        if (binkd_exit)
        {
          del_socket(new_sockfd);
          soclose(new_sockfd);
          continue;
        }
        rel_grow_handles (6);
        ext_rand=rand();
        /* never resolve name in here, will be done during session */
        aiErr = getnameinfo((struct sockaddr *)&client_addr, client_addr_len,
            host, sizeof(host), service, sizeof(service),
            NI_NUMERICHOST | NI_NUMERICSERV);
        if (aiErr == 0) 
          Log (3, "incoming from %s (%s)", host, service);
        else
        {
          Log(2, "Error in getnameinfo(): %s (%d)", gai_strerror(aiErr), aiErr);
          Log(3, "incoming from unknown");
        }
  
        /* Creating a new process for the incoming connection */
        threadsafe(++n_servers);
        if ((pid = branch (serv, (void *) &new_sockfd, sizeof (new_sockfd))) < 0)
        {
          del_socket(new_sockfd);
          soclose(new_sockfd);
          rel_grow_handles (-6);
          threadsafe(--n_servers);
          PostSem(&eothread);
          Log (1, "servmgr branch(): cannot branch out");
          sleep(1);
        }
        else
        {
          Log (5, "started server #%i, id=%i", n_servers, pid);
#if defined(HAVE_FORK) && !defined(HAVE_THREADS)
          soclose (new_sockfd);
#endif
        }
      }
    }
  }
}