[PATCH 2/2] audit: show (grand)parents information of an audit context

From: Daniel Walker
Date: Tue Feb 02 2021 - 16:30:28 EST


From: Phil Zhang <xuanyzha@xxxxxxxxx>

To ease the root cause analysis of SELinux AVCs, this new feature
traverses task structs to iteratively find all parent processes
starting with the denied process and ending at the kernel. Meanwhile,
it prints out the command lines and subject contexts of those parents.

This provides developers a clear view of how processes were spawned
and where transitions happened, without the need to reproduce the
issue and manually audit interesting events.

Example on bash over ssh:
$ runcon -u system_u -r system_r -t polaris_hm_t ls
...
type=PARENT msg=audit(1610548241.033:255): subj=root:unconfined_r:unconfined_t:s0-s0:c0.c1023 cmdline="-bash"
type=PARENT msg=audit(1610548241.033:255): subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 cmdline="sshd: root@pts/0"
type=PARENT msg=audit(1610548241.033:255): subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 cmdline="/tmp/sw/rp/0/0/rp_security/mount/usr/sbin/sshd
type=PARENT msg=audit(1610548241.033:255): subj=system_u:system_r:init_t:s0 cmdline="/init"
type=PARENT msg=audit(1610548241.033:255): subj=system_u:system_r:kernel_t:s0
...

Cc: xe-linux-external@xxxxxxxxx
Signed-off-by: Phil Zhang <xuanyzha@xxxxxxxxx>
Signed-off-by: Daniel Walker <danielwa@xxxxxxxxx>
---
include/uapi/linux/audit.h | 5 ++-
init/Kconfig | 7 +++++
kernel/audit.c | 3 +-
kernel/auditsc.c | 64 ++++++++++++++++++++++++++++++++++++++
4 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 7bea44b1c028..8f1a2880b198 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -119,6 +119,7 @@
#define AUDIT_BPF 1334 /* BPF subsystem */
#define AUDIT_EVENT_LISTENER 1335 /* Task joined multicast read socket */
#define AUDIT_UBACKTRACE 1336 /* User land backtrace */
+#define AUDIT_PARENT 1340 /* Process Parent emit event */

#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -485,7 +486,9 @@ struct audit_features {
#define AUDIT_FEATURE_ONLY_UNSET_LOGINUID 0
#define AUDIT_FEATURE_LOGINUID_IMMUTABLE 1
#define AUDIT_FEATURE_UBACKTRACE_CONTEXT 2
-#define AUDIT_LAST_FEATURE AUDIT_FEATURE_UBACKTRACE_CONTEXT
+#define AUDIT_FEATURE_LIST_PARENTS 3
+#define AUDIT_LAST_FEATURE AUDIT_FEATURE_LIST_PARENTS
+

#define audit_feature_valid(x) ((x) >= 0 && (x) <= AUDIT_LAST_FEATURE)
#define AUDIT_FEATURE_TO_MASK(x) (1 << ((x) & 31)) /* mask for __u32 */
diff --git a/init/Kconfig b/init/Kconfig
index 4327a8afb1f9..2dbc1c2aa833 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -446,6 +446,13 @@ config AUDIT_USER_BACKTRACE_SIZE
depends on AUDIT_USER_BACKTRACE
default 40

+config AUDIT_LIST_PARENTS
+ bool "Displaying parent processes in audit context messages"
+ def_bool n
+ depends on AUDITSYSCALL
+ help
+ Capture contexts and cmdlines of parent processes when auditing syscalls
+
source "kernel/irq/Kconfig"
source "kernel/time/Kconfig"
source "kernel/Kconfig.preempt"
diff --git a/kernel/audit.c b/kernel/audit.c
index 4608cddb4bb9..834adc462f47 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -165,10 +165,11 @@ static struct audit_features af = {.vers = AUDIT_FEATURE_VERSION,
.features = 0,
.lock = 0,};

-static char *audit_feature_names[3] = {
+static char *audit_feature_names[4] = {
"only_unset_loginuid",
"loginuid_immutable",
"ubacktrace_context",
+ "list_parents",
};

/**
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index d048b01345b8..c27e9f928bf1 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -96,6 +96,10 @@
/* number of audit rules */
int audit_n_rules;

+/* max length of per audit entry and max number of entries */
+#define MAX_PARENT_AUDIT_LEN 256
+#define MAX_PARENT_ENTRY_CNT 16
+
/* determines whether we collect data for signals sent */
int audit_signals;

@@ -1472,6 +1476,61 @@ static void audit_log_proctitle(void)
audit_log_end(ab);
}

+static void audit_log_parents(void)
+{
+ int res, len, item_cnt;
+ u32 sid;
+ char *buf;
+ char *ctx = NULL;
+ struct audit_context *context = audit_context();
+ struct audit_buffer *ab;
+ struct task_struct *task_iter;
+
+ if (!context || context->dummy)
+ return;
+
+ buf = kmalloc(MAX_PARENT_AUDIT_LEN, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ rcu_read_lock();
+ task_iter = rcu_dereference(current->real_parent);
+ for (item_cnt = 0; item_cnt < MAX_PARENT_ENTRY_CNT; ++item_cnt) {
+ ab = audit_log_start(context, GFP_ATOMIC, AUDIT_PARENT);
+ if (!ab)
+ break;
+
+ // get subject context
+ security_task_getsecid(task_iter, &sid);
+ if (sid) {
+ res = security_secid_to_secctx(sid, &ctx, &len);
+ if (!res) {
+ audit_log_format(ab, "subj=%-60s", ctx);
+ security_release_secctx(ctx, len);
+ }
+ }
+
+ // get cmdline
+ res = get_cmdline(task_iter, buf, MAX_PARENT_AUDIT_LEN);
+ if (res) {
+ res = audit_proctitle_rtrim(buf, res);
+ if (res) {
+ audit_log_format(ab, " proctitle=");
+ audit_log_n_untrustedstring(ab, buf, res);
+ }
+ }
+
+ audit_log_end(ab);
+
+ if (task_iter == task_iter->real_parent)
+ break;
+ task_iter = rcu_dereference(task_iter->real_parent);
+ }
+ rcu_read_unlock();
+
+ kfree(buf);
+}
+
#ifdef CONFIG_AUDIT_USER_BACKTRACE
static void audit_log_print_backtrace(struct audit_buffer *ab,
struct task_struct *tsk,
@@ -1682,6 +1741,11 @@ static void audit_log_exit(void)

audit_log_proctitle();

+#ifdef CONFIG_AUDIT_LIST_PARENTS
+ if (is_audit_feature_set(AUDIT_FEATURE_LIST_PARENTS))
+ audit_log_parents();
+#endif /* CONFIG_AUDIT_LIST_PARENTS */
+
#ifdef CONFIG_AUDIT_USER_BACKTRACE
if (is_audit_feature_set(AUDIT_FEATURE_UBACKTRACE_CONTEXT))
audit_log_ubacktrace(current, context);
--
2.17.1