selinux/stable-7.1 PR 20260507
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmn8tJEUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXN/Qw//XAX9lG6yOCVR/JnuhNOjgENvqANu 2aY33ylr+nqqQ3bDR7uJKrxZ74Vu+r4+SKFF2mHmVJkIkOie2XsFBYh68Q2QiIQd ZROxTnPYeP/eEsL1Px6ZjyILqD4gkNUtSQvH3FoydFcF784VcMwvPoTeLENzqZkV RDcVypiYNiROfVS4cDXYiVZKZuQeMg7/9IX5ZVDsdYv2DwlYgj5LsfM+HdB95vf1 MoysATSwOo6VUUvri+i9BNVC3ZYeSpEvC9NZazb3QKWH/cpvDrzZh0Qz+Mn2TFVt X2o9syY2K8nm/ZxUTVmbni2Y8xB71a3WRJjx3vdc/ZpsOQ38hMMnLfxCCB/xNvQ1 Z8/qUOVT0fiI4AfJdQjVZkO7viKQfRlQAtkT+nAVIESnsZdBckluT7uFSH4nvzcC JNK6Uzvhs4lhrYyMA9rEQOpswspcKxyeYPZN5rFtlz3zUzGeYG0pTNZ6B9Tjl8DK xnqcm/ySTGx8m6/w29xDdubOjj+J2U+oGOkaGAWyXDxGbJ2vWPvqQrbRfFrMuzPh sO5IYKcQ6MbEIzcUErlASXVxJj2KfCjEnbTDpocOVEY6vczAPtQuZme3WyQ80GuV wUYXDEBYEJG27vjHXtN5ClZs6NRY2niyphq3yh+gWoJFens76ord9zU6Pq8IF4xd XcPbqMhY1Q65i3c= =l+qi -----END PGP SIGNATURE----- Merge tag 'selinux-pr-20260507' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux Pull selinux fixes from Paul Moore: - Allow for multiple opens of /sys/fs/selinux/policy Prevent a single process from blocking others from reading the SELinux policy loaded in the kernel. This does have the side effect of potentially allowing userspace to trigger additional kernel memory allocations as part of the open/read operation, but this is mitigated by requiring the SELinux security/read_policy permission. - Reduce the critical sections where the SELinux policy mutex is held This includes the patch to the policy loader code where we move the permission checks and an allocation outside the mutex as well as the the patch to checkreqprot which drops the code/lock entirely. While the checkreqprot code had effectively been dropped in an earlier release, portions of the code still remained that would have triggered the mutex to perform an IMA measurement. This finally drops all of that while preserving the user visible behavior. - Eliminate potential sources of log spamming There were a few areas where processes could flood the system logs and hide other, more critical events. The previously disabled checkreqprot and runtime disable knobs in selinuxfs were two such areas that have now been greatly simplified and a pr_err() replaced with a pr_err_once(). The third such place is the /sys/fs/selinux/user file, which hasn't been used by a userspace release since 2020 and was scheduled for removal after 2025; this effectively disables this functionality, but similar to checkreqprot, it is done in a way that should not break old userspace. * tag 'selinux-pr-20260507' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: shrink critical section in sel_write_load() selinux: allow multiple opens of /sys/fs/selinux/policy selinux: prune /sys/fs/selinux/user selinux: prune /sys/fs/selinux/disable selinux: prune /sys/fs/selinux/checkreqprotmaster
commit
917719c412
|
|
@ -312,8 +312,6 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
|
|||
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
|
||||
u32 *sid);
|
||||
|
||||
int security_get_user_sids(u32 fromsid, const char *username, u32 **sids, u32 *nel);
|
||||
|
||||
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
|
||||
|
||||
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ struct selinux_fs_info {
|
|||
int *bool_pending_values;
|
||||
struct dentry *class_dir;
|
||||
unsigned long last_class_ino;
|
||||
bool policy_opened;
|
||||
unsigned long last_ino;
|
||||
struct super_block *sb;
|
||||
};
|
||||
|
|
@ -272,35 +271,13 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
|
|||
size_t count, loff_t *ppos)
|
||||
|
||||
{
|
||||
char *page;
|
||||
ssize_t length;
|
||||
int new_value;
|
||||
|
||||
if (count >= PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
/* No partial writes. */
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
page = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
if (sscanf(page, "%d", &new_value) != 1) {
|
||||
length = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
length = count;
|
||||
|
||||
if (new_value) {
|
||||
pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n");
|
||||
pr_err("SELinux: Runtime disable is not supported, use selinux=0 on the kernel cmdline.\n");
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(page);
|
||||
return length;
|
||||
/*
|
||||
* Setting disable is no longer supported, see
|
||||
* https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable
|
||||
*/
|
||||
pr_err_once("SELinux: %s (%d) wrote to disable. This is no longer supported.\n",
|
||||
current->comm, current->pid);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations sel_disable_ops = {
|
||||
|
|
@ -362,44 +339,31 @@ struct policy_load_memory {
|
|||
|
||||
static int sel_open_policy(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
|
||||
struct policy_load_memory *plm = NULL;
|
||||
int rc;
|
||||
|
||||
BUG_ON(filp->private_data);
|
||||
|
||||
mutex_lock(&selinux_state.policy_mutex);
|
||||
|
||||
rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
|
||||
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
|
||||
if (rc)
|
||||
goto err;
|
||||
return rc;
|
||||
|
||||
rc = -EBUSY;
|
||||
if (fsi->policy_opened)
|
||||
goto err;
|
||||
|
||||
rc = -ENOMEM;
|
||||
plm = kzalloc_obj(*plm);
|
||||
if (!plm)
|
||||
goto err;
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&selinux_state.policy_mutex);
|
||||
rc = security_read_policy(&plm->data, &plm->len);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
if ((size_t)i_size_read(inode) != plm->len) {
|
||||
inode_lock(inode);
|
||||
i_size_write(inode, plm->len);
|
||||
inode_unlock(inode);
|
||||
}
|
||||
|
||||
fsi->policy_opened = 1;
|
||||
mutex_unlock(&selinux_state.policy_mutex);
|
||||
|
||||
filp->private_data = plm;
|
||||
|
||||
mutex_unlock(&selinux_state.policy_mutex);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
mutex_unlock(&selinux_state.policy_mutex);
|
||||
|
|
@ -412,13 +376,8 @@ err:
|
|||
|
||||
static int sel_release_policy(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
|
||||
struct policy_load_memory *plm = filp->private_data;
|
||||
|
||||
BUG_ON(!plm);
|
||||
|
||||
fsi->policy_opened = 0;
|
||||
|
||||
vfree(plm->data);
|
||||
kfree(plm);
|
||||
|
||||
|
|
@ -594,34 +553,31 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
|||
if (!count)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&selinux_state.policy_mutex);
|
||||
|
||||
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
|
||||
SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
|
||||
if (length)
|
||||
goto out;
|
||||
return length;
|
||||
|
||||
data = vmalloc(count);
|
||||
if (!data) {
|
||||
length = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(data, buf, count) != 0) {
|
||||
length = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&selinux_state.policy_mutex);
|
||||
length = security_load_policy(data, count, &load_state);
|
||||
if (length) {
|
||||
pr_warn_ratelimited("SELinux: failed to load policy\n");
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
}
|
||||
fsi = file_inode(file)->i_sb->s_fs_info;
|
||||
length = sel_make_policy_nodes(fsi, load_state.policy);
|
||||
if (length) {
|
||||
pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n");
|
||||
selinux_policy_cancel(&load_state);
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
selinux_policy_commit(&load_state);
|
||||
|
|
@ -631,8 +587,9 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
|||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
|
||||
out:
|
||||
out_unlock:
|
||||
mutex_unlock(&selinux_state.policy_mutex);
|
||||
out:
|
||||
vfree(data);
|
||||
return length;
|
||||
}
|
||||
|
|
@ -689,46 +646,13 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
|
|||
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *page;
|
||||
ssize_t length;
|
||||
unsigned int new_value;
|
||||
|
||||
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
|
||||
SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
|
||||
NULL);
|
||||
if (length)
|
||||
return length;
|
||||
|
||||
if (count >= PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
/* No partial writes. */
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
page = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
if (sscanf(page, "%u", &new_value) != 1) {
|
||||
length = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
length = count;
|
||||
|
||||
if (new_value) {
|
||||
char comm[sizeof(current->comm)];
|
||||
|
||||
strscpy(comm, current->comm);
|
||||
pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n",
|
||||
comm, current->pid);
|
||||
}
|
||||
|
||||
selinux_ima_measure_state();
|
||||
|
||||
out:
|
||||
kfree(page);
|
||||
return length;
|
||||
/*
|
||||
* Setting checkreqprot is no longer supported, see
|
||||
* https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-checkreqprot
|
||||
*/
|
||||
pr_err_once("SELinux: %s (%d) wrote to checkreqprot. This is no longer supported.\n",
|
||||
current->comm, current->pid);
|
||||
return count;
|
||||
}
|
||||
static const struct file_operations sel_checkreqprot_ops = {
|
||||
.read = sel_read_checkreqprot,
|
||||
|
|
@ -1073,69 +997,11 @@ out:
|
|||
|
||||
static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
char *con = NULL, *user = NULL, *ptr;
|
||||
u32 sid, *sids = NULL;
|
||||
ssize_t length;
|
||||
char *newcon;
|
||||
int rc;
|
||||
u32 i, len, nsids;
|
||||
|
||||
pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!"
|
||||
" This will not be supported in the future; please update your"
|
||||
" userspace.\n", current->comm, current->pid);
|
||||
ssleep(5);
|
||||
|
||||
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
|
||||
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
|
||||
NULL);
|
||||
if (length)
|
||||
goto out;
|
||||
|
||||
length = -ENOMEM;
|
||||
con = kzalloc(size + 1, GFP_KERNEL);
|
||||
if (!con)
|
||||
goto out;
|
||||
|
||||
length = -ENOMEM;
|
||||
user = kzalloc(size + 1, GFP_KERNEL);
|
||||
if (!user)
|
||||
goto out;
|
||||
|
||||
length = -EINVAL;
|
||||
if (sscanf(buf, "%s %s", con, user) != 2)
|
||||
goto out;
|
||||
|
||||
length = security_context_str_to_sid(con, &sid, GFP_KERNEL);
|
||||
if (length)
|
||||
goto out;
|
||||
|
||||
length = security_get_user_sids(sid, user, &sids, &nsids);
|
||||
if (length)
|
||||
goto out;
|
||||
|
||||
length = sprintf(buf, "%u", nsids) + 1;
|
||||
ptr = buf + length;
|
||||
for (i = 0; i < nsids; i++) {
|
||||
rc = security_sid_to_context(sids[i], &newcon, &len);
|
||||
if (rc) {
|
||||
length = rc;
|
||||
goto out;
|
||||
}
|
||||
if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
|
||||
kfree(newcon);
|
||||
length = -ERANGE;
|
||||
goto out;
|
||||
}
|
||||
memcpy(ptr, newcon, len);
|
||||
kfree(newcon);
|
||||
ptr += len;
|
||||
length += len;
|
||||
}
|
||||
out:
|
||||
kfree(sids);
|
||||
kfree(user);
|
||||
kfree(con);
|
||||
return length;
|
||||
pr_err_once("SELinux: %s (%d) wrote to user. This is no longer supported.\n",
|
||||
current->comm, current->pid);
|
||||
buf[0] = '0';
|
||||
buf[1] = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
|
||||
|
|
|
|||
|
|
@ -2746,131 +2746,6 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define SIDS_NEL 25
|
||||
|
||||
/**
|
||||
* security_get_user_sids - Obtain reachable SIDs for a user.
|
||||
* @fromsid: starting SID
|
||||
* @username: username
|
||||
* @sids: array of reachable SIDs for user
|
||||
* @nel: number of elements in @sids
|
||||
*
|
||||
* Generate the set of SIDs for legal security contexts
|
||||
* for a given user that can be reached by @fromsid.
|
||||
* Set *@sids to point to a dynamically allocated
|
||||
* array containing the set of SIDs. Set *@nel to the
|
||||
* number of elements in the array.
|
||||
*/
|
||||
|
||||
int security_get_user_sids(u32 fromsid,
|
||||
const char *username,
|
||||
u32 **sids,
|
||||
u32 *nel)
|
||||
{
|
||||
struct selinux_policy *policy;
|
||||
struct policydb *policydb;
|
||||
struct sidtab *sidtab;
|
||||
struct context *fromcon, usercon;
|
||||
u32 *mysids = NULL, *mysids2, sid;
|
||||
u32 i, j, mynel, maxnel = SIDS_NEL;
|
||||
struct user_datum *user;
|
||||
struct role_datum *role;
|
||||
struct ebitmap_node *rnode, *tnode;
|
||||
int rc;
|
||||
|
||||
*sids = NULL;
|
||||
*nel = 0;
|
||||
|
||||
if (!selinux_initialized())
|
||||
return 0;
|
||||
|
||||
mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL);
|
||||
if (!mysids)
|
||||
return -ENOMEM;
|
||||
|
||||
retry:
|
||||
mynel = 0;
|
||||
rcu_read_lock();
|
||||
policy = rcu_dereference(selinux_state.policy);
|
||||
policydb = &policy->policydb;
|
||||
sidtab = policy->sidtab;
|
||||
|
||||
context_init(&usercon);
|
||||
|
||||
rc = -EINVAL;
|
||||
fromcon = sidtab_search(sidtab, fromsid);
|
||||
if (!fromcon)
|
||||
goto out_unlock;
|
||||
|
||||
rc = -EINVAL;
|
||||
user = symtab_search(&policydb->p_users, username);
|
||||
if (!user)
|
||||
goto out_unlock;
|
||||
|
||||
usercon.user = user->value;
|
||||
|
||||
ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
|
||||
role = policydb->role_val_to_struct[i];
|
||||
usercon.role = i + 1;
|
||||
ebitmap_for_each_positive_bit(&role->types, tnode, j) {
|
||||
usercon.type = j + 1;
|
||||
|
||||
if (mls_setup_user_range(policydb, fromcon, user,
|
||||
&usercon))
|
||||
continue;
|
||||
|
||||
rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
|
||||
if (rc == -ESTALE) {
|
||||
rcu_read_unlock();
|
||||
goto retry;
|
||||
}
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
if (mynel < maxnel) {
|
||||
mysids[mynel++] = sid;
|
||||
} else {
|
||||
rc = -ENOMEM;
|
||||
maxnel += SIDS_NEL;
|
||||
mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
|
||||
if (!mysids2)
|
||||
goto out_unlock;
|
||||
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
|
||||
kfree(mysids);
|
||||
mysids = mysids2;
|
||||
mysids[mynel++] = sid;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
if (rc || !mynel) {
|
||||
kfree(mysids);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
|
||||
if (!mysids2) {
|
||||
kfree(mysids);
|
||||
return rc;
|
||||
}
|
||||
for (i = 0, j = 0; i < mynel; i++) {
|
||||
struct av_decision dummy_avd;
|
||||
rc = avc_has_perm_noaudit(fromsid, mysids[i],
|
||||
SECCLASS_PROCESS, /* kernel value */
|
||||
PROCESS__TRANSITION, AVC_STRICT,
|
||||
&dummy_avd);
|
||||
if (!rc)
|
||||
mysids2[j++] = mysids[i];
|
||||
cond_resched();
|
||||
}
|
||||
kfree(mysids);
|
||||
*sids = mysids2;
|
||||
*nel = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __security_genfs_sid - Helper to obtain a SID for a file in a filesystem
|
||||
* @policy: policy
|
||||
|
|
|
|||
Loading…
Reference in New Issue