Skip to content

Commit 641cd7b

Browse files
committed
Merge branch 'bpf-lsm'
KP Singh says: ==================== ** 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. ** Changelog: - v8 -> v9: https://lore.kernel.org/bpf/[email protected]/ * Fixed a selftest crash when CONFIG_LSM doesn't have "bpf". * Added James' Ack. * Rebase. - 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. ==================== Signed-off-by: Daniel Borkmann <[email protected]>
2 parents e5fb60e + 4dece7f commit 641cd7b

File tree

31 files changed

+985
-670
lines changed

31 files changed

+985
-670
lines changed

Documentation/bpf/bpf_lsm.rst

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
.. SPDX-License-Identifier: GPL-2.0+
2+
.. Copyright (C) 2020 Google LLC.
3+
4+
================
5+
LSM BPF Programs
6+
================
7+
8+
These BPF programs allow runtime instrumentation of the LSM hooks by privileged
9+
users to implement system-wide MAC (Mandatory Access Control) and Audit
10+
policies using eBPF.
11+
12+
Structure
13+
---------
14+
15+
The example shows an eBPF program that can be attached to the ``file_mprotect``
16+
LSM hook:
17+
18+
.. c:function:: int file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot);
19+
20+
Other LSM hooks which can be instrumented can be found in
21+
``include/linux/lsm_hooks.h``.
22+
23+
eBPF programs that use :doc:`/bpf/btf` do not need to include kernel headers
24+
for accessing information from the attached eBPF program's context. They can
25+
simply declare the structures in the eBPF program and only specify the fields
26+
that need to be accessed.
27+
28+
.. code-block:: c
29+
30+
struct mm_struct {
31+
unsigned long start_brk, brk, start_stack;
32+
} __attribute__((preserve_access_index));
33+
34+
struct vm_area_struct {
35+
unsigned long start_brk, brk, start_stack;
36+
unsigned long vm_start, vm_end;
37+
struct mm_struct *vm_mm;
38+
} __attribute__((preserve_access_index));
39+
40+
41+
.. note:: The order of the fields is irrelevant.
42+
43+
This can be further simplified (if one has access to the BTF information at
44+
build time) by generating the ``vmlinux.h`` with:
45+
46+
.. code-block:: console
47+
48+
# bpftool btf dump file <path-to-btf-vmlinux> format c > vmlinux.h
49+
50+
.. note:: ``path-to-btf-vmlinux`` can be ``/sys/kernel/btf/vmlinux`` if the
51+
build environment matches the environment the BPF programs are
52+
deployed in.
53+
54+
The ``vmlinux.h`` can then simply be included in the BPF programs without
55+
requiring the definition of the types.
56+
57+
The eBPF programs can be declared using the``BPF_PROG``
58+
macros defined in `tools/lib/bpf/bpf_tracing.h`_. In this
59+
example:
60+
61+
* ``"lsm/file_mprotect"`` indicates the LSM hook that the program must
62+
be attached to
63+
* ``mprotect_audit`` is the name of the eBPF program
64+
65+
.. code-block:: c
66+
67+
SEC("lsm/file_mprotect")
68+
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
69+
unsigned long reqprot, unsigned long prot, int ret)
70+
{
71+
/* ret is the return value from the previous BPF program
72+
* or 0 if it's the first hook.
73+
*/
74+
if (ret != 0)
75+
return ret;
76+
77+
int is_heap;
78+
79+
is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
80+
vma->vm_end <= vma->vm_mm->brk);
81+
82+
/* Return an -EPERM or write information to the perf events buffer
83+
* for auditing
84+
*/
85+
if (is_heap)
86+
return -EPERM;
87+
}
88+
89+
The ``__attribute__((preserve_access_index))`` is a clang feature that allows
90+
the BPF verifier to update the offsets for the access at runtime using the
91+
:doc:`/bpf/btf` information. Since the BPF verifier is aware of the types, it
92+
also validates all the accesses made to the various types in the eBPF program.
93+
94+
Loading
95+
-------
96+
97+
eBPF programs can be loaded with the :manpage:`bpf(2)` syscall's
98+
``BPF_PROG_LOAD`` operation:
99+
100+
.. code-block:: c
101+
102+
struct bpf_object *obj;
103+
104+
obj = bpf_object__open("./my_prog.o");
105+
bpf_object__load(obj);
106+
107+
This can be simplified by using a skeleton header generated by ``bpftool``:
108+
109+
.. code-block:: console
110+
111+
# bpftool gen skeleton my_prog.o > my_prog.skel.h
112+
113+
and the program can be loaded by including ``my_prog.skel.h`` and using
114+
the generated helper, ``my_prog__open_and_load``.
115+
116+
Attachment to LSM Hooks
117+
-----------------------
118+
119+
The LSM allows attachment of eBPF programs as LSM hooks using :manpage:`bpf(2)`
120+
syscall's ``BPF_RAW_TRACEPOINT_OPEN`` operation or more simply by
121+
using the libbpf helper ``bpf_program__attach_lsm``.
122+
123+
The program can be detached from the LSM hook by *destroying* the ``link``
124+
link returned by ``bpf_program__attach_lsm`` using ``bpf_link__destroy``.
125+
126+
One can also use the helpers generated in ``my_prog.skel.h`` i.e.
127+
``my_prog__attach`` for attachment and ``my_prog__destroy`` for cleaning up.
128+
129+
Examples
130+
--------
131+
132+
An example eBPF program can be found in
133+
`tools/testing/selftests/bpf/progs/lsm.c`_ and the corresponding
134+
userspace code in `tools/testing/selftests/bpf/prog_tests/test_lsm.c`_
135+
136+
.. Links
137+
.. _tools/lib/bpf/bpf_tracing.h:
138+
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/lib/bpf/bpf_tracing.h
139+
.. _tools/testing/selftests/bpf/progs/lsm.c:
140+
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/progs/lsm.c
141+
.. _tools/testing/selftests/bpf/prog_tests/test_lsm.c:
142+
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/prog_tests/test_lsm.c

Documentation/bpf/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Program types
4545
prog_cgroup_sockopt
4646
prog_cgroup_sysctl
4747
prog_flow_dissector
48+
bpf_lsm
4849

4950

5051
Testing and debugging BPF

MAINTAINERS

+1
Original file line numberDiff line numberDiff line change
@@ -3147,6 +3147,7 @@ R: Martin KaFai Lau <[email protected]>
31473147
R: Song Liu <[email protected]>
31483148
R: Yonghong Song <[email protected]>
31493149
R: Andrii Nakryiko <[email protected]>
3150+
R: KP Singh <[email protected]>
31503151
31513152
31523153
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git

include/linux/bpf.h

+3
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,9 @@ extern const struct bpf_func_proto bpf_tcp_sock_proto;
15151515
extern const struct bpf_func_proto bpf_jiffies64_proto;
15161516
extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto;
15171517

1518+
const struct bpf_func_proto *bpf_tracing_func_proto(
1519+
enum bpf_func_id func_id, const struct bpf_prog *prog);
1520+
15181521
/* Shared helpers among cBPF and eBPF. */
15191522
void bpf_user_rnd_init_once(void);
15201523
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);

include/linux/bpf_lsm.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
/*
4+
* Copyright (C) 2020 Google LLC.
5+
*/
6+
7+
#ifndef _LINUX_BPF_LSM_H
8+
#define _LINUX_BPF_LSM_H
9+
10+
#include <linux/bpf.h>
11+
#include <linux/lsm_hooks.h>
12+
13+
#ifdef CONFIG_BPF_LSM
14+
15+
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
16+
RET bpf_lsm_##NAME(__VA_ARGS__);
17+
#include <linux/lsm_hook_defs.h>
18+
#undef LSM_HOOK
19+
20+
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
21+
const struct bpf_prog *prog);
22+
23+
#else /* !CONFIG_BPF_LSM */
24+
25+
static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
26+
const struct bpf_prog *prog)
27+
{
28+
return -EOPNOTSUPP;
29+
}
30+
31+
#endif /* CONFIG_BPF_LSM */
32+
33+
#endif /* _LINUX_BPF_LSM_H */

include/linux/bpf_types.h

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
7070
void *, void *)
7171
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
7272
void *, void *)
73+
#ifdef CONFIG_BPF_LSM
74+
BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
75+
void *, void *)
76+
#endif /* CONFIG_BPF_LSM */
7377
#endif
7478

7579
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)

0 commit comments

Comments
 (0)