|
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 |
}
|