diff --git a/security/selinux/avc.c b/security/selinux/avc.c index b91f914d292e..2a9d4888384b 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -190,8 +190,8 @@ avc_xperms_decision_lookup(u8 driver, u8 base_perm, } static inline unsigned int -avc_xperms_has_perm(struct extended_perms_decision *xpd, - u8 perm, u8 which) +avc_xperms_has_perm(const struct extended_perms_decision *xpd, + u8 perm, u8 which) { unsigned int rc = 0; @@ -247,7 +247,7 @@ static void avc_xperms_free(struct avc_xperms_node *xp_node) } static void avc_copy_xperms_decision(struct extended_perms_decision *dest, - struct extended_perms_decision *src) + const struct extended_perms_decision *src) { dest->base_perm = src->base_perm; dest->driver = src->driver; @@ -269,7 +269,7 @@ static void avc_copy_xperms_decision(struct extended_perms_decision *dest, */ static inline void avc_quick_copy_xperms_decision(u8 perm, struct extended_perms_decision *dest, - struct extended_perms_decision *src) + const struct extended_perms_decision *src) { /* * compute index of the u32 of the 256 bits (8 u32s) that contain this @@ -323,7 +323,7 @@ static struct avc_xperms_decision_node } static int avc_add_xperms_decision(struct avc_node *node, - struct extended_perms_decision *src) + const struct extended_perms_decision *src) { struct avc_xperms_decision_node *dest_xpd; @@ -348,7 +348,7 @@ static struct avc_xperms_node *avc_xperms_alloc(void) } static int avc_xperms_populate(struct avc_node *node, - struct avc_xperms_node *src) + const struct avc_xperms_node *src) { struct avc_xperms_node *dest; struct avc_xperms_decision_node *dest_xpd; @@ -381,14 +381,17 @@ static int avc_xperms_populate(struct avc_node *node, } static inline u32 avc_xperms_audit_required(u32 requested, - struct av_decision *avd, - struct extended_perms_decision *xpd, - u8 perm, - int result, - u32 *deniedp) + const struct av_decision *avd, + const struct extended_perms_decision *xpd, + u8 perm, + int result, + u32 *deniedp) { u32 denied, audited; + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + return 0; + denied = requested & ~avd->allowed; if (unlikely(denied)) { audited = denied & avd->auditdeny; @@ -411,8 +414,8 @@ static inline u32 avc_xperms_audit_required(u32 requested, } static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass, - u32 requested, struct av_decision *avd, - struct extended_perms_decision *xpd, + u32 requested, const struct av_decision *avd, + const struct extended_perms_decision *xpd, u8 perm, int result, struct common_audit_data *ad) { @@ -509,7 +512,8 @@ static struct avc_node *avc_alloc_node(void) return node; } -static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) +static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, + u16 tclass, const struct av_decision *avd) { node->ae.ssid = ssid; node->ae.tsid = tsid; @@ -603,7 +607,8 @@ static int avc_latest_notif_update(u32 seqno, int is_insert) * the access vectors into a cache entry. */ static void avc_insert(u32 ssid, u32 tsid, u16 tclass, - struct av_decision *avd, struct avc_xperms_node *xp_node) + const struct av_decision *avd, + const struct avc_xperms_node *xp_node) { struct avc_node *pos, *node = NULL; u32 hvalue; @@ -828,7 +833,7 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events) */ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm, u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno, - struct extended_perms_decision *xpd, u32 flags) + const struct extended_perms_decision *xpd, u32 flags) { u32 hvalue; int rc = 0; diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 01b5167fee1a..87027a934a11 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -61,8 +61,9 @@ struct selinux_audit_data { void __init avc_init(void); -static inline u32 avc_audit_required(u32 requested, struct av_decision *avd, - int result, u32 auditdeny, u32 *deniedp) +static inline u32 avc_audit_required(u32 requested, + const struct av_decision *avd, int result, + u32 auditdeny, u32 *deniedp) { u32 denied, audited; @@ -121,7 +122,7 @@ int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, * before calling the auditing code. */ static inline int avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, - struct av_decision *avd, int result, + const struct av_decision *avd, int result, struct common_audit_data *a) { u32 audited, denied; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index a0be7f1b5993..4256838d496c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -75,8 +75,8 @@ struct selinux_fs_info { char **bool_pending_names; int *bool_pending_values; struct dentry *class_dir; - unsigned long last_class_ino; - unsigned long last_ino; + u64 last_class_ino; + u64 last_ino; struct super_block *sb; }; @@ -247,11 +247,6 @@ static int sel_mmap_handle_status(struct file *filp, /* only allows one page from the head */ if (vma->vm_pgoff > 0 || size != PAGE_SIZE) return -EIO; - /* disallow writable mapping */ - if (vma->vm_flags & VM_WRITE) - return -EPERM; - /* disallow mprotect() turns it into writable */ - vm_flags_clear(vma, VM_MAYWRITE); return remap_pfn_range(vma, vma->vm_start, page_to_pfn(status), @@ -304,15 +299,14 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_ int **bool_pending_values); static int sel_make_classes(struct selinux_policy *newpolicy, struct dentry *class_dir, - unsigned long *last_class_ino); + u64 *last_class_ino); /* declaration for sel_make_class_dirs */ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, - unsigned long *ino); + u64 *ino); /* declaration for sel_make_policy_nodes */ -static struct dentry *sel_make_swapover_dir(struct super_block *sb, - unsigned long *ino); +static struct dentry *sel_make_swapover_dir(struct super_block *sb, u64 *ino); static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -467,7 +461,7 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, unsigned int bool_num = 0; char **bool_names = NULL; int *bool_values = NULL; - unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ + u64 tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ tmp_parent = sel_make_swapover_dir(fsi->sb, &tmp_ino); if (IS_ERR(tmp_parent)) @@ -1568,30 +1562,32 @@ static int sel_make_initcon_files(struct dentry *dir) return err; } -static inline unsigned long sel_class_to_ino(u16 class) +static inline u64 sel_class_to_ino(u16 class) { return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; } -static inline u16 sel_ino_to_class(unsigned long ino) +static inline u16 sel_ino_to_class(u64 ino) { - return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1); + u32 ino_masked = ino & SEL_INO_MASK; + return ino_masked / (SEL_VEC_MAX + 1); } -static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) +static inline u64 sel_perm_to_ino(u16 class, u32 perm) { return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; } -static inline u32 sel_ino_to_perm(unsigned long ino) +static inline u32 sel_ino_to_perm(u64 ino) { - return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); + u32 ino_masked = ino & SEL_INO_MASK; + return ino_masked % (SEL_VEC_MAX + 1); } static ssize_t sel_read_class(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - unsigned long ino = file_inode(file)->i_ino; + u64 ino = file_inode(file)->i_ino; char res[TMPBUFLEN]; ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_class(ino)); return simple_read_from_buffer(buf, count, ppos, res, len); @@ -1605,7 +1601,7 @@ static const struct file_operations sel_class_ops = { static ssize_t sel_read_perm(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - unsigned long ino = file_inode(file)->i_ino; + u64 ino = file_inode(file)->i_ino; char res[TMPBUFLEN]; ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino)); return simple_read_from_buffer(buf, count, ppos, res, len); @@ -1622,7 +1618,7 @@ static ssize_t sel_read_policycap(struct file *file, char __user *buf, int value; char tmpbuf[TMPBUFLEN]; ssize_t length; - unsigned long i_ino = file_inode(file)->i_ino; + u64 i_ino = file_inode(file)->i_ino; value = security_policycap_supported(i_ino & SEL_INO_MASK); length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); @@ -1698,7 +1694,7 @@ static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, static int sel_make_classes(struct selinux_policy *newpolicy, struct dentry *class_dir, - unsigned long *last_class_ino) + u64 *last_class_ino) { u32 i, nclasses; int rc; @@ -1763,7 +1759,7 @@ static int sel_make_policycap(struct dentry *dir) } static struct dentry *sel_make_dir(struct dentry *dir, const char *name, - unsigned long *ino) + u64 *ino) { struct inode *inode; @@ -1792,8 +1788,7 @@ static const struct inode_operations swapover_dir_inode_operations = { .permission = reject_all, }; -static struct dentry *sel_make_swapover_dir(struct super_block *sb, - unsigned long *ino) +static struct dentry *sel_make_swapover_dir(struct super_block *sb, u64 *ino) { struct dentry *dentry; struct inode *inode; @@ -1820,6 +1815,17 @@ static struct dentry *sel_make_swapover_dir(struct super_block *sb, #define NULL_FILE_NAME "null" +static void sel_mark_immutable(struct dentry *root, const char *name) +{ + struct qstr q = QSTR(name); + struct dentry *dentry = try_lookup_noperm(&q, root); + + if (!IS_ERR_OR_NULL(dentry)) { + d_inode(dentry)->i_flags |= S_IMMUTABLE; + dput(dentry); + } +} + static int sel_fill_super(struct super_block *sb, struct fs_context *fc) { struct selinux_fs_info *fsi; @@ -1859,6 +1865,9 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc) if (ret) goto err; + sel_mark_immutable(sb->s_root, "status"); + sel_mark_immutable(sb->s_root, "policy"); + fsi = sb->s_fs_info; fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino); if (IS_ERR(fsi->bool_dir)) { diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 40c62600650e..b48f8256dd4c 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -400,44 +400,6 @@ int mls_range_set(struct context *context, struct mls_range *range) return rc; } -int mls_setup_user_range(struct policydb *p, struct context *fromcon, - struct user_datum *user, struct context *usercon) -{ - if (p->mls_enabled) { - struct mls_level *fromcon_sen = &(fromcon->range.level[0]); - struct mls_level *fromcon_clr = &(fromcon->range.level[1]); - struct mls_level *user_low = &(user->range.level[0]); - struct mls_level *user_clr = &(user->range.level[1]); - struct mls_level *user_def = &(user->dfltlevel); - struct mls_level *usercon_sen = &(usercon->range.level[0]); - struct mls_level *usercon_clr = &(usercon->range.level[1]); - - /* Honor the user's default level if we can */ - if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) - *usercon_sen = *user_def; - else if (mls_level_between(fromcon_sen, user_def, user_clr)) - *usercon_sen = *fromcon_sen; - else if (mls_level_between(fromcon_clr, user_low, user_def)) - *usercon_sen = *user_low; - else - return -EINVAL; - - /* Lower the clearance of available contexts - if the clearance of "fromcon" is lower than - that of the user's default clearance (but - only if the "fromcon" clearance dominates - the user's computed sensitivity level) */ - if (mls_level_dom(user_clr, fromcon_clr)) - *usercon_clr = *fromcon_clr; - else if (mls_level_dom(fromcon_clr, user_clr)) - *usercon_clr = *user_clr; - else - return -EINVAL; - } - - return 0; -} - /* * Convert the MLS fields in the security context * structure `oldc' from the values specified in the diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 93cde1b22992..c107b5182fb7 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -46,9 +46,6 @@ int mls_compute_sid(struct policydb *p, struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, struct context *newcontext, bool sock); -int mls_setup_user_range(struct policydb *p, struct context *fromcon, - struct user_datum *user, struct context *usercon); - #ifdef CONFIG_NETLABEL void mls_export_netlbl_lvl(struct policydb *p, struct context *context, struct netlbl_lsm_secattr *secattr); diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 51df2ebd1211..d36a03f104c2 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -42,9 +42,6 @@ static inline int mls_level_dom(const struct mls_level *l1, #define mls_level_incomp(l1, l2) \ (!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1))) -#define mls_level_between(l1, l2, l3) \ - (mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1))) - #define mls_range_contains(r1, r2) \ (mls_level_dom(&(r2).level[0], &(r1).level[0]) && \ mls_level_dom(&(r1).level[1], &(r2).level[1])) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index b75c977c6673..fe0c0792583f 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -560,6 +560,9 @@ static int common_index(void *key, void *datum, void *datap) if (!comdatum->value || comdatum->value > p->p_commons.nprim) return -EINVAL; + if (p->sym_val_to_name[SYM_COMMONS][comdatum->value - 1]) + return -EINVAL; + p->sym_val_to_name[SYM_COMMONS][comdatum->value - 1] = key; return 0; @@ -575,6 +578,9 @@ static int class_index(void *key, void *datum, void *datap) if (!cladatum->value || cladatum->value > p->p_classes.nprim) return -EINVAL; + if (p->class_val_to_struct[cladatum->value - 1]) + return -EINVAL; + p->sym_val_to_name[SYM_CLASSES][cladatum->value - 1] = key; p->class_val_to_struct[cladatum->value - 1] = cladatum; return 0; @@ -591,6 +597,9 @@ static int role_index(void *key, void *datum, void *datap) role->bounds > p->p_roles.nprim) return -EINVAL; + if (p->role_val_to_struct[role->value - 1]) + return -EINVAL; + p->sym_val_to_name[SYM_ROLES][role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; return 0; @@ -613,6 +622,8 @@ static int type_index(void *key, void *datum, void *datap) } if (typdatum->primary) { + if (p->type_val_to_struct[typdatum->value - 1]) + return -EINVAL; p->sym_val_to_name[SYM_TYPES][typdatum->value - 1] = key; p->type_val_to_struct[typdatum->value - 1] = typdatum; } @@ -631,6 +642,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum->bounds > p->p_users.nprim) return -EINVAL; + if (p->user_val_to_struct[usrdatum->value - 1]) + return -EINVAL; + p->sym_val_to_name[SYM_USERS][usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; return 0; @@ -647,8 +661,11 @@ static int sens_index(void *key, void *datum, void *datap) if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim) return -EINVAL; - if (!levdatum->isalias) + if (!levdatum->isalias) { + if (p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1]) + return -EINVAL; p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key; + } return 0; } @@ -664,8 +681,11 @@ static int cat_index(void *key, void *datum, void *datap) if (!catdatum->value || catdatum->value > p->p_cats.nprim) return -EINVAL; - if (!catdatum->isalias) + if (!catdatum->isalias) { + if (p->sym_val_to_name[SYM_CATS][catdatum->value - 1]) + return -EINVAL; p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key; + } return 0; } @@ -687,6 +707,64 @@ static int sens_cat_index_check(void *key, void *datum, void *datap) return 0; } +static int role_index_check(void *key, void *datum, void *datap) +{ + const struct policydb *p = datap; + const struct role_datum *role = datum; + struct ebitmap_node *node; + u32 bit; + + ebitmap_for_each_positive_bit(&role->dominates, node, bit) { + if (!policydb_role_isvalid(p, bit + 1)) { + pr_err("SELinux: role %s declares dominance on undefined role %u\n", + (const char *)key, bit + 1); + return -EINVAL; + } + } + + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (!policydb_type_isvalid(p, bit + 1)) { + pr_err("SELinux: role %s authorizes undefined type %u\n", + (const char *)key, bit + 1); + return -EINVAL; + } + } + + return 0; +} + +static int user_index_check(void *key, void *datum, void *datap) +{ + const struct policydb *p = datap; + const struct user_datum *usr = datum; + struct ebitmap_node *node; + u32 bit; + + ebitmap_for_each_positive_bit(&usr->roles, node, bit) { + if (!policydb_role_isvalid(p, bit + 1)) { + pr_err("SELinux: user %s authorizes undefined role %u\n", + (const char *)key, bit + 1); + return -EINVAL; + } + } + + if (p->mls_enabled) { + if (!mls_range_isvalid(p, &usr->range)) { + pr_err("SELinux: user %s has an invalid MLS range\n", + (const char *)key); + return -EINVAL; + } + + if (!mls_level_isvalid(p, &usr->dfltlevel)) { + pr_err("SELinux: user %s has an invalid MLS default level\n", + (const char *)key); + return -EINVAL; + } + } + + return 0; +} + /* clang-format off */ static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = { common_index, @@ -740,8 +818,9 @@ static inline void symtab_hash_eval(struct symtab *s) */ static int policydb_index(struct policydb *p) { + struct ebitmap_node *node; int i, rc; - u32 v; + u32 bit, v; if (p->mls_enabled) pr_debug( @@ -816,6 +895,31 @@ static int policydb_index(struct policydb *p) goto out; } + rc = hashtab_map(&p->p_roles.table, role_index_check, p); + if (rc) + goto out; + + rc = hashtab_map(&p->p_users.table, user_index_check, p); + if (rc) + goto out; + + ebitmap_for_each_positive_bit(&p->permissive_map, node, bit) { + if (!policydb_simpletype_isvalid(p, bit)) { + pr_err("SELinux: permissive map refers to invalid type %u\n", + bit); + rc = -EINVAL; + goto out; + } + } + ebitmap_for_each_positive_bit(&p->neveraudit_map, node, bit) { + if (!policydb_simpletype_isvalid(p, bit)) { + pr_err("SELinux: neveraudit map refers to invalid type %u\n", + bit); + rc = -EINVAL; + goto out; + } + } + rc = 0; out: return rc; @@ -1037,13 +1141,13 @@ bool policydb_context_isvalid(const struct policydb *p, const struct context *c) const struct role_datum *role; const struct user_datum *usrdatum; - if (!c->role || c->role > p->p_roles.nprim) + if (!policydb_role_isvalid(p, c->role)) return false; - if (!c->user || c->user > p->p_users.nprim) + if (!policydb_user_isvalid(p, c->user)) return false; - if (!c->type || c->type > p->p_types.nprim) + if (!policydb_simpletype_isvalid(p, c->type)) return false; if (c->role != OBJECT_R_VAL) { @@ -1051,7 +1155,7 @@ bool policydb_context_isvalid(const struct policydb *p, const struct context *c) * Role must be authorized for the type. */ role = p->role_val_to_struct[c->role - 1]; - if (!role || !ebitmap_get_bit(&role->types, c->type - 1)) + if (!ebitmap_get_bit(&role->types, c->type - 1)) /* role may not be associated with type */ return false; @@ -1059,9 +1163,6 @@ bool policydb_context_isvalid(const struct policydb *p, const struct context *c) * User must be authorized for the role. */ usrdatum = p->user_val_to_struct[c->user - 1]; - if (!usrdatum) - return false; - if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1)) /* user may not be associated with role */ return false; @@ -2350,6 +2451,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp if (rc) goto out; + rc = -EINVAL; + if (ebitmap_get_highest_set_bit(&datum->stypes) >= p->p_types.nprim) + goto out; + rc = next_entry(buf, fp, sizeof(u32)); if (rc) goto out; @@ -2847,7 +2952,8 @@ int policydb_read(struct policydb *p, struct policy_file *fp) goto bad; } - if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) { + val = le32_to_cpu(buf[1]); + if (val & POLICYDB_CONFIG_MLS) { p->mls_enabled = 1; rc = -EINVAL; @@ -2858,8 +2964,13 @@ int policydb_read(struct policydb *p, struct policy_file *fp) goto bad; } } - p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN); - p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN); + rc = -EINVAL; + if ((val & (REJECT_UNKNOWN | ALLOW_UNKNOWN)) == (REJECT_UNKNOWN | ALLOW_UNKNOWN)) { + pr_err("SELinux: policydb configuration both rejects and allows unknown classes and permissions\n"); + goto bad; + } + p->reject_unknown = !!(val & REJECT_UNKNOWN); + p->allow_unknown = !!(val & ALLOW_UNKNOWN); if (p->policyvers >= POLICYDB_VERSION_POLCAP) { rc = ebitmap_read(&p->policycaps, fp);