From: KP Singh <[email protected]>
# v7 -> v8
https://lore.kernel.org/bpf/[email protected]/
* Removed CAP_MAC_ADMIN check from bpf_lsm_verify_prog. LSMs can add it
in their own bpf_prog hook. This can be revisited as a separate patch.
* Added Andrii and James' Ack/Review tags.
* Fixed an indentation issue and missing newlines in selftest error
a cases.
* Updated a comment as suggested by Alexei.
* Updated the documentation to use the newer libbpf API and some other
fixes.
* Rebase
# v6 -> v7
https://lore.kernel.org/bpf/[email protected]/
* Removed __weak from the LSM attachment nops per Kees' suggestion.
Will send a separate patch (if needed) to update the noinline
definition in include/linux/compiler_attributes.h.
* waitpid to wait specifically for the forked child in selftests.
* Comment format fixes in security/... as suggested by Casey.
* Added Acks from Kees and Andrii and Casey's Reviewed-by: tags to
the respective patches.
* Rebase
# v5 -> v6
https://lore.kernel.org/bpf/[email protected]/
* Updated LSM_HOOK macro to define a default value and cleaned up the
BPF LSM hook declarations.
* Added Yonghong's Acks and Kees' Reviewed-by tags.
* Simplification of the selftest code.
* Rebase and fixes suggested by Andrii and Yonghong and some other minor
fixes noticed in internal review.
# v4 -> v5
https://lore.kernel.org/bpf/[email protected]/
* Removed static keys and special casing of BPF calls from the LSM
framework.
* Initialized the BPF callbacks (nops) as proper LSM hooks.
* Updated to using the newly introduced BPF_TRAMP_MODIFY_RETURN
trampolines in https://lkml.org/lkml/2020/3/4/877
* Addressed Andrii's feedback and rebased.
# v3 -> v4
* Moved away from allocating a separate security_hook_heads and adding a
new special case for arch_prepare_bpf_trampoline to using BPF fexit
trampolines called from the right place in the LSM hook and toggled by
static keys based on the discussion in:
https://lore.kernel.org/bpf/CAG48ez25mW+_oCxgCtbiGMX07g_ph79UOJa07h=o_6B6+Q-u5g@mail.gmail.com/
* Since the code does not deal with security_hook_heads anymore, it goes
from "being a BPF LSM" to "BPF program attachment to LSM hooks".
* Added a new test case which ensures that the BPF programs' return value
is reflected by the LSM hook.
# v2 -> v3 does not change the overall design and has some minor fixes:
* LSM_ORDER_LAST is introduced to represent the behaviour of the BPF LSM
* Fixed the inadvertent clobbering of the LSM Hook error codes
* Added GPL license requirement to the commit log
* The lsm_hook_idx is now the more conventional 0-based index
* Some changes were split into a separate patch ("Load btf_vmlinux only
once per object")
https://lore.kernel.org/bpf/[email protected]/
* Addressed Andrii's feedback on the BTF implementation
* Documentation update for using generated vmlinux.h to simplify
programs
* Rebase
# Changes since v1
https://lore.kernel.org/bpf/[email protected]
* Eliminate the requirement to maintain LSM hooks separately in
security/bpf/hooks.h Use BPF trampolines to dynamically allocate
security hooks
* Drop the use of securityfs as bpftool provides the required
introspection capabilities. Update the tests to use the bpf_skeleton
and global variables
* Use O_CLOEXEC anonymous fds to represent BPF attachment in line with
the other BPF programs with the possibility to use bpf program pinning
in the future to provide "permanent attachment".
* Drop the logic based on prog names for handling re-attachment.
* Drop bpf_lsm_event_output from this series and send it as a separate
patch.
# Motivation
Google does analysis of rich runtime security data to detect and thwart
threats in real-time. Currently, this is done in custom kernel modules
but we would like to replace this with something that's upstream and
useful to others.
The current kernel infrastructure for providing telemetry (Audit, Perf
etc.) is disjoint from access enforcement (i.e. LSMs). Augmenting the
information provided by audit requires kernel changes to audit, its
policy language and user-space components. Furthermore, building a MAC
policy based on the newly added telemetry data requires changes to
various LSMs and their respective policy languages.
This patchset allows BPF programs to be attached to LSM hooks This
facilitates a unified and dynamic (not requiring re-compilation of the
kernel) audit and MAC policy.
# Why an LSM?
Linux Security Modules target security behaviours rather than the
kernel's API. For example, it's easy to miss out a newly added system
call for executing processes (eg. execve, execveat etc.) but the LSM
framework ensures that all process executions trigger the relevant hooks
irrespective of how the process was executed.
Allowing users to implement LSM hooks at runtime also benefits the LSM
eco-system by enabling a quick feedback loop from the security community
about the kind of behaviours that the LSM Framework should be targeting.
# How does it work?
The patchset introduces a new eBPF (https://docs.cilium.io/en/v1.6/bpf/)
program type BPF_PROG_TYPE_LSM which can only be attached to LSM hooks.
Loading and attachment of BPF programs requires CAP_SYS_ADMIN.
The new LSM registers nop functions (bpf_lsm_<hook_name>) as LSM hook
callbacks. Their purpose is to provide a definite point where BPF
programs can be attached as BPF_TRAMP_MODIFY_RETURN trampoline programs
for hooks that return an int, and BPF_TRAMP_FEXIT trampoline programs
for void LSM hooks.
Audit logs can be written using a format chosen by the eBPF program to
the perf events buffer or to global eBPF variables or maps and can be
further processed in user-space.
# BTF Based Design
The current design uses BTF:
* https://facebookmicrosites.github.io/bpf/blog/2018/11/14/btf-enhancement.html
* https://lwn.net/Articles/803258
which allows verifiable read-only structure accesses by field names
rather than fixed offsets. This allows accessing the hook parameters
using a dynamically created context which provides a certain degree of
ABI stability:
// Only declare the structure and fields intended to be used
// in the program
struct vm_area_struct {
unsigned long vm_start;
} __attribute__((preserve_access_index));
// Declare the eBPF program mprotect_audit which attaches to
// to the file_mprotect LSM hook and accepts three arguments.
SEC("lsm/file_mprotect")
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot, int ret)
{
unsigned long vm_start = vma->vm_start;
return 0;
}
By relocating field offsets, BTF makes a large portion of kernel data
structures readily accessible across kernel versions without requiring a
large corpus of BPF helper functions and requiring recompilation with
every kernel version. The BTF type information is also used by the BPF
verifier to validate memory accesses within the BPF program and also
prevents arbitrary writes to the kernel memory.
The limitations of BTF compatibility are described in BPF Co-Re
(http://vger.kernel.org/bpfconf2019_talks/bpf-core.pdf, i.e. field
renames, #defines and changes to the signature of LSM hooks). This
design imposes that the MAC policy (eBPF programs) be updated when the
inspected kernel structures change outside of BTF compatibility
guarantees. In practice, this is only required when a structure field
used by a current policy is removed (or renamed) or when the used LSM
hooks change. We expect the maintenance cost of these changes to be
acceptable as compared to the design presented in the RFC.
(https://lore.kernel.org/bpf/[email protected]/).
# Usage Examples
A simple example and some documentation is included in the patchset.
In order to better illustrate the capabilities of the framework some
more advanced prototype (not-ready for review) code has also been
published separately:
* Logging execution events (including environment variables and
arguments)
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c
* Detecting deletion of running executables:
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_detect_exec_unlink.c
* Detection of writes to /proc/<pid>/mem:
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c
We have updated Google's internal telemetry infrastructure and have
started deploying this LSM on our Linux Workstations. This gives us more
confidence in the real-world applications of such a system.
KP Singh (8):
bpf: Introduce BPF_PROG_TYPE_LSM
security: Refactor declaration of LSM hooks
bpf: lsm: provide attachment points for BPF LSM programs
bpf: lsm: Implement attach, detach and execution
bpf: lsm: Initialize the BPF LSM hooks
tools/libbpf: Add support for BPF_PROG_TYPE_LSM
bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
bpf: lsm: Add Documentation
Documentation/bpf/bpf_lsm.rst | 146 ++++
Documentation/bpf/index.rst | 1 +
MAINTAINERS | 1 +
include/linux/bpf.h | 3 +
include/linux/bpf_lsm.h | 33 +
include/linux/bpf_types.h | 4 +
include/linux/lsm_hook_defs.h | 381 +++++++++++
include/linux/lsm_hooks.h | 628 +-----------------
include/uapi/linux/bpf.h | 2 +
init/Kconfig | 12 +
kernel/bpf/Makefile | 1 +
kernel/bpf/bpf_lsm.c | 59 ++
kernel/bpf/btf.c | 9 +-
kernel/bpf/syscall.c | 57 +-
kernel/bpf/trampoline.c | 17 +-
kernel/bpf/verifier.c | 19 +-
kernel/trace/bpf_trace.c | 12 +-
security/Kconfig | 10 +-
security/Makefile | 2 +
security/bpf/Makefile | 5 +
security/bpf/hooks.c | 26 +
security/security.c | 41 +-
tools/include/uapi/linux/bpf.h | 2 +
tools/lib/bpf/bpf.c | 3 +-
tools/lib/bpf/libbpf.c | 39 +-
tools/lib/bpf/libbpf.h | 4 +
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_probes.c | 1 +
tools/testing/selftests/bpf/config | 2 +
.../selftests/bpf/prog_tests/test_lsm.c | 86 +++
tools/testing/selftests/bpf/progs/lsm.c | 48 ++
31 files changed, 987 insertions(+), 670 deletions(-)
create mode 100644 Documentation/bpf/bpf_lsm.rst
create mode 100644 include/linux/bpf_lsm.h
create mode 100644 include/linux/lsm_hook_defs.h
create mode 100644 kernel/bpf/bpf_lsm.c
create mode 100644 security/bpf/Makefile
create mode 100644 security/bpf/hooks.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/test_lsm.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm.c
--
2.20.1
From: KP Singh <[email protected]>
* Load/attach a BPF program that hooks to file_mprotect (int)
and bprm_committed_creds (void).
* Perform an action that triggers the hook.
* Verify if the audit event was received using the shared global
variables for the process executed.
* Verify if the mprotect returns a -EPERM.
Signed-off-by: KP Singh <[email protected]>
Acked-by: Andrii Nakryiko <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
Reviewed-by: Thomas Garnier <[email protected]>
Reviewed-by: James Morris <[email protected]>
---
tools/testing/selftests/bpf/config | 2 +
.../selftests/bpf/prog_tests/test_lsm.c | 86 +++++++++++++++++++
tools/testing/selftests/bpf/progs/lsm.c | 48 +++++++++++
3 files changed, 136 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/test_lsm.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm.c
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index 5dc109f4c097..60e3ae5d4e48 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -35,3 +35,5 @@ CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
CONFIG_IPV6_SIT=m
CONFIG_BPF_JIT=y
+CONFIG_BPF_LSM=y
+CONFIG_SECURITY=y
diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
new file mode 100644
index 000000000000..fcd839e88540
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+
+#include <test_progs.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#include "lsm.skel.h"
+
+char *CMD_ARGS[] = {"true", NULL};
+
+int heap_mprotect(void)
+{
+ void *buf;
+ long sz;
+ int ret;
+
+ sz = sysconf(_SC_PAGESIZE);
+ if (sz < 0)
+ return sz;
+
+ buf = memalign(sz, 2 * sz);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ ret = mprotect(buf, sz, PROT_READ | PROT_EXEC);
+ free(buf);
+ return ret;
+}
+
+int exec_cmd(int *monitored_pid)
+{
+ int child_pid, child_status;
+
+ child_pid = fork();
+ if (child_pid == 0) {
+ *monitored_pid = getpid();
+ execvp(CMD_ARGS[0], CMD_ARGS);
+ return -EINVAL;
+ } else if (child_pid > 0) {
+ waitpid(child_pid, &child_status, 0);
+ return child_status;
+ }
+
+ return -EINVAL;
+}
+
+void test_test_lsm(void)
+{
+ struct lsm *skel = NULL;
+ int err, duration = 0;
+
+ skel = lsm__open_and_load();
+ if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
+ goto close_prog;
+
+ err = lsm__attach(skel);
+ if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
+ goto close_prog;
+
+ err = exec_cmd(&skel->bss->monitored_pid);
+ if (CHECK(err < 0, "exec_cmd", "err %d errno %d\n", err, errno))
+ goto close_prog;
+
+ CHECK(skel->bss->bprm_count != 1, "bprm_count", "bprm_count = %d\n",
+ skel->bss->bprm_count);
+
+ skel->bss->monitored_pid = getpid();
+
+ err = heap_mprotect();
+ if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
+ errno))
+ goto close_prog;
+
+ CHECK(skel->bss->mprotect_count != 1, "mprotect_count",
+ "mprotect_count = %d\n", skel->bss->mprotect_count);
+
+close_prog:
+ lsm__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm.c b/tools/testing/selftests/bpf/progs/lsm.c
new file mode 100644
index 000000000000..a4e3c223028d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020 Google LLC.
+ */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+
+char _license[] SEC("license") = "GPL";
+
+int monitored_pid = 0;
+int mprotect_count = 0;
+int bprm_count = 0;
+
+SEC("lsm/file_mprotect")
+int BPF_PROG(test_int_hook, struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot, int ret)
+{
+ if (ret != 0)
+ return ret;
+
+ __u32 pid = bpf_get_current_pid_tgid() >> 32;
+ int is_heap = 0;
+
+ is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk);
+
+ if (is_heap && monitored_pid == pid) {
+ mprotect_count++;
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+SEC("lsm/bprm_committed_creds")
+int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
+{
+ __u32 pid = bpf_get_current_pid_tgid() >> 32;
+
+ if (monitored_pid == pid)
+ bprm_count++;
+
+ return 0;
+}
--
2.20.1
From: KP Singh <[email protected]>
* The hooks are initialized using the definitions in
include/linux/lsm_hook_defs.h.
* The LSM can be enabled / disabled with CONFIG_BPF_LSM.
Signed-off-by: KP Singh <[email protected]>
Acked-by: Kees Cook <[email protected]>
Acked-by: James Morris <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
---
security/Kconfig | 10 +++++-----
security/Makefile | 2 ++
security/bpf/Makefile | 5 +++++
security/bpf/hooks.c | 26 ++++++++++++++++++++++++++
4 files changed, 38 insertions(+), 5 deletions(-)
create mode 100644 security/bpf/Makefile
create mode 100644 security/bpf/hooks.c
diff --git a/security/Kconfig b/security/Kconfig
index 2a1a2d396228..cd3cc7da3a55 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -277,11 +277,11 @@ endchoice
config LSM
string "Ordered list of enabled LSMs"
- default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
- default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
- default "lockdown,yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO
- default "lockdown,yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC
- default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
+ default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
+ default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
+ default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
+ default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
+ default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
help
A comma-separated list of LSMs, in initialization order.
Any LSMs left off this list will be ignored. This can be
diff --git a/security/Makefile b/security/Makefile
index 746438499029..22e73a3482bd 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -12,6 +12,7 @@ subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
+subdir-$(CONFIG_BPF_LSM) += bpf
# always enable default capabilities
obj-y += commoncap.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
+obj-$(CONFIG_BPF_LSM) += bpf/
# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
diff --git a/security/bpf/Makefile b/security/bpf/Makefile
new file mode 100644
index 000000000000..c7a89a962084
--- /dev/null
+++ b/security/bpf/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2020 Google LLC.
+
+obj-$(CONFIG_BPF_LSM) := hooks.o
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
new file mode 100644
index 000000000000..32d32d485451
--- /dev/null
+++ b/security/bpf/hooks.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+#include <linux/lsm_hooks.h>
+#include <linux/bpf_lsm.h>
+
+static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {
+ #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
+ #include <linux/lsm_hook_defs.h>
+ #undef LSM_HOOK
+};
+
+static int __init bpf_lsm_init(void)
+{
+ security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), "bpf");
+ pr_info("LSM support for eBPF active\n");
+ return 0;
+}
+
+DEFINE_LSM(bpf) = {
+ .name = "bpf",
+ .init = bpf_lsm_init,
+};
--
2.20.1
From: KP Singh <[email protected]>
Introduce types and configs for bpf programs that can be attached to
LSM hooks. The programs can be enabled by the config option
CONFIG_BPF_LSM.
Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
Reviewed-by: Thomas Garnier <[email protected]>
Acked-by: Yonghong Song <[email protected]>
Acked-by: Andrii Nakryiko <[email protected]>
Acked-by: James Morris <[email protected]>
---
MAINTAINERS | 1 +
include/linux/bpf.h | 3 +++
include/linux/bpf_types.h | 4 ++++
include/uapi/linux/bpf.h | 2 ++
init/Kconfig | 12 ++++++++++++
kernel/bpf/Makefile | 1 +
kernel/bpf/bpf_lsm.c | 17 +++++++++++++++++
kernel/trace/bpf_trace.c | 12 ++++++------
tools/include/uapi/linux/bpf.h | 2 ++
tools/lib/bpf/libbpf_probes.c | 1 +
10 files changed, 49 insertions(+), 6 deletions(-)
create mode 100644 kernel/bpf/bpf_lsm.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5dbee41045bc..3197fe9256b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3147,6 +3147,7 @@ R: Martin KaFai Lau <[email protected]>
R: Song Liu <[email protected]>
R: Yonghong Song <[email protected]>
R: Andrii Nakryiko <[email protected]>
+R: KP Singh <[email protected]>
L: [email protected]
L: [email protected]
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index bdb981c204fa..af81ec7b783c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1513,6 +1513,9 @@ extern const struct bpf_func_proto bpf_tcp_sock_proto;
extern const struct bpf_func_proto bpf_jiffies64_proto;
extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto;
+const struct bpf_func_proto *bpf_tracing_func_proto(
+ enum bpf_func_id func_id, const struct bpf_prog *prog);
+
/* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void);
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index c81d4ece79a4..ba0c2d56f8a3 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -70,6 +70,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
void *, void *)
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
void *, void *)
+#ifdef CONFIG_BPF_LSM
+BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
+ void *, void *)
+#endif /* CONFIG_BPF_LSM */
#endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 5d01c5c7e598..9f2673c58788 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
+ BPF_PROG_TYPE_LSM,
};
enum bpf_attach_type {
@@ -211,6 +212,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT,
BPF_MODIFY_RETURN,
+ BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/init/Kconfig b/init/Kconfig
index 20a6ac33761c..deae572d1927 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1616,6 +1616,18 @@ config KALLSYMS_BASE_RELATIVE
# end of the "standard kernel features (expert users)" menu
# syscall, maps, verifier
+
+config BPF_LSM
+ bool "LSM Instrumentation with BPF"
+ depends on BPF_SYSCALL
+ depends on SECURITY
+ depends on BPF_JIT
+ help
+ Enables instrumentation of the security hooks with eBPF programs for
+ implementing dynamic MAC and Audit Policies.
+
+ If you are unsure how to answer this question, answer N.
+
config BPF_SYSCALL
bool "Enable bpf() system call"
select BPF
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 046ce5d98033..f2d7be596966 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
endif
ifeq ($(CONFIG_BPF_JIT),y)
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
+obj-${CONFIG_BPF_LSM} += bpf_lsm.o
endif
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
new file mode 100644
index 000000000000..82875039ca90
--- /dev/null
+++ b/kernel/bpf/bpf_lsm.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+
+#include <linux/filter.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
+
+const struct bpf_prog_ops lsm_prog_ops = {
+};
+
+const struct bpf_verifier_ops lsm_verifier_ops = {
+ .get_func_proto = bpf_tracing_func_proto,
+ .is_valid_access = btf_ctx_access,
+};
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index e619eedb5919..37ffceab608f 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -779,8 +779,8 @@ static const struct bpf_func_proto bpf_send_signal_thread_proto = {
.arg1_type = ARG_ANYTHING,
};
-static const struct bpf_func_proto *
-tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+const struct bpf_func_proto *
+bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
@@ -865,7 +865,7 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_override_return_proto;
#endif
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -975,7 +975,7 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_tp;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -1082,7 +1082,7 @@ pe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_read_branch_records:
return &bpf_read_branch_records_proto;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -1210,7 +1210,7 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_raw_tp;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 5d01c5c7e598..9f2673c58788 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
+ BPF_PROG_TYPE_LSM,
};
enum bpf_attach_type {
@@ -211,6 +212,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT,
BPF_MODIFY_RETURN,
+ BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index b782ebef6ac9..2c92059c0c90 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -108,6 +108,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
+ case BPF_PROG_TYPE_LSM:
default:
break;
}
--
2.20.1
Hey KP,
On 3/27/20 8:28 PM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> # v7 -> v8
>
> https://lore.kernel.org/bpf/[email protected]/
>
> * Removed CAP_MAC_ADMIN check from bpf_lsm_verify_prog. LSMs can add it
> in their own bpf_prog hook. This can be revisited as a separate patch.
> * Added Andrii and James' Ack/Review tags.
> * Fixed an indentation issue and missing newlines in selftest error
> a cases.
> * Updated a comment as suggested by Alexei.
> * Updated the documentation to use the newer libbpf API and some other
> fixes.
> * Rebase
>
> # v6 -> v7
>
> https://lore.kernel.org/bpf/[email protected]/
>
[...]
> KP Singh (8):
> bpf: Introduce BPF_PROG_TYPE_LSM
> security: Refactor declaration of LSM hooks
> bpf: lsm: provide attachment points for BPF LSM programs
> bpf: lsm: Implement attach, detach and execution
> bpf: lsm: Initialize the BPF LSM hooks
> tools/libbpf: Add support for BPF_PROG_TYPE_LSM
> bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
> bpf: lsm: Add Documentation
I was about to apply, but then I'm getting the following selftest issue on
the added LSM one, ptal:
# ./test_progs
[...]
#65/1 test_global_func1.o:OK
#65/2 test_global_func2.o:OK
#65/3 test_global_func3.o:OK
#65/4 test_global_func4.o:OK
#65/5 test_global_func5.o:OK
#65/6 test_global_func6.o:OK
#65/7 test_global_func7.o:OK
#65 test_global_funcs:OK
test_test_lsm:PASS:skel_load 0 nsec
test_test_lsm:PASS:attach 0 nsec
test_test_lsm:PASS:exec_cmd 0 nsec
test_test_lsm:FAIL:bprm_count bprm_count = 0
test_test_lsm:FAIL:heap_mprotect want errno=EPERM, got 22
#66 test_lsm:FAIL
test_test_overhead:PASS:obj_open_file 0 nsec
test_test_overhead:PASS:find_probe 0 nsec
test_test_overhead:PASS:find_probe 0 nsec
test_test_overhead:PASS:find_probe 0 nsec
test_test_overhead:PASS:find_probe 0 nsec
test_test_overhead:PASS:find_probe 0 nsec
Caught signal #11!
Stack trace:
./test_progs(crash_handler+0x31)[0x56100f25eb51]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x12890)[0x7f9d8d225890]
/lib/x86_64-linux-gnu/libc.so.6(+0x18ef2d)[0x7f9d8cfb0f2d]
/lib/x86_64-linux-gnu/libc.so.6(__libc_calloc+0x372)[0x7f9d8cebc3a2]
/usr/local/lib/libelf.so.1(+0x33ce)[0x7f9d8d85a3ce]
/usr/local/lib/libelf.so.1(+0x3fb2)[0x7f9d8d85afb2]
./test_progs(btf__parse_elf+0x15d)[0x56100f27a141]
./test_progs(libbpf_find_kernel_btf+0x169)[0x56100f27ee83]
./test_progs(+0x43906)[0x56100f266906]
./test_progs(bpf_object__load_xattr+0xe5)[0x56100f26e93c]
./test_progs(bpf_object__load+0x47)[0x56100f26eafd]
./test_progs(test_test_overhead+0x252)[0x56100f24a922]
./test_progs(main+0x212)[0x56100f22f772]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x7f9d8ce43b97]
./test_progs(_start+0x2a)[0x56100f22f8fa]
Segmentation fault (core dumped)
#
(Before the series, it runs through fine on my side.)
Thanks,
Daniel
On 28-Mar 18:18, Daniel Borkmann wrote:
> Hey KP,
>
> On 3/27/20 8:28 PM, KP Singh wrote:
> > From: KP Singh <[email protected]>
> >
> > # v7 -> v8
> >
> > https://lore.kernel.org/bpf/[email protected]/
> >
> > * Removed CAP_MAC_ADMIN check from bpf_lsm_verify_prog. LSMs can add it
> > in their own bpf_prog hook. This can be revisited as a separate patch.
> > * Added Andrii and James' Ack/Review tags.
> > * Fixed an indentation issue and missing newlines in selftest error
> > a cases.
> > * Updated a comment as suggested by Alexei.
> > * Updated the documentation to use the newer libbpf API and some other
> > fixes.
> > * Rebase
> >
> > # v6 -> v7
> >
> > https://lore.kernel.org/bpf/[email protected]/
> >
> [...]
> > KP Singh (8):
> > bpf: Introduce BPF_PROG_TYPE_LSM
> > security: Refactor declaration of LSM hooks
> > bpf: lsm: provide attachment points for BPF LSM programs
> > bpf: lsm: Implement attach, detach and execution
> > bpf: lsm: Initialize the BPF LSM hooks
> > tools/libbpf: Add support for BPF_PROG_TYPE_LSM
> > bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
> > bpf: lsm: Add Documentation
>
> I was about to apply, but then I'm getting the following selftest issue on
> the added LSM one, ptal:
>
> # ./test_progs
> [...]
> #65/1 test_global_func1.o:OK
> #65/2 test_global_func2.o:OK
> #65/3 test_global_func3.o:OK
> #65/4 test_global_func4.o:OK
> #65/5 test_global_func5.o:OK
> #65/6 test_global_func6.o:OK
> #65/7 test_global_func7.o:OK
> #65 test_global_funcs:OK
> test_test_lsm:PASS:skel_load 0 nsec
> test_test_lsm:PASS:attach 0 nsec
> test_test_lsm:PASS:exec_cmd 0 nsec
> test_test_lsm:FAIL:bprm_count bprm_count = 0
> test_test_lsm:FAIL:heap_mprotect want errno=EPERM, got 22
The test seems to pass for me [classic, "works on my machine" ;)]
./test_progs -t test_lsm
#66 test_lsm:OK
Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED
and also in the complete run of test_progs.
Since the attachment succeeds and the hook does not get called, it
seems like "bpf" LSM is not being initialized and the hook, although
present, does not get called.
This indicates that "bpf" is not in CONFIG_LSM. It should, however, be
there by default as we added it to default value of CONFIG_LSM and
also for other DEFAULT_SECURITY_* options.
Let me know if that's the case and it fixes it.
- KP
> #66 test_lsm:FAIL
> test_test_overhead:PASS:obj_open_file 0 nsec
> test_test_overhead:PASS:find_probe 0 nsec
> test_test_overhead:PASS:find_probe 0 nsec
> test_test_overhead:PASS:find_probe 0 nsec
> test_test_overhead:PASS:find_probe 0 nsec
> test_test_overhead:PASS:find_probe 0 nsec
> Caught signal #11!
> Stack trace:
> ./test_progs(crash_handler+0x31)[0x56100f25eb51]
> /lib/x86_64-linux-gnu/libpthread.so.0(+0x12890)[0x7f9d8d225890]
> /lib/x86_64-linux-gnu/libc.so.6(+0x18ef2d)[0x7f9d8cfb0f2d]
> /lib/x86_64-linux-gnu/libc.so.6(__libc_calloc+0x372)[0x7f9d8cebc3a2]
> /usr/local/lib/libelf.so.1(+0x33ce)[0x7f9d8d85a3ce]
> /usr/local/lib/libelf.so.1(+0x3fb2)[0x7f9d8d85afb2]
> ./test_progs(btf__parse_elf+0x15d)[0x56100f27a141]
> ./test_progs(libbpf_find_kernel_btf+0x169)[0x56100f27ee83]
> ./test_progs(+0x43906)[0x56100f266906]
> ./test_progs(bpf_object__load_xattr+0xe5)[0x56100f26e93c]
> ./test_progs(bpf_object__load+0x47)[0x56100f26eafd]
> ./test_progs(test_test_overhead+0x252)[0x56100f24a922]
> ./test_progs(main+0x212)[0x56100f22f772]
> /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x7f9d8ce43b97]
> ./test_progs(_start+0x2a)[0x56100f22f8fa]
> Segmentation fault (core dumped)
> #
>
> (Before the series, it runs through fine on my side.)
>
> Thanks,
> Daniel
On Sat, Mar 28, 2020 at 08:56:36PM +0100, KP Singh wrote:
> Since the attachment succeeds and the hook does not get called, it
> seems like "bpf" LSM is not being initialized and the hook, although
> present, does not get called.
>
> This indicates that "bpf" is not in CONFIG_LSM. It should, however, be
> there by default as we added it to default value of CONFIG_LSM and
> also for other DEFAULT_SECURITY_* options.
>
> Let me know if that's the case and it fixes it.
Is the selftest expected to at least fail cleanly (i.e. not segfault)
when the BPF LSF is not built into the kernel?
--
Kees Cook
On Sat, Mar 28, 2020 at 10:50 PM Kees Cook <[email protected]> wrote:
>
> On Sat, Mar 28, 2020 at 08:56:36PM +0100, KP Singh wrote:
> > Since the attachment succeeds and the hook does not get called, it
> > seems like "bpf" LSM is not being initialized and the hook, although
> > present, does not get called.
> >
> > This indicates that "bpf" is not in CONFIG_LSM. It should, however, be
> > there by default as we added it to default value of CONFIG_LSM and
> > also for other DEFAULT_SECURITY_* options.
> >
> > Let me know if that's the case and it fixes it.
>
> Is the selftest expected to at least fail cleanly (i.e. not segfault)
I am not sure where the crash comes from, it does not look like it's test_lsm,
it seems to happen in test_overhead. Both seem to run fine for me.
- KP
> when the BPF LSF is not built into the kernel?
>
> --
> Kees Cook
On 28-Mar 23:30, KP Singh wrote:
> On Sat, Mar 28, 2020 at 10:50 PM Kees Cook <[email protected]> wrote:
> >
> > On Sat, Mar 28, 2020 at 08:56:36PM +0100, KP Singh wrote:
> > > Since the attachment succeeds and the hook does not get called, it
> > > seems like "bpf" LSM is not being initialized and the hook, although
> > > present, does not get called.
> > >
> > > This indicates that "bpf" is not in CONFIG_LSM. It should, however, be
> > > there by default as we added it to default value of CONFIG_LSM and
> > > also for other DEFAULT_SECURITY_* options.
> > >
> > > Let me know if that's the case and it fixes it.
> >
> > Is the selftest expected to at least fail cleanly (i.e. not segfault)
>
> I am not sure where the crash comes from, it does not look like it's test_lsm,
> it seems to happen in test_overhead. Both seem to run fine for me.
So I was able to reproduce the crash:
* Remove "bpf" from CONFIG_LSM
./test_progs -n 66,67
test_test_lsm:PASS:skel_load 0 nsec
test_test_lsm:PASS:attach 0 nsec
test_test_lsm:PASS:exec_cmd 0 nsec
test_test_lsm:FAIL:bprm_count bprm_count = 0
test_test_lsm:FAIL:heap_mprotect want errno=EPERM, got 0
#66 test_lsm:FAIL
Caught signal #11!
Stack trace:
./test_progs(crash_handler+0x1f)[0x55b7f9867acf]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x13520)[0x7fcf1467e520]
/lib/x86_64-linux-gnu/libc.so.6(+0x15f73d)[0x7fcf1460a73d]
/lib/x86_64-linux-gnu/libc.so.6(__libc_calloc+0x2ca)[0x7fcf1453286a]
/usr/lib/x86_64-linux-gnu/libelf.so.1(+0x37
[snip]
* The crash went away when I removed the heap_mprotect call, now the BPF
hook attached did not allow this operation, so it had no side-effects.
Which lead me to believe the crash could be a side-effect of this
operation. So I did:
--- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -29,7 +29,7 @@ int heap_mprotect(void)
if (buf == NULL)
return -ENOMEM;
- ret = mprotect(buf, sz, PROT_READ | PROT_EXEC);
+ ret = mprotect(buf, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
free(buf);
return ret;
}
and the crash went away. Which made me realize that the free
operation does not like memory without PROT_WRITE, So I did this:
diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
index fcd839e88540..78f125cc09b3 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -30,7 +30,7 @@ int heap_mprotect(void)
return -ENOMEM;
ret = mprotect(buf, sz, PROT_READ | PROT_EXEC);
- free(buf);
+ // free(buf);
return ret;
}
and the crash went away as well. So it indeed was a combination of:
* CONFIG_LSM not enabling the hook
* mprotect marking the memory as non-writeable
* free being called on the memory.
I will send a v9 which has the PROT_WRITE on the mprotect. Thanks
for noticing this!
- KP
>
> - KP
>
> > when the BPF LSF is not built into the kernel?
> >
> > --
> > Kees Cook
On 3/29/20 1:07 AM, KP Singh wrote:
> On 28-Mar 23:30, KP Singh wrote:
>> On Sat, Mar 28, 2020 at 10:50 PM Kees Cook <[email protected]> wrote:
>>>
>>> On Sat, Mar 28, 2020 at 08:56:36PM +0100, KP Singh wrote:
>>>> Since the attachment succeeds and the hook does not get called, it
>>>> seems like "bpf" LSM is not being initialized and the hook, although
>>>> present, does not get called.
>>>>
>>>> This indicates that "bpf" is not in CONFIG_LSM. It should, however, be
>>>> there by default as we added it to default value of CONFIG_LSM and
>>>> also for other DEFAULT_SECURITY_* options.
>>>>
>>>> Let me know if that's the case and it fixes it.
>>>
>>> Is the selftest expected to at least fail cleanly (i.e. not segfault)
>>
>> I am not sure where the crash comes from, it does not look like it's test_lsm,
>> it seems to happen in test_overhead. Both seem to run fine for me.
>
> So I was able to reproduce the crash:
>
> * Remove "bpf" from CONFIG_LSM
>
> ./test_progs -n 66,67
> test_test_lsm:PASS:skel_load 0 nsec
> test_test_lsm:PASS:attach 0 nsec
> test_test_lsm:PASS:exec_cmd 0 nsec
> test_test_lsm:FAIL:bprm_count bprm_count = 0
> test_test_lsm:FAIL:heap_mprotect want errno=EPERM, got 0
> #66 test_lsm:FAIL
> Caught signal #11!
> Stack trace:
> ./test_progs(crash_handler+0x1f)[0x55b7f9867acf]
> /lib/x86_64-linux-gnu/libpthread.so.0(+0x13520)[0x7fcf1467e520]
> /lib/x86_64-linux-gnu/libc.so.6(+0x15f73d)[0x7fcf1460a73d]
> /lib/x86_64-linux-gnu/libc.so.6(__libc_calloc+0x2ca)[0x7fcf1453286a]
> /usr/lib/x86_64-linux-gnu/libelf.so.1(+0x37
>
> [snip]
>
> * The crash went away when I removed the heap_mprotect call, now the BPF
> hook attached did not allow this operation, so it had no side-effects.
> Which lead me to believe the crash could be a side-effect of this
> operation. So I did:
>
> --- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
> @@ -29,7 +29,7 @@ int heap_mprotect(void)
> if (buf == NULL)
> return -ENOMEM;
>
> - ret = mprotect(buf, sz, PROT_READ | PROT_EXEC);
> + ret = mprotect(buf, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
> free(buf);
> return ret;
> }
>
> and the crash went away. Which made me realize that the free
> operation does not like memory without PROT_WRITE, So I did this:
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
> index fcd839e88540..78f125cc09b3 100644
> --- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
> @@ -30,7 +30,7 @@ int heap_mprotect(void)
> return -ENOMEM;
>
> ret = mprotect(buf, sz, PROT_READ | PROT_EXEC);
> - free(buf);
> + // free(buf);
> return ret;
> }
>
> and the crash went away as well. So it indeed was a combination of:
>
> * CONFIG_LSM not enabling the hook
> * mprotect marking the memory as non-writeable
> * free being called on the memory.
>
> I will send a v9 which has the PROT_WRITE on the mprotect. Thanks
> for noticing this!
And also explains the stack trace for __libc_calloc() where it's trying to zero the
area later on.
Thanks for the quick debugging,
Daniel