mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
selinux/stable-7.3 PR 20260919
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmqvPSwUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXP1rBAAkK6sP+lzXynr/abxWio9UCvlN6NF jqMXtKeUeLNg/lJakXKhja/FGaK9w+Rm+zlXQQvVE7YAO5Db758JzCXMTHlK6etD T5d+GVdZj/oVcAvfGhov0guNHZqcfKTGWOXKtGV8RQWYL+f6IC1IIHnz9wNeWIfY emkfOTgAJpCkX9nXS5xTdQNUiPkUV80LFGqxXCMhxiumvyzqOhUteytfNh3UXXcs ldOWRBstUumudBe/MJR09REtQve3emHc7bZUAxSKwXxzdOPTf7UEy4Y7QXCN3Opn Dn6Dsrt6lnpIZyzUK/eOnZvQ639gDqTMW2XTmin0JIf2KGG/PhqnvOT07hOPl+X5 wqXh2wCFn+tXVi5t+S2myMUVZK56GwfRH9kMCo6ca1Ui36iYYEeoJYt7z5tsE6P5 YBdvKKJ/MPR1/uEwP/TB3UDK0o4OBjyujc6/Ka5iBoIHXKpx7MBLOo/Q0h/Wu2A0 7HYvG/4lYdWp3p5iR8cdZ+ZgdTQDWi6t5BD3JWOfWi1gHCeGgZCHTCZ9mdSoFS18 9GpXFXTR4yM5pfwxQEAu6jFH0esSB92HJBd/yS7sU8EIUQ+0XmOge+gVPifMrhWj yOHCR7hlAYE5ds51w3ZVOYI0slkYImqCHEdlCz1DiKzyLmQQmfJvshOIvBCl7uwn LRHu6Mo0cEBMAvc= =kiQE -----END PGP SIGNATURE----- Merge tag 'selinux-pr-20260919' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux Pull selinux fixes from Paul Moore: - Ensure that the cached SELinux access decisions are correct - Fix the SELinux overlayfs code to properly track the top-level/user information on multiple stacked overlayfs filesystems - Fix the SELinux overlayfs code to properly enforce mprotect() access control policy on all of the different layers in multiple stacked overlayfs filesystems * tag 'selinux-pr-20260919' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: recheck intermediate backing files on mprotect() selinux: preserve user SID across nested backing files selinux: always fill AVC decision in avc_has_perm_noaudit()
This commit is contained in:
commit
4a910e594a
|
|
@ -1149,8 +1149,11 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
|||
u32 denied;
|
||||
struct avc_node *node;
|
||||
|
||||
if (WARN_ON(!requested))
|
||||
if (WARN_ON(!requested)) {
|
||||
/* Provide a deny-all, audit-all decision to the caller. */
|
||||
*avd = (struct av_decision){ .auditdeny = 0xffffffff };
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
node = avc_lookup(ssid, tsid, tclass);
|
||||
|
|
|
|||
|
|
@ -1674,24 +1674,30 @@ static int cred_has_capability(const struct cred *cred,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Check whether a task has a particular permission to an inode.
|
||||
The 'adp' parameter is optional and allows other audit
|
||||
data to be passed (e.g. the dentry). */
|
||||
/*
|
||||
* Check whether a SID has a particular permission to an inode. The 'adp'
|
||||
* parameter is optional and allows other audit data to be passed (e.g. the
|
||||
* dentry).
|
||||
*/
|
||||
static int inode_sid_has_perm(u32 sid, struct inode *inode, u32 perms,
|
||||
struct common_audit_data *adp)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
isec = selinux_inode(inode);
|
||||
|
||||
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
|
||||
}
|
||||
|
||||
static int inode_has_perm(const struct cred *cred,
|
||||
struct inode *inode,
|
||||
u32 perms,
|
||||
struct common_audit_data *adp)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
u32 sid;
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
sid = cred_sid(cred);
|
||||
isec = selinux_inode(inode);
|
||||
|
||||
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
|
||||
return inode_sid_has_perm(cred_sid(cred), inode, perms, adp);
|
||||
}
|
||||
|
||||
/* Same as inode_has_perm, but pass explicit audit data containing
|
||||
|
|
@ -3843,17 +3849,74 @@ static int selinux_file_alloc_security(struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 selinux_file_user_sid(const struct file *file)
|
||||
{
|
||||
if (unlikely(file->f_mode & FMODE_BACKING))
|
||||
return selinux_backing_file(file)->uf_sid;
|
||||
return selinux_file(file)->sid;
|
||||
}
|
||||
|
||||
static int selinux_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
struct backing_file_security_struct *bfsec;
|
||||
const struct backing_file_security_struct *ubfsec;
|
||||
struct backing_file_security_layer *layer;
|
||||
u32 i;
|
||||
|
||||
bfsec = selinux_backing_file(backing_file);
|
||||
bfsec->uf_sid = selinux_file(user_file)->sid;
|
||||
bfsec->uf_sid = selinux_file_user_sid(user_file);
|
||||
if (!(user_file->f_mode & FMODE_BACKING))
|
||||
return 0;
|
||||
|
||||
ubfsec = selinux_backing_file(user_file);
|
||||
/* a wrapped count would make kmalloc_array() return ZERO_SIZE_PTR */
|
||||
if (unlikely(ubfsec->layer_count == U32_MAX))
|
||||
return -EOVERFLOW;
|
||||
|
||||
/*
|
||||
* The final VMA only retains the lowest backing file, so record the
|
||||
* whole chain here rather than in the mmap hook, where concurrent
|
||||
* mappings would have to be serialized. Size it dynamically: erofs
|
||||
* inode sharing adds a backing file without bumping s_stack_depth.
|
||||
*/
|
||||
bfsec->layers = kmalloc_array(ubfsec->layer_count + 1,
|
||||
sizeof(*bfsec->layers), GFP_KERNEL);
|
||||
if (!bfsec->layers)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ubfsec->layer_count; i++) {
|
||||
layer = &bfsec->layers[i];
|
||||
*layer = ubfsec->layers[i];
|
||||
path_get(&layer->path);
|
||||
}
|
||||
|
||||
/* f_path, not file_user_path(): this layer, not the top-level file */
|
||||
layer = &bfsec->layers[i];
|
||||
layer->path = user_file->f_path;
|
||||
layer->mounter_sid = cred_sid(user_file->f_cred);
|
||||
layer->fd_sid = selinux_file(user_file)->sid;
|
||||
path_get(&layer->path);
|
||||
bfsec->layer_count = ubfsec->layer_count + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selinux_backing_file_free(struct file *backing_file)
|
||||
{
|
||||
struct backing_file_security_struct *bfsec;
|
||||
|
||||
/* security_backing_file_free() may be called twice after an error */
|
||||
if (!backing_file_security(backing_file))
|
||||
return;
|
||||
|
||||
bfsec = selinux_backing_file(backing_file);
|
||||
while (bfsec->layer_count)
|
||||
path_put(&bfsec->layers[--bfsec->layer_count].path);
|
||||
kfree(bfsec->layers);
|
||||
bfsec->layers = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
|
|
@ -3971,6 +4034,53 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
|
|||
|
||||
static int default_noexec __ro_after_init;
|
||||
|
||||
static u32 file_map_prot_to_av(unsigned long prot, bool shared)
|
||||
{
|
||||
u32 av = FILE__READ;
|
||||
|
||||
if (shared && (prot & PROT_WRITE))
|
||||
av |= FILE__WRITE;
|
||||
if (prot & PROT_EXEC)
|
||||
av |= FILE__EXECUTE;
|
||||
|
||||
return av;
|
||||
}
|
||||
|
||||
static int backing_mounters_has_perm(const struct file *file, u32 av)
|
||||
{
|
||||
const struct backing_file_security_struct *bfsec;
|
||||
const struct backing_file_security_layer *layer;
|
||||
struct common_audit_data ad;
|
||||
struct inode *inode;
|
||||
u32 i;
|
||||
int rc;
|
||||
|
||||
if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
bfsec = selinux_backing_file(file);
|
||||
for (i = 0; i < bfsec->layer_count; i++) {
|
||||
layer = &bfsec->layers[i];
|
||||
inode = d_inode(layer->path.dentry);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = layer->path;
|
||||
|
||||
if (layer->mounter_sid != layer->fd_sid) {
|
||||
rc = avc_has_perm(layer->mounter_sid, layer->fd_sid,
|
||||
SECCLASS_FD, FD__USE, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = inode_sid_has_perm(layer->mounter_sid, inode, av, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __file_map_prot_check(const struct file *file, unsigned long prot,
|
||||
bool shared, bool mounter_check,
|
||||
bool bf_user_file)
|
||||
|
|
@ -4004,14 +4114,10 @@ static int __file_map_prot_check(const struct file *file, unsigned long prot,
|
|||
if (file) {
|
||||
const struct cred *cred = mounter_check ?
|
||||
file->f_cred : current_cred();
|
||||
/* "read" always possible, "write" only if shared */
|
||||
u32 av = FILE__READ;
|
||||
if (shared && prot_write)
|
||||
av |= FILE__WRITE;
|
||||
if (prot_exec)
|
||||
av |= FILE__EXECUTE;
|
||||
|
||||
return __file_has_perm(cred, file, av, bf_user_file);
|
||||
return __file_has_perm(cred, file,
|
||||
file_map_prot_to_av(prot, shared),
|
||||
bf_user_file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -4106,6 +4212,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
|||
int rc;
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
u32 av;
|
||||
const struct file *file = vma->vm_file;
|
||||
bool backing_file;
|
||||
bool shared = vma->vm_flags & VM_SHARED;
|
||||
|
|
@ -4149,6 +4256,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
|||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
rc = backing_mounters_has_perm(file,
|
||||
FILE__EXECMOD);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = file_has_perm(file->f_cred, file,
|
||||
FILE__EXECMOD);
|
||||
if (rc)
|
||||
|
|
@ -4161,6 +4272,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
|||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
av = file_map_prot_to_av(prot, shared);
|
||||
rc = backing_mounters_has_perm(file, av);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = file_map_prot_check(file, prot, shared, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -7619,6 +7734,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
|
|||
LSM_HOOK_INIT(file_permission, selinux_file_permission),
|
||||
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
|
||||
LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
|
||||
LSM_HOOK_INIT(backing_file_free, selinux_backing_file_free),
|
||||
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
|
||||
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
|
||||
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
|
||||
|
|
|
|||
|
|
@ -86,8 +86,16 @@ struct file_security_struct {
|
|||
u32 pseqno; /* Policy seqno at the time of file open */
|
||||
};
|
||||
|
||||
struct backing_file_security_layer {
|
||||
struct path path; /* this layer's real path */
|
||||
u32 mounter_sid; /* SID of the mounter that opened it */
|
||||
u32 fd_sid; /* SID of its open file description */
|
||||
};
|
||||
|
||||
struct backing_file_security_struct {
|
||||
u32 uf_sid; /* associated user file fsec->sid */
|
||||
u32 uf_sid; /* top-level user file fsec->sid */
|
||||
u32 layer_count; /* number of intermediate backing files */
|
||||
struct backing_file_security_layer *layers;
|
||||
};
|
||||
|
||||
struct superblock_security_struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user