Re: f_modown and LSM inconsistency (was [PATCH v2 1/4] Landlock: Add signal control)
From: Mickaël Salaün <mic@digikod.net>
Date: 2024-08-12 14:55:34
Also in:
linux-fsdevel, lkml
On Mon, Aug 12, 2024 at 03:09:08PM +0200, Jann Horn wrote:
On Mon, Aug 12, 2024 at 12:04 AM Paul Moore [off-list ref] wrote:quoted
On Fri, Aug 9, 2024 at 10:01 AM Jann Horn [off-list ref] wrote:quoted
On Fri, Aug 9, 2024 at 3:18 PM Mickaël Salaün [off-list ref] wrote:quoted
Talking about f_modown() and security_file_set_fowner(), it looks like there are some issues: On Fri, Aug 09, 2024 at 02:44:06PM +0200, Jann Horn wrote:quoted
On Fri, Aug 9, 2024 at 12:59 PM Mickaël Salaün [off-list ref] wrote:[...]quoted
quoted
BTW, I don't understand why neither SELinux nor Smack use (explicit) atomic operations nor lock.Yeah, I think they're sloppy and kinda wrong - but it sorta works in practice mostly because they don't have to do any refcounting around this?quoted
And it looks weird that security_file_set_fowner() isn't called by f_modown() with the same locking to avoid races.True. I imagine maybe the thought behind this design could have been that LSMs should have their own locking, and that calling an LSM hook with IRQs off is a little weird? But the way the LSMs actually use the hook now, it might make sense to call the LSM with the lock held and IRQs off...Would it be OK (for VFS, SELinux, and Smack maintainers) to move the security_file_set_fowner() call into f_modown(), especially where UID/EUID are populated. That would only call security_file_set_fowner() when the fown is actually set, which I think could also fix a bug for SELinux and Smack. Could we replace the uid and euid fields with a pointer to the current credentials? This would enables LSMs to not copy the same kind of credential informations and save some memory, simplify credential management, and improve consistency.To clarify: These two paragraphs are supposed to be two alternative options, right? One option is to call security_file_set_fowner() with the lock held, the other option is to completely rip out the security_file_set_fowner() hook and instead let the VFS provide LSMs with the creds they need for the file_send_sigiotask hook?I'm not entirely clear on what is being proposed either. Some quick pseudo code might do wonders here to help clarify things. From a LSM perspective I suspect we are always going to need some sort of hook in the F_SETOWN code path as the LSM needs to potentially capture state/attributes/something-LSM-specific at that context/point-in-time.The only thing LSMs currently do there is capture state from current->cred. So if the VFS takes care of capturing current->cred there, we should be able to rip out all the file_set_fowner stuff. Something like this (totally untested):
I just sent a quite similar patch just before syncing my emails... The main difference seems to be related to the initialization of the f_owner's credentials.
quoted hunk ↗ jump to hunk
diff --git a/fs/fcntl.c b/fs/fcntl.c index 300e5d9ad913..17f159bf625f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c@@ -98,8 +98,9 @@ static void f_modown(struct file *filp, struct pid*pid, enum pid_type type, if (pid) { const struct cred *cred = current_cred(); - filp->f_owner.uid = cred->uid; - filp->f_owner.euid = cred->euid; + if (filp->f_owner.owner_cred) + put_cred(filp->f_owner.owner_cred); + filp->f_owner.owner_cred = get_current_cred(); } } write_unlock_irq(&filp->f_owner.lock);@@ -108,7 +109,6 @@ static void f_modown(struct file *filp, struct pid*pid, enum pid_type type, void __f_setown(struct file *filp, struct pid *pid, enum pid_type type, int force) { - security_file_set_fowner(filp); f_modown(filp, pid, type, force); } EXPORT_SYMBOL(__f_setown);diff --git a/fs/file_table.c b/fs/file_table.c index ca7843dde56d..440796fc8e91 100644 --- a/fs/file_table.c +++ b/fs/file_table.c@@ -426,6 +426,8 @@ static void __fput(struct file *file) } fops_put(file->f_op); put_pid(file->f_owner.pid); + if (file->f_owner.owner_cred) + put_cred(file->f_owner.owner_cred); put_file_access(file); dput(dentry); if (unlikely(mode & FMODE_NEED_UNMOUNT))diff --git a/include/linux/fs.h b/include/linux/fs.h index fd34b5755c0b..43bfad373bf9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h@@ -950,7 +950,7 @@ struct fown_struct { rwlock_t lock; /* protects pid, uid, euid fields */ struct pid *pid; /* pid or -pgrp where SIGIO should be sent */ enum pid_type pid_type; /* Kind of process group SIGIOshould be sent to */ - kuid_t uid, euid; /* uid/euid of process setting the owner */ + const struct cred __rcu *owner_cred; int signum; /* posix.1b rt signal to be delivered on IO */ };diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 855db460e08b..2c0935dd079e 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h@@ -197,7 +197,6 @@ LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) LSM_HOOK(int, 0, file_fcntl, struct file *file, unsigned int cmd, unsigned long arg) -LSM_HOOK(void, LSM_RET_VOID, file_set_fowner, struct file *file) LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk, struct fown_struct *fown, int sig) LSM_HOOK(int, 0, file_receive, struct file *file)diff --git a/include/linux/security.h b/include/linux/security.h index 1390f1efb4f0..3343db05fa2e 100644 --- a/include/linux/security.h +++ b/include/linux/security.h@@ -1079,11 +1079,6 @@ static inline int security_file_fcntl(structfile *file, unsigned int cmd, return 0; } -static inline void security_file_set_fowner(struct file *file) -{ - return; -} - static inline int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig)diff --git a/security/security.c b/security/security.c index 8cee5b6c6e6d..a53d8d7fe815 100644 --- a/security/security.c +++ b/security/security.c@@ -2924,20 +2924,6 @@ int security_file_fcntl(struct file *file,unsigned int cmd, unsigned long arg) return call_int_hook(file_fcntl, file, cmd, arg); } -/** - * security_file_set_fowner() - Set the file owner info in the LSM blob - * @file: the file - * - * Save owner security information (typically from current->security) in - * file->f_security for later use by the send_sigiotask hook. - * - * Return: Returns 0 on success. - */ -void security_file_set_fowner(struct file *file) -{ - call_void_hook(file_set_fowner, file); -} - /** * security_file_send_sigiotask() - Check if sending SIGIO/SIGURG is allowed * @tsk: target taskdiff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 55c78c318ccd..37675d280837 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c@@ -3649,7 +3649,6 @@ static int selinux_file_alloc_security(struct file *file) u32 sid = current_sid(); fsec->sid = sid; - fsec->fown_sid = sid; return 0; }@@ -3923,24 +3922,16 @@ static int selinux_file_fcntl(struct file*file, unsigned int cmd, return err; } -static void selinux_file_set_fowner(struct file *file) -{ - struct file_security_struct *fsec; - - fsec = selinux_file(file); - fsec->fown_sid = current_sid(); -} - static int selinux_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { - struct file *file; + /* struct fown_struct is never outside the context of a struct file */ + struct file *file = container_of(fown, struct file, f_owner); u32 sid = task_sid_obj(tsk); u32 perm; struct file_security_struct *fsec; - - /* struct fown_struct is never outside the context of a struct file */ - file = container_of(fown, struct file, f_owner); + struct cred_struct *fown_cred = rcu_dereference(fown->owner_cred); + u32 fown_sid = cred_sid(fown_cred ?: file->f_cred); fsec = selinux_file(file);@@ -3949,7 +3940,7 @@ static int selinux_file_send_sigiotask(structtask_struct *tsk, else perm = signal_to_av(signum); - return avc_has_perm(fsec->fown_sid, sid, + return avc_has_perm(fown_sid, sid, SECCLASS_PROCESS, perm, NULL); }diff --git a/security/selinux/include/objsec.hb/security/selinux/include/objsec.h index dea1d6f3ed2d..d55b7f8d3a3d 100644--- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h@@ -56,7 +56,6 @@ struct inode_security_struct { struct file_security_struct { u32 sid; /* SID of open file description */ - u32 fown_sid; /* SID of file owner (for SIGIO) */ u32 isid; /* SID of inode at the time of file open */ u32 pseqno; /* Policy seqno at the time of file open */ };diff --git a/security/smack/smack.h b/security/smack/smack.h index 041688e5a77a..06bac00cc796 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h@@ -328,12 +328,6 @@ static inline struct task_smack *smack_cred(conststruct cred *cred) return cred->security + smack_blob_sizes.lbs_cred; } -static inline struct smack_known **smack_file(const struct file *file) -{ - return (struct smack_known **)(file->f_security + - smack_blob_sizes.lbs_file); -} - static inline struct inode_smack *smack_inode(const struct inode *inode) { return inode->i_security + smack_blob_sizes.lbs_inode;diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 4164699cd4f6..02caa8b9d456 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c@@ -1675,26 +1675,6 @@ static void smack_inode_getsecid(struct inode*inode, u32 *secid) * label changing that SELinux does. */ -/** - * smack_file_alloc_security - assign a file security blob - * @file: the object - * - * The security blob for a file is a pointer to the master - * label list, so no allocation is done. - * - * f_security is the owner security information. It - * isn't used on file access checks, it's for send_sigio. - * - * Returns 0 - */ -static int smack_file_alloc_security(struct file *file) -{ - struct smack_known **blob = smack_file(file); - - *blob = smk_of_current(); - return 0; -} - /** * smack_file_ioctl - Smack check on ioctls * @file: the object@@ -1913,18 +1893,6 @@ static int smack_mmap_file(struct file *file, return rc; } -/** - * smack_file_set_fowner - set the file security blob value - * @file: object in question - * - */ -static void smack_file_set_fowner(struct file *file) -{ - struct smack_known **blob = smack_file(file); - - *blob = smk_of_current(); -} - /** * smack_file_send_sigiotask - Smack on sigio * @tsk: The target task@@ -1946,6 +1914,7 @@ static int smack_file_send_sigiotask(structtask_struct *tsk, struct file *file; int rc; struct smk_audit_info ad; + struct cred_struct *fown_cred = rcu_dereference(fown->owner_cred); /* * struct fown_struct is never outside the context of a struct file@@ -1953,8 +1922,7 @@ static int smack_file_send_sigiotask(structtask_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - blob = smack_file(file); - skp = *blob; + skp = smk_of_task(fown_cred ?: file->f_cred); rc = smk_access(skp, tkp, MAY_DELIVER, NULL); rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);@@ -5045,7 +5013,6 @@ static int smack_uring_cmd(struct io_uring_cmd *ioucmd) struct lsm_blob_sizes smack_blob_sizes __ro_after_init = { .lbs_cred = sizeof(struct task_smack), - .lbs_file = sizeof(struct smack_known *), .lbs_inode = sizeof(struct inode_smack), .lbs_ipc = sizeof(struct smack_known *), .lbs_msg_msg = sizeof(struct smack_known *),@@ -5104,7 +5071,6 @@ static struct security_hook_list smack_hooks[]__ro_after_init = { LSM_HOOK_INIT(file_fcntl, smack_file_fcntl), LSM_HOOK_INIT(mmap_file, smack_mmap_file), LSM_HOOK_INIT(mmap_addr, cap_mmap_addr), - LSM_HOOK_INIT(file_set_fowner, smack_file_set_fowner), LSM_HOOK_INIT(file_send_sigiotask, smack_file_send_sigiotask), LSM_HOOK_INIT(file_receive, smack_file_receive),quoted
While I think it is okay if we want to consider relocating the security_file_set_fowner() within the F_SETOWN call path, I don't think we can remove it, even if we add additional LSM security blobs.