On 27/09/2022 21:54, Casey Schaufler wrote:
Create two new prctl() options PR_LSM_ATTR_SET and PR_LSM_ATTR_GET
which change and report the Interface LSM respectively.
The LSM ID number of an active LSM that supplies hooks for
human readable data may be passed in the arg2 value with the
PR_LSM_ATTR_SET option. The PR_LSM_ATT_GET option returns the
LSM ID currently in use. At this point there can only be one LSM
capable of display active. A helper function lsm_task_ilsm() is
provided to get the interface lsm slot for a task_struct.
Security modules that wish to restrict this action may provide
a task_prctl hook to do so. Each such security module is
responsible for defining its policy.
AppArmor hook initially provided by John Johansen
<john.johansen(a)canonical.com>. SELinux hook initially provided by
Stephen Smalley <stephen.smalley.work(a)gmail.com>
Signed-off-by: Casey Schaufler <casey(a)schaufler-ca.com>
---
[...]
diff --git a/security/security.c b/security/security.c
index 80133d6e982c..43d2431dbda0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -28,6 +28,7 @@
#include <linux/backing-dev.h>
#include <linux/string.h>
#include <linux/msg.h>
+#include <linux/prctl.h>
#include <uapi/linux/lsm.h>
#include <net/flow.h>
#include <net/sock.h>
@@ -81,7 +82,16 @@ static struct kmem_cache *lsm_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
-static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
+
+/*
+ * The task blob includes the "interface_lsm" slot used for
+ * chosing which module presents contexts.
+ * Using a long to avoid potential alignment issues with
+ * module assigned task blobs.
+ */
+static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init = {
+ .lbs_task = sizeof(long),
+};
/* Boot-time LSM user choice */
static __initdata const char *chosen_lsm_order;
@@ -691,6 +701,8 @@ int lsm_inode_alloc(struct inode *inode)
*/
static int lsm_task_alloc(struct task_struct *task)
{
+ int *ilsm;
+
if (blob_sizes.lbs_task == 0) {
task->security = NULL;
return 0;
@@ -699,6 +711,15 @@ static int lsm_task_alloc(struct task_struct *task)
task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
if (task->security == NULL)
return -ENOMEM;
+
+ /*
+ * The start of the task blob contains the "interface" LSM slot number.
+ * Start with it set to the invalid slot number, indicating that the
+ * default first registered LSM be displayed.
+ */
+ ilsm = task->security;
+ *ilsm = LSMBLOB_INVALID;
+
return 0;
}
@@ -1765,14 +1786,26 @@ int security_file_open(struct file *file)
int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
{
+ int *oilsm = current->security;
+ int *nilsm;
int rc = lsm_task_alloc(task);
- if (rc)
+ if (unlikely(rc))
return rc;
+
rc = call_int_hook(task_alloc, 0, task, clone_flags);
- if (unlikely(rc))
+ if (unlikely(rc)) {
security_task_free(task);
- return rc;
+ return rc;
+ }
+
+ if (oilsm) {
+ nilsm = task->security;
+ if (nilsm)
+ *nilsm = *oilsm;
+ }
+
+ return 0;
}
void security_task_free(struct task_struct *task)
@@ -2031,10 +2064,15 @@ int security_task_kill(struct task_struct *p, struct
kernel_siginfo *info,
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
+ int *ilsm = current->security;
int thisrc;
+ int slot;
int rc = LSM_RET_DEFAULT(task_prctl);
struct security_hook_list *hp;
+ if (lsm_slot == 0)
+ return -EINVAL;
+
hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != LSM_RET_DEFAULT(task_prctl)) {
@@ -2043,6 +2081,25 @@ int security_task_prctl(int option, unsigned long arg2, unsigned
long arg3,
break;
}
}
+
+ switch (option) {
+ case PR_LSM_ATTR_SET:
+ if (rc && rc != LSM_RET_DEFAULT(task_prctl))
+ return rc;
+ for (slot = 0; slot < lsm_slot; slot++)
+ if (lsm_slotlist[slot]->id == arg2) {
This doesn't build if LSMBLOB_ENTRIES == 0
+ *ilsm = lsm_slotlist[slot]->slot;
+ return 0;
+ }
+ return -EINVAL;
+ case PR_LSM_ATTR_GET:
+ if (rc && rc != LSM_RET_DEFAULT(task_prctl))
+ return rc;
+ if (*ilsm != LSMBLOB_INVALID)
+ return lsm_slotlist[*ilsm]->id;
+ return lsm_slotlist[0]->id;
+ }
+
return rc;
}