diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 12:41:00 +0100 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 12:41:00 +0100 |
| commit | 231e9d447ea97033ae8b8dff7b910e6269d7c5af (patch) | |
| tree | fe9189eb264ea2acf0319ffd4b42abc8419f31a0 | |
| parent | 87599bd29856ea7bfdd62591c581c8be5a4719ee (diff) | |
| parent | 033182baeab63ce96a6eb8aef1a6cd444fcf9519 (diff) | |
Merge tag 'selinux-pr-20260615' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore:
"A number of SELinux patches, almost all of which are either minor
fixes or hardening patches:
- Additional verifications when loading new SELinux policy
Multiple patches by Christian Göttsche to add additional
validations to the code responsible for loading and parsing SELinux
policy as it is loaded into the kernel.
- Avoid nontransitive comparisons comparisons in our sorting code
Done to prevent unexpected sorting results due to overflow. Qualys
documented a similar issue with glibc
https://www.qualys.com/2024/01/30/qsort.txt
- Consistently use u16 for SELinux security classes
- Move from page allocations to kmalloc() based allocations
Unfortunately one of these patches had to be reverted, but you
should see a fixed version during the next merge window.
- Move from kmalloc_objs() to kzalloc_objs() in the policy load code
- Reorder sel_kill_sb() slightly to match other pseudo filesystems
- Simplify things with QSTR() instead of QSTR_INIT()
- Minor comment typo fixes"
* tag 'selinux-pr-20260615' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
selinux: revert use of __getname() in selinux_genfs_get_sid()
selinux: comment spelling fix in ibpkey.c
selinux: comment typo fix in selinuxfs.c
selinux: hooks: use __getname() to allocate path buffer
selinux: use k[mz]alloc() to allocate temporary buffers
selinux: check for simple types
selinux: more strict bounds check
selinux: beef up isvalid checks
selinux: reorder policydb_index()
selinux: check type attr map overflows
selinux: check length fields in policies
selinux: more strict policy parsing
selinux: use u16 for security classes
selinux: avoid nontransitive comparison
selinux: switch two allocations to use kzalloc_objs()
selinux: fix sel_kill_sb()
selinux: use QSTR() instead of QSTR_INIT() in init_sel_fs
| -rw-r--r-- | security/selinux/ibpkey.c | 2 | ||||
| -rw-r--r-- | security/selinux/include/security.h | 1 | ||||
| -rw-r--r-- | security/selinux/selinuxfs.c | 27 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.c | 49 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.h | 13 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.c | 39 | ||||
| -rw-r--r-- | security/selinux/ss/constraint.h | 1 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.c | 27 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.h | 1 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.h | 4 | ||||
| -rw-r--r-- | security/selinux/ss/mls.c | 66 | ||||
| -rw-r--r-- | security/selinux/ss/mls.h | 6 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 358 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.h | 56 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 13 | ||||
| -rw-r--r-- | security/selinux/ss/symtab.c | 2 | ||||
| -rw-r--r-- | security/selinux/ss/symtab.h | 2 |
17 files changed, 512 insertions, 155 deletions
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c index 93a5637fbcd8..ae09d59aaa06 100644 --- a/security/selinux/ibpkey.c +++ b/security/selinux/ibpkey.c @@ -2,7 +2,7 @@ /* * Pkey table * - * SELinux must keep a mapping of Infinband PKEYs to labels/SIDs. This + * SELinux must keep a mapping of Infiniband PKEYs to labels/SIDs. This * mapping is maintained as part of the normal policy but a fast cache is * needed to reduce the lookup overhead. * diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 0babb8992181..a74415e3afd3 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -236,6 +236,7 @@ int security_read_policy(void **data, size_t *len); int security_read_state_kernel(void **data, size_t *len); int security_policycap_supported(unsigned int req_cap); +/* Maximum supported number of permissions per class */ #define SEL_VEC_MAX 32 struct av_decision { u32 allowed; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 25ca7d714014..5aaaf69410bb 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -94,9 +94,8 @@ static int selinux_fs_info_create(struct super_block *sb) return 0; } -static void selinux_fs_info_free(struct super_block *sb) +static void selinux_fs_info_free(struct selinux_fs_info *fsi) { - struct selinux_fs_info *fsi = sb->s_fs_info; unsigned int i; if (fsi) { @@ -105,8 +104,7 @@ static void selinux_fs_info_free(struct super_block *sb) kfree(fsi->bool_pending_names); kfree(fsi->bool_pending_values); } - kfree(sb->s_fs_info); - sb->s_fs_info = NULL; + kfree(fsi); } #define SEL_INITCON_INO_OFFSET 0x01000000 @@ -785,7 +783,7 @@ static const struct file_operations transaction_ops = { /* * payload - write methods * If the method has a response, the response should be put in buf, - * and the length returned. Otherwise return 0 or and -error. + * and the length returned. Otherwise return 0 or -error. */ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) @@ -1244,7 +1242,7 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_ char **names, *page; u32 i, num; - page = (char *)get_zeroed_page(GFP_KERNEL); + page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!page) return -ENOMEM; @@ -1290,7 +1288,7 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_ ret = sel_attach_file(bool_dir, names[i], inode); } out: - free_page((unsigned long)page); + kfree(page); return ret; } @@ -1349,14 +1347,14 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, char *page; ssize_t length; - page = (char *)__get_free_page(GFP_KERNEL); + page = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!page) return -ENOMEM; length = avc_get_hash_stats(page); if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, page, length); - free_page((unsigned long)page); + kfree(page); return length; } @@ -1367,7 +1365,7 @@ static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, char *page; ssize_t length; - page = (char *)__get_free_page(GFP_KERNEL); + page = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!page) return -ENOMEM; @@ -1375,7 +1373,7 @@ static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, page, length); - free_page((unsigned long)page); + kfree(page); return length; } @@ -1959,8 +1957,10 @@ static int sel_init_fs_context(struct fs_context *fc) static void sel_kill_sb(struct super_block *sb) { - selinux_fs_info_free(sb); + struct selinux_fs_info *fsi = sb->s_fs_info; + kill_anon_super(sb); + selinux_fs_info_free(fsi); } static struct file_system_type sel_fs_type = { @@ -1973,8 +1973,7 @@ struct path selinux_null __ro_after_init; int __init init_sel_fs(void) { - struct qstr null_name = QSTR_INIT(NULL_FILE_NAME, - sizeof(NULL_FILE_NAME)-1); + struct qstr null_name = QSTR(NULL_FILE_NAME); int err; if (!selinux_enabled_boot) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index d12ca337e649..0f94edd01f69 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -316,7 +316,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po struct avtab_extended_perms xperms; __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; int rc; - unsigned int set, vers = pol->policyvers; + unsigned int vers = pol->policyvers; memset(&key, 0, sizeof(struct avtab_key)); memset(&datum, 0, sizeof(struct avtab_datum)); @@ -327,9 +327,12 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po pr_err("SELinux: avtab: truncated entry\n"); return rc; } + /* Read five or more items: source type, target type, + * target class, AV type, and at least one datum. + */ items2 = le32_to_cpu(buf32[0]); - if (items2 > ARRAY_SIZE(buf32)) { - pr_err("SELinux: avtab: entry overflow\n"); + if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) { + pr_err("SELinux: avtab: invalid item count\n"); return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32) * items2); @@ -358,6 +361,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po return -EINVAL; } + if (!policydb_type_isvalid(pol, key.source_type) || + !policydb_type_isvalid(pol, key.target_type) || + !policydb_class_isvalid(pol, key.target_class)) { + pr_err("SELinux: avtab: invalid type or class\n"); + return -EINVAL; + } + val = le32_to_cpu(buf32[items++]); enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; @@ -376,8 +386,20 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po for (i = 0; i < ARRAY_SIZE(spec_order); i++) { if (val & spec_order[i]) { + if (items >= items2) { + pr_err("SELinux: avtab: entry has too many items (%d/%d)\n", + items + 1, items2); + return -EINVAL; + } key.specified = spec_order[i] | enabled; datum.u.data = le32_to_cpu(buf32[items++]); + + if ((key.specified & AVTAB_TYPE) && + !policydb_simpletype_isvalid(pol, datum.u.data)) { + pr_err("SELinux: avtab: invalid type\n"); + return -EINVAL; + } + rc = insertf(a, &key, &datum, p); if (rc) return rc; @@ -411,9 +433,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po return -EINVAL; } - set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV)); - if (!set || set > 1) { - pr_err("SELinux: avtab: more than one specifier\n"); + if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) { + pr_err("SELinux: avtab: not exactly one specifier\n"); + return -EINVAL; + } + + if (key.specified & ~AVTAB_SPECIFIER_MASK) { + pr_err("SELinux: avtab: invalid specifier\n"); return -EINVAL; } @@ -438,6 +464,10 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po pr_err("SELinux: avtab: truncated entry\n"); return rc; } + if (!avtab_is_valid_xperm_specified(xperms.specified)) + pr_warn_once_policyload(pol, + "SELinux: avtab: unsupported xperm specifier %#x\n", + xperms.specified); rc = next_entry(&xperms.driver, fp, sizeof(u8)); if (rc) { pr_err("SELinux: avtab: truncated entry\n"); @@ -461,7 +491,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po datum.u.data = le32_to_cpu(*buf32); } if ((key.specified & AVTAB_TYPE) && - !policydb_type_isvalid(pol, datum.u.data)) { + !policydb_simpletype_isvalid(pol, datum.u.data)) { pr_err("SELinux: avtab: invalid type\n"); return -EINVAL; } @@ -492,6 +522,11 @@ int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol) goto bad; } + /* avtab_read_item() reads at least 96 bytes for any valid entry */ + rc = size_check(3 * sizeof(u32), nel, fp); + if (rc) + goto bad; + rc = avtab_alloc(a, nel); if (rc) goto bad; diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 850b3453f259..1de4cce288a7 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -44,6 +44,7 @@ struct avtab_key { AVTAB_XPERMS_DONTAUDIT) #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ #define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ +#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED) u16 specified; /* what field is specified */ }; @@ -68,6 +69,18 @@ struct avtab_extended_perms { struct extended_perms_data perms; }; +static inline bool avtab_is_valid_xperm_specified(u8 specified) +{ + switch (specified) { + case AVTAB_XPERMS_IOCTLFUNCTION: + case AVTAB_XPERMS_IOCTLDRIVER: + case AVTAB_XPERMS_NLMSG: + return true; + default: + return false; + } +} + struct avtab_datum { union { u32 data; /* access vector or type value */ diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 824c3f896151..2956dd7edea7 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -12,6 +12,7 @@ #include "security.h" #include "conditional.h" +#include "policydb.h" #include "services.h" /* @@ -165,7 +166,7 @@ void cond_policydb_destroy(struct policydb *p) int cond_init_bool_indexes(struct policydb *p) { kfree(p->bool_val_to_struct); - p->bool_val_to_struct = kmalloc_objs(*p->bool_val_to_struct, + p->bool_val_to_struct = kzalloc_objs(*p->bool_val_to_struct, p->p_bools.nprim); if (!p->bool_val_to_struct) return -ENOMEM; @@ -199,19 +200,12 @@ int cond_index_bool(void *key, void *datum, void *datap) return 0; } -static int bool_isvalid(struct cond_bool_datum *b) -{ - if (!(b->state == 0 || b->state == 1)) - return 0; - return 1; -} - int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp) { char *key = NULL; struct cond_bool_datum *booldatum; __le32 buf[3]; - u32 len; + u32 len, val; int rc; booldatum = kzalloc_obj(*booldatum); @@ -223,11 +217,12 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp) goto err; booldatum->value = le32_to_cpu(buf[0]); - booldatum->state = le32_to_cpu(buf[1]); + val = le32_to_cpu(buf[1]); rc = -EINVAL; - if (!bool_isvalid(booldatum)) + if (!val_is_boolean(val)) goto err; + booldatum->state = (int)val; len = le32_to_cpu(buf[2]); @@ -241,6 +236,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp) return 0; err: + pr_err("SELinux: conditional: failed to read boolean\n"); cond_destroy_bool(key, booldatum, NULL); return rc; } @@ -334,6 +330,11 @@ static int cond_read_av_list(struct policydb *p, struct policy_file *fp, if (len == 0) return 0; + /* avtab_read_item() reads at least 96 bytes for any valid entry */ + rc = size_check(3 * sizeof(u32), len, fp); + if (rc) + return rc; + list->nodes = kzalloc_objs(*list->nodes, len); if (!list->nodes) return -ENOMEM; @@ -362,7 +363,8 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr) return 0; } - if (expr->boolean > p->p_bools.nprim) { + if (expr->expr_type == COND_BOOL && + (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) { pr_err("SELinux: conditional expressions uses unknown bool.\n"); return 0; } @@ -383,6 +385,12 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, struct pol /* expr */ len = le32_to_cpu(buf[1]); + + /* we will read 64 bytes per node */ + rc = size_check(2 * sizeof(u32), len, fp); + if (rc) + return rc; + node->expr.nodes = kzalloc_objs(*node->expr.nodes, len); if (!node->expr.nodes) return -ENOMEM; @@ -421,6 +429,11 @@ int cond_read_list(struct policydb *p, struct policy_file *fp) len = le32_to_cpu(buf[0]); + /* cond_read_node() reads at least 128 bytes for any valid node */ + rc = size_check(4 * sizeof(u32), len, fp); + if (rc) + return rc; + p->cond_list = kzalloc_objs(*p->cond_list, len); if (!p->cond_list) return -ENOMEM; @@ -709,7 +722,7 @@ static int duplicate_policydb_bools(struct policydb *newdb, struct cond_bool_datum **cond_bool_array; int rc; - cond_bool_array = kmalloc_objs(*orig->bool_val_to_struct, + cond_bool_array = kzalloc_objs(*orig->bool_val_to_struct, orig->p_bools.nprim); if (!cond_bool_array) return -ENOMEM; diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 203033cfad67..1d75a8a044df 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -50,6 +50,7 @@ struct constraint_expr { u32 op; /* operator */ struct ebitmap names; /* names */ + /* internally unused, only forwarded via policydb_write() */ struct type_set *type_names; struct constraint_expr *next; /* next expression */ diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 43bc19e21960..ea894361fed6 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -257,6 +257,33 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, return 1; } +u32 ebitmap_get_highest_set_bit(const struct ebitmap *e) +{ + const struct ebitmap_node *n; + unsigned long unit; + u32 pos = 0; + + n = e->node; + if (!n) + return 0; + + while (n->next) + n = n->next; + + for (unsigned int i = EBITMAP_UNIT_NUMS; i > 0; i--) { + unit = n->maps[i - 1]; + if (unit == 0) + continue; + + pos = (i - 1) * EBITMAP_UNIT_SIZE; + while (unit >>= 1) + pos++; + break; + } + + return n->startbit + pos; +} + int ebitmap_get_bit(const struct ebitmap *e, u32 bit) { const struct ebitmap_node *n; diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index c9569998f287..ae05cc8e4672 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -126,6 +126,7 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2); int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit); +u32 ebitmap_get_highest_set_bit(const struct ebitmap *e); int ebitmap_get_bit(const struct ebitmap *e, u32 bit); int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value); void ebitmap_destroy(struct ebitmap *e); diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index deba82d78c3a..c641fb12916b 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -94,11 +94,11 @@ static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, * Returns NULL if no entry has the specified key or * the datum of the entry otherwise. */ -static inline void *hashtab_search(struct hashtab *h, const void *key, +static inline void *hashtab_search(const struct hashtab *h, const void *key, struct hashtab_key_params key_params) { u32 hvalue; - struct hashtab_node *cur; + const struct hashtab_node *cur; if (!h->size) return NULL; diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index a6e49269f535..3cd36e2015fa 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -32,7 +32,7 @@ int mls_compute_context_len(struct policydb *p, struct context *context) { int i, l, len, head, prev; - char *nm; + const char *nm; struct ebitmap *e; struct ebitmap_node *node; @@ -86,7 +86,8 @@ int mls_compute_context_len(struct policydb *p, struct context *context) void mls_sid_to_context(struct policydb *p, struct context *context, char **scontext) { - char *scontextp, *nm; + const char *nm; + char *scontextp; int i, l, head, prev; struct ebitmap *e; struct ebitmap_node *node; @@ -155,27 +156,44 @@ void mls_sid_to_context(struct policydb *p, struct context *context, *scontext = scontextp; } -int mls_level_isvalid(struct policydb *p, struct mls_level *l) +bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l) { - struct level_datum *levdatum; + const char *name; + const struct level_datum *levdatum; + struct ebitmap_node *node; + u32 bit; + int rc; if (!l->sens || l->sens > p->p_levels.nprim) - return 0; - levdatum = symtab_search(&p->p_levels, - sym_name(p, SYM_LEVELS, l->sens - 1)); + return false; + + name = sym_name(p, SYM_LEVELS, l->sens - 1); + if (!name) + return false; + + levdatum = symtab_search(&p->p_levels, name); if (!levdatum) - return 0; + return false; /* - * Return 1 iff all the bits set in l->cat are also be set in + * Validate that all bits set in l->cat are also be set in * levdatum->level->cat and no bit in l->cat is larger than * p->p_cats.nprim. */ - return ebitmap_contains(&levdatum->level.cat, &l->cat, - p->p_cats.nprim); + rc = ebitmap_contains(&levdatum->level.cat, &l->cat, + p->p_cats.nprim); + if (!rc) + return false; + + ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) { + if (!sym_name(p, SYM_CATS, bit)) + return false; + } + + return true; } -int mls_range_isvalid(struct policydb *p, struct mls_range *r) +bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r) { return (mls_level_isvalid(p, &r->level[0]) && mls_level_isvalid(p, &r->level[1]) && @@ -183,32 +201,32 @@ int mls_range_isvalid(struct policydb *p, struct mls_range *r) } /* - * Return 1 if the MLS fields in the security context + * Return true if the MLS fields in the security context * structure `c' are valid. Return 0 otherwise. */ -int mls_context_isvalid(struct policydb *p, struct context *c) +bool mls_context_isvalid(const struct policydb *p, const struct context *c) { - struct user_datum *usrdatum; + const struct user_datum *usrdatum; if (!p->mls_enabled) - return 1; + return true; if (!mls_range_isvalid(p, &c->range)) - return 0; + return false; if (c->role == OBJECT_R_VAL) - return 1; + return true; /* * User must be authorized for the MLS range. */ if (!c->user || c->user > p->p_users.nprim) - return 0; + return false; usrdatum = p->user_val_to_struct[c->user - 1]; - if (!mls_range_contains(usrdatum->range, c->range)) - return 0; /* user may not be associated with range */ + if (!usrdatum || !mls_range_contains(usrdatum->range, c->range)) + return false; /* user may not be associated with range */ - return 1; + return true; } /* @@ -449,8 +467,8 @@ int mls_convert_context(struct policydb *oldp, struct policydb *newp, return 0; for (l = 0; l < 2; l++) { - char *name = sym_name(oldp, SYM_LEVELS, - oldc->range.level[l].sens - 1); + const char *name = sym_name(oldp, SYM_LEVELS, + oldc->range.level[l].sens - 1); levdatum = symtab_search(&newp->p_levels, name); diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 07980636751f..93cde1b22992 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -27,9 +27,9 @@ int mls_compute_context_len(struct policydb *p, struct context *context); void mls_sid_to_context(struct policydb *p, struct context *context, char **scontext); -int mls_context_isvalid(struct policydb *p, struct context *c); -int mls_range_isvalid(struct policydb *p, struct mls_range *r); -int mls_level_isvalid(struct policydb *p, struct mls_level *l); +bool mls_context_isvalid(const struct policydb *p, const struct context *c); +bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r); +bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l); int mls_context_to_sid(struct policydb *p, char oldc, char *scontext, struct context *context, struct sidtab *s, u32 def_sid); diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 738fd47f33e6..ead504a639e3 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/audit.h> +#include <linux/sort.h> #include "security.h" #include "policydb.h" @@ -429,11 +430,11 @@ static int filenametr_cmp(const void *k1, const void *k2) const struct filename_trans_key *ft2 = k2; int v; - v = ft1->ttype - ft2->ttype; + v = cmp_int(ft1->ttype, ft2->ttype); if (v) return v; - v = ft1->tclass - ft2->tclass; + v = cmp_int(ft1->tclass, ft2->tclass); if (v) return v; @@ -464,15 +465,15 @@ static int rangetr_cmp(const void *k1, const void *k2) const struct range_trans *key1 = k1, *key2 = k2; int v; - v = key1->source_type - key2->source_type; + v = cmp_int(key1->source_type, key2->source_type); if (v) return v; - v = key1->target_type - key2->target_type; + v = cmp_int(key1->target_type, key2->target_type); if (v) return v; - v = key1->target_class - key2->target_class; + v = cmp_int(key1->target_class, key2->target_class); return v; } @@ -501,15 +502,15 @@ static int role_trans_cmp(const void *k1, const void *k2) const struct role_trans_key *key1 = k1, *key2 = k2; int v; - v = key1->role - key2->role; + v = cmp_int(key1->role, key2->role); if (v) return v; - v = key1->type - key2->type; + v = cmp_int(key1->type, key2->type); if (v) return v; - return key1->tclass - key2->tclass; + return cmp_int(key1->tclass, key2->tclass); } static const struct hashtab_key_params roletr_key_params = { @@ -638,13 +639,11 @@ static int sens_index(void *key, void *datum, void *datap) levdatum = datum; p = datap; - if (!levdatum->isalias) { - if (!levdatum->level.sens || - levdatum->level.sens > p->p_levels.nprim) - return -EINVAL; + if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim) + return -EINVAL; + if (!levdatum->isalias) p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key; - } return 0; } @@ -657,12 +656,11 @@ static int cat_index(void *key, void *datum, void *datap) catdatum = datum; p = datap; - if (!catdatum->isalias) { - if (!catdatum->value || catdatum->value > p->p_cats.nprim) - return -EINVAL; + if (!catdatum->value || catdatum->value > p->p_cats.nprim) + return -EINVAL; + if (!catdatum->isalias) p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key; - } return 0; } @@ -735,7 +733,6 @@ static int policydb_index(struct policydb *p) pr_debug("SELinux: %d classes, %d rules\n", p->p_classes.nprim, p->te_avtab.nel); - avtab_hash_eval(&p->te_avtab, "rules"); symtab_hash_eval(p->symtab); p->class_val_to_struct = kzalloc_objs(*p->class_val_to_struct, @@ -931,44 +928,76 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s) return 0; } -int policydb_class_isvalid(struct policydb *p, unsigned int class) +bool policydb_class_isvalid(const struct policydb *p, u16 class) { if (!class || class > p->p_classes.nprim) - return 0; - return 1; + return false; + if (!p->sym_val_to_name[SYM_CLASSES][class - 1]) + return false; + return true; +} + +bool policydb_user_isvalid(const struct policydb *p, u32 user) +{ + if (!user || user > p->p_users.nprim) + return false; + if (!p->sym_val_to_name[SYM_USERS][user - 1]) + return false; + return true; } -int policydb_role_isvalid(struct policydb *p, unsigned int role) +bool policydb_role_isvalid(const struct policydb *p, u32 role) { if (!role || role > p->p_roles.nprim) - return 0; - return 1; + return false; + if (!p->sym_val_to_name[SYM_ROLES][role - 1]) + return false; + return true; } -int policydb_type_isvalid(struct policydb *p, unsigned int type) +bool policydb_type_isvalid(const struct policydb *p, u32 type) { if (!type || type > p->p_types.nprim) - return 0; - return 1; + return false; + if (!p->sym_val_to_name[SYM_TYPES][type - 1]) + return false; + return true; +} + +bool policydb_simpletype_isvalid(const struct policydb *p, u32 type) +{ + const struct type_datum *datum; + + if (!type || type > p->p_types.nprim) + return false; + + datum = p->type_val_to_struct[type - 1]; + if (!datum) + return false; + + if (datum->attribute) + return false; + + return true; } /* - * Return 1 if the fields in the security context + * Return true if the fields in the security context * structure `c' are valid. Return 0 otherwise. */ -int policydb_context_isvalid(struct policydb *p, struct context *c) +bool policydb_context_isvalid(const struct policydb *p, const struct context *c) { - struct role_datum *role; - struct user_datum *usrdatum; + const struct role_datum *role; + const struct user_datum *usrdatum; if (!c->role || c->role > p->p_roles.nprim) - return 0; + return false; if (!c->user || c->user > p->p_users.nprim) - return 0; + return false; if (!c->type || c->type > p->p_types.nprim) - return 0; + return false; if (c->role != OBJECT_R_VAL) { /* @@ -977,24 +1006,24 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) role = p->role_val_to_struct[c->role - 1]; if (!role || !ebitmap_get_bit(&role->types, c->type - 1)) /* role may not be associated with type */ - return 0; + return false; /* * User must be authorized for the role. */ usrdatum = p->user_val_to_struct[c->user - 1]; if (!usrdatum) - return 0; + return false; if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1)) /* user may not be associated with role */ - return 0; + return false; } if (!mls_context_isvalid(p, c)) - return 0; + return false; - return 1; + return true; } /* @@ -1107,6 +1136,9 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len) if ((len == 0) || (len == (u32)-1)) return -EINVAL; + if (size_check(sizeof(char), len, fp)) + return -EINVAL; + str = kmalloc(len + 1, flags | __GFP_NOWARN); if (!str) return -ENOMEM; @@ -1140,6 +1172,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f len = le32_to_cpu(buf[0]); perdatum->value = le32_to_cpu(buf[1]); + rc = -EINVAL; + if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX) + goto bad; rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) @@ -1174,6 +1209,14 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file len = le32_to_cpu(buf[0]); comdatum->value = le32_to_cpu(buf[1]); nel = le32_to_cpu(buf[3]); + rc = -EINVAL; + if (nel > SEL_VEC_MAX) + goto bad; + + /* perm_read() reads at least 64 bytes for any valid permission */ + rc = size_check(2 * sizeof(u32), nel, fp); + if (rc) + goto bad; rc = symtab_init(&comdatum->permissions, nel); if (rc) @@ -1323,7 +1366,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * char *key = NULL; struct class_datum *cladatum; __le32 buf[6]; - u32 i, len, l |
