summaryrefslogtreecommitdiff
path: root/security/selinux/ss
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-02 14:31:51 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-02 14:31:51 +0200
commit864c971e923f55d3ff5ac3ebc87aab8108d30c8a (patch)
tree3fd77c646490ad640a4cb7f37c63a1eaf8c2bd7b /security/selinux/ss
parent19ccd439d0087525b48dfcc8e584a0a940230744 (diff)
parent1c732c6b94f0faee1526bd375add2fe10cba2e26 (diff)
Merge v6.18.49linux-rolling-lts
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'security/selinux/ss')
-rw-r--r--security/selinux/ss/avtab.c35
-rw-r--r--security/selinux/ss/avtab.h13
-rw-r--r--security/selinux/ss/conditional.c25
-rw-r--r--security/selinux/ss/constraint.h1
-rw-r--r--security/selinux/ss/policydb.c254
-rw-r--r--security/selinux/ss/policydb.h31
-rw-r--r--security/selinux/ss/services.c8
7 files changed, 290 insertions, 77 deletions
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace..33556922f15e 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -349,7 +349,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));
@@ -360,9 +360,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);
@@ -391,6 +394,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;
@@ -409,6 +419,11 @@ 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++]);
rc = insertf(a, &key, &datum, p);
@@ -444,9 +459,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;
}
@@ -471,6 +490,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");
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 1bebfcb9c6a1..280dc57df8d3 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -165,7 +165,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_array(
+ p->bool_val_to_struct = kcalloc(
p->p_bools.nprim, sizeof(*p->bool_val_to_struct), GFP_KERNEL);
if (!p->bool_val_to_struct)
return -ENOMEM;
@@ -199,19 +199,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(sizeof(*booldatum), GFP_KERNEL);
@@ -223,11 +216,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 +235,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;
}
@@ -362,7 +357,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;
}
@@ -710,9 +706,8 @@ static int duplicate_policydb_bools(struct policydb *newdb,
struct cond_bool_datum **cond_bool_array;
int rc;
- cond_bool_array = kmalloc_array(orig->p_bools.nprim,
- sizeof(*orig->bool_val_to_struct),
- GFP_KERNEL);
+ cond_bool_array = kcalloc(orig->p_bools.nprim,
+ sizeof(*orig->bool_val_to_struct), GFP_KERNEL);
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/policydb.c b/security/selinux/ss/policydb.c
index fdb8a62da04a..483d7d54642a 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,13 +638,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 +655,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;
}
@@ -951,7 +948,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -1142,7 +1139,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1160,6 +1168,16 @@ 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;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1168,9 +1186,6 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
- /* indexes an nprim-sized array in security_get_permissions() */
- if (perdatum->value > s->nprim)
- goto bad;
return 0;
bad:
@@ -1183,7 +1198,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
@@ -1197,22 +1212,36 @@ 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;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1348,7 +1377,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, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1361,13 +1390,25 @@ 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;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1402,11 +1443,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
@@ -1430,16 +1482,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;
+ default:
+ goto bad;
+ }
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- cladatum->default_type = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_TARGET:
+ case DEFAULT_SOURCE:
+ cladatum->default_type = val;
+ break;
+ default:
+ goto bad;
+ }
}
rc = symtab_insert(s, key, cladatum);
@@ -1449,6 +1544,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1640,7 +1737,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len;
+ u32 len, val;
levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
@@ -1651,7 +1748,11 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- levdatum->isalias = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ levdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1667,6 +1768,8 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1676,7 +1779,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len;
+ u32 len, val;
catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
@@ -1688,7 +1791,11 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- catdatum->isalias = le32_to_cpu(buf[2]);
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ catdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1700,6 +1807,8 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1881,7 +1990,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel;
+ u32 i, nel, val;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1912,7 +2021,11 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rt->target_class = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto out;
+ rt->target_class = val;
} else
rt->target_class = p->process_class;
@@ -1949,6 +2062,8 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
+ if (rc)
+ pr_err("SELinux: invalid range\n");
return rc;
}
@@ -1957,7 +2072,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype;
+ u32 len, stype, otype, val;
__le32 buf[4];
int rc;
@@ -1976,9 +2091,17 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
+ rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
+ if (!policydb_type_isvalid(p, stype))
+ goto out;
key.ttype = le32_to_cpu(buf[1]);
- key.tclass = le32_to_cpu(buf[2]);
+ if (!policydb_type_isvalid(p, key.ttype))
+ goto out;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ key.tclass = val;
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -2034,6 +2157,9 @@ out:
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2042,7 +2168,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, tclass, ndatum, i;
+ u32 len, ttype, ndatum, i, val;
+ u16 tclass;
__le32 buf[3];
int rc;
@@ -2061,8 +2188,15 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
+ rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- tclass = le32_to_cpu(buf[1]);
+ if (!policydb_type_isvalid(p, ttype))
+ goto out;
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ tclass = val;
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2092,6 +2226,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ if (!policydb_type_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2123,6 +2261,9 @@ out:
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2170,7 +2311,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2;
+ u32 i, j, nel, nel2, len, len2, val;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2240,7 +2381,11 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- newc->v.sclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
+ goto out;
+ newc->v.sclass = val;
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2277,6 +2422,9 @@ out:
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2285,7 +2433,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len;
+ u32 j, nel, len, val;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2349,11 +2497,25 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
- c->u.port.protocol = le32_to_cpu(buf[0]);
- c->u.port.low_port = le32_to_cpu(buf[1]);
- c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0],
- p, fp);
+
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U8_MAX)
+ goto out;
+ c->u.port.protocol = val;
+ val = le32_to_cpu(buf[1]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.low_port = val;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.high_port = val;
+ if (c->u.port.low_port == 0 ||
+ c->u.port.low_port > c->u.port.high_port)
+ goto out;
+
+ rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto out;
break;
@@ -2471,6 +2633,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2485,7 +2649,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm, val;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2677,7 +2841,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rtk->tclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto bad;
+ rtk->tclass = val;
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742f..20b834581106 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
+ u16 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
+ u32 bounds; /* boundary of role, 0 for none */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u16 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -110,7 +110,8 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
+ u32 bounds; /* boundary of type, 0 for none */
+ /* internally unused, only forwarded via policydb_write() */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -118,7 +119,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
+ u32 bounds; /* bounds of user, 0 for none */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -139,7 +140,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs (can be 0 for wildcard) */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +323,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
@@ -388,9 +389,23 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+static inline bool val_is_boolean(u32 value)
+{
+ return value == 0 || value == 1;
+}
+
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
+#define pr_warn_once_policyload(policy, fmt, ...) \
+ do { \
+ static const void *prev_policy__; \
+ if (prev_policy__ != policy) { \
+ pr_warn(fmt, ##__VA_ARGS__); \
+ prev_policy__ = policy; \
+ } \
+ } while (0)
+
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 82608b34260f..fb150025359b 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
- BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[32];
+ char *permission_names[SEL_VEC_MAX];
int index;
u32 length;
bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < 32; index++) {
+ for (index = 0; index < SEL_VEC_MAX; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
@@ -3290,7 +3288,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u32 value = datum->value - 1;
+ u16 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])