113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <unistd.h> 213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/types.h> 313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/stat.h> 413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/mman.h> 513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/mount.h> 613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/utsname.h> 713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <fcntl.h> 813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <stdlib.h> 913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <stdio.h> 1013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <ctype.h> 1113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <string.h> 1213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <errno.h> 1313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include "selinux_internal.h" 1413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sepol/sepol.h> 1513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sepol/policydb.h> 1613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <dlfcn.h> 1713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include "policy.h" 1813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <limits.h> 1913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 2013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleint security_load_policy(void *data, size_t len) 2113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{ 2213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle char path[PATH_MAX]; 2313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int fd, ret; 2413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 2513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (!selinux_mnt) { 2613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle errno = ENOENT; 2713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return -1; 2813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 2913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 3013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle snprintf(path, sizeof path, "%s/load", selinux_mnt); 3113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fd = open(path, O_RDWR); 3213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (fd < 0) 3313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return -1; 3413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 3513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle ret = write(fd, data, len); 3613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle close(fd); 3713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (ret < 0) 3813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return -1; 3913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return 0; 4013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle} 4113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 4213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlehidden_def(security_load_policy) 4313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 4413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleint load_setlocaldefs hidden = 1; 4513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 4613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#undef max 4713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define max(a, b) (((a) > (b)) ? (a) : (b)) 4813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 4913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleint selinux_mkload_policy(int preservebools) 5013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{ 5113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int kernvers = security_policyvers(); 5213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION, vers; 5313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int setlocaldefs = load_setlocaldefs; 544611c09d6b22ff2aebb55388d777bcf8921dd50bStephen Smalley char path[PATH_MAX]; 5513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle struct stat sb; 5613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle struct utsname uts; 5713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle size_t size; 5813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void *map, *data; 594611c09d6b22ff2aebb55388d777bcf8921dd50bStephen Smalley int fd, rc = -1, prot; 6013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle sepol_policydb_t *policydb; 6113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle sepol_policy_file_t *pf; 6213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int usesepol = 0; 6313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*vers_max)(void) = NULL; 6413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*vers_min)(void) = NULL; 6513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*policy_file_create)(sepol_policy_file_t **) = NULL; 6613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void (*policy_file_free)(sepol_policy_file_t *) = NULL; 6713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void (*policy_file_set_mem)(sepol_policy_file_t *, char*, size_t) = NULL; 6813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*policydb_create)(sepol_policydb_t **) = NULL; 6913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void (*policydb_free)(sepol_policydb_t *) = NULL; 7013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*policydb_read)(sepol_policydb_t *, sepol_policy_file_t *) = NULL; 7113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*policydb_set_vers)(sepol_policydb_t *, unsigned int) = NULL; 7213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*policydb_to_image)(sepol_handle_t *, sepol_policydb_t *, void **, size_t *) = NULL; 7313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*genbools_array)(void *data, size_t len, char **names, int *values, int nel) = NULL; 7413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*genusers)(void *data, size_t len, const char *usersdir, void **newdata, size_t * newlen) = NULL; 7513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int (*genbools)(void *data, size_t len, char *boolpath) = NULL; 7613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 7713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#ifdef SHARED 7813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle char *errormsg = NULL; 7913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void *libsepolh = NULL; 8013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle libsepolh = dlopen("libsepol.so.1", RTLD_NOW); 8113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (libsepolh) { 8213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle usesepol = 1; 8313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle dlerror(); 8413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define DLERR() if ((errormsg = dlerror())) goto dlclose; 8513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers_max = dlsym(libsepolh, "sepol_policy_kern_vers_max"); 8613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 8713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers_min = dlsym(libsepolh, "sepol_policy_kern_vers_min"); 8813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 8913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 9013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_create = dlsym(libsepolh, "sepol_policy_file_create"); 9113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 9213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free = dlsym(libsepolh, "sepol_policy_file_free"); 9313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 9413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_set_mem = dlsym(libsepolh, "sepol_policy_file_set_mem"); 9513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 9613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_create = dlsym(libsepolh, "sepol_policydb_create"); 9713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 9813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_free = dlsym(libsepolh, "sepol_policydb_free"); 9913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 10013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_read = dlsym(libsepolh, "sepol_policydb_read"); 10113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 10213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_set_vers = dlsym(libsepolh, "sepol_policydb_set_vers"); 10313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 10413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_to_image = dlsym(libsepolh, "sepol_policydb_to_image"); 10513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 10613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genbools_array = dlsym(libsepolh, "sepol_genbools_array"); 10713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 10813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genusers = dlsym(libsepolh, "sepol_genusers"); 10913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 11013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genbools = dlsym(libsepolh, "sepol_genbools"); 11113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle DLERR(); 11213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 11313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#undef DLERR 11413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 11513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#else 11613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle usesepol = 1; 11713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers_max = sepol_policy_kern_vers_max; 11813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers_min = sepol_policy_kern_vers_min; 11913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_create = sepol_policy_file_create; 12013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free = sepol_policy_file_free; 12113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_set_mem = sepol_policy_file_set_mem; 12213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_create = sepol_policydb_create; 12313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_free = sepol_policydb_free; 12413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_read = sepol_policydb_read; 12513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_set_vers = sepol_policydb_set_vers; 12613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_to_image = sepol_policydb_to_image; 12713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genbools_array = sepol_genbools_array; 12813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genusers = sepol_genusers; 12913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle genbools = sepol_genbools; 13013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 13113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#endif 13213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 13313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 13413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Check whether we need to support local boolean and user definitions. 13513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 13613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (setlocaldefs) { 13713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (access(selinux_booleans_path(), F_OK) == 0) 13813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto checkbool; 13913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle snprintf(path, sizeof path, "%s.local", selinux_booleans_path()); 14013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (access(path, F_OK) == 0) 14113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto checkbool; 14213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle snprintf(path, sizeof path, "%s/local.users", selinux_users_path()); 14313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (access(path, F_OK) == 0) 14413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto checkbool; 14513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* No local definition files, so disable setlocaldefs. */ 14613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle setlocaldefs = 0; 14713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 14813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 14913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlecheckbool: 15013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 15113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * As of Linux 2.6.22, the kernel preserves boolean 15213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * values across a reload, so we do not need to 15313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * preserve them in userspace. 15413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 15513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (preservebools && uname(&uts) == 0 && strverscmp(uts.release, "2.6.22") >= 0) 15613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle preservebools = 0; 15713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 15813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (usesepol) { 15913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle maxvers = vers_max(); 16013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle minvers = vers_min(); 16113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (!setlocaldefs && !preservebools) 16213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle maxvers = max(kernvers, maxvers); 16313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 16413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 16513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers = maxvers; 16613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle search: 16713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle snprintf(path, sizeof(path), "%s.%d", 16813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle selinux_binary_policy_path(), vers); 16913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fd = open(path, O_RDONLY); 17013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle while (fd < 0 && errno == ENOENT 17113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle && --vers >= minvers) { 17213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Check prior versions to see if old policy is available */ 17313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle snprintf(path, sizeof(path), "%s.%d", 17413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle selinux_binary_policy_path(), vers); 17513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fd = open(path, O_RDONLY); 17613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 17713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (fd < 0) { 17813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, 17913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle "SELinux: Could not open policy file <= %s.%d: %s\n", 18013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle selinux_binary_policy_path(), maxvers, strerror(errno)); 18113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto dlclose; 18213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 18313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 18413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (fstat(fd, &sb) < 0) { 18513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, 18613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle "SELinux: Could not stat policy file %s: %s\n", 18713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle path, strerror(errno)); 18813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto close; 18913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 19013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 19113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle prot = PROT_READ; 19213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (setlocaldefs || preservebools) 19313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle prot |= PROT_WRITE; 19413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 19513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle size = sb.st_size; 19613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0); 19713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (map == MAP_FAILED) { 19813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, 19913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle "SELinux: Could not map policy file %s: %s\n", 20013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle path, strerror(errno)); 20113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto close; 20213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 20313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 20413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (vers > kernvers && usesepol) { 20513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Need to downgrade to kernel-supported version. */ 20613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (policy_file_create(&pf)) 20713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto unmap; 20813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (policydb_create(&policydb)) { 20913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free(pf); 21013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto unmap; 21113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 21213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_set_mem(pf, data, size); 21313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (policydb_read(policydb, pf)) { 21413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free(pf); 21513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_free(policydb); 21613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto unmap; 21713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 21813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (policydb_set_vers(policydb, kernvers) || 21913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_to_image(NULL, policydb, &data, &size)) { 22013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Downgrade failed, keep searching. */ 22113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, 22213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle "SELinux: Could not downgrade policy file %s, searching for an older version.\n", 22313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle path); 22413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free(pf); 22513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_free(policydb); 22613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle munmap(map, sb.st_size); 22713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle close(fd); 22813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle vers--; 22913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto search; 23013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 23113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policy_file_free(pf); 23213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle policydb_free(policydb); 23313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 23413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 23513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (usesepol) { 23613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (setlocaldefs) { 23713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle void *olddata = data; 23813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle size_t oldsize = size; 23913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = genusers(olddata, oldsize, selinux_users_path(), 24013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle &data, &size); 24113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (rc < 0) { 24213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Fall back to the prior image if genusers failed. */ 24313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle data = olddata; 24413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle size = oldsize; 24513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = 0; 24613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } else { 24713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (olddata != map) 24813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(olddata); 24913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 25013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 25113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 25213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#ifndef DISABLE_BOOL 25313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (preservebools) { 2544611c09d6b22ff2aebb55388d777bcf8921dd50bStephen Smalley int *values, len, i; 2554611c09d6b22ff2aebb55388d777bcf8921dd50bStephen Smalley char **names; 25613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = security_get_boolean_names(&names, &len); 25713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (!rc) { 25813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle values = malloc(sizeof(int) * len); 25913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (!values) 26013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto unmap; 26113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle for (i = 0; i < len; i++) 26213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle values[i] = 26313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle security_get_boolean_active(names[i]); 26413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle (void)genbools_array(data, size, names, values, 26513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle len); 26613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(values); 26713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle for (i = 0; i < len; i++) 26813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(names[i]); 26913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(names); 27013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 27113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } else if (setlocaldefs) { 27213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle (void)genbools(data, size, 27313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle (char *)selinux_booleans_path()); 27413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 27513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#endif 27613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 27713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 27813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 27913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = security_load_policy(data, size); 28013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 28113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (rc) 28213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, 28313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle "SELinux: Could not load policy file %s: %s\n", 28413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle path, strerror(errno)); 28513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 28613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle unmap: 28713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (data != map) 28813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(data); 28913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle munmap(map, sb.st_size); 29013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle close: 29113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle close(fd); 29213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle dlclose: 29313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#ifdef SHARED 29413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (errormsg) 29513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, "libselinux: %s\n", errormsg); 29613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (libsepolh) 29713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle dlclose(libsepolh); 29813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#endif 29913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return rc; 30013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle} 30113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 30213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlehidden_def(selinux_mkload_policy) 30313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 30413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle/* 30513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Mount point for selinuxfs. 30613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * This definition is private to the function below. 30713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Everything else uses the location determined during 30813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * libselinux startup via /proc/mounts (see init_selinuxmnt). 30913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * We only need the hardcoded definition for the initial mount 31013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * required for the initial policy load. 31113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 31213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleint selinux_init_load_policy(int *enforce) 31313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{ 31413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle int rc = 0, orig_enforce = 0, seconfig = -2, secmdline = -1; 31513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle FILE *cfg; 31613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle char *buf; 31713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 31813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 31913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Reread the selinux configuration in case it has changed. 32013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Example: Caller has chroot'd and is now loading policy from 32113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * chroot'd environment. 32213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 3237d19f9df510daef5dc929df5854c2dda2a64f475Chad Sellers selinux_reset_config(); 32413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 32513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 32613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Get desired mode (disabled, permissive, enforcing) from 32713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * /etc/selinux/config. 32813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 32913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle selinux_getenforcemode(&seconfig); 33013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 33113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Check for an override of the mode via the kernel command line. */ 3325c6729b4d26fe6b3e64f9301efe6b0fa7d5c8487Daniel J Walsh rc = mount("proc", "/proc", "proc", 0, 0); 33313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle cfg = fopen("/proc/cmdline", "r"); 33413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (cfg) { 33513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle char *tmp; 33613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle buf = malloc(selinux_page_size); 33713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (!buf) { 33813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fclose(cfg); 33913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return -1; 34013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 34113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (fgets(buf, selinux_page_size, cfg) && 34213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle (tmp = strstr(buf, "enforcing="))) { 34313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (tmp == buf || isspace(*(tmp - 1))) { 34413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle secmdline = 34513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle atoi(tmp + sizeof("enforcing=") - 1); 34613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 34713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 34813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fclose(cfg); 34913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle free(buf); 35013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 3517610baa968ad499667c60c222cba326b737c532aStephen Smalley#ifndef MNT_DETACH 35213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define MNT_DETACH 2 3537610baa968ad499667c60c222cba326b737c532aStephen Smalley#endif 35413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (rc == 0) 35513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle umount2("/proc", MNT_DETACH); 35613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 35713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 35813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Determine the final desired mode. 35913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Command line argument takes precedence, then config file. 36013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 36113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (secmdline >= 0) 36213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *enforce = secmdline; 36313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle else if (seconfig >= 0) 36413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *enforce = seconfig; 36513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle else 36613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *enforce = 0; /* unspecified or disabled */ 36713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 36813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 36913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Check for the existence of SELinux via selinuxfs, and 37013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * mount it if present for use in the calls below. 37113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 372b82b7e02dfcd46db75a94352815830fdb651fa94Daniel P. Berrange const char *mntpoint = NULL; 373ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen /* First make sure /sys is mounted */ 374ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen if (mount("sysfs", "/sys", "sysfs", 0, 0) == 0 || errno == EBUSY) { 375ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen if (mount(SELINUXFS, SELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { 376ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen mntpoint = SELINUXMNT; 377ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen } else { 378ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen /* check old mountpoint */ 379ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen if (mount(SELINUXFS, OLDSELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { 380ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen mntpoint = OLDSELINUXMNT; 381ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen } 382ef3e072f581b2418f9bb1703170ad34f32c9408dSven Vermeulen } 383e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh } else { 384e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh /* check old mountpoint */ 385e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh if (mount(SELINUXFS, OLDSELINUXMNT, SELINUXFS, 0, 0) == 0 || errno == EBUSY) { 386e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh mntpoint = OLDSELINUXMNT; 387e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh } 388e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh } 389e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh 390e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh if (! mntpoint ) { 391b3b19fdce58ff6ddfa6dfb8e5576c922c96e1e45Eric Paris if (errno == ENODEV || !selinuxfs_exists()) { 39213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 39313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * SELinux was disabled in the kernel, either 39413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * omitted entirely or disabled at boot via selinux=0. 39513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * This takes precedence over any config or 39613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * commandline enforcing setting. 39713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 39813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *enforce = 0; 39913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } else { 40013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Only emit this error if selinux was not disabled */ 40113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle fprintf(stderr, "Mount failed for selinuxfs on %s: %s\n", SELINUXMNT, strerror(errno)); 40213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 40313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 40413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto noload; 40513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 406e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh set_selinuxmnt(mntpoint); 40713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 40813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 40913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Note: The following code depends on having selinuxfs 41013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * already mounted and selinuxmnt set above. 41113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 41213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 41313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (seconfig == -1) { 41413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Runtime disable of SELinux. */ 41513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = security_disable(); 41613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (rc == 0) { 41713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Successfully disabled, so umount selinuxfs too. */ 418e3cab998b48ab293a9962faf9779d70ca339c65dDaniel J Walsh umount(selinux_mnt); 4191629d2f89a8c5f758413b87b94740aaaa5f21144Daniel J Walsh fini_selinuxmnt(); 420241fac27288a431198ac2fa72198b5a799a91775Will Woods goto noload; 421241fac27288a431198ac2fa72198b5a799a91775Will Woods } else { 422241fac27288a431198ac2fa72198b5a799a91775Will Woods /* 423241fac27288a431198ac2fa72198b5a799a91775Will Woods * It's possible that this failed because policy has 424241fac27288a431198ac2fa72198b5a799a91775Will Woods * already been loaded. We can't disable SELinux now, 425241fac27288a431198ac2fa72198b5a799a91775Will Woods * so the best we can do is force it to be permissive. 426241fac27288a431198ac2fa72198b5a799a91775Will Woods */ 427241fac27288a431198ac2fa72198b5a799a91775Will Woods *enforce = 0; 42813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 42913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 43013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 43113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 43213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * If necessary, change the kernel enforcing status to match 43313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * the desired mode. 43413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 43513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle orig_enforce = rc = security_getenforce(); 43613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (rc < 0) 43713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle goto noload; 43813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle if (orig_enforce != *enforce) { 43913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle rc = security_setenforce(*enforce); 440b985905d2f58836993acf03edc0395acd1f3f7f1Stephen Smalley if (rc < 0) { 441b985905d2f58836993acf03edc0395acd1f3f7f1Stephen Smalley fprintf(stderr, "SELinux: Unable to switch to %s mode: %s\n", (*enforce ? "enforcing" : "permissive"), strerror(errno)); 442b985905d2f58836993acf03edc0395acd1f3f7f1Stephen Smalley if (*enforce) 443b985905d2f58836993acf03edc0395acd1f3f7f1Stephen Smalley goto noload; 444b985905d2f58836993acf03edc0395acd1f3f7f1Stephen Smalley } 44513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle } 44613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 447241fac27288a431198ac2fa72198b5a799a91775Will Woods if (seconfig == -1) 448241fac27288a431198ac2fa72198b5a799a91775Will Woods goto noload; 449241fac27288a431198ac2fa72198b5a799a91775Will Woods 45013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* Load the policy. */ 45113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return selinux_mkload_policy(0); 45213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle 45313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle noload: 45413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle /* 45513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Only return 0 on a successful completion of policy load. 45613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * In any other case, we want to return an error so that init 45713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * knows not to proceed with the re-exec for the domain transition. 45813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Depending on the *enforce setting, init will halt (> 0) or proceed 45913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * normally (otherwise). 46013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */ 46113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle return -1; 46213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle} 463