summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorCasey Schaufler <casey@schaufler-ca.com>2023-09-12 13:56:49 -0700
committerPaul Moore <paul@paul-moore.com>2023-11-12 22:54:42 -0500
commita04a1198088a1378d0389c250cc684f649bcc91e (patch)
treeff85800f1e355d8db266297206221353bdac6245 /security
parent267c068e5f8b81b68cc4247c94dbba90a21a634e (diff)
LSM: syscalls for current process attributes
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@schaufler-ca.com> Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Serge Hallyn <serge@hallyn.com> Reviewed-by: John Johansen <john.johansen@canonical.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'security')
-rw-r--r--security/Makefile1
-rw-r--r--security/lsm_syscalls.c57
-rw-r--r--security/security.c152
3 files changed, 210 insertions, 0 deletions
diff --git a/security/Makefile b/security/Makefile
index 18121f8f85cd..59f238490665 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_KEYS) += keys/
# always enable default capabilities
obj-y += commoncap.o
+obj-$(CONFIG_SECURITY) += lsm_syscalls.o
obj-$(CONFIG_MMU) += min_addr.o
# Object file lists
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
new file mode 100644
index 000000000000..226ae80d9683
--- /dev/null
+++ b/security/lsm_syscalls.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * System calls implementing the Linux Security Module API.
+ *
+ * Copyright (C) 2022 Casey Schaufler <casey@schaufler-ca.com>
+ * Copyright (C) 2022 Intel Corporation
+ */
+
+#include <asm/current.h>
+#include <linux/compiler_types.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/security.h>
+#include <linux/stddef.h>
+#include <linux/syscalls.h>
+#include <linux/types.h>
+#include <linux/lsm_hooks.h>
+#include <uapi/linux/lsm.h>
+
+/**
+ * sys_lsm_set_self_attr - Set current task's security module attribute
+ * @attr: which attribute to set
+ * @ctx: the LSM contexts
+ * @size: size of @ctx
+ * @flags: reserved for future use
+ *
+ * Sets the calling task's LSM context. On success this function
+ * returns 0. If the attribute specified cannot be set a negative
+ * value indicating the reason for the error is returned.
+ */
+SYSCALL_DEFINE4(lsm_set_self_attr, unsigned int, attr, struct lsm_ctx __user *,
+ ctx, size_t, size, u32, flags)
+{
+ return security_setselfattr(attr, ctx, size, flags);
+}
+
+/**
+ * sys_lsm_get_self_attr - Return current task's security module attributes
+ * @attr: which attribute to return
+ * @ctx: the user-space destination for the information, or NULL
+ * @size: pointer to the size of space available to receive the data
+ * @flags: special handling options. LSM_FLAG_SINGLE indicates that only
+ * attributes associated with the LSM identified in the passed @ctx be
+ * reported.
+ *
+ * Returns the calling task's LSM contexts. On success this
+ * function returns the number of @ctx array elements. This value
+ * may be zero if there are no LSM contexts assigned. If @size is
+ * insufficient to contain the return data -E2BIG is returned and
+ * @size is set to the minimum required size. In all other cases
+ * a negative value indicating the error is returned.
+ */
+SYSCALL_DEFINE4(lsm_get_self_attr, unsigned int, attr, struct lsm_ctx __user *,
+ ctx, size_t __user *, size, u32, flags)
+{
+ return security_getselfattr(attr, ctx, size, flags);
+}
diff --git a/security/security.c b/security/security.c
index c66f9faefa40..9757d009113f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3837,6 +3837,158 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode)
}
EXPORT_SYMBOL(security_d_instantiate);
+/*
+ * Please keep this in sync with it's counterpart in security/lsm_syscalls.c
+ */
+
+/**
+ * security_getselfattr - Read an LSM attribute of the current process.
+ * @attr: which attribute to return
+ * @uctx: the user-space destination for the information, or NULL
+ * @size: pointer to the size of space available to receive the data
+ * @flags: special handling options. LSM_FLAG_SINGLE indicates that only
+ * attributes associated with the LSM identified in the passed @ctx be
+ * reported.
+ *
+ * A NULL value for @uctx can be used to get both the number of attributes
+ * and the size of the data.
+ *
+ * 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 attr, struct lsm_ctx __user *uctx,
+ size_t __user *size, u32 flags)
+{
+ struct security_hook_list *hp;
+ struct lsm_ctx lctx = { .id = LSM_ID_UNDEF, };
+ u8 __user *base = (u8 __user *)uctx;
+ size_t total = 0;
+ size_t entrysize;
+ size_t left;
+ bool toobig = false;
+ bool single = false;
+ int count = 0;
+ int rc;
+
+ if (attr == LSM_ATTR_UNDEF)
+ return -EINVAL;
+ if (size == NULL)
+ return -EINVAL;
+ if (get_user(left, size))
+ return -EFAULT;
+
+ if (flags) {
+ /*
+ * Only flag supported is LSM_FLAG_SINGLE
+ */
+ if (flags != LSM_FLAG_SINGLE)
+ return -EINVAL;
+ if (uctx && copy_from_user(&lctx, uctx, sizeof(lctx)))
+ return -EFAULT;
+ /*
+ * If the LSM ID isn't specified it is an error.
+ */
+ if (lctx.id == LSM_ID_UNDEF)
+ return -EINVAL;
+ single = true;
+ }
+
+ /*
+ * In the usual case gather all the data from the LSMs.
+ * In the single case only get the data from the LSM specified.
+ */
+ hlist_for_each_entry(hp, &security_hook_heads.getselfattr, list) {
+ if (single && lctx.id != hp->lsmid->id)
+ continue;
+ entrysize = left;
+ if (base)
+ uctx = (struct lsm_ctx __user *)(base + total);
+ rc = hp->hook.getselfattr(attr, uctx, &entrysize, flags);
+ if (rc == -EOPNOTSUPP) {
+ rc = 0;
+ continue;
+ }
+ if (rc == -E2BIG) {
+ toobig = true;
+ left = 0;
+ } else if (rc < 0)
+ return rc;
+ else
+ left -= entrysize;
+
+ total += entrysize;
+ count += rc;
+ if (single)
+ break;
+ }
+ if (put_user(total, size))
+ return -EFAULT;
+ if (toobig)
+ return -E2BIG;
+ if (count == 0)
+ return LSM_RET_DEFAULT(getselfattr);
+ return count;
+}
+
+/*
+ * Please keep this in sync with it's counterpart in security/lsm_syscalls.c
+ */
+
+/**
+ * security_setselfattr - Set an LSM attribute on the current process.
+ * @attr: which attribute to set
+ * @uctx: the user-space source for the information
+ * @size: the size of the data
+ * @flags: reserved for future use, must be 0
+ *
+ * Set an LSM attribute for the current process. The LSM, attribute
+ * and new value are included in @uctx.
+ *
+ * Returns 0 on success, -EINVAL if the input is inconsistent, -EFAULT
+ * if the user buffer is inaccessible, E2BIG if size is too big, or an
+ * LSM specific failure.
+ */
+int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
+ size_t size, u32 flags)
+{
+ struct security_hook_list *hp;
+ struct lsm_ctx *lctx;
+ int rc = LSM_RET_DEFAULT(setselfattr);
+
+ if (flags)
+ return -EINVAL;
+ if (size < sizeof(*lctx))
+ return -EINVAL;
+ if (size > PAGE_SIZE)
+ return -E2BIG;
+
+ lctx = kmalloc(size, GFP_KERNEL);
+ if (lctx == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(lctx, uctx, size)) {
+ rc = -EFAULT;
+ goto free_out;
+ }
+
+ if (size < lctx->len || size < lctx->ctx_len + sizeof(*lctx) ||
+ lctx->len < lctx->ctx_len + sizeof(*lctx)) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+
+ hlist_for_each_entry(hp, &security_hook_heads.setselfattr, list)
+ if ((hp->lsmid->id) == lctx->id) {
+ rc = hp->hook.setselfattr(attr, lctx, size, flags);
+ break;
+ }
+
+free_out:
+ kfree(lctx);
+ return rc;
+}
+
/**
* security_getprocattr() - Read an attribute for a task
* @p: the task