diff options
| -rw-r--r-- | include/linux/memfd.h | 2 | ||||
| -rw-r--r-- | mm/memfd.c | 14 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 26 | ||||
| -rw-r--r-- | security/selinux/include/classmap.h | 2 | ||||
| -rw-r--r-- | security/selinux/include/policycap.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/policycap_names.h | 1 | ||||
| -rw-r--r-- | security/selinux/include/security.h | 5 |
7 files changed, 44 insertions, 7 deletions
diff --git a/include/linux/memfd.h b/include/linux/memfd.h index 6f606d9573c3..cc74de3dbcfe 100644 --- a/include/linux/memfd.h +++ b/include/linux/memfd.h @@ -4,6 +4,8 @@ #include <linux/file.h> +#define MEMFD_ANON_NAME "[memfd]" + #ifdef CONFIG_MEMFD_CREATE extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg); struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx); diff --git a/mm/memfd.c b/mm/memfd.c index 1d109c1acf21..a61acbe5ded3 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -433,6 +433,8 @@ static struct file *alloc_file(const char *name, unsigned int flags) { unsigned int *file_seals; struct file *file; + struct inode *inode; + int err = 0; if (flags & MFD_HUGETLB) { file = hugetlb_file_setup(name, 0, VM_NORESERVE, @@ -444,12 +446,20 @@ static struct file *alloc_file(const char *name, unsigned int flags) } if (IS_ERR(file)) return file; + + inode = file_inode(file); + err = security_inode_init_security_anon(inode, + &QSTR(MEMFD_ANON_NAME), NULL); + if (err) { + fput(file); + file = ERR_PTR(err); + return file; + } + file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; file->f_flags |= O_LARGEFILE; if (flags & MFD_NOEXEC_SEAL) { - struct inode *inode = file_inode(file); - inode->i_mode &= ~0111; file_seals = memfd_file_seals_ptr(file); if (file_seals) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index dfc22da42f30..a22b1920242f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -93,6 +93,7 @@ #include <linux/fanotify.h> #include <linux/io_uring/cmd.h> #include <uapi/linux/lsm.h> +#include <linux/memfd.h> #include "avc.h" #include "objsec.h" @@ -2319,6 +2320,10 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm) new_tsec = selinux_cred(bprm->cred); isec = inode_security(inode); + if (WARN_ON(isec->sclass != SECCLASS_FILE && + isec->sclass != SECCLASS_MEMFD_FILE)) + return -EACCES; + /* Default to the current task SID. */ new_tsec->sid = old_tsec->sid; new_tsec->osid = old_tsec->sid; @@ -2371,8 +2376,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm) ad.u.file = bprm->file; if (new_tsec->sid == old_tsec->sid) { - rc = avc_has_perm(old_tsec->sid, isec->sid, - SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); + rc = avc_has_perm(old_tsec->sid, isec->sid, isec->sclass, + FILE__EXECUTE_NO_TRANS, &ad); if (rc) return rc; } else { @@ -2382,8 +2387,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm) if (rc) return rc; - rc = avc_has_perm(new_tsec->sid, isec->sid, - SECCLASS_FILE, FILE__ENTRYPOINT, &ad); + rc = avc_has_perm(new_tsec->sid, isec->sid, isec->sclass, + FILE__ENTRYPOINT, &ad); if (rc) return rc; @@ -2978,10 +2983,18 @@ static int selinux_inode_init_security_anon(struct inode *inode, struct common_audit_data ad; struct inode_security_struct *isec; int rc; + bool is_memfd = false; if (unlikely(!selinux_initialized())) return 0; + if (name != NULL && name->name != NULL && + !strcmp(name->name, MEMFD_ANON_NAME)) { + if (!selinux_policycap_memfd_class()) + return 0; + is_memfd = true; + } + isec = selinux_inode(inode); /* @@ -3001,7 +3014,10 @@ static int selinux_inode_init_security_anon(struct inode *inode, isec->sclass = context_isec->sclass; isec->sid = context_isec->sid; } else { - isec->sclass = SECCLASS_ANON_INODE; + if (is_memfd) + isec->sclass = SECCLASS_MEMFD_FILE; + else + isec->sclass = SECCLASS_ANON_INODE; rc = security_transition_sid( sid, sid, isec->sclass, name, &isec->sid); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 5665aa5e7853..3ec85142771f 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = { { "anon_inode", { COMMON_FILE_PERMS, NULL } }, { "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } }, { "user_namespace", { "create", NULL } }, + { "memfd_file", + { COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } }, /* last one */ { NULL, {} } }; diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h index 135a969f873c..231d02227e59 100644 --- a/security/selinux/include/policycap.h +++ b/security/selinux/include/policycap.h @@ -18,6 +18,7 @@ enum { POLICYDB_CAP_NETIF_WILDCARD, POLICYDB_CAP_GENFS_SECLABEL_WILDCARD, POLICYDB_CAP_FUNCTIONFS_SECLABEL, + POLICYDB_CAP_MEMFD_CLASS, __POLICYDB_CAP_MAX }; #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1) diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h index ff8882887651..454dab37bda3 100644 --- a/security/selinux/include/policycap_names.h +++ b/security/selinux/include/policycap_names.h @@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = { "netif_wildcard", "genfs_seclabel_wildcard", "functionfs_seclabel", + "memfd_class", }; /* clang-format on */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 0f954a40d3fc..5d1dad8058b1 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void) selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]); } +static inline bool selinux_policycap_memfd_class(void) +{ + return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]); +} + struct selinux_policy_convert_data; struct selinux_load_state { |