diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/include/conditional.h | 2 | ||||
-rw-r--r-- | security/selinux/include/security.h | 16 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 69 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 85 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 10 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.h | 2 |
6 files changed, 104 insertions, 80 deletions
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h index 539ab357707d..b09343346e3f 100644 --- a/security/selinux/include/conditional.h +++ b/security/selinux/include/conditional.h @@ -13,7 +13,7 @@ #include "security.h" -int security_get_bools(struct selinux_state *state, +int security_get_bools(struct selinux_policy *policy, u32 *len, char ***names, int **values); int security_set_bools(struct selinux_state *state, u32 len, int *values); diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 02dd91c12235..c68ed2beadff 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -85,6 +85,7 @@ extern int selinux_enabled_boot; struct selinux_avc; struct selinux_ss; +struct selinux_policy; struct selinux_state { #ifdef CONFIG_SECURITY_SELINUX_DISABLE @@ -210,7 +211,12 @@ static inline bool selinux_policycap_genfs_seclabel_symlinks(void) int security_mls_enabled(struct selinux_state *state); int security_load_policy(struct selinux_state *state, - void *data, size_t len); + void *data, size_t len, + struct selinux_policy **newpolicyp); +void selinux_policy_commit(struct selinux_state *state, + struct selinux_policy *newpolicy); +void selinux_policy_cancel(struct selinux_state *state, + struct selinux_policy *policy); int security_read_policy(struct selinux_state *state, void **data, size_t *len); size_t security_policydb_len(struct selinux_state *state); @@ -344,9 +350,9 @@ int security_net_peersid_resolve(struct selinux_state *state, u32 xfrm_sid, u32 *peer_sid); -int security_get_classes(struct selinux_state *state, +int security_get_classes(struct selinux_policy *policy, char ***classes, int *nclasses); -int security_get_permissions(struct selinux_state *state, +int security_get_permissions(struct selinux_policy *policy, char *class, char ***perms, int *nperms); int security_get_reject_unknown(struct selinux_state *state); int security_get_allow_unknown(struct selinux_state *state); @@ -366,6 +372,10 @@ int security_genfs_sid(struct selinux_state *state, const char *fstype, char *name, u16 sclass, u32 *sid); +int selinux_policy_genfs_sid(struct selinux_policy *policy, + const char *fstype, char *name, u16 sclass, + u32 *sid); + #ifdef CONFIG_NETLABEL int security_netlbl_secattr_to_sid(struct selinux_state *state, struct netlbl_lsm_secattr *secattr, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 4781314c2510..131816878e50 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -346,9 +346,10 @@ static const struct file_operations sel_policyvers_ops = { }; /* declaration for sel_write_load */ -static int sel_make_bools(struct selinux_fs_info *fsi); -static int sel_make_classes(struct selinux_fs_info *fsi); -static int sel_make_policycap(struct selinux_fs_info *fsi); +static int sel_make_bools(struct selinux_fs_info *fsi, + struct selinux_policy *newpolicy); +static int sel_make_classes(struct selinux_fs_info *fsi, + struct selinux_policy *newpolicy); /* declaration for sel_make_class_dirs */ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, @@ -508,28 +509,23 @@ static const struct file_operations sel_policy_ops = { .llseek = generic_file_llseek, }; -static int sel_make_policy_nodes(struct selinux_fs_info *fsi) +static int sel_make_policy_nodes(struct selinux_fs_info *fsi, + struct selinux_policy *newpolicy) { int ret; - ret = sel_make_bools(fsi); + ret = sel_make_bools(fsi, newpolicy); if (ret) { pr_err("SELinux: failed to load policy booleans\n"); return ret; } - ret = sel_make_classes(fsi); + ret = sel_make_classes(fsi, newpolicy); if (ret) { pr_err("SELinux: failed to load policy classes\n"); return ret; } - ret = sel_make_policycap(fsi); - if (ret) { - pr_err("SELinux: failed to load policy capabilities\n"); - return ret; - } - return 0; } @@ -538,6 +534,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, { struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_policy *newpolicy; ssize_t length; void *data = NULL; @@ -563,15 +560,19 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, if (copy_from_user(data, buf, count) != 0) goto out; - length = security_load_policy(fsi->state, data, count); + length = security_load_policy(fsi->state, data, count, &newpolicy); if (length) { pr_warn_ratelimited("SELinux: failed to load policy\n"); goto out; } - length = sel_make_policy_nodes(fsi); - if (length) + length = sel_make_policy_nodes(fsi, newpolicy); + if (length) { + selinux_policy_cancel(fsi->state, newpolicy); goto out1; + } + + selinux_policy_commit(fsi->state, newpolicy); length = count; @@ -1333,7 +1334,8 @@ static void sel_remove_entries(struct dentry *de) #define BOOL_DIR_NAME "booleans" -static int sel_make_bools(struct selinux_fs_info *fsi) +static int sel_make_bools(struct selinux_fs_info *fsi, + struct selinux_policy *newpolicy) { int ret; ssize_t len; @@ -1362,7 +1364,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi) if (!page) goto out; - ret = security_get_bools(fsi->state, &num, &names, &values); + ret = security_get_bools(newpolicy, &num, &names, &values); if (ret) goto out; @@ -1388,7 +1390,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi) } isec = selinux_inode(inode); - ret = security_genfs_sid(fsi->state, "selinuxfs", page, + ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page, SECCLASS_FILE, &sid); if (ret) { pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", @@ -1791,14 +1793,14 @@ static const struct file_operations sel_policycap_ops = { .llseek = generic_file_llseek, }; -static int sel_make_perm_files(char *objclass, int classvalue, - struct dentry *dir) +static int sel_make_perm_files(struct selinux_policy *newpolicy, + char *objclass, int classvalue, + struct dentry *dir) { - struct selinux_fs_info *fsi = dir->d_sb->s_fs_info; int i, rc, nperms; char **perms; - rc = security_get_permissions(fsi->state, objclass, &perms, &nperms); + rc = security_get_permissions(newpolicy, objclass, &perms, &nperms); if (rc) return rc; @@ -1831,8 +1833,9 @@ out: return rc; } -static int sel_make_class_dir_entries(char *classname, int index, - struct dentry *dir) +static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, + char *classname, int index, + struct dentry *dir) { struct super_block *sb = dir->d_sb; struct selinux_fs_info *fsi = sb->s_fs_info; @@ -1858,12 +1861,13 @@ static int sel_make_class_dir_entries(char *classname, int index, if (IS_ERR(dentry)) return PTR_ERR(dentry); - rc = sel_make_perm_files(classname, index, dentry); + rc = sel_make_perm_files(newpolicy, classname, index, dentry); return rc; } -static int sel_make_classes(struct selinux_fs_info *fsi) +static int sel_make_classes(struct selinux_fs_info *fsi, + struct selinux_policy *newpolicy) { int rc, nclasses, i; @@ -1872,7 +1876,7 @@ static int sel_make_classes(struct selinux_fs_info *fsi) /* delete any existing entries */ sel_remove_entries(fsi->class_dir); - rc = security_get_classes(fsi->state, &classes, &nclasses); + rc = security_get_classes(newpolicy, &classes, &nclasses); if (rc) return rc; @@ -1890,7 +1894,7 @@ static int sel_make_classes(struct selinux_fs_info *fsi) } /* i+1 since class values are 1-indexed */ - rc = sel_make_class_dir_entries(classes[i], i + 1, + rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1, class_name_dir); if (rc) goto out; @@ -1909,8 +1913,6 @@ static int sel_make_policycap(struct selinux_fs_info *fsi) struct dentry *dentry = NULL; struct inode *inode = NULL; - sel_remove_entries(fsi->policycap_dir); - for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { if (iter < ARRAY_SIZE(selinux_policycap_names)) dentry = d_alloc_name(fsi->policycap_dir, @@ -2075,9 +2077,12 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) goto err; } - ret = sel_make_policy_nodes(fsi); - if (ret) + ret = sel_make_policycap(fsi); + if (ret) { + pr_err("SELinux: failed to load policy capabilities\n"); goto err; + } + return 0; err: pr_err("SELinux: %s: failed while creating inodes\n", diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 137d9396742a..a3f26b03c123 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2133,8 +2133,16 @@ static void selinux_policy_free(struct selinux_policy *policy) kfree(policy); } -static void selinux_policy_commit(struct selinux_state *state, - struct selinux_policy *newpolicy) +void selinux_policy_cancel(struct selinux_state *state, + struct selinux_policy *policy) +{ + + sidtab_cancel_convert(&state->ss->policy->sidtab); + selinux_policy_free(policy); +} + +void selinux_policy_commit(struct selinux_state *state, + struct selinux_policy *newpolicy) { struct selinux_policy *oldpolicy; u32 seqno; @@ -2195,7 +2203,8 @@ static void selinux_policy_commit(struct selinux_state *state, * This function will flush the access vector cache after * loading the new policy. */ -int security_load_policy(struct selinux_state *state, void *data, size_t len) +int security_load_policy(struct selinux_state *state, void *data, size_t len, + struct selinux_policy **newpolicyp) { struct selinux_policy *newpolicy; struct sidtab_convert_params convert_params; @@ -2226,7 +2235,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) if (!selinux_initialized(state)) { /* First policy load, so no need to preserve state from old policy */ - selinux_policy_commit(state, newpolicy); + *newpolicyp = newpolicy; return 0; } @@ -2262,7 +2271,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) goto err; } - selinux_policy_commit(state, newpolicy); + *newpolicyp = newpolicy; return 0; err: selinux_policy_free(newpolicy); @@ -2688,17 +2697,15 @@ out: * Obtain a SID to use for a file in a filesystem that * cannot support xattr or use a fixed labeling behavior like * transition SIDs or task SIDs. - * - * The caller must acquire the policy_rwlock before calling this function. */ -static inline int __security_genfs_sid(struct selinux_state *state, +static inline int __security_genfs_sid(struct selinux_policy *policy, const char *fstype, char *path, u16 orig_sclass, u32 *sid) { - struct policydb *policydb = &state->ss->policy->policydb; - struct sidtab *sidtab = &state->ss->policy->sidtab; + struct policydb *policydb = &policy->policydb; + struct sidtab *sidtab = &policy->sidtab; int len; u16 sclass; struct genfs *genfs; @@ -2708,7 +2715,7 @@ static inline int __security_genfs_sid(struct selinux_state *state, while (path[0] == '/' && path[1] == '/') path++; - sclass = unmap_class(&state->ss->policy->map, orig_sclass); + sclass = unmap_class(&policy->map, orig_sclass); *sid = SECINITSID_UNLABELED; for (genfs = policydb->genfs; genfs; genfs = genfs->next) { @@ -2763,11 +2770,22 @@ int security_genfs_sid(struct selinux_state *state, int retval; read_lock(&state->ss->policy_rwlock); - retval = __security_genfs_sid(state, fstype, path, orig_sclass, sid); + retval = __security_genfs_sid(state->ss->policy, + fstype, path, orig_sclass, sid); read_unlock(&state->ss->policy_rwlock); return retval; } +int selinux_policy_genfs_sid(struct selinux_policy *policy, + const char *fstype, + char *path, + u16 orig_sclass, + u32 *sid) +{ + /* no lock required, policy is not yet accessible by other threads */ + return __security_genfs_sid(policy, fstype, path, orig_sclass, sid); +} + /** * security_fs_use - Determine how to handle labeling for a filesystem. * @sb: superblock in question @@ -2803,8 +2821,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) } sbsec->sid = c->sid[0]; } else { - rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR, - &sbsec->sid); + rc = __security_genfs_sid(state->ss->policy, fstype, "/", + SECCLASS_DIR, &sbsec->sid); if (rc) { sbsec->behavior = SECURITY_FS_USE_NONE; rc = 0; @@ -2818,23 +2836,14 @@ out: return rc; } -int security_get_bools(struct selinux_state *state, +int security_get_bools(struct selinux_policy *policy, u32 *len, char ***names, int **values) { struct policydb *policydb; u32 i; int rc; - if (!selinux_initialized(state)) { - *len = 0; - *names = NULL; - *values = NULL; - return 0; - } - - read_lock(&state->ss->policy_rwlock); - - policydb = &state->ss->policy->policydb; + policydb = &policy->policydb; *names = NULL; *values = NULL; @@ -2865,7 +2874,6 @@ int security_get_bools(struct selinux_state *state, } rc = 0; out: - read_unlock(&state->ss->policy_rwlock); return rc; err: if (*names) { @@ -2958,7 +2966,9 @@ static int security_preserve_bools(struct selinux_state *state, struct cond_bool_datum *booldatum; u32 i, nbools = 0; - rc = security_get_bools(state, &nbools, &bnames, &bvalues); + read_lock(&state->ss->policy_rwlock); + rc = security_get_bools(state->ss->policy, &nbools, &bnames, &bvalues); + read_unlock(&state->ss->policy_rwlock); if (rc) goto out; for (i = 0; i < nbools; i++) { @@ -3169,21 +3179,13 @@ static int get_classes_callback(void *k, void *d, void *args) return 0; } -int security_get_classes(struct selinux_state *state, +int security_get_classes(struct selinux_policy *policy, char ***classes, int *nclasses) { struct policydb *policydb; int rc; - if (!selinux_initialized(state)) { - *nclasses = 0; - *classes = NULL; - return 0; - } - - read_lock(&state->ss->policy_rwlock); - - policydb = &state->ss->policy->policydb; + policydb = &policy->policydb; rc = -ENOMEM; *nclasses = policydb->p_classes.nprim; @@ -3201,7 +3203,6 @@ int security_get_classes(struct selinux_state *state, } out: - read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3218,16 +3219,14 @@ static int get_permissions_callback(void *k, void *d, void *args) return 0; } -int security_get_permissions(struct selinux_state *state, +int security_get_permissions(struct selinux_policy *policy, char *class, char ***perms, int *nperms) { struct policydb *policydb; int rc, i; struct class_datum *match; - read_lock(&state->ss->policy_rwlock); - - policydb = &state->ss->policy->policydb; + policydb = &policy->policydb; rc = -EINVAL; match = symtab_search(&policydb->p_classes, class); @@ -3256,11 +3255,9 @@ int security_get_permissions(struct selinux_state *state, goto err; out: - read_unlock(&state->ss->policy_rwlock); return rc; err: - read_unlock(&state->ss->policy_rwlock); for (i = 0; i < *nperms; i++) kfree((*perms)[i]); kfree(*perms); diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index eb6d27b5aeb4..5ee190bd30f5 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -464,6 +464,16 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params) return 0; } +void sidtab_cancel_convert(struct sidtab *s) +{ + unsigned long flags; + + /* cancelling policy load - disable live convert of sidtab */ + spin_lock_irqsave(&s->lock, flags); + s->convert = NULL; + spin_unlock_irqrestore(&s->lock, flags); +} + static void sidtab_destroy_entry(struct sidtab_entry *entry) { context_destroy(&entry->context); diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index f2a84560b8b3..80c744d07ad6 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -123,6 +123,8 @@ static inline struct context *sidtab_search_force(struct sidtab *s, u32 sid) int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params); +void sidtab_cancel_convert(struct sidtab *s); + int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); void sidtab_destroy(struct sidtab *s); |