Chris PeBenito 473ea7
#include <unistd.h>
Chris PeBenito 473ea7
#include <sys/types.h>
Chris PeBenito 473ea7
#include <sys/stat.h>
Chris PeBenito 473ea7
#include <sys/mman.h>
Chris PeBenito 473ea7
#include <sys/mount.h>
Chris PeBenito 473ea7
#include <fcntl.h>
Chris PeBenito 473ea7
#include <stdlib.h>
Chris PeBenito 473ea7
#include <stdio.h>
Chris PeBenito 473ea7
#include <ctype.h>
Chris PeBenito 473ea7
#include <string.h>
Chris PeBenito 473ea7
#include <errno.h>
Chris PeBenito 473ea7
#include "selinux_internal.h"
Chris PeBenito 473ea7
#include <sepol/sepol.h>
Chris PeBenito f9d771
#include <sepol/policydb.h>
Chris PeBenito 473ea7
#include "policy.h"
Chris PeBenito 473ea7
#include <limits.h>
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int security_load_policy(void *data, size_t len)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	char path[PATH_MAX];
Chris PeBenito 473ea7
	int fd, ret;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	snprintf(path, sizeof path, "%s/load", selinux_mnt);
Chris PeBenito 473ea7
	fd = open(path, O_RDWR);
Chris PeBenito 473ea7
	if (fd < 0) 
Chris PeBenito 473ea7
		return -1;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	ret = write(fd, data, len);
Chris PeBenito 473ea7
	close(fd);
Chris PeBenito 473ea7
	if (ret < 0)
Chris PeBenito 473ea7
		return -1;
Chris PeBenito 473ea7
	return 0;
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
hidden_def(security_load_policy)
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int load_setlocaldefs hidden = 1;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int selinux_mkload_policy(int preservebools)
Chris PeBenito 473ea7
{
Chris PeBenito f9d771
	int vers = sepol_policy_kern_vers_max();
Chris PeBenito f9d771
	int kernvers = security_policyvers();
Chris PeBenito 473ea7
	char path[PATH_MAX], **names;
Chris PeBenito 473ea7
	struct stat sb;
Chris PeBenito 473ea7
	size_t size;
Chris PeBenito 473ea7
	void *map, *data;
Chris PeBenito 473ea7
	int fd, rc = -1, *values, len, i, prot;
Chris PeBenito f9d771
	sepol_policydb_t *policydb;
Chris PeBenito f9d771
	sepol_policy_file_t *pf;
Chris PeBenito 473ea7
Chris PeBenito f9d771
search:
Chris PeBenito 473ea7
	snprintf(path, sizeof(path), "%s.%d", 
Chris PeBenito 473ea7
		 selinux_binary_policy_path(), vers);
Chris PeBenito 473ea7
	fd = open(path, O_RDONLY);
Chris PeBenito f9d771
	while (fd < 0 && errno == ENOENT && --vers >= sepol_policy_kern_vers_min()) {
Chris PeBenito 473ea7
		/* Check prior versions to see if old policy is available */
Chris PeBenito 473ea7
		snprintf(path, sizeof(path), "%s.%d", 
Chris PeBenito 473ea7
			 selinux_binary_policy_path(), vers);
Chris PeBenito 473ea7
		fd = open(path, O_RDONLY);
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
	if (fd < 0)
Chris PeBenito 473ea7
		return -1;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	if (fstat(fd, &sb) < 0)
Chris PeBenito 473ea7
		goto close;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	prot = PROT_READ;
Chris PeBenito 473ea7
	if (load_setlocaldefs || preservebools) 
Chris PeBenito 473ea7
		prot |= PROT_WRITE;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	size = sb.st_size;
Chris PeBenito 473ea7
	data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0);
Chris PeBenito 473ea7
	if (map == MAP_FAILED) 
Chris PeBenito 473ea7
		goto close;
Chris PeBenito 473ea7
Chris PeBenito f9d771
	if (vers > kernvers) {
Chris PeBenito f9d771
		/* Need to downgrade to kernel-supported version. */
Chris PeBenito f9d771
		if (sepol_policy_file_create(&pf))
Chris PeBenito f9d771
			goto unmap;
Chris PeBenito f9d771
		if (sepol_policydb_create(&policydb)) {
Chris PeBenito f9d771
			sepol_policy_file_free(pf);
Chris PeBenito f9d771
			goto unmap;
Chris PeBenito f9d771
		}
Chris PeBenito f9d771
		sepol_policy_file_set_mem(pf, data, size);
Chris PeBenito f9d771
		if (sepol_policydb_read(policydb, pf)) {
Chris PeBenito f9d771
			sepol_policy_file_free(pf);
Chris PeBenito f9d771
			sepol_policydb_free(policydb);
Chris PeBenito f9d771
			goto unmap;
Chris PeBenito f9d771
		}
Chris PeBenito f9d771
		if (sepol_policydb_set_vers(policydb, kernvers) ||
Chris PeBenito f9d771
		    sepol_policydb_to_image(policydb, &data, &size)) {
Chris PeBenito f9d771
			/* Downgrade failed, keep searching. */
Chris PeBenito f9d771
			sepol_policy_file_free(pf);
Chris PeBenito f9d771
			sepol_policydb_free(policydb);
Chris PeBenito f9d771
			munmap(map, sb.st_size);
Chris PeBenito f9d771
			close(fd);			
Chris PeBenito f9d771
			vers--;
Chris PeBenito f9d771
			goto search;
Chris PeBenito f9d771
		}
Chris PeBenito f9d771
		sepol_policy_file_free(pf);
Chris PeBenito f9d771
		sepol_policydb_free(policydb);
Chris PeBenito f9d771
	}
Chris PeBenito f9d771
Chris PeBenito 473ea7
	if (load_setlocaldefs) {
Chris PeBenito f9d771
		void *olddata = data;
Chris PeBenito f9d771
		size_t oldsize = size;
Chris PeBenito f9d771
		rc = sepol_genusers(olddata, oldsize, selinux_users_path(), &data, &size);
Chris PeBenito 473ea7
		if (rc < 0) {
Chris PeBenito f9d771
			/* Fall back to the prior image if genusers failed. */
Chris PeBenito f9d771
			data = olddata;
Chris PeBenito f9d771
			size = oldsize;
Chris PeBenito 473ea7
			rc = 0;
Chris PeBenito f9d771
		} else {
Chris PeBenito f9d771
			if (olddata != map)
Chris PeBenito f9d771
				free(olddata);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	if (preservebools) {
Chris PeBenito 473ea7
		rc = security_get_boolean_names(&names, &len;;
Chris PeBenito 473ea7
		if (!rc) {
Chris PeBenito 473ea7
			values = malloc(sizeof(int)*len);
Chris PeBenito 473ea7
			if (!values)
Chris PeBenito 473ea7
				goto unmap;
Chris PeBenito 473ea7
			for (i = 0; i < len; i++)
Chris PeBenito 473ea7
				values[i] = security_get_boolean_active(names[i]);
Chris PeBenito 473ea7
			(void) sepol_genbools_array(data, size, names, values, len);
Chris PeBenito 473ea7
			free(values);
Chris PeBenito 473ea7
			for (i = 0; i < len; i++)
Chris PeBenito 473ea7
				free(names[i]);
Chris PeBenito 473ea7
			free(names);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
	} else if (load_setlocaldefs) {
Chris PeBenito 473ea7
		(void) sepol_genbools(data, size, (char*)selinux_booleans_path());
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	rc = security_load_policy(data, size);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
unmap:
Chris PeBenito 473ea7
	if (data != map)
Chris PeBenito 473ea7
		free(data);
Chris PeBenito 473ea7
	munmap(map, sb.st_size);
Chris PeBenito 473ea7
close:
Chris PeBenito 473ea7
	close(fd);
Chris PeBenito 473ea7
	return rc;
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
hidden_def(selinux_mkload_policy)
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/*
Chris PeBenito 473ea7
 * Mount point for selinuxfs. 
Chris PeBenito 473ea7
 * This definition is private to the function below.
Chris PeBenito 473ea7
 * Everything else uses the location determined during 
Chris PeBenito 473ea7
 * libselinux startup via /proc/mounts (see init_selinuxmnt).  
Chris PeBenito 473ea7
 * We only need the hardcoded definition for the initial mount 
Chris PeBenito 473ea7
 * required for the initial policy load.
Chris PeBenito 473ea7
 */
Chris PeBenito 473ea7
#define SELINUXMNT "/selinux/"
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int selinux_init_load_policy(int *enforce)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	int rc = 0, orig_enforce = 0, seconfig = -2, secmdline = -1;
Chris PeBenito 473ea7
	FILE *cfg;
Chris PeBenito 473ea7
	char buf[4096];
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/*
Chris PeBenito 473ea7
	 * Get desired mode (disabled, permissive, enforcing) from 
Chris PeBenito 473ea7
	 * /etc/selinux/config. 
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
	selinux_getenforcemode(&seconfig);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/* Check for an override of the mode via the kernel command line. */
Chris PeBenito 473ea7
	rc = mount("none", "/proc", "proc", 0, 0);
Chris PeBenito 473ea7
	cfg = fopen("/proc/cmdline", "r");
Chris PeBenito 473ea7
	if (cfg) {
Chris PeBenito 473ea7
		char *tmp;
Chris PeBenito 473ea7
		if (fgets(buf, 4096, cfg) && 
Chris PeBenito 473ea7
		    (tmp = strstr(buf,"enforcing="))) {
Chris PeBenito 473ea7
			if (tmp == buf || isspace(*(tmp-1))) {
Chris PeBenito 473ea7
				secmdline = atoi(tmp+sizeof("enforcing=")-1);
Chris PeBenito 473ea7
			}
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		fclose(cfg);
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
#define MNT_DETACH 2
Chris PeBenito 473ea7
	if (rc == 0)
Chris PeBenito 473ea7
		umount2("/proc", MNT_DETACH);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/* 
Chris PeBenito 473ea7
	 * Determine the final desired mode.
Chris PeBenito 473ea7
	 * Command line argument takes precedence, then config file. 
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
	if (secmdline >= 0)
Chris PeBenito 473ea7
		*enforce = secmdline; 
Chris PeBenito 473ea7
	else if (seconfig >= 0)
Chris PeBenito 473ea7
		*enforce = seconfig;
Chris PeBenito 473ea7
	else
Chris PeBenito 473ea7
		*enforce = 0; /* unspecified or disabled */
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/*
Chris PeBenito 473ea7
	 * Check for the existence of SELinux via selinuxfs, and 
Chris PeBenito 473ea7
	 * mount it if present for use in the calls below.  
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
	if (mount("none", SELINUXMNT, "selinuxfs", 0, 0) < 0) {
Chris PeBenito 473ea7
		if (errno == ENODEV) {
Chris PeBenito 473ea7
			/*
Chris PeBenito 473ea7
			 * SELinux was disabled in the kernel, either
Chris PeBenito 473ea7
			 * omitted entirely or disabled at boot via selinux=0.
Chris PeBenito 473ea7
			 * This takes precedence over any config or
Chris PeBenito 473ea7
			 * commandline enforcing setting.
Chris PeBenito 473ea7
			 */
Chris PeBenito 473ea7
			*enforce = 0;
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		goto noload;
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
	set_selinuxmnt(SELINUXMNT);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/*
Chris PeBenito 473ea7
	 * Note:  The following code depends on having selinuxfs 
Chris PeBenito 473ea7
	 * already mounted and selinuxmnt set above.
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	if (seconfig == -1) {
Chris PeBenito 473ea7
		/* Runtime disable of SELinux. */
Chris PeBenito 473ea7
		rc = security_disable();
Chris PeBenito 473ea7
		if (rc == 0) {
Chris PeBenito 473ea7
			/* Successfully disabled, so umount selinuxfs too. */
Chris PeBenito 473ea7
			umount(SELINUXMNT);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		/*
Chris PeBenito 473ea7
		 * If we failed to disable, SELinux will still be 
Chris PeBenito 473ea7
		 * effectively permissive, because no policy is loaded. 
Chris PeBenito 473ea7
		 * No need to call security_setenforce(0) here.
Chris PeBenito 473ea7
		 */
Chris PeBenito 473ea7
		goto noload;
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/*
Chris PeBenito 473ea7
	 * If necessary, change the kernel enforcing status to match 
Chris PeBenito 473ea7
	 * the desired mode. 
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
	orig_enforce = rc = security_getenforce();
Chris PeBenito 473ea7
	if (rc < 0)
Chris PeBenito 473ea7
		goto noload;
Chris PeBenito 473ea7
	if (orig_enforce != *enforce) {
Chris PeBenito 473ea7
		rc = security_setenforce(*enforce);
Chris PeBenito 473ea7
		if (rc < 0)
Chris PeBenito 473ea7
			goto noload;
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	/* Load the policy. */
Chris PeBenito 473ea7
	return selinux_mkload_policy(0);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
noload:
Chris PeBenito 473ea7
	/*
Chris PeBenito 473ea7
	 * Only return 0 on a successful completion of policy load.
Chris PeBenito 473ea7
	 * In any other case, we want to return an error so that init
Chris PeBenito 473ea7
	 * knows not to proceed with the re-exec for the domain transition.
Chris PeBenito 473ea7
	 * Depending on the *enforce setting, init will halt (> 0) or proceed
Chris PeBenito 473ea7
	 * normally (otherwise).
Chris PeBenito 473ea7
	 */
Chris PeBenito 473ea7
	return -1;
Chris PeBenito 473ea7
}