Commit 641cd7b0 authored by Daniel Borkmann's avatar Daniel Borkmann

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/20190910115527.5235-1-kpsingh@chromium.org/).

** 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/20200327192854.31150-1-kpsingh@chromium.org/
* Fixed a selftest crash when CONFIG_LSM doesn't have "bpf".
* Added James' Ack.
* Rebase.

- v7 -> v8:
  https://lore.kernel.org/bpf/20200326142823.26277-1-kpsingh@chromium.org/
* 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/20200325152629.6904-1-kpsingh@chromium.org/
* 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/20200323164415.12943-1-kpsingh@chromium.org/
* 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/20200220175250.10795-1-kpsingh@chromium.org/
* 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/20200117212825.11755-1-kpsingh@chromium.org/
* 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/20191220154208.15895-1-kpsingh@chromium.org
* 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: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents e5fb60ee 4dece7f3
.. SPDX-License-Identifier: GPL-2.0+
.. Copyright (C) 2020 Google LLC.
================
LSM BPF Programs
================
These BPF programs allow runtime instrumentation of the LSM hooks by privileged
users to implement system-wide MAC (Mandatory Access Control) and Audit
policies using eBPF.
Structure
---------
The example shows an eBPF program that can be attached to the ``file_mprotect``
LSM hook:
.. c:function:: int file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot);
Other LSM hooks which can be instrumented can be found in
``include/linux/lsm_hooks.h``.
eBPF programs that use :doc:`/bpf/btf` do not need to include kernel headers
for accessing information from the attached eBPF program's context. They can
simply declare the structures in the eBPF program and only specify the fields
that need to be accessed.
.. code-block:: c
struct mm_struct {
unsigned long start_brk, brk, start_stack;
} __attribute__((preserve_access_index));
struct vm_area_struct {
unsigned long start_brk, brk, start_stack;
unsigned long vm_start, vm_end;
struct mm_struct *vm_mm;
} __attribute__((preserve_access_index));
.. note:: The order of the fields is irrelevant.
This can be further simplified (if one has access to the BTF information at
build time) by generating the ``vmlinux.h`` with:
.. code-block:: console
# bpftool btf dump file <path-to-btf-vmlinux> format c > vmlinux.h
.. note:: ``path-to-btf-vmlinux`` can be ``/sys/kernel/btf/vmlinux`` if the
build environment matches the environment the BPF programs are
deployed in.
The ``vmlinux.h`` can then simply be included in the BPF programs without
requiring the definition of the types.
The eBPF programs can be declared using the``BPF_PROG``
macros defined in `tools/lib/bpf/bpf_tracing.h`_. In this
example:
* ``"lsm/file_mprotect"`` indicates the LSM hook that the program must
be attached to
* ``mprotect_audit`` is the name of the eBPF program
.. code-block:: c
SEC("lsm/file_mprotect")
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot, int ret)
{
/* ret is the return value from the previous BPF program
* or 0 if it's the first hook.
*/
if (ret != 0)
return ret;
int is_heap;
is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk);
/* Return an -EPERM or write information to the perf events buffer
* for auditing
*/
if (is_heap)
return -EPERM;
}
The ``__attribute__((preserve_access_index))`` is a clang feature that allows
the BPF verifier to update the offsets for the access at runtime using the
:doc:`/bpf/btf` information. Since the BPF verifier is aware of the types, it
also validates all the accesses made to the various types in the eBPF program.
Loading
-------
eBPF programs can be loaded with the :manpage:`bpf(2)` syscall's
``BPF_PROG_LOAD`` operation:
.. code-block:: c
struct bpf_object *obj;
obj = bpf_object__open("./my_prog.o");
bpf_object__load(obj);
This can be simplified by using a skeleton header generated by ``bpftool``:
.. code-block:: console
# bpftool gen skeleton my_prog.o > my_prog.skel.h
and the program can be loaded by including ``my_prog.skel.h`` and using
the generated helper, ``my_prog__open_and_load``.
Attachment to LSM Hooks
-----------------------
The LSM allows attachment of eBPF programs as LSM hooks using :manpage:`bpf(2)`
syscall's ``BPF_RAW_TRACEPOINT_OPEN`` operation or more simply by
using the libbpf helper ``bpf_program__attach_lsm``.
The program can be detached from the LSM hook by *destroying* the ``link``
link returned by ``bpf_program__attach_lsm`` using ``bpf_link__destroy``.
One can also use the helpers generated in ``my_prog.skel.h`` i.e.
``my_prog__attach`` for attachment and ``my_prog__destroy`` for cleaning up.
Examples
--------
An example eBPF program can be found in
`tools/testing/selftests/bpf/progs/lsm.c`_ and the corresponding
userspace code in `tools/testing/selftests/bpf/prog_tests/test_lsm.c`_
.. Links
.. _tools/lib/bpf/bpf_tracing.h:
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/lib/bpf/bpf_tracing.h
.. _tools/testing/selftests/bpf/progs/lsm.c:
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/progs/lsm.c
.. _tools/testing/selftests/bpf/prog_tests/test_lsm.c:
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/prog_tests/test_lsm.c
...@@ -45,6 +45,7 @@ Program types ...@@ -45,6 +45,7 @@ Program types
prog_cgroup_sockopt prog_cgroup_sockopt
prog_cgroup_sysctl prog_cgroup_sysctl
prog_flow_dissector prog_flow_dissector
bpf_lsm
Testing and debugging BPF Testing and debugging BPF
......
...@@ -3147,6 +3147,7 @@ R: Martin KaFai Lau <kafai@fb.com> ...@@ -3147,6 +3147,7 @@ R: Martin KaFai Lau <kafai@fb.com>
R: Song Liu <songliubraving@fb.com> R: Song Liu <songliubraving@fb.com>
R: Yonghong Song <yhs@fb.com> R: Yonghong Song <yhs@fb.com>
R: Andrii Nakryiko <andriin@fb.com> R: Andrii Nakryiko <andriin@fb.com>
R: KP Singh <kpsingh@chromium.org>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
L: bpf@vger.kernel.org L: bpf@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
......
...@@ -1515,6 +1515,9 @@ extern const struct bpf_func_proto bpf_tcp_sock_proto; ...@@ -1515,6 +1515,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_jiffies64_proto;
extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_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. */ /* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void); void bpf_user_rnd_init_once(void);
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 Google LLC.
*/
#ifndef _LINUX_BPF_LSM_H
#define _LINUX_BPF_LSM_H
#include <linux/bpf.h>
#include <linux/lsm_hooks.h>
#ifdef CONFIG_BPF_LSM
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
RET bpf_lsm_##NAME(__VA_ARGS__);
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog);
#else /* !CONFIG_BPF_LSM */
static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_BPF_LSM */
#endif /* _LINUX_BPF_LSM_H */
...@@ -70,6 +70,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops, ...@@ -70,6 +70,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
void *, void *) void *, void *)
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension, BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
void *, void *) void *, void *)
#ifdef CONFIG_BPF_LSM
BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
void *, void *)
#endif /* CONFIG_BPF_LSM */
#endif #endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
......
This diff is collapsed.
This diff is collapsed.
...@@ -181,6 +181,7 @@ enum bpf_prog_type { ...@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM,
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -211,6 +212,7 @@ enum bpf_attach_type { ...@@ -211,6 +212,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY, BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT, BPF_TRACE_FEXIT,
BPF_MODIFY_RETURN, BPF_MODIFY_RETURN,
BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
}; };
......
...@@ -1616,6 +1616,18 @@ config KALLSYMS_BASE_RELATIVE ...@@ -1616,6 +1616,18 @@ config KALLSYMS_BASE_RELATIVE
# end of the "standard kernel features (expert users)" menu # end of the "standard kernel features (expert users)" menu
# syscall, maps, verifier # 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 config BPF_SYSCALL
bool "Enable bpf() system call" bool "Enable bpf() system call"
select BPF select BPF
......
...@@ -29,4 +29,5 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o ...@@ -29,4 +29,5 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
endif endif
ifeq ($(CONFIG_BPF_JIT),y) ifeq ($(CONFIG_BPF_JIT),y)
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
obj-${CONFIG_BPF_LSM} += bpf_lsm.o
endif endif
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Google LLC.
*/
#include <linux/filter.h>
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/lsm_hooks.h>
#include <linux/bpf_lsm.h>
#include <linux/kallsyms.h>
#include <linux/bpf_verifier.h>
/* For every LSM hook that allows attachment of BPF programs, declare a nop
* function where a BPF program can be attached.
*/
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
{ \
return DEFAULT; \
}
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
#define BPF_LSM_SYM_PREFX "bpf_lsm_"
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
{
if (!prog->gpl_compatible) {
bpf_log(vlog,
"LSM programs must have a GPL compatible license\n");
return -EINVAL;
}
if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name,
sizeof(BPF_LSM_SYM_PREFX) - 1)) {
bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
prog->aux->attach_btf_id, prog->aux->attach_func_name);
return -EINVAL;
}
return 0;
}
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,
};
...@@ -3710,7 +3710,21 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, ...@@ -3710,7 +3710,21 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
} }
if (arg == nr_args) { if (arg == nr_args) {
if (prog->expected_attach_type == BPF_TRACE_FEXIT) { if (prog->expected_attach_type == BPF_TRACE_FEXIT ||
prog->expected_attach_type == BPF_LSM_MAC) {
/* When LSM programs are attached to void LSM hooks
* they use FEXIT trampolines and when attached to
* int LSM hooks, they use MODIFY_RETURN trampolines.
*
* While the LSM programs are BPF_MODIFY_RETURN-like
* the check:
*
* if (ret_type != 'int')
* return -EINVAL;
*
* is _not_ done here. This is still safe as LSM hooks
* have only void and int return types.
*/
if (!t) if (!t)
return true; return true;
t = btf_type_by_id(btf, t->type); t = btf_type_by_id(btf, t->type);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/nospec.h> #include <linux/nospec.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <uapi/linux/btf.h> #include <uapi/linux/btf.h>
#include <linux/bpf_lsm.h>
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
...@@ -1935,6 +1936,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type, ...@@ -1935,6 +1936,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
switch (prog_type) { switch (prog_type) {
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_STRUCT_OPS: case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT: case BPF_PROG_TYPE_EXT:
break; break;
...@@ -2366,10 +2368,28 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) ...@@ -2366,10 +2368,28 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
struct file *link_file; struct file *link_file;
int link_fd, err; int link_fd, err;
if (prog->expected_attach_type != BPF_TRACE_FENTRY && switch (prog->type) {
prog->expected_attach_type != BPF_TRACE_FEXIT && case BPF_PROG_TYPE_TRACING:
prog->expected_attach_type != BPF_MODIFY_RETURN && if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
prog->type != BPF_PROG_TYPE_EXT) { prog->expected_attach_type != BPF_TRACE_FEXIT &&
prog->expected_attach_type != BPF_MODIFY_RETURN) {
err = -EINVAL;
goto out_put_prog;
}
break;
case BPF_PROG_TYPE_EXT:
if (prog->expected_attach_type != 0) {
err = -EINVAL;
goto out_put_prog;
}
break;
case BPF_PROG_TYPE_LSM:
if (prog->expected_attach_type != BPF_LSM_MAC) {
err = -EINVAL;
goto out_put_prog;
}
break;
default:
err = -EINVAL; err = -EINVAL;
goto out_put_prog; goto out_put_prog;
} }
...@@ -2448,16 +2468,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) ...@@ -2448,16 +2468,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
if (IS_ERR(prog)) if (IS_ERR(prog))
return PTR_ERR(prog); return PTR_ERR(prog);
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT && switch (prog->type) {
prog->type != BPF_PROG_TYPE_TRACING && case BPF_PROG_TYPE_TRACING:
prog->type != BPF_PROG_TYPE_EXT && case BPF_PROG_TYPE_EXT:
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) { case BPF_PROG_TYPE_LSM:
err = -EINVAL;
goto out_put_prog;
}
if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
if (attr->raw_tracepoint.name) { if (attr->raw_tracepoint.name) {
/* The attach point for this category of programs /* The attach point for this category of programs
* should be specified via btf_id during program load. * should be specified via btf_id during program load.
...@@ -2465,11 +2479,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) ...@@ -2465,11 +2479,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
err = -EINVAL; err = -EINVAL;
goto out_put_prog; goto out_put_prog;
} }
if (prog->expected_attach_type == BPF_TRACE_RAW_TP) if (prog->type == BPF_PROG_TYPE_TRACING &&
prog->expected_attach_type == BPF_TRACE_RAW_TP) {
tp_name = prog->aux->attach_func_name; tp_name = prog->aux->attach_func_name;
else break;
return bpf_tracing_prog_attach(prog); }
} else { return bpf_tracing_prog_attach(prog);
case BPF_PROG_TYPE_RAW_TRACEPOINT:
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
if (strncpy_from_user(buf, if (strncpy_from_user(buf,
u64_to_user_ptr(attr->raw_tracepoint.name), u64_to_user_ptr(attr->raw_tracepoint.name),
sizeof(buf) - 1) < 0) { sizeof(buf) - 1) < 0) {
...@@ -2478,6 +2495,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) ...@@ -2478,6 +2495,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
} }
buf[sizeof(buf) - 1] = 0; buf[sizeof(buf) - 1] = 0;
tp_name = buf; tp_name = buf;
break;
default:
err = -EINVAL;
goto out_put_prog;
} }
btp = bpf_get_raw_tracepoint(tp_name); btp = bpf_get_raw_tracepoint(tp_name);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/rbtree_latch.h> #include <linux/rbtree_latch.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/btf.h>
/* dummy _ops. The verifier will operate on target program's ops. */ /* dummy _ops. The verifier will operate on target program's ops. */
const struct bpf_verifier_ops bpf_extension_verifier_ops = { const struct bpf_verifier_ops bpf_extension_verifier_ops = {
...@@ -233,15 +234,23 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) ...@@ -233,15 +234,23 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
return err; return err;
} }
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t) static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
{ {
switch (t) { switch (prog->expected_attach_type) {
case BPF_TRACE_FENTRY: case BPF_TRACE_FENTRY:
return BPF_TRAMP_FENTRY; return BPF_TRAMP_FENTRY;
case BPF_MODIFY_RETURN: case BPF_MODIFY_RETURN:
return BPF_TRAMP_MODIFY_RETURN; return BPF_TRAMP_MODIFY_RETURN;
case BPF_TRACE_FEXIT: case BPF_TRACE_FEXIT:
return BPF_TRAMP_FEXIT; return BPF_TRAMP_FEXIT;
case BPF_LSM_MAC:
if (!prog->aux->attach_func_proto->type)
/* The function returns void, we cannot modify its
* return value.
*/
return BPF_TRAMP_FEXIT;
else
return BPF_TRAMP_MODIFY_RETURN;
default: default:
return BPF_TRAMP_REPLACE; return BPF_TRAMP_REPLACE;
} }
...@@ -255,7 +264,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog) ...@@ -255,7 +264,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
int cnt; int cnt;
tr = prog->aux->trampoline; tr = prog->aux->trampoline;
kind = bpf_attach_type_to_tramp(prog->expected_attach_type); kind = bpf_attach_type_to_tramp(prog);
mutex_lock(&tr->mutex); mutex_lock(&tr->mutex);
if (tr->extension_prog) { if (tr->extension_prog) {
/* cannot attach fentry/fexit if extension prog is attached. /* cannot attach fentry/fexit if extension prog is attached.
...@@ -305,7 +314,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog) ...@@ -305,7 +314,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
int err; int err;
tr = prog->aux->trampoline; tr = prog->aux->trampoline;
kind = bpf_attach_type_to_tramp(prog->expected_attach_type); kind = bpf_attach_type_to_tramp(prog);
mutex_lock(&tr->mutex); mutex_lock(&tr->mutex);
if (kind == BPF_TRAMP_REPLACE) { if (kind == BPF_TRAMP_REPLACE) {
WARN_ON_ONCE(!tr->extension_prog); WARN_ON_ONCE(!tr->extension_prog);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/error-injection.h> #include <linux/error-injection.h>
#include <linux/bpf_lsm.h>
#include "disasm.h" #include "disasm.h"
...@@ -6492,8 +6493,9 @@ static int check_return_code(struct bpf_verifier_env *env) ...@@ -6492,8 +6493,9 @@ static int check_return_code(struct bpf_verifier_env *env)
struct tnum range = tnum_range(0, 1); struct tnum range = tnum_range(0, 1);
int err; int err;
/* The struct_ops func-ptr's return type could be "void" */ /* LSM and struct_ops func-ptr's return type could be "void" */
if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS && if ((env->prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
env->prog->type == BPF_PROG_TYPE_LSM) &&
!prog->aux->attach_func_proto->type) !prog->aux->attach_func_proto->type)
return 0; return 0;
...@@ -9923,7 +9925,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9923,7 +9925,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
return check_struct_ops_btf_id(env); return check_struct_ops_btf_id(env);
if (prog->type != BPF_PROG_TYPE_TRACING && !prog_extension) if (prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_LSM &&
!prog_extension)
return 0; return 0;
if (!btf_id) { if (!btf_id) {
...@@ -10054,8 +10058,16 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -10054,8 +10058,16 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
return -EINVAL; return -EINVAL;
/* fallthrough */ /* fallthrough */
case BPF_MODIFY_RETURN: case BPF_MODIFY_RETURN:
case BPF_LSM_MAC:
case BPF_TRACE_FENTRY: case BPF_TRACE_FENTRY:
case BPF_TRACE_FEXIT: case BPF_TRACE_FEXIT:
prog->aux->attach_func_name = tname;
if (prog->type == BPF_PROG_TYPE_LSM) {
ret = bpf_lsm_verify_prog(&env->log, prog);
if (ret < 0)
return ret;
}
if (!btf_type_is_func(t)) { if (!btf_type_is_func(t)) {
verbose(env, "attach_btf_id %u is not a function\n", verbose(env, "attach_btf_id %u is not a function\n",
btf_id); btf_id);
...@@ -10070,7 +10082,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -10070,7 +10082,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
tr = bpf_trampoline_lookup(key); tr = bpf_trampoline_lookup(key);
if (!tr) if (!tr)
return -ENOMEM; return -ENOMEM;
prog->aux->attach_func_name = tname;
/* t is either vmlinux type or another program's type */ /* t is either vmlinux type or another program's type */
prog->aux->attach_func_proto = t; prog->aux->attach_func_proto = t;
mutex_lock(&tr->mutex); mutex_lock(&tr->mutex);
......
...@@ -779,8 +779,8 @@ static const struct bpf_func_proto bpf_send_signal_thread_proto = { ...@@ -779,8 +779,8 @@ static const struct bpf_func_proto bpf_send_signal_thread_proto = {
.arg1_type = ARG_ANYTHING, .arg1_type = ARG_ANYTHING,
}; };
static const struct bpf_func_proto * const struct bpf_func_proto *
tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{ {
switch (func_id) { switch (func_id) {
case BPF_FUNC_map_lookup_elem: 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) ...@@ -865,7 +865,7 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_override_return_proto; return &bpf_override_return_proto;
#endif #endif
default: 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) ...@@ -975,7 +975,7 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_get_stack: case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_tp; return &bpf_get_stack_proto_tp;
default: 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) ...@@ -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: case BPF_FUNC_read_branch_records:
return &bpf_read_branch_records_proto; return &bpf_read_branch_records_proto;
default: 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) ...@@ -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: case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_raw_tp; return &bpf_get_stack_proto_raw_tp;
default: default:
return tracing_func_proto(func_id, prog); return bpf_tracing_func_proto(func_id, prog);
} }
} }
......
...@@ -277,11 +277,11 @@ endchoice ...@@ -277,11 +277,11 @@ endchoice
config LSM config LSM
string "Ordered list of enabled LSMs" 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,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
default "lockdown,yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
default "lockdown,yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
help help
A comma-separated list of LSMs, in initialization order. A comma-separated list of LSMs, in initialization order.
Any LSMs left off this list will be ignored. This can be Any LSMs left off this list will be ignored. This can be
......
...@@ -12,6 +12,7 @@ subdir-$(CONFIG_SECURITY_YAMA) += yama ...@@ -12,6 +12,7 @@ subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
subdir-$(CONFIG_BPF_LSM) += bpf
# always enable default capabilities # always enable default capabilities
obj-y += commoncap.o obj-y += commoncap.o
...@@ -30,6 +31,7 @@ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ ...@@ -30,6 +31,7 @@ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
obj-$(CONFIG_BPF_LSM) += bpf/
# Object integrity file lists # Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity subdir-$(CONFIG_INTEGRITY) += integrity
......
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2020 Google LLC.
obj-$(CONFIG_BPF_LSM) := hooks.o
// 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,
};
...@@ -668,6 +668,25 @@ static void __init lsm_early_task(struct task_struct *task) ...@@ -668,6 +668,25 @@ static void __init lsm_early_task(struct task_struct *task)
panic("%s: Early task alloc failed.\n", __func__); panic("%s: Early task alloc failed.\n", __func__);
} }
/*
* The default value of the LSM hook is defined in linux/lsm_hook_defs.h and
* can be accessed with:
*
* LSM_RET_DEFAULT(<hook_name>)
*
* The macros below define static constants for the default value of each
* LSM hook.
*/
#define LSM_RET_DEFAULT(NAME) (NAME##_default)
#define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME)
#define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \
static const int LSM_RET_DEFAULT(NAME) = (DEFAULT);
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME)
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
/* /*
* Hook list operation macros. * Hook list operation macros.
* *
...@@ -1338,16 +1357,16 @@ int security_inode_getsecurity(struct inode *inode, const char *name, void **buf ...@@ -1338,16 +1357,16 @@ int security_inode_getsecurity(struct inode *inode, const char *name, void **buf
int rc; int rc;
if (unlikely(IS_PRIVATE(inode))) if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP; return LSM_RET_DEFAULT(inode_getsecurity);
/* /*
* Only one module will provide an attribute with a given name. * Only one module will provide an attribute with a given name.
*/ */
hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) { hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc); rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
if (rc != -EOPNOTSUPP) if (rc != LSM_RET_DEFAULT(inode_getsecurity))
return rc; return rc;
} }
return -EOPNOTSUPP; return LSM_RET_DEFAULT(inode_getsecurity);
} }
int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
...@@ -1356,17 +1375,17 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void ...@@ -1356,17 +1375,17 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void
int rc; int rc;
if (unlikely(IS_PRIVATE(inode))) if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP; return LSM_RET_DEFAULT(inode_setsecurity);
/* /*
* Only one module will provide an attribute with a given name. * Only one module will provide an attribute with a given name.
*/ */
hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) { hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
rc = hp->hook.inode_setsecurity(inode, name, value, size, rc = hp->hook.inode_setsecurity(inode, name, value, size,
flags); flags);
if (rc != -EOPNOTSUPP) if (rc != LSM_RET_DEFAULT(inode_setsecurity))
return rc; return rc;
} }
return -EOPNOTSUPP; return LSM_RET_DEFAULT(inode_setsecurity);
} }
int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
...@@ -1740,12 +1759,12 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, ...@@ -1740,12 +1759,12 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) unsigned long arg4, unsigned long arg5)
{ {
int thisrc; int thisrc;
int rc = -ENOSYS; int rc = LSM_RET_DEFAULT(task_prctl);
struct security_hook_list *hp; struct security_hook_list *hp;
hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) { hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5); thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != -ENOSYS) { if (thisrc != LSM_RET_DEFAULT(task_prctl)) {
rc = thisrc; rc = thisrc;
if (thisrc != 0) if (thisrc != 0)
break; break;
...@@ -1917,7 +1936,7 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name, ...@@ -1917,7 +1936,7 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
continue; continue;
return hp->hook.getprocattr(p, name, value); return hp->hook.getprocattr(p, name, value);
} }
return -EINVAL; return LSM_RET_DEFAULT(getprocattr);
} }
int security_setprocattr(const char *lsm, const char *name, void *value, int security_setprocattr(const char *lsm, const char *name, void *value,
...@@ -1930,7 +1949,7 @@ int security_setprocattr(const char *lsm, const char *name, void *value, ...@@ -1930,7 +1949,7 @@ int security_setprocattr(const char *lsm, const char *name, void *value,
continue; continue;
return hp->hook.setprocattr(name, value, size); return hp->hook.setprocattr(name, value, size);
} }
return -EINVAL; return LSM_RET_DEFAULT(setprocattr);
} }
int security_netlink_send(struct sock *sk, struct sk_buff *skb) int security_netlink_send(struct sock *sk, struct sk_buff *skb)
...@@ -2315,7 +2334,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x, ...@@ -2315,7 +2334,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
const struct flowi *fl) const struct flowi *fl)
{ {
struct security_hook_list *hp; struct security_hook_list *hp;
int rc = 1; int rc = LSM_RET_DEFAULT(xfrm_state_pol_flow_match);
/* /*
* Since this function is expected to return 0 or 1, the judgment * Since this function is expected to return 0 or 1, the judgment
......
...@@ -181,6 +181,7 @@ enum bpf_prog_type { ...@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM,
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -211,6 +212,7 @@ enum bpf_attach_type { ...@@ -211,6 +212,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY, BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT, BPF_TRACE_FEXIT,
BPF_MODIFY_RETURN, BPF_MODIFY_RETURN,
BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
}; };
......
...@@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, ...@@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
attr.prog_type = load_attr->prog_type; attr.prog_type = load_attr->prog_type;
attr.expected_attach_type = load_attr->expected_attach_type; attr.expected_attach_type = load_attr->expected_attach_type;
if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS) { if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS ||
attr.prog_type == BPF_PROG_TYPE_LSM) {
attr.attach_btf_id = load_attr->attach_btf_id; attr.attach_btf_id = load_attr->attach_btf_id;
} else if (attr.prog_type == BPF_PROG_TYPE_TRACING || } else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
attr.prog_type == BPF_PROG_TYPE_EXT) { attr.prog_type == BPF_PROG_TYPE_EXT) {
......
...@@ -2358,7 +2358,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj) ...@@ -2358,7 +2358,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
static inline bool libbpf_prog_needs_vmlinux_btf(struct bpf_program *prog) static inline bool libbpf_prog_needs_vmlinux_btf(struct bpf_program *prog)
{ {
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
prog->type == BPF_PROG_TYPE_LSM)
return true; return true;
/* BPF_PROG_TYPE_TRACING programs which do not attach to other programs /* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
...@@ -4866,7 +4867,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, ...@@ -4866,7 +4867,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.insns = insns; load_attr.insns = insns;
load_attr.insns_cnt = insns_cnt; load_attr.insns_cnt = insns_cnt;
load_attr.license = license; load_attr.license = license;
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) { if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
prog->type == BPF_PROG_TYPE_LSM) {
load_attr.attach_btf_id = prog->attach_btf_id; load_attr.attach_btf_id = prog->attach_btf_id;
} else if (prog->type == BPF_PROG_TYPE_TRACING || } else if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) { prog->type == BPF_PROG_TYPE_EXT) {
...@@ -4957,6 +4959,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver) ...@@ -4957,6 +4959,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
int err = 0, fd, i, btf_id; int err = 0, fd, i, btf_id;
if ((prog->type == BPF_PROG_TYPE_TRACING || if ((prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_LSM ||
prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) { prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
btf_id = libbpf_find_attach_btf_id(prog); btf_id = libbpf_find_attach_btf_id(prog);
if (btf_id <= 0) if (btf_id <= 0)
...@@ -6196,6 +6199,7 @@ bool bpf_program__is_##NAME(const struct bpf_program *prog) \ ...@@ -6196,6 +6199,7 @@ bool bpf_program__is_##NAME(const struct bpf_program *prog) \
} \ } \
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER); BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
BPF_PROG_TYPE_FNS(lsm, BPF_PROG_TYPE_LSM);
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE); BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS); BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT); BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
...@@ -6262,6 +6266,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, ...@@ -6262,6 +6266,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
struct bpf_program *prog); struct bpf_program *prog);
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec, static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog); struct bpf_program *prog);
static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
struct bpf_program *prog);
struct bpf_sec_def { struct bpf_sec_def {
const char *sec; const char *sec;
...@@ -6312,6 +6318,10 @@ static const struct bpf_sec_def section_defs[] = { ...@@ -6312,6 +6318,10 @@ static const struct bpf_sec_def section_defs[] = {
SEC_DEF("freplace/", EXT, SEC_DEF("freplace/", EXT,
.is_attach_btf = true, .is_attach_btf = true,
.attach_fn = attach_trace), .attach_fn = attach_trace),
SEC_DEF("lsm/", LSM,
.is_attach_btf = true,
.expected_attach_type = BPF_LSM_MAC,
.attach_fn = attach_lsm),
BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP), BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT), BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN), BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
...@@ -6574,6 +6584,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj, ...@@ -6574,6 +6584,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
} }
#define BTF_TRACE_PREFIX "btf_trace_" #define BTF_TRACE_PREFIX "btf_trace_"
#define BTF_LSM_PREFIX "bpf_lsm_"
#define BTF_MAX_NAME_SIZE 128 #define BTF_MAX_NAME_SIZE 128
static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix, static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
...@@ -6601,6 +6612,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name, ...@@ -6601,6 +6612,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,
if (attach_type == BPF_TRACE_RAW_TP) if (attach_type == BPF_TRACE_RAW_TP)
err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name, err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,
BTF_KIND_TYPEDEF); BTF_KIND_TYPEDEF);
else if (attach_type == BPF_LSM_MAC)
err = find_btf_by_prefix_kind(btf, BTF_LSM_PREFIX, name,
BTF_KIND_FUNC);
else else
err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
...@@ -7465,7 +7479,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, ...@@ -7465,7 +7479,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
return bpf_program__attach_raw_tracepoint(prog, tp_name); return bpf_program__attach_raw_tracepoint(prog, tp_name);
} }
struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) /* Common logic for all BPF program types that attach to a btf_id */
static struct bpf_link *bpf_program__attach_btf_id(struct bpf_program *prog)
{ {
char errmsg[STRERR_BUFSIZE]; char errmsg[STRERR_BUFSIZE];
struct bpf_link *link; struct bpf_link *link;
...@@ -7487,7 +7502,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) ...@@ -7487,7 +7502,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
if (pfd < 0) { if (pfd < 0) {
pfd = -errno; pfd = -errno;
free(link); free(link);
pr_warn("program '%s': failed to attach to trace: %s\n", pr_warn("program '%s': failed to attach: %s\n",
bpf_program__title(prog, false), bpf_program__title(prog, false),
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
return ERR_PTR(pfd); return ERR_PTR(pfd);
...@@ -7496,12 +7511,28 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) ...@@ -7496,12 +7511,28 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
return (struct bpf_link *)link; return (struct bpf_link *)link;
} }
struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
{
return bpf_program__attach_btf_id(prog);
}
struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog)
{
return bpf_program__attach_btf_id(prog);
}
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec, static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog) struct bpf_program *prog)
{ {
return bpf_program__attach_trace(prog); return bpf_program__attach_trace(prog);
} }
static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
struct bpf_program *prog)
{
return bpf_program__attach_lsm(prog);
}
struct bpf_link *bpf_program__attach(struct bpf_program *prog) struct bpf_link *bpf_program__attach(struct bpf_program *prog)
{ {
const struct bpf_sec_def *sec_def; const struct bpf_sec_def *sec_def;
......
...@@ -248,6 +248,8 @@ bpf_program__attach_raw_tracepoint(struct bpf_program *prog, ...@@ -248,6 +248,8 @@ bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
LIBBPF_API struct bpf_link * LIBBPF_API struct bpf_link *
bpf_program__attach_trace(struct bpf_program *prog); bpf_program__attach_trace(struct bpf_program *prog);
LIBBPF_API struct bpf_link *
bpf_program__attach_lsm(struct bpf_program *prog);
struct bpf_map; struct bpf_map;
LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map); LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map);
struct bpf_insn; struct bpf_insn;
...@@ -321,6 +323,7 @@ LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog); ...@@ -321,6 +323,7 @@ LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog); LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog); LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog); LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_lsm(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog); LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog); LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog); LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
...@@ -347,6 +350,7 @@ LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog); ...@@ -347,6 +350,7 @@ LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_lsm(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
......
...@@ -244,6 +244,9 @@ LIBBPF_0.0.8 { ...@@ -244,6 +244,9 @@ LIBBPF_0.0.8 {
bpf_link__pin_path; bpf_link__pin_path;
bpf_link__unpin; bpf_link__unpin;
bpf_map__set_initial_value; bpf_map__set_initial_value;
bpf_program__attach_lsm;
bpf_program__is_lsm;
bpf_program__set_attach_target; bpf_program__set_attach_target;
bpf_program__set_lsm;
bpf_set_link_xdp_fd_opts; bpf_set_link_xdp_fd_opts;
} LIBBPF_0.0.7; } LIBBPF_0.0.7;
...@@ -108,6 +108,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, ...@@ -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_TRACING:
case BPF_PROG_TYPE_STRUCT_OPS: case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT: case BPF_PROG_TYPE_EXT:
case BPF_PROG_TYPE_LSM:
default: default:
break; break;
} }
......
...@@ -35,3 +35,5 @@ CONFIG_MPLS_ROUTING=m ...@@ -35,3 +35,5 @@ CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m CONFIG_MPLS_IPTUNNEL=m
CONFIG_IPV6_SIT=m CONFIG_IPV6_SIT=m
CONFIG_BPF_JIT=y CONFIG_BPF_JIT=y
CONFIG_BPF_LSM=y
CONFIG_SECURITY=y
// 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_WRITE | 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);
}
// 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment