Skip to content

Instantly share code, notes, and snippets.

@jmgao
Created September 14, 2021 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmgao/9c17bcc93d65472d9dd2a17362ed4075 to your computer and use it in GitHub Desktop.
Save jmgao/9c17bcc93d65472d9dd2a17362ed4075 to your computer and use it in GitHub Desktop.
From 2c43231ecfd0c0fbb6850ebb6d65b0f62c29ba4a Mon Sep 17 00:00:00 2001
From: Josh Gao <jmgao@fb.com>
Date: Mon, 13 Sep 2021 17:16:17 -0700
Subject: [PATCH] selinux: optionally send signal on audit.
Allow processes to opt into receiving a signal upon audit failure, via
a new file at /proc/PID/selinux/audit_signal.
This is intended to be used primarily as a debugging facility, either
with a userspace signal handler/core dump, or interactively with
SIGSTOP. This is similar to the tracepoint that was added in <COMMIT>,
but with three advantages:
- not requiring a tracer running as root, which is unpalatable for
deployment across dogfood populations
- being synchronous, which allows for interactive debugging at the
point of failure and inspecting the values of things that might not
be captured in the tracepoint stack dump, like syscall arguments on
the heap
- having the full context of the process available for the unwinder,
for things such as unwinding through JIT-compiled stack frames
This functionality is guarded by a new access vector in process2.
Signed-off-by: Josh Gao <jmgao@fb.com>
---
fs/exec.c | 4 +
fs/proc/base.c | 37 +++++++++
include/linux/sched/signal.h | 3 +
include/linux/selinux.h | 4 +
kernel/fork.c | 4 +
security/selinux/Makefile | 2 +-
security/selinux/avc.c | 19 +++++
security/selinux/hooks.c | 24 ------
security/selinux/include/classmap.h | 3 +-
security/selinux/include/objsec.h | 23 ++++++
security/selinux/proc_selinux.c | 121 ++++++++++++++++++++++++++++
11 files changed, 218 insertions(+), 26 deletions(-)
create mode 100644 security/selinux/proc_selinux.c
diff --git a/fs/exec.c b/fs/exec.c
index aa9d20cbd689..760550bbe432 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1380,6 +1380,10 @@ void setup_new_exec(struct linux_binprm * bprm)
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
+
+#ifdef CONFIG_SECURITY_SELINUX
+ current->signal->selinux_audit_signal = 0;
+#endif
}
EXPORT_SYMBOL(setup_new_exec);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 0d617159511a..2d9684cacb37 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -96,6 +96,7 @@
#include <linux/posix-timers.h>
#include <linux/cpufreq_times.h>
#include <trace/events/oom.h>
+#include <linux/selinux.h>
#include "internal.h"
#include "fd.h"
@@ -3350,6 +3351,36 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
}
#endif /* CONFIG_LIVEPATCH */
+#ifdef CONFIG_SECURITY_SELINUX
+static const struct pid_entry selinux_dir_stuff[] = {
+ REG("audit_signal", 0644, proc_selinux_audit_signal_operations),
+};
+
+static int proc_selinux_dir_readdir(struct file *file, struct dir_context *ctx)
+{
+ return proc_pident_readdir(file, ctx, selinux_dir_stuff,
+ ARRAY_SIZE(selinux_dir_stuff));
+}
+
+static const struct file_operations proc_selinux_dir_operations = {
+ .read = generic_read_dir,
+ .iterate_shared = proc_selinux_dir_readdir,
+ .llseek = generic_file_llseek,
+};
+
+static struct dentry *proc_selinux_dir_lookup(struct inode *dir,
+ struct dentry *dentry,
+ unsigned int flags)
+{
+ return proc_pident_lookup(dir, dentry, selinux_dir_stuff,
+ ARRAY_SIZE(selinux_dir_stuff));
+}
+
+static const struct inode_operations proc_selinux_dir_inode_operations = {
+ .lookup = proc_selinux_dir_lookup,
+};
+#endif
+
/*
* Thread groups
*/
@@ -3472,6 +3503,9 @@ static const struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_CPU_FREQ_TIMES
ONE("time_in_state", 0444, proc_time_in_state_show),
#endif
+#ifdef CONFIG_SECURITY_SELINUX
+ DIR("selinux", 0555, proc_selinux_dir_inode_operations, proc_selinux_dir_operations),
+#endif
};
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3857,6 +3891,9 @@ static const struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_CPU_FREQ_TIMES
ONE("time_in_state", 0444, proc_time_in_state_show),
#endif
+#ifdef CONFIG_SECURITY_SELINUX
+ DIR("selinux", 0555, proc_selinux_dir_inode_operations, proc_selinux_dir_operations),
+#endif
};
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 660d78c9af6c..85a6f70aea0e 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -217,6 +217,9 @@ struct signal_struct {
unsigned audit_tty;
struct tty_audit_buf *tty_audit_buf;
#endif
+#ifdef CONFIG_SECURITY_SELINUX
+ int selinux_audit_signal;
+#endif
/*
* Thread is the potential origin of an oom condition; kill first on
diff --git a/include/linux/selinux.h b/include/linux/selinux.h
index 44f459612690..c5c81544622c 100644
--- a/include/linux/selinux.h
+++ b/include/linux/selinux.h
@@ -24,12 +24,16 @@ struct kern_ipc_perm;
* selinux_is_enabled - is SELinux enabled?
*/
bool selinux_is_enabled(void);
+
+extern const struct file_operations proc_selinux_audit_signal_operations;
+
#else
static inline bool selinux_is_enabled(void)
{
return false;
}
+
#endif /* CONFIG_SECURITY_SELINUX */
#endif /* _LINUX_SELINUX_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 55ec95af8eaf..bbee1c665192 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1560,6 +1560,10 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
sig->oom_score_adj = current->signal->oom_score_adj;
sig->oom_score_adj_min = current->signal->oom_score_adj_min;
+#ifdef CONFIG_SECURITY_SELINUX
+ sig->selinux_audit_signal = current->signal->selinux_audit_signal;
+#endif
+
mutex_init(&sig->cred_guard_mutex);
return 0;
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index ef0f184119b3..61b1dd20f1f7 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
- netnode.o netport.o exports.o\
+ netnode.o netport.o proc_selinux.o exports.o\
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 3d1cff23a458..fab0da37974d 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -800,6 +800,25 @@ noinline int slow_avc_audit(struct selinux_state *state,
a->selinux_audit_data = &sad;
common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback);
+
+ if (denied) {
+ int audit_signal = 0;
+ unsigned long flags;
+ siginfo_t info;
+
+ if (lock_task_sighand(current, &flags)) {
+ audit_signal = current->signal->selinux_audit_signal;
+ unlock_task_sighand(current, &flags);
+ }
+
+ if (unlikely(audit_signal)) {
+ memset(&info, 0, sizeof(info));
+ info.si_signo = audit_signal;
+ info.si_code = SI_KERNEL;
+ send_sig_info(audit_signal, &info, current);
+ }
+ }
+
return 0;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ac57e125e615..5145ca012cab 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -221,30 +221,6 @@ static void cred_init_security(void)
cred->security = tsec;
}
-/*
- * get the security ID of a set of credentials
- */
-static inline u32 cred_sid(const struct cred *cred)
-{
- const struct task_security_struct *tsec;
-
- tsec = cred->security;
- return tsec->sid;
-}
-
-/*
- * get the objective security ID of a task
- */
-static inline u32 task_sid(const struct task_struct *task)
-{
- u32 sid;
-
- rcu_read_lock();
- sid = cred_sid(__task_cred(task));
- rcu_read_unlock();
- return sid;
-}
-
/* Allocate and free functions for each kind of security blob. */
static int inode_alloc_security(struct inode *inode)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 201f7e588a29..e687c61ab3fb 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -51,7 +51,8 @@ struct security_class_mapping secclass_map[] = {
"execmem", "execstack", "execheap", "setkeycreate",
"setsockcreate", "getrlimit", NULL } },
{ "process2",
- { "nnp_transition", "nosuid_transition", NULL } },
+ { "nnp_transition", "nosuid_transition", "getauditsignal",
+ "setauditsignal", NULL } },
{ "system",
{ "ipc_info", "syslog_read", "syslog_mod",
"syslog_console", "module_request", "module_load", NULL } },
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 4b0da5f903f4..3d55f590f621 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -47,6 +47,29 @@ static inline u32 current_sid(void)
return tsec->sid;
}
+/*
+ * get the security ID of a set of credentials
+ */
+static inline u32 cred_sid(const struct cred *cred)
+{
+ const struct task_security_struct *tsec = cred->security;
+
+ return tsec->sid;
+}
+
+/*
+ * get the objective security ID of a task
+ */
+static inline u32 task_sid(const struct task_struct *task)
+{
+ u32 sid;
+
+ rcu_read_lock();
+ sid = cred_sid(__task_cred(task));
+ rcu_read_unlock();
+ return sid;
+}
+
enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* initialized */
diff --git a/security/selinux/proc_selinux.c b/security/selinux/proc_selinux.c
new file mode 100644
index 000000000000..7c654ea1ccac
--- /dev/null
+++ b/security/selinux/proc_selinux.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implementation of /proc/$PID/selinux/.
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/uaccess.h>
+
+#include "../fs/proc/internal.h" /* only for get_proc_task() in ->open() */
+
+#include <linux/selinux.h>
+#include "avc.h"
+#include "security.h"
+#include "objsec.h"
+
+static ssize_t selinux_audit_signal_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF];
+ int audit_signal;
+ int err = -ESRCH;
+ unsigned long flags;
+ size_t len;
+
+ task = get_proc_task(file_inode(file));
+ if (!task)
+ return -ESRCH;
+
+ err = avc_has_perm(&selinux_state, current_sid(), task_sid(task),
+ SECCLASS_PROCESS, PROCESS2__GETAUDITSIGNAL, NULL);
+ if (err)
+ goto out;
+
+ if (!lock_task_sighand(task, &flags)) {
+ err = -ESRCH;
+ goto out;
+ }
+
+ audit_signal = task->signal->selinux_audit_signal;
+ unlock_task_sighand(task, &flags);
+
+ len = snprintf(buffer, sizeof(buffer), "%d\n", audit_signal);
+ err = simple_read_from_buffer(buf, count, ppos, buffer, len);
+
+out:
+ put_task_struct(task);
+ return err;
+}
+
+static ssize_t __selinux_audit_signal_set(struct task_struct *task, int signal)
+{
+ int err;
+ unsigned long flags;
+
+ err = avc_has_perm(&selinux_state, current_sid(), task_sid(task),
+ SECCLASS_PROCESS, PROCESS2__SETAUDITSIGNAL, NULL);
+ if (err)
+ goto out;
+
+ if (!lock_task_sighand(task, &flags)) {
+ err = -ESRCH;
+ goto out;
+ }
+
+ task->signal->selinux_audit_signal = signal;
+ unlock_task_sighand(task, &flags);
+
+out:
+ return err;
+}
+
+static ssize_t selinux_audit_signal_write(struct file *file,
+ const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF];
+ int audit_signal;
+ int err;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err = kstrtoint(strstrip(buffer), 0, &audit_signal);
+ if (err)
+ goto out;
+ if (!valid_signal(audit_signal)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ task = get_proc_task(file_inode(file));
+ if (!task) {
+ err = -ESRCH;
+ goto out;
+ }
+
+ err = __selinux_audit_signal_set(task, audit_signal);
+ put_task_struct(task);
+
+out:
+ return err < 0 ? err : count;
+}
+
+const struct file_operations proc_selinux_audit_signal_operations = {
+ .read = selinux_audit_signal_read,
+ .write = selinux_audit_signal_write,
+ .llseek = default_llseek,
+};
--
2.30.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment