#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" #ifndef ANDROID #include <sepol/sepol.h> #include <sepol/policydb.h> #endif #include <dlfcn.h> #include "policy.h" #include <limits.h> #ifndef MNT_DETACH #define MNT_DETACH 2 #endif 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 | O_CLOEXEC); if (fd < 0) return -1; ret = write(fd, data, len); close(fd); if (ret < 0) return -1; return 0; } hidden_def(security_load_policy) #ifndef ANDROID 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]; struct stat sb; struct utsname uts; size_t size; void *map, *data; int fd, rc = -1, 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, const 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 | O_CLOEXEC); 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 | O_CLOEXEC); } 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); } } if (preservebools) { int *values, len, i; char **names; rc = security_get_boolean_names(&names, &len); if (!rc) { values = malloc(sizeof(int) * len); if (!values) { free(names); 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, selinux_booleans_path()); } } 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. */ selinux_reset_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("proc", "/proc", "proc", 0, 0); cfg = fopen("/proc/cmdline", "re"); 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); } /* * 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. */ const char *mntpoint = NULL; /* First make sure /sys is mounted */ if (mount("sysfs", "/sys", "sysfs", 0, 0) == 0 || errno == EBUSY) { if (mount(SELINUXFS, SELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { mntpoint = SELINUXMNT; } else { /* check old mountpoint */ if (mount(SELINUXFS, OLDSELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { mntpoint = OLDSELINUXMNT; } } } else { /* check old mountpoint */ if (mount(SELINUXFS, OLDSELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { mntpoint = OLDSELINUXMNT; } } if (! mntpoint ) { if (errno == ENODEV || !selinuxfs_exists()) { /* * 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)); } if (rc == 0) umount2("/proc", MNT_DETACH); goto noload; } set_selinuxmnt(mntpoint); if (rc == 0) umount2("/proc", MNT_DETACH); /* * 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(selinux_mnt); fini_selinuxmnt(); goto noload; } else { /* * It's possible that this failed because policy has * already been loaded. We can't disable SELinux now, * so the best we can do is force it to be permissive. */ *enforce = 0; } } /* * 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) { fprintf(stderr, "SELinux: Unable to switch to %s mode: %s\n", (*enforce ? "enforcing" : "permissive"), strerror(errno)); if (*enforce) goto noload; } } if (seconfig == -1) { umount(selinux_mnt); fini_selinuxmnt(); 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; } #endif