diff options
Diffstat (limited to 'libselinux/src/load_policy.c')
-rw-r--r-- | libselinux/src/load_policy.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/libselinux/src/load_policy.c b/libselinux/src/load_policy.c new file mode 100644 index 00000000..63f66099 --- /dev/null +++ b/libselinux/src/load_policy.c @@ -0,0 +1,431 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include "selinux_internal.h" +#include <sepol/sepol.h> +#include <sepol/policydb.h> +#include <dlfcn.h> +#include "policy.h" +#include <limits.h> + +int security_load_policy(void *data, size_t len) +{ + char path[PATH_MAX]; + int fd, ret; + + if (!selinux_mnt) { + errno = ENOENT; + return -1; + } + + snprintf(path, sizeof path, "%s/load", selinux_mnt); + fd = open(path, O_RDWR); + if (fd < 0) + return -1; + + ret = write(fd, data, len); + close(fd); + if (ret < 0) + return -1; + return 0; +} + +hidden_def(security_load_policy) + +int load_setlocaldefs hidden = 1; + +#undef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +int selinux_mkload_policy(int preservebools) +{ + int kernvers = security_policyvers(); + int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION, vers; + int setlocaldefs = load_setlocaldefs; + char path[PATH_MAX], **names; + struct stat sb; + struct utsname uts; + size_t size; + void *map, *data; + int fd, rc = -1, *values, len, i, prot; + sepol_policydb_t *policydb; + sepol_policy_file_t *pf; + int usesepol = 0; + int (*vers_max)(void) = NULL; + int (*vers_min)(void) = NULL; + int (*policy_file_create)(sepol_policy_file_t **) = NULL; + void (*policy_file_free)(sepol_policy_file_t *) = NULL; + void (*policy_file_set_mem)(sepol_policy_file_t *, char*, size_t) = NULL; + int (*policydb_create)(sepol_policydb_t **) = NULL; + void (*policydb_free)(sepol_policydb_t *) = NULL; + int (*policydb_read)(sepol_policydb_t *, sepol_policy_file_t *) = NULL; + int (*policydb_set_vers)(sepol_policydb_t *, unsigned int) = NULL; + int (*policydb_to_image)(sepol_handle_t *, sepol_policydb_t *, void **, size_t *) = NULL; + int (*genbools_array)(void *data, size_t len, char **names, int *values, int nel) = NULL; + int (*genusers)(void *data, size_t len, const char *usersdir, void **newdata, size_t * newlen) = NULL; + int (*genbools)(void *data, size_t len, char *boolpath) = NULL; + +#ifdef SHARED + char *errormsg = NULL; + void *libsepolh = NULL; + libsepolh = dlopen("libsepol.so.1", RTLD_NOW); + if (libsepolh) { + usesepol = 1; + dlerror(); +#define DLERR() if ((errormsg = dlerror())) goto dlclose; + vers_max = dlsym(libsepolh, "sepol_policy_kern_vers_max"); + DLERR(); + vers_min = dlsym(libsepolh, "sepol_policy_kern_vers_min"); + DLERR(); + + policy_file_create = dlsym(libsepolh, "sepol_policy_file_create"); + DLERR(); + policy_file_free = dlsym(libsepolh, "sepol_policy_file_free"); + DLERR(); + policy_file_set_mem = dlsym(libsepolh, "sepol_policy_file_set_mem"); + DLERR(); + policydb_create = dlsym(libsepolh, "sepol_policydb_create"); + DLERR(); + policydb_free = dlsym(libsepolh, "sepol_policydb_free"); + DLERR(); + policydb_read = dlsym(libsepolh, "sepol_policydb_read"); + DLERR(); + policydb_set_vers = dlsym(libsepolh, "sepol_policydb_set_vers"); + DLERR(); + policydb_to_image = dlsym(libsepolh, "sepol_policydb_to_image"); + DLERR(); + genbools_array = dlsym(libsepolh, "sepol_genbools_array"); + DLERR(); + genusers = dlsym(libsepolh, "sepol_genusers"); + DLERR(); + genbools = dlsym(libsepolh, "sepol_genbools"); + DLERR(); + +#undef DLERR + } +#else + usesepol = 1; + vers_max = sepol_policy_kern_vers_max; + vers_min = sepol_policy_kern_vers_min; + policy_file_create = sepol_policy_file_create; + policy_file_free = sepol_policy_file_free; + policy_file_set_mem = sepol_policy_file_set_mem; + policydb_create = sepol_policydb_create; + policydb_free = sepol_policydb_free; + policydb_read = sepol_policydb_read; + policydb_set_vers = sepol_policydb_set_vers; + policydb_to_image = sepol_policydb_to_image; + genbools_array = sepol_genbools_array; + genusers = sepol_genusers; + genbools = sepol_genbools; + +#endif + + /* + * Check whether we need to support local boolean and user definitions. + */ + if (setlocaldefs) { + if (access(selinux_booleans_path(), F_OK) == 0) + goto checkbool; + snprintf(path, sizeof path, "%s.local", selinux_booleans_path()); + if (access(path, F_OK) == 0) + goto checkbool; + snprintf(path, sizeof path, "%s/local.users", selinux_users_path()); + if (access(path, F_OK) == 0) + goto checkbool; + /* No local definition files, so disable setlocaldefs. */ + setlocaldefs = 0; + } + +checkbool: + /* + * As of Linux 2.6.22, the kernel preserves boolean + * values across a reload, so we do not need to + * preserve them in userspace. + */ + if (preservebools && uname(&uts) == 0 && strverscmp(uts.release, "2.6.22") >= 0) + preservebools = 0; + + if (usesepol) { + maxvers = vers_max(); + minvers = vers_min(); + if (!setlocaldefs && !preservebools) + maxvers = max(kernvers, maxvers); + } + + vers = maxvers; + search: + snprintf(path, sizeof(path), "%s.%d", + selinux_binary_policy_path(), vers); + fd = open(path, O_RDONLY); + while (fd < 0 && errno == ENOENT + && --vers >= minvers) { + /* Check prior versions to see if old policy is available */ + snprintf(path, sizeof(path), "%s.%d", + selinux_binary_policy_path(), vers); + fd = open(path, O_RDONLY); + } + if (fd < 0) { + fprintf(stderr, + "SELinux: Could not open policy file <= %s.%d: %s\n", + selinux_binary_policy_path(), maxvers, strerror(errno)); + goto dlclose; + } + + if (fstat(fd, &sb) < 0) { + fprintf(stderr, + "SELinux: Could not stat policy file %s: %s\n", + path, strerror(errno)); + goto close; + } + + prot = PROT_READ; + if (setlocaldefs || preservebools) + prot |= PROT_WRITE; + + size = sb.st_size; + data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, + "SELinux: Could not map policy file %s: %s\n", + path, strerror(errno)); + goto close; + } + + if (vers > kernvers && usesepol) { + /* Need to downgrade to kernel-supported version. */ + if (policy_file_create(&pf)) + goto unmap; + if (policydb_create(&policydb)) { + policy_file_free(pf); + goto unmap; + } + policy_file_set_mem(pf, data, size); + if (policydb_read(policydb, pf)) { + policy_file_free(pf); + policydb_free(policydb); + goto unmap; + } + if (policydb_set_vers(policydb, kernvers) || + policydb_to_image(NULL, policydb, &data, &size)) { + /* Downgrade failed, keep searching. */ + fprintf(stderr, + "SELinux: Could not downgrade policy file %s, searching for an older version.\n", + path); + policy_file_free(pf); + policydb_free(policydb); + munmap(map, sb.st_size); + close(fd); + vers--; + goto search; + } + policy_file_free(pf); + policydb_free(policydb); + } + + if (usesepol) { + if (setlocaldefs) { + void *olddata = data; + size_t oldsize = size; + rc = genusers(olddata, oldsize, selinux_users_path(), + &data, &size); + if (rc < 0) { + /* Fall back to the prior image if genusers failed. */ + data = olddata; + size = oldsize; + rc = 0; + } else { + if (olddata != map) + free(olddata); + } + } + +#ifndef DISABLE_BOOL + if (preservebools) { + rc = security_get_boolean_names(&names, &len); + if (!rc) { + values = malloc(sizeof(int) * len); + if (!values) + goto unmap; + for (i = 0; i < len; i++) + values[i] = + security_get_boolean_active(names[i]); + (void)genbools_array(data, size, names, values, + len); + free(values); + for (i = 0; i < len; i++) + free(names[i]); + free(names); + } + } else if (setlocaldefs) { + (void)genbools(data, size, + (char *)selinux_booleans_path()); + } +#endif + } + + + rc = security_load_policy(data, size); + + if (rc) + fprintf(stderr, + "SELinux: Could not load policy file %s: %s\n", + path, strerror(errno)); + + unmap: + if (data != map) + free(data); + munmap(map, sb.st_size); + close: + close(fd); + dlclose: +#ifdef SHARED + if (errormsg) + fprintf(stderr, "libselinux: %s\n", errormsg); + if (libsepolh) + dlclose(libsepolh); +#endif + return rc; +} + +hidden_def(selinux_mkload_policy) + +/* + * Mount point for selinuxfs. + * This definition is private to the function below. + * Everything else uses the location determined during + * libselinux startup via /proc/mounts (see init_selinuxmnt). + * We only need the hardcoded definition for the initial mount + * required for the initial policy load. + */ +int selinux_init_load_policy(int *enforce) +{ + int rc = 0, orig_enforce = 0, seconfig = -2, secmdline = -1; + FILE *cfg; + char *buf; + + /* + * Reread the selinux configuration in case it has changed. + * Example: Caller has chroot'd and is now loading policy from + * chroot'd environment. + */ + reset_selinux_config(); + + /* + * Get desired mode (disabled, permissive, enforcing) from + * /etc/selinux/config. + */ + selinux_getenforcemode(&seconfig); + + /* Check for an override of the mode via the kernel command line. */ + rc = mount("none", "/proc", "proc", 0, 0); + cfg = fopen("/proc/cmdline", "r"); + if (cfg) { + char *tmp; + buf = malloc(selinux_page_size); + if (!buf) { + fclose(cfg); + return -1; + } + if (fgets(buf, selinux_page_size, cfg) && + (tmp = strstr(buf, "enforcing="))) { + if (tmp == buf || isspace(*(tmp - 1))) { + secmdline = + atoi(tmp + sizeof("enforcing=") - 1); + } + } + fclose(cfg); + free(buf); + } +#define MNT_DETACH 2 + if (rc == 0) + umount2("/proc", MNT_DETACH); + + /* + * Determine the final desired mode. + * Command line argument takes precedence, then config file. + */ + if (secmdline >= 0) + *enforce = secmdline; + else if (seconfig >= 0) + *enforce = seconfig; + else + *enforce = 0; /* unspecified or disabled */ + + /* + * Check for the existence of SELinux via selinuxfs, and + * mount it if present for use in the calls below. + */ + if (mount("none", SELINUXMNT, "selinuxfs", 0, 0) < 0) { + if (errno == ENODEV) { + /* + * SELinux was disabled in the kernel, either + * omitted entirely or disabled at boot via selinux=0. + * This takes precedence over any config or + * commandline enforcing setting. + */ + *enforce = 0; + } else { + /* Only emit this error if selinux was not disabled */ + fprintf(stderr, "Mount failed for selinuxfs on %s: %s\n", SELINUXMNT, strerror(errno)); + } + + goto noload; + } + set_selinuxmnt(SELINUXMNT); + + /* + * Note: The following code depends on having selinuxfs + * already mounted and selinuxmnt set above. + */ + + if (seconfig == -1) { + /* Runtime disable of SELinux. */ + rc = security_disable(); + if (rc == 0) { + /* Successfully disabled, so umount selinuxfs too. */ + umount(SELINUXMNT); + } + /* + * If we failed to disable, SELinux will still be + * effectively permissive, because no policy is loaded. + * No need to call security_setenforce(0) here. + */ + goto noload; + } + + /* + * If necessary, change the kernel enforcing status to match + * the desired mode. + */ + orig_enforce = rc = security_getenforce(); + if (rc < 0) + goto noload; + if (orig_enforce != *enforce) { + rc = security_setenforce(*enforce); + if (rc < 0) + goto noload; + } + + /* Load the policy. */ + return selinux_mkload_policy(0); + + noload: + /* + * Only return 0 on a successful completion of policy load. + * In any other case, we want to return an error so that init + * knows not to proceed with the re-exec for the domain transition. + * Depending on the *enforce setting, init will halt (> 0) or proceed + * normally (otherwise). + */ + return -1; +} |