mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 08:01:12 +02:00
selinux/stable-7.2 PR 20260805
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmpzc6gUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXPZug//SUeo33WvIYrLhlMYwPaVh9jqaR7c TG/bdUUofqPHxCXAMdmdqBVv3p3xU+7h6ds4CHFmgiWCUJ53ApbH+tl+lPxOzxTC awIWed5tJbFBiZ9ZQNc2hwaY5ETM0wQsVoZDAqWBZeu+Yf76ynjJN5fwxXRKBtcO 7DJR/NpPj1lHdp6AR3rfB/RQUVhg/0CnlFYb04Ef5aIwZ8fdlFh99GaZsQtMZvfO do6Yud/iNyEsL42lp7Zf21Ejlq8ubToLUCKEMygT2iDhKBq69DbOgMABkH0m6Bco A98s1KYARddzxT98bzRZkhzxiun0r9QVprYWzFPGi+pMkXi5eG6qgEMq/tkST313 kD3GsQPPQxY5k/AklD57/CoeI9rU7ioeZH/ZfFxDHupl5ExogI26VJ3h2obkdq3A aMZlr/rl5PPJD9qxjw8qFQd7Wn8A7Q1p041Bbf3pi1xxeVvdvP2z/e/MU9RQ7Xpr UIQNqd2QU3/uVstY743oximLRNQnRL7Aqq8r5KrbqeP9mlX6r+eEv2q0nBuqIAcR 7FOnkIn86ptl6prbTbG1FEYeRVypxAGuJrA6dDv8h2r4c4BuRc1Za3vVqluXH2Vg OxExgrP2Dg1TMQFAse4dTQlGOltLl1FvTd9JBm1t3kxpi0pbby2mUcHg3xjC/Cbm 8Dbxe9fEmsK+/jc= =YbZ/ -----END PGP SIGNATURE----- Merge tag 'selinux-pr-20260805' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux Pull selinux fixes from Paul Moore: - Continue to improve the validation of SELinux policies during load - Fix a SELinux regression caused by bpffs changes in v7.2-rc1 - Fix a SELinux preformance regression caused by SELinux changes in v7.2-rc1 * tag 'selinux-pr-20260805' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: check level category sets once at load time selinux: require every boolean value to be defined selinux: reject an unclaimed class value in security_get_classes() selinux: require a class's permission values to cover its permission count selinux: do not cancel a policy conversion that never started selinux: bpf: check SBLABEL_MNT before isec init selinux: reject a class permission count below its inherited common selinux: reject a permission value exceeding the class permission count
This commit is contained in:
commit
c5096fec0c
|
|
@ -2974,6 +2974,10 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
|||
|
||||
sbsec = selinux_superblock(dir->i_sb);
|
||||
|
||||
if (!selinux_initialized() ||
|
||||
!(sbsec->flags & SBLABEL_MNT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
newsid = crsec->create_sid;
|
||||
newsclass = inode_mode_to_security_class(inode->i_mode);
|
||||
rc = selinux_determine_inode_label(crsec, dir, qstr, newsclass, &newsid);
|
||||
|
|
@ -2988,10 +2992,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
|||
isec->initialized = LABEL_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!selinux_initialized() ||
|
||||
!(sbsec->flags & SBLABEL_MNT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
xattr = lsm_get_xattr_slot(xattrs, xattr_count);
|
||||
if (xattr) {
|
||||
rc = security_sid_to_context_force(newsid,
|
||||
|
|
|
|||
|
|
@ -160,9 +160,6 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
|
|||
{
|
||||
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 false;
|
||||
|
|
@ -176,21 +173,14 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
|
|||
return false;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* l is valid iff every bit in l->cat is set in levdatum->level.cat
|
||||
* and no bit in l->cat is larger than p->p_cats.nprim.
|
||||
* policydb_index() has already verified that every bit set in
|
||||
* levdatum->level.cat names a defined category, so containment is
|
||||
* sufficient here.
|
||||
*/
|
||||
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;
|
||||
return ebitmap_contains(&levdatum->level.cat, &l->cat,
|
||||
p->p_cats.nprim);
|
||||
}
|
||||
|
||||
bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r)
|
||||
|
|
|
|||
|
|
@ -665,6 +665,23 @@ static int cat_index(void *key, void *datum, void *datap)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sens_cat_index_check(void *key, void *datum, void *datap)
|
||||
{
|
||||
struct policydb *p = datap;
|
||||
struct level_datum *levdatum = datum;
|
||||
struct ebitmap_node *node;
|
||||
u32 bit;
|
||||
|
||||
ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
|
||||
if (bit >= p->p_cats.nprim || !sym_name(p, SYM_CATS, bit)) {
|
||||
pr_err("SELinux: sensitivity %s allows undefined category %u\n",
|
||||
(const char *)key, bit + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = {
|
||||
common_index,
|
||||
|
|
@ -719,6 +736,7 @@ static inline void symtab_hash_eval(struct symtab *s)
|
|||
static int policydb_index(struct policydb *p)
|
||||
{
|
||||
int i, rc;
|
||||
u32 v;
|
||||
|
||||
if (p->mls_enabled)
|
||||
pr_debug(
|
||||
|
|
@ -769,6 +787,30 @@ static int policydb_index(struct policydb *p)
|
|||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A sparse class value is absorbed by policydb_class_isvalid() and
|
||||
* its siblings, but no such predicate exists for booleans: every
|
||||
* user of bool_val_to_struct[] walks it by index and dereferences
|
||||
* each entry -- cond_evaluate_expr(), the two getters and
|
||||
* security_set_bools() -- so an unclaimed one has no consumer that
|
||||
* can tolerate it.
|
||||
*/
|
||||
for (v = 0; v < p->p_bools.nprim; v++) {
|
||||
if (!p->bool_val_to_struct[v]) {
|
||||
pr_err("SELinux: boolean %u is declared but not defined\n",
|
||||
v + 1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->mls_enabled) {
|
||||
rc = hashtab_map(&p->p_levels.table, sens_cat_index_check, p);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
|
|
@ -1154,7 +1196,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;
|
||||
|
|
@ -1175,6 +1228,13 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
|
|||
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)
|
||||
|
|
@ -1195,7 +1255,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_obj(*comdatum);
|
||||
|
|
@ -1222,17 +1282,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
|
|||
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);
|
||||
|
|
@ -1366,7 +1437,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, val;
|
||||
u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
|
||||
int rc;
|
||||
|
||||
cladatum = kzalloc_obj(*cladatum);
|
||||
|
|
@ -1399,6 +1470,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
|
|||
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]);
|
||||
|
||||
|
|
@ -1419,13 +1494,36 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
|
|||
cladatum->comkey);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* security_get_permissions() maps the common's permissions
|
||||
* into an array sized by this class's nprim, so a class must
|
||||
* declare at least as many as the common it inherits.
|
||||
*/
|
||||
if (cladatum->permissions.nprim <
|
||||
cladatum->comdatum->permissions.nprim) {
|
||||
pr_err("SELinux: class %s has fewer permissions than common %s\n",
|
||||
key, cladatum->comkey);
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2221,7 +2221,9 @@ void selinux_policy_cancel(struct selinux_load_state *load_state)
|
|||
oldpolicy = rcu_dereference_protected(state->policy,
|
||||
lockdep_is_held(&state->policy_mutex));
|
||||
|
||||
sidtab_cancel_convert(oldpolicy->sidtab);
|
||||
/* a first load has no outgoing policy and converted nothing */
|
||||
if (oldpolicy)
|
||||
sidtab_cancel_convert(oldpolicy->sidtab);
|
||||
selinux_policy_free(load_state->policy);
|
||||
kfree(load_state->convert_data);
|
||||
}
|
||||
|
|
@ -3302,6 +3304,7 @@ int security_get_classes(struct selinux_policy *policy,
|
|||
char ***classes, u32 *nclasses)
|
||||
{
|
||||
struct policydb *policydb;
|
||||
u32 i;
|
||||
int rc;
|
||||
|
||||
policydb = &policy->policydb;
|
||||
|
|
@ -3314,16 +3317,29 @@ int security_get_classes(struct selinux_policy *policy,
|
|||
|
||||
rc = hashtab_map(&policydb->p_classes.table, get_classes_callback,
|
||||
*classes);
|
||||
if (rc) {
|
||||
u32 i;
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < *nclasses; i++)
|
||||
kfree((*classes)[i]);
|
||||
kfree(*classes);
|
||||
/*
|
||||
* The class symtab may be sparse, which policydb_class_isvalid() exists
|
||||
* to absorb; the callback fills this array by value, so an unclaimed
|
||||
* one leaves a NULL that sel_make_classes() hands to sel_make_dir().
|
||||
*/
|
||||
for (i = 0; i < *nclasses; i++) {
|
||||
if (!(*classes)[i]) {
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
|
||||
err:
|
||||
for (i = 0; i < *nclasses; i++)
|
||||
kfree((*classes)[i]);
|
||||
kfree(*classes);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_permissions_callback(void *k, void *d, void *args)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user