aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/selinux/ibpkey.c2
-rw-r--r--security/selinux/include/security.h1
-rw-r--r--security/selinux/selinuxfs.c27
-rw-r--r--security/selinux/ss/avtab.c49
-rw-r--r--security/selinux/ss/avtab.h13
-rw-r--r--security/selinux/ss/conditional.c39
-rw-r--r--security/selinux/ss/constraint.h1
-rw-r--r--security/selinux/ss/ebitmap.c27
-rw-r--r--security/selinux/ss/ebitmap.h1
-rw-r--r--security/selinux/ss/hashtab.h4
-rw-r--r--security/selinux/ss/mls.c66
-rw-r--r--security/selinux/ss/mls.h6
-rw-r--r--security/selinux/ss/policydb.c358
-rw-r--r--security/selinux/ss/policydb.h56
-rw-r--r--security/selinux/ss/services.c13
-rw-r--r--security/selinux/ss/symtab.c2
-rw-r--r--security/selinux/ss/symtab.h2
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, len2, ncons, nel;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc_obj(*cladatum);
@@ -1336,8 +1379,21 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
- cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
+
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (val > U16_MAX)
+ goto bad;
+ cladatum->value = val;
+
+ /* 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(&cladatum->permissions, nel);
if (rc)
@@ -1393,16 +1449,59 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- cladatum->default_user = le32_to_cpu(buf[0]);
- cladatum->default_role = le32_to_cpu(buf[1]);
- cladatum->default_range = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_user = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[1]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_role = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[2]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE_LOW:
+ case DEFAULT_SOURCE_HIGH:
+ case DEFAULT_SOURCE_LOW_HIGH:
+ case DEFAULT_TARGET_LOW:
+ case DEFAULT_TARGET_HIGH:
+ case DEFAULT_TARGET_LOW_HIGH:
+ case DEFAULT_GLBLUB:
+ cladatum->default_range = val;
+ break;