[PATCH] RFC: selinux avc trace

From: peter enderborg
Date: Thu Jul 30 2020 - 10:31:23 EST


I did manage to rebase it but this is about my approach.

Compared to ThiÃbaud Weksteen patch this adds:

1 Filtering. Types goes to trace so we can put up a filter for contexts or type etc.

2 It tries also to cover non denies. And upon that you should be able to do coverage tools.
I think many systems have a lot more rules that what is needed, but there is good way
to find out what. A other way us to make a stat page for the rules, but this way connect to
userspace and can be used for test cases.

This code need a lot more work, but it shows how the filter should work (extra info is not right)
and there are memory leaks, extra debug info and nonsense variable etc.

From: Peter Enderborg <peter.enderborg@xxxxxxxx>
Date: Thu, 30 Jul 2020 14:44:53 +0200
Subject: [PATCH] RFC: selinux avc trace

This is not done yet. But it shows a trace for selinux avc.
---
Âinclude/trace/events/avc.h |Â 92 +++++++++++++++++++++++++++++
Âsecurity/selinux/avc.cÂÂÂÂ | 115 ++++++++++++++++++++++++++++++++++++-
Â2 files changed, 205 insertions(+), 2 deletions(-)
Âcreate mode 100644 include/trace/events/avc.h

diff --git a/include/trace/events/avc.h b/include/trace/events/avc.h
new file mode 100644
index 000000000000..28c1044e918b
--- /dev/null
+++ b/include/trace/events/avc.h
@@ -0,0 +1,92 @@
+/*
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Author: Peter Enderborg <Peter.Enderborg@xxxxxxxx>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM avc
+
+#if !defined(_TRACE_AVC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_AVC_H
+
+#include <linux/tracepoint.h>
+TRACE_EVENT(avc_data,
+ÂÂÂ ÂÂÂ TP_PROTO(u32 requested,
+ÂÂÂ ÂÂÂ ÂÂÂÂ u32 denied,
+ÂÂÂ ÂÂÂ ÂÂÂÂ u32 audited,
+ÂÂÂ ÂÂÂ ÂÂÂÂ int result,
+ÂÂÂ ÂÂÂ ÂÂÂÂ const char *msg
+ÂÂÂ ÂÂÂ ÂÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_ARGS(requested, denied, audited, result,msg),
+
+ÂÂÂ ÂÂÂ TP_STRUCT__entry(
+ÂÂÂ ÂÂÂ ÂÂÂ Â__field(u32, requested)
+ÂÂÂ ÂÂÂ ÂÂÂ Â__field(u32, denied)
+ÂÂÂ ÂÂÂ ÂÂÂ Â__field(u32, audited)
+ÂÂÂ ÂÂÂ ÂÂÂ Â__field(int, result)
+ÂÂÂ ÂÂÂ ÂÂÂ Â__array(char, msg, 255)
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_fast_assign(
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ __entry->requestedÂÂÂ = requested;
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ __entry->deniedÂÂÂ = denied;
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ __entry->auditedÂÂÂ = audited;
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ __entry->resultÂÂÂ = result;
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ memcpy(__entry->msg, msg, 255);
+ÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_printk("requested=0x%x denied=%d audited=%d result=%d msg=%s",
+ÂÂÂ ÂÂÂ ÂÂÂÂÂ __entry->requested, __entry->denied, __entry->audited, __entry->result, __entry->msg
+ÂÂÂ ÂÂÂ ÂÂÂÂÂ )
+);
+TRACE_EVENT(avc_req,
+ÂÂÂ ÂÂÂ TP_PROTO(u32 requested,
+ÂÂÂ ÂÂÂ ÂÂÂÂ u32 denied,
+ÂÂÂ ÂÂÂ ÂÂÂÂ u32 audited,
+ÂÂÂ ÂÂÂ ÂÂÂÂ int result,
+ÂÂÂ ÂÂÂ ÂÂÂÂ const char *msg,
+ÂÂÂ ÂÂÂ ÂÂÂÂ u32 ssid,
+ÂÂÂ ÂÂÂ ÂÂÂÂ struct selinux_state *state
+ÂÂÂ ÂÂÂ ÂÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_ARGS(requested, denied, audited, result,msg, ssid, state),
+
+ÂÂÂ ÂÂÂ TP_STRUCT__entry(
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, requested)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, denied)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, audited)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(int, result)
+ÂÂÂ ÂÂÂ ÂÂÂ __array(char, msg, 255)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, ssid)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(struct selinux_state *, state)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(char*, scontext)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, ssid_len)
+ÂÂÂ ÂÂÂ ÂÂÂ __field(u32, ssid_res)
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_fast_assign(
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->requestedÂÂÂ = requested;
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->deniedÂÂÂ = denied;
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->auditedÂÂÂ = audited;
+ÂÂÂ ÂÂÂ ÂÂÂ Â__entry->resultÂÂÂ = result;
+ÂÂÂ ÂÂÂ ÂÂÂ memcpy(__entry->msg, msg, 255);
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->ssidÂÂÂ = ssid;
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->stateÂÂÂ = state;
+ÂÂÂ ÂÂÂ ÂÂÂ __entry->ssid_res = security_sid_to_context(
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ state,
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ ssid,
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ &__entry->scontext,
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂ &__entry->ssid_len);
+ÂÂÂ ),
+
+ÂÂÂ ÂÂÂ TP_printk("requested=0x%x denied=%d audited=%d result=%d msg=%s ssid=%d state=%p scontext=%s slen=%d s=%d",
+ÂÂÂ ÂÂÂ ÂÂÂÂÂ __entry->requested, __entry->denied, __entry->audited, __entry->result, __entry->msg, __entry->ssid, __entry->state,__entry->scontext,__entry->ssid_len, __entry->ssid_res
+ÂÂÂ ÂÂÂ ÂÂÂÂÂ )
+);
+
+#endif /* _TRACE_AVC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index d18cb32a242a..d8cb9a23d669 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -30,6 +30,8 @@
Â#include "avc.h"
Â#include "avc_ss.h"
Â#include "classmap.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/avc.h>
Â
Â#define AVC_CACHE_SLOTSÂÂÂ ÂÂÂ ÂÂÂ 512
Â#define AVC_DEF_CACHE_THRESHOLDÂÂÂ ÂÂÂ 512
@@ -126,6 +128,41 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
ÂÂÂÂ return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
Â}
Â
+static int avc_dump_avs(char *ab, u16 tclass, u32 av)
+{
+ÂÂÂ const char **perms;
+ÂÂÂ int i, perm;
+ÂÂÂ int rp;
+
+ÂÂÂ if (av == 0) {
+ÂÂÂ ÂÂÂ rp = sprintf(ab, " null");
+ÂÂÂ ÂÂÂ return rp;
+ÂÂÂ }
+
+ÂÂÂ BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
+ÂÂÂ perms = secclass_map[tclass-1].perms;
+
+ÂÂÂ rp = sprintf(ab, " {");
+ÂÂÂ i = 0;
+ÂÂÂ perm = 1;
+ÂÂÂ while (i < (sizeof(av) * 8)) {
+ÂÂÂ ÂÂÂ if ((perm & av) && perms[i]) {
+ÂÂÂ ÂÂÂ ÂÂÂ rp +=sprintf(ab+rp, " %s", perms[i]);
+ÂÂÂ ÂÂÂ ÂÂÂ av &= ~perm;
+ÂÂÂ ÂÂÂ }
+ÂÂÂ ÂÂÂ i++;
+ÂÂÂ ÂÂÂ perm <<= 1;
+ÂÂÂ }
+
+ÂÂÂ if (av)
+ÂÂÂ ÂÂÂ rp += sprintf(ab+rp, " 0x%x", av);
+
+ÂÂÂ rp +=sprintf(ab+rp, " }");
+ÂÂÂ return rp;
+}
+
+
+
Â/**
 * avc_init - Initialize the AVC.
 *
@@ -421,8 +458,12 @@ static inline int avc_xperms_audit(struct selinux_state *state,
Â
ÂÂÂÂ audited = avc_xperms_audit_required(
ÂÂÂÂ ÂÂÂ ÂÂÂ requested, avd, xpd, perm, result, &denied);
-ÂÂÂ if (likely(!audited))
+ÂÂÂ if (likely(!audited)) {
+ÂÂÂ ÂÂÂ trace_avc_data(requested, denied, audited, -1,"foo");
ÂÂÂÂ ÂÂÂ return 0;
+ÂÂÂ }
+
+ÂÂÂ trace_avc_data(requested, denied, audited, -1,"bar");
ÂÂÂÂ return slow_avc_audit(state, ssid, tsid, tclass, requested,
ÂÂÂÂ ÂÂÂ ÂÂÂ audited, denied, result, ad);
Â}
@@ -541,6 +582,34 @@ static inline struct avc_node *avc_search_node(struct selinux_avc *avc,
ÂÂÂÂ return ret;
Â}
Â
+static int avc_dump_querys(struct selinux_state *state, char *ab, u32 ssid, u32 tsid, u16 tclass)
+{
+ÂÂÂ int rc;
+ÂÂÂ char *scontext;
+ÂÂÂ u32 scontext_len;
+ÂÂÂ int rp;
+
+ÂÂÂ rc = security_sid_to_context(state,ssid, &scontext, &scontext_len);
+ÂÂÂ if (rc)
+ÂÂÂ ÂÂÂ rp = sprintf(ab, "ssid=%d", ssid);
+ÂÂÂ else {
+ÂÂÂ ÂÂÂ rp = sprintf(ab, "scontext=%s", scontext);
+ÂÂÂ ÂÂÂ kfree(scontext);
+ÂÂÂ }
+
+ÂÂÂ rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
+ÂÂÂ if (rc)
+ÂÂÂ ÂÂÂ rp +=sprintf(ab+rp, " tsid=%d", tsid);
+ÂÂÂ else {
+ÂÂÂ ÂÂÂ rp +=sprintf(ab+rp, " tcontext=%s", scontext);
+ÂÂÂ ÂÂÂ kfree(scontext);
+ÂÂÂ }
+
+ÂÂÂ BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
+ÂÂÂ rp += sprintf(ab+rp, " tclass=%s", secclass_map[tclass-1].name);
+ÂÂÂ return rp;
+}
+
Â/**
 * avc_lookup - Look up an AVC entry.
 * @ssid: source security identifier
@@ -690,6 +759,7 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
ÂÂÂÂ ÂÂÂ audit_log_format(ab, " 0x%x", av);
Â
ÂÂÂÂ audit_log_format(ab, " } for ");
+
Â}
Â
Â/**
@@ -780,6 +850,7 @@ 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);
+ÂÂÂ pr_info("done lsm");
ÂÂÂÂ return 0;
Â}
Â
@@ -1105,6 +1176,34 @@ int avc_has_extended_perms(struct selinux_state *state,
ÂÂÂÂ ÂÂÂ return rc2;
ÂÂÂÂ return rc;
Â}
+static int avc_dump_extra_info_s(char *ab,
+ÂÂÂ ÂÂÂ struct common_audit_data *ad)
+{
+ÂÂÂ struct task_struct *tsk = current;
+ÂÂÂ int rp;
+
+ÂÂÂ if (tsk && tsk->pid) {
+ÂÂÂ ÂÂÂ rp = sprintf(ab, " ppid=%d pcomm=", tsk->parent->pid);
+ÂÂÂ ÂÂÂ rp += sprintf(ab+rp, tsk->parent->comm);
+
+ÂÂÂ ÂÂÂ if (tsk->group_leader->pid != tsk->pid) {
+ÂÂÂ ÂÂÂ ÂÂÂ rp +=sprintf(ab+rp, " pgid=%d pgcomm=",
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ tsk->group_leader->pid);
+ÂÂÂ ÂÂÂ ÂÂÂ rp += sprintf(ab+rp,
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ tsk->group_leader->comm);
+ÂÂÂ ÂÂÂ } else if (tsk->parent->group_leader->pid) {
+ÂÂÂ ÂÂÂ ÂÂÂ rp += sprintf(ab+rp, " pgid=%d pgcomm=",
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ tsk->parent->group_leader->pid);
+ÂÂÂ ÂÂÂ ÂÂÂ rp += sprintf(ab+rp,
+ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ tsk->parent->group_leader->comm);
+ÂÂÂ ÂÂÂ }
+ÂÂÂ } else {
+ÂÂÂ ÂÂÂ rp = sprintf(ab," no task %p", tsk);
+ÂÂÂ }
+ÂÂÂ return rp;
+}
+
+
Â
Â/**
 * avc_has_perm_noaudit - Check permissions but perform no auditing.
@@ -1178,14 +1277,26 @@ int avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass,
Â{
ÂÂÂÂ struct av_decision avd;
ÂÂÂÂ int rc, rc2;
+ÂÂÂ char *lb;
+ÂÂÂ int x;
+
+ÂÂÂ lb = kmalloc(1024, GFP_KERNEL);
Â
ÂÂÂÂ rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
ÂÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ Â &avd);
Â
ÂÂÂÂ rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
ÂÂÂÂ ÂÂÂ ÂÂÂ auditdata, 0);
-ÂÂÂ if (rc2)
+ÂÂÂ if (rc2)Â {
+ÂÂÂ ÂÂÂ kfree(lb);
ÂÂÂÂ ÂÂÂ return rc2;
+ÂÂÂ }
+
+ÂÂÂ x = avc_dump_avs(lb, tclass, 42);
+ÂÂÂ x += avc_dump_querys(state, lb+x,ssid, tsid, tclass);
+ÂÂÂÂÂÂÂ if(1)ÂÂÂ x += avc_dump_extra_info_s(lb+x, auditdata);
+ÂÂÂ trace_avc_data(requested, rc, 0, rc2,lb);
+ÂÂÂ kfree(lb);
ÂÂÂÂ return rc;
Â}
Â
--
2.17.1