On 4/18/2023 2:49 PM, Paul Moore wrote: > On Tue, Apr 11, 2023 at 12:01 PM Casey Schaufler <casey@xxxxxxxxxxxxxxxx> wrote: >> Create a system call lsm_get_self_attr() to provide the security >> module maintained attributes of the current process. >> Create a system call lsm_set_self_attr() to set a security >> module maintained attribute of the current process. >> Historically these attributes have been exposed to user space via >> entries in procfs under /proc/self/attr. >> >> The attribute value is provided in a lsm_ctx structure. The structure >> identifies the size of the attribute, and the attribute value. The format >> of the attribute value is defined by the security module. A flags field >> is included for LSM specific information. It is currently unused and must >> be 0. The total size of the data, including the lsm_ctx structure and any >> padding, is maintained as well. >> >> struct lsm_ctx { >> __u64 id; >> __u64 flags; >> __u64 len; >> __u64 ctx_len; >> __u8 ctx[]; >> }; >> >> Two new LSM hooks are used to interface with the LSMs. >> security_getselfattr() collects the lsm_ctx values from the >> LSMs that support the hook, accounting for space requirements. >> security_setselfattr() identifies which LSM the attribute is >> intended for and passes it along. >> >> Signed-off-by: Casey Schaufler <casey@xxxxxxxxxxxxxxxx> >> --- >> Documentation/userspace-api/lsm.rst | 15 +++++ >> include/linux/lsm_hook_defs.h | 4 ++ >> include/linux/lsm_hooks.h | 9 +++ >> include/linux/security.h | 19 ++++++ >> include/linux/syscalls.h | 5 ++ >> include/uapi/linux/lsm.h | 30 +++++++++ >> kernel/sys_ni.c | 4 ++ >> security/Makefile | 1 + >> security/lsm_syscalls.c | 55 ++++++++++++++++ >> security/security.c | 98 +++++++++++++++++++++++++++++ >> 10 files changed, 240 insertions(+) >> create mode 100644 security/lsm_syscalls.c > .. > >> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h >> index 33a0ee3bcb2e..97487d66dca9 100644 >> --- a/include/linux/syscalls.h >> +++ b/include/linux/syscalls.h >> @@ -71,6 +71,7 @@ struct clone_args; >> struct open_how; >> struct mount_attr; >> struct landlock_ruleset_attr; >> +struct lsm_ctx; >> enum landlock_rule_type; >> >> #include <linux/types.h> >> @@ -1058,6 +1059,10 @@ asmlinkage long sys_memfd_secret(unsigned int flags); >> asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long len, >> unsigned long home_node, >> unsigned long flags); >> +asmlinkage long sys_lsm_get_self_attr(unsigned int attr, struct lsm_ctx *ctx, >> + size_t *size, __u32 flags); >> +asmlinkage long sys_lsm_set_self_attr(unsigned int attr, struct lsm_ctx *ctx, >> + __u32 flags); > As pointed out by the kernel test robot, the above declaration is > missing the @size parameter. Yup. >> diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h >> index f27c9a9cc376..b10dfab8a4d9 100644 >> --- a/include/uapi/linux/lsm.h >> +++ b/include/uapi/linux/lsm.h >> @@ -9,6 +9,36 @@ >> #ifndef _UAPI_LINUX_LSM_H >> #define _UAPI_LINUX_LSM_H >> >> +#include <linux/types.h> >> +#include <linux/unistd.h> >> + >> +/** >> + * struct lsm_ctx - LSM context information >> + * @id: the LSM id number, see LSM_ID_XXX >> + * @flags: LSM specific flags >> + * @len: length of the lsm_ctx struct, @ctx and any other data or padding >> + * @ctx_len: the size of @ctx >> + * @ctx: the LSM context value >> + * >> + * The @len field MUST be equal to the size of the lsm_ctx struct >> + * plus any additional padding and/or data placed after @ctx. >> + * >> + * In all cases @ctx_len MUST be equal to the length of @ctx. >> + * If @ctx is a string value it should be nul terminated with >> + * @ctx_len equal to `strlen(@ctx) + 1`. Binary values are >> + * supported. >> + * >> + * The @flags and @ctx fields SHOULD only be interpreted by the >> + * LSM specified by @id; they MUST be set to zero/0 when not used. >> + */ >> +struct lsm_ctx { >> + __u64 id; >> + __u64 flags; >> + __u64 len; >> + __u64 ctx_len; >> + __u8 ctx[]; >> +}; > Sorry, style nitpick since this needs to be respun anyway for the > syscalls.h fix at the very least ... I *really* dislike when variable > declarations, and field declarations in the case composite variables, > are aligned with the vertically neighboring declarations; just use a > single space please: I'll do it, but the tab after type has been accepted for forever. As an aside, I pulled out my 1978 K&R to prove my point and discovered that it isn't consistent regarding this style. > struct lsm_ctx { > <tab>__u64 id; > <tab>__u64 flags; > <tab>__u64 len; > <tab>__u64 ctx_len; > <tab>__u8 ctx[]; > } > >> diff --git a/security/security.c b/security/security.c >> index 38ca0e646cac..bfe9a1a426b2 100644 >> --- a/security/security.c >> +++ b/security/security.c >> @@ -2167,6 +2167,104 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) >> } >> EXPORT_SYMBOL(security_d_instantiate); >> >> +/** >> + * security_getselfattr - Read an LSM attribute of the current process. >> + * @attr: which attribute to return >> + * @ctx: the user-space destination for the information, or NULL >> + * @size: the size of space available to receive the data >> + * @flags: reserved for future use, must be 0 >> + * >> + * Returns the number of attributes found on success, negative value >> + * on error. @size is reset to the total size of the data. >> + * If @size is insufficient to contain the data -E2BIG is returned. >> + */ >> +int security_getselfattr(unsigned int __user attr, struct lsm_ctx __user *ctx, >> + size_t __user *size, u32 __user flags) >> +{ >> + struct security_hook_list *hp; >> + u8 __user *base = (u8 __user *)ctx; >> + size_t total = 0; >> + size_t entrysize; >> + size_t left; >> + bool toobig = false; >> + int count = 0; >> + int rc; >> + >> + if (attr == 0) >> + return -EINVAL; >> + if (flags) >> + return -EINVAL; > I like Mickaël's idea of supporting a flag (LSM_FLG_SINGLE?) which > allows one to request a single LSM's attribute. I don't, but I'll incorporate it. > I don't think that > support has to be part of this initial patchset, but I do think it > would be good to have it in the same PR that goes up to Linus during > the merge window. As this patch set is intended to be what goes to Linus (isn't it?) I'll put it in. > If that's not something you want to do, let me know > and I'll write up a quick patch on top of this patchset. > >> + if (size == NULL) >> + return -EINVAL; >> + if (get_user(left, size)) >> + return -EFAULT; >> + >> + hlist_for_each_entry(hp, &security_hook_heads.getselfattr, list) { >> + entrysize = left; >> + if (base) >> + ctx = (struct lsm_ctx __user *)(base + total); >> + rc = hp->hook.getselfattr(attr, ctx, &entrysize, flags); >> + if (rc == -EOPNOTSUPP) { >> + rc = 0; >> + continue; >> + } >> + if (rc == -E2BIG) { >> + toobig = true; >> + left = 0; >> + break; > It just occurred to me while reading this that we stop calculating the > potential lsm_ctx size after we hit the first LSM where we go beyond > the size given, `rc == -E2BIG`. I realize that the required size may > change between calls to lsm_get_self_attr(2), but it seems like we > should at least run through all the LSMs and total up the required > buffer size, no? The "break" should be a "continue". Artifact of an earlier version that had a switch statement. Fix forthcoming. > > I may have missed something in the snippet below, but I think the code > change should be pretty minor: > > if (rc == -EOPNOTSUPP) { > rc = 0; > continue; > } else if (rc == -E2BIG) { > toobig = true; > base = NULL; > } else if (rc < 0) { > return rc; > } > left = (toobig ? 0 : left - entrysize); > total += entrysize; > > count += rc; > >> + } >> + if (rc < 0) >> + return rc; >> + >> + left -= entrysize; >> + total += entrysize; >> + count += rc; >> + } >> + if (count == 0) >> + return LSM_RET_DEFAULT(getselfattr); >> + if (put_user(total, size)) >> + return -EFAULT; >> + if (toobig) >> + return -E2BIG; >> + return count; >> +} > -- > paul-moore.com