Allow for audit messages to be emitted upon BPF program load and
unload for having a timeline of events. The load itself is in
syscall context, so additional info about the process initiating
the BPF prog creation can be logged and later directly correlated
to the unload event.

The only info really needed from BPF side is the globally unique
prog ID where then audit user space tooling can query / dump all
info needed about the specific BPF program right upon load event
and enrich the record, thus these changes needed here can be kept
small and non-intrusive to the core.

Raw example output:

  # auditctl -D
  # auditctl -a always,exit -F arch=x86_64 -S bpf
  # ausearch --start recent -m 1332
  [...]
  ----
  time->Thu Oct  4 15:29:11 2018
  type=PROCTITLE msg=audit(1538659751.047:174): 
proctitle=2E2F746573745F76657269666965720030
  type=SYSCALL msg=audit(1538659751.047:174): arch=c000003e syscall=321 
success=yes exit=3
    a0=5 a1=7ffe6f46dec0 a2=48 a3=40bd40 items=0 ppid=1892 pid=2020 auid=1000 
uid=0 gid=0
    euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 
comm="test_verifier"
    exe="/home/foo/test_verifier" key=(null)
  type=UNKNOWN[1332] msg=audit(1538659751.047:174): auid=1000 uid=0 gid=0 ses=3 
pid=2020
    comm="test_verifier" exe="/home/foo/test_verifier" prog-id=2 event=LOAD
  ----
  time->Thu Oct  4 15:29:11 2018
  type=UNKNOWN[1332] msg=audit(1538659751.047:176): prog-id=2 event=UNLOAD
  ----
  [...]

Signed-off-by: Daniel Borkmann <dan...@iogearbox.net>
Cc: Jiri Olsa <jo...@kernel.org>
---
 include/linux/audit.h      |  3 +++
 include/uapi/linux/audit.h |  1 +
 kernel/auditsc.c           |  2 +-
 kernel/bpf/syscall.c       | 31 +++++++++++++++++++++++++++++++
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9334fbe..9495c84 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -152,6 +152,7 @@ extern void             audit_log_key(struct audit_buffer 
*ab,
 extern void                audit_log_link_denied(const char *operation);
 extern void                audit_log_lost(const char *message);
 
+extern void audit_log_task(struct audit_buffer *ab);
 extern int audit_log_task_context(struct audit_buffer *ab);
 extern void audit_log_task_info(struct audit_buffer *ab,
                                struct task_struct *tsk);
@@ -198,6 +199,8 @@ static inline void audit_log_key(struct audit_buffer *ab, 
char *key)
 { }
 static inline void audit_log_link_denied(const char *string)
 { }
+static inline void audit_log_task(struct audit_buffer *ab)
+{ }
 static inline int audit_log_task_context(struct audit_buffer *ab)
 {
        return 0;
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 818ae69..a749881 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -114,6 +114,7 @@
 #define AUDIT_REPLACE          1329    /* Replace auditd if this packet 
unanswerd */
 #define AUDIT_KERN_MODULE      1330    /* Kernel Module events */
 #define AUDIT_FANOTIFY         1331    /* Fanotify access decision */
+#define AUDIT_BPF              1332    /* BPF subsystem */
 
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index b2d1f04..6c498d9 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2427,7 +2427,7 @@ void __audit_fanotify(unsigned int response)
                AUDIT_FANOTIFY, "resp=%u", response);
 }
 
-static void audit_log_task(struct audit_buffer *ab)
+void audit_log_task(struct audit_buffer *ab)
 {
        kuid_t auid, uid;
        kgid_t gid;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 5742df2..83afbd1 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -31,6 +31,7 @@
 #include <linux/timekeeping.h>
 #include <linux/ctype.h>
 #include <linux/nospec.h>
+#include <linux/audit.h>
 
 #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
                           (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -1011,6 +1012,34 @@ static void free_used_maps(struct bpf_prog_aux *aux)
        kfree(aux->used_maps);
 }
 
+enum bpf_event {
+       BPF_EVENT_LOAD,
+       BPF_EVENT_UNLOAD,
+};
+
+static const char * const bpf_event_audit_str[] = {
+       [BPF_EVENT_LOAD]   = "LOAD",
+       [BPF_EVENT_UNLOAD] = "UNLOAD",
+};
+
+static void bpf_audit_prog(const struct bpf_prog *prog, enum bpf_event event)
+{
+       bool has_task_context = event == BPF_EVENT_LOAD;
+       struct audit_buffer *ab;
+
+       if (audit_enabled == AUDIT_OFF)
+               return;
+       ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_BPF);
+       if (unlikely(!ab))
+               return;
+       if (has_task_context)
+               audit_log_task(ab);
+       audit_log_format(ab, "%sprog-id=%u event=%s",
+                        has_task_context ? " " : "",
+                        prog->aux->id, bpf_event_audit_str[event]);
+       audit_log_end(ab);
+}
+
 int __bpf_prog_charge(struct user_struct *user, u32 pages)
 {
        unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
@@ -1112,6 +1141,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
 static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
 {
        if (atomic_dec_and_test(&prog->aux->refcnt)) {
+               bpf_audit_prog(prog, BPF_EVENT_UNLOAD);
                /* bpf_prog_free_id() must be called first */
                bpf_prog_free_id(prog, do_idr_lock);
                bpf_prog_kallsyms_del_all(prog);
@@ -1452,6 +1482,7 @@ static int bpf_prog_load(union bpf_attr *attr)
        }
 
        bpf_prog_kallsyms_add(prog);
+       bpf_audit_prog(prog, BPF_EVENT_LOAD);
        return err;
 
 free_used_maps:
-- 
2.9.5

Reply via email to