From: KP Singh <[email protected]>
# v4 -> v5
https://lwn.net/Articles/813057/
* 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 collected from
internal Linux deployments 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 | 150 +++++
Documentation/bpf/index.rst | 1 +
MAINTAINERS | 1 +
include/linux/bpf.h | 7 +
include/linux/bpf_lsm.h | 32 +
include/linux/bpf_types.h | 4 +
include/linux/lsm_hook_names.h | 354 ++++++++++
include/linux/lsm_hooks.h | 622 +-----------------
include/uapi/linux/bpf.h | 2 +
init/Kconfig | 10 +
kernel/bpf/Makefile | 1 +
kernel/bpf/bpf_lsm.c | 65 ++
kernel/bpf/btf.c | 9 +-
kernel/bpf/syscall.c | 26 +-
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 | 55 ++
tools/include/uapi/linux/bpf.h | 2 +
tools/lib/bpf/bpf.c | 3 +-
tools/lib/bpf/libbpf.c | 41 +-
tools/lib/bpf/libbpf.h | 4 +
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_probes.c | 1 +
tools/testing/selftests/bpf/lsm_helpers.h | 19 +
.../selftests/bpf/prog_tests/lsm_test.c | 112 ++++
.../selftests/bpf/progs/lsm_int_hook.c | 54 ++
.../selftests/bpf/progs/lsm_void_hook.c | 41 ++
31 files changed, 1038 insertions(+), 646 deletions(-)
create mode 100644 Documentation/bpf/bpf_lsm.rst
create mode 100644 include/linux/bpf_lsm.h
create mode 100644 include/linux/lsm_hook_names.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/lsm_helpers.h
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
--
2.20.1
From: KP Singh <[email protected]>
* Load/attach a BPF program to the file_mprotect (int) and
bprm_committed_creds (void) LSM hooks.
* Perform an action that triggers the hook.
* Verify if the audit event was received using a shared global
result variable.
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]>
---
tools/testing/selftests/bpf/lsm_helpers.h | 19 +++
.../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++
.../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++
.../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++
4 files changed, 226 insertions(+)
create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
new file mode 100644
index 000000000000..3de230df93db
--- /dev/null
+++ b/tools/testing/selftests/bpf/lsm_helpers.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+#ifndef _LSM_HELPERS_H
+#define _LSM_HELPERS_H
+
+struct lsm_prog_result {
+ /* This ensures that the LSM Hook only monitors the PID requested
+ * by the loader
+ */
+ __u32 monitored_pid;
+ /* The number of calls to the prog for the monitored PID.
+ */
+ __u32 count;
+};
+
+#endif /* _LSM_HELPERS_H */
diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
new file mode 100644
index 000000000000..5fd6b8f569f7
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
@@ -0,0 +1,112 @@
+// 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_helpers.h"
+#include "lsm_void_hook.skel.h"
+#include "lsm_int_hook.skel.h"
+
+char *LS_ARGS[] = {"true", NULL};
+
+int heap_mprotect(void)
+{
+ void *buf;
+ long sz;
+
+ sz = sysconf(_SC_PAGESIZE);
+ if (sz < 0)
+ return sz;
+
+ buf = memalign(sz, 2 * sz);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ return mprotect(buf, sz, PROT_READ | PROT_EXEC);
+}
+
+int exec_ls(struct lsm_prog_result *result)
+{
+ int child_pid;
+
+ child_pid = fork();
+ if (child_pid == 0) {
+ result->monitored_pid = getpid();
+ execvp(LS_ARGS[0], LS_ARGS);
+ return -EINVAL;
+ } else if (child_pid > 0)
+ return wait(NULL);
+
+ return -EINVAL;
+}
+
+void test_lsm_void_hook(void)
+{
+ struct lsm_prog_result *result;
+ struct lsm_void_hook *skel = NULL;
+ int err, duration = 0;
+
+ skel = lsm_void_hook__open_and_load();
+ if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n"))
+ goto close_prog;
+
+ err = lsm_void_hook__attach(skel);
+ if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err))
+ goto close_prog;
+
+ result = &skel->bss->result;
+
+ err = exec_ls(result);
+ if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno))
+ goto close_prog;
+
+ if (CHECK(result->count != 1, "count", "count = %d", result->count))
+ goto close_prog;
+
+ CHECK_FAIL(result->count != 1);
+
+close_prog:
+ lsm_void_hook__destroy(skel);
+}
+
+void test_lsm_int_hook(void)
+{
+ struct lsm_prog_result *result;
+ struct lsm_int_hook *skel = NULL;
+ int err, duration = 0;
+
+ skel = lsm_int_hook__open_and_load();
+ if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n"))
+ goto close_prog;
+
+ err = lsm_int_hook__attach(skel);
+ if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err))
+ goto close_prog;
+
+ result = &skel->bss->result;
+ result->monitored_pid = getpid();
+
+ err = heap_mprotect();
+ if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
+ errno))
+ goto close_prog;
+
+ CHECK_FAIL(result->count != 1);
+
+close_prog:
+ lsm_int_hook__destroy(skel);
+}
+
+void test_lsm_test(void)
+{
+ test_lsm_void_hook();
+ test_lsm_int_hook();
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
new file mode 100644
index 000000000000..1c5028ddca61
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020 Google LLC.
+ */
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+#include "lsm_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct lsm_prog_result result = {
+ .monitored_pid = 0,
+ .count = 0,
+};
+
+/*
+ * Define some of the structs used in the BPF program.
+ * Only the field names and their sizes need to be the
+ * same as the kernel type, the order is irrelevant.
+ */
+struct mm_struct {
+ unsigned long start_brk, brk;
+} __attribute__((preserve_access_index));
+
+struct vm_area_struct {
+ unsigned long vm_start, vm_end;
+ struct mm_struct *vm_mm;
+} __attribute__((preserve_access_index));
+
+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();
+ int is_heap = 0;
+
+ is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk);
+
+ if (is_heap && result.monitored_pid == pid) {
+ result.count++;
+ ret = -EPERM;
+ }
+
+ return ret;
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
new file mode 100644
index 000000000000..4d01a8536413
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+#include "lsm_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct lsm_prog_result result = {
+ .monitored_pid = 0,
+ .count = 0,
+};
+
+/*
+ * Define some of the structs used in the BPF program.
+ * Only the field names and their sizes need to be the
+ * same as the kernel type, the order is irrelevant.
+ */
+struct linux_binprm {
+ const char *filename;
+} __attribute__((preserve_access_index));
+
+SEC("lsm/bprm_committed_creds")
+int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
+{
+ __u32 pid = bpf_get_current_pid_tgid();
+ char fmt[] = "lsm(bprm_committed_creds): process executed %s\n";
+
+ bpf_trace_printk(fmt, sizeof(fmt), bprm->filename);
+ if (result.monitored_pid == pid)
+ result.count++;
+
+ return 0;
+}
--
2.20.1
From: KP Singh <[email protected]>
The bpf_lsm_ nops are initialized into the LSM framework like any other
LSM. Some LSM hooks do not have 0 as their default return value. The
__weak symbol for these hooks is overridden by a corresponding
definition in security/bpf/hooks.c
The LSM can be enabled / disabled with CONFIG_LSM.
Signed-off-by: KP Singh <[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 | 55 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 67 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..68e5824868f9
--- /dev/null
+++ b/security/bpf/hooks.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+#include <linux/lsm_hooks.h>
+#include <linux/bpf_lsm.h>
+
+/* Some LSM hooks do not have 0 as their default return values. Override the
+ * __weak definitons generated by default for these hooks
+ */
+noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
+ void **buffer, bool alloc)
+{
+ return -EOPNOTSUPP;
+}
+
+noinline int bpf_lsm_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size,
+ int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+noinline int bpf_lsm_task_prctl(int option, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5)
+{
+ return -ENOSYS;
+}
+
+noinline int bpf_lsm_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp,
+ const struct flowi *fl)
+{
+ return 1;
+}
+
+static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {
+ #define LSM_HOOK(RET, NAME, ...) LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
+ #include <linux/lsm_hook_names.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]>
The information about the different types of LSM hooks is scattered
in two locations i.e. union security_list_options and
struct security_hook_heads. Rather than duplicating this information
even further for BPF_PROG_TYPE_LSM, define all the hooks with the
LSM_HOOK macro in lsm_hook_names.h which is then used to generate all
the data structures required by the LSM framework.
Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
---
include/linux/lsm_hook_names.h | 354 +++++++++++++++++++
include/linux/lsm_hooks.h | 622 +--------------------------------
2 files changed, 360 insertions(+), 616 deletions(-)
create mode 100644 include/linux/lsm_hook_names.h
diff --git a/include/linux/lsm_hook_names.h b/include/linux/lsm_hook_names.h
new file mode 100644
index 000000000000..412e4ca24c9b
--- /dev/null
+++ b/include/linux/lsm_hook_names.h
@@ -0,0 +1,354 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Linux Security Module Hook declarations.
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <[email protected]>
+ * Copyright (C) 2001 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <[email protected]>
+ * Copyright (C) 2001 James Morris <[email protected]>
+ * Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
+ * Copyright (C) 2015 Intel Corporation.
+ * Copyright (C) 2015 Casey Schaufler <[email protected]>
+ * Copyright (C) 2016 Mellanox Techonologies
+ * Copyright (C) 2020 Google LLC.
+ */
+
+/* The macro LSM_HOOK is used to define the data structures required by the
+ * the LSM framework using the pattern:
+ *
+ * struct security_hook_heads {
+ * #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
+ * #include <linux/lsm_hook_names.h>
+ * #undef LSM_HOOK
+ * };
+ */
+LSM_HOOK(int, binder_set_context_mgr, struct task_struct *mgr)
+LSM_HOOK(int, binder_transaction, struct task_struct *from,
+ struct task_struct *to)
+LSM_HOOK(int, binder_transfer_binder, struct task_struct *from,
+ struct task_struct *to)
+LSM_HOOK(int, binder_transfer_file, struct task_struct *from,
+ struct task_struct *to, struct file *file)
+LSM_HOOK(int, ptrace_access_check, struct task_struct *child, unsigned int mode)
+LSM_HOOK(int, ptrace_traceme, struct task_struct *parent)
+LSM_HOOK(int, capget, struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+LSM_HOOK(int, capset, struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective, const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
+LSM_HOOK(int, capable, const struct cred *cred, struct user_namespace *ns,
+ int cap, unsigned int opts)
+LSM_HOOK(int, quotactl, int cmds, int type, int id, struct super_block *sb)
+LSM_HOOK(int, quota_on, struct dentry *dentry)
+LSM_HOOK(int, syslog, int type)
+LSM_HOOK(int, settime, const struct timespec64 *ts, const struct timezone *tz)
+LSM_HOOK(int, vm_enough_memory, struct mm_struct *mm, long pages)
+LSM_HOOK(int, bprm_set_creds, struct linux_binprm *bprm)
+LSM_HOOK(int, bprm_check_security, struct linux_binprm *bprm)
+LSM_HOOK(void, bprm_committing_creds, struct linux_binprm *bprm)
+LSM_HOOK(void, bprm_committed_creds, struct linux_binprm *bprm)
+LSM_HOOK(int, fs_context_dup, struct fs_context *fc, struct fs_context *src_sc)
+LSM_HOOK(int, fs_context_parse_param, struct fs_context *fc,
+ struct fs_parameter *param)
+LSM_HOOK(int, sb_alloc_security, struct super_block *sb)
+LSM_HOOK(void, sb_free_security, struct super_block *sb)
+LSM_HOOK(void, sb_free_mnt_opts, void *mnt_opts)
+LSM_HOOK(int, sb_eat_lsm_opts, char *orig, void **mnt_opts)
+LSM_HOOK(int, sb_remount, struct super_block *sb, void *mnt_opts)
+LSM_HOOK(int, sb_kern_mount, struct super_block *sb)
+LSM_HOOK(int, sb_show_options, struct seq_file *m, struct super_block *sb)
+LSM_HOOK(int, sb_statfs, struct dentry *dentry)
+LSM_HOOK(int, sb_mount, const char *dev_name, const struct path *path,
+ const char *type, unsigned long flags, void *data)
+LSM_HOOK(int, sb_umount, struct vfsmount *mnt, int flags)
+LSM_HOOK(int, sb_pivotroot, const struct path *old_path,
+ const struct path *new_path)
+LSM_HOOK(int, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
+ unsigned long kern_flags, unsigned long *set_kern_flags)
+LSM_HOOK(int, sb_clone_mnt_opts, const struct super_block *oldsb,
+ struct super_block *newsb, unsigned long kern_flags,
+ unsigned long *set_kern_flags)
+LSM_HOOK(int, sb_add_mnt_opt, const char *option, const char *val, int len,
+ void **mnt_opts)
+LSM_HOOK(int, move_mount, const struct path *from_path,
+ const struct path *to_path)
+LSM_HOOK(int, dentry_init_security, struct dentry *dentry, int mode,
+ const struct qstr *name, void **ctx, u32 *ctxlen)
+LSM_HOOK(int, dentry_create_files_as, struct dentry *dentry, int mode,
+ struct qstr *name, const struct cred *old, struct cred *new)
+#ifdef CONFIG_SECURITY_PATH
+LSM_HOOK(int, path_unlink, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, path_mkdir, const struct path *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, path_rmdir, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, path_mknod, const struct path *dir, struct dentry *dentry,
+ umode_t mode, unsigned int dev)
+LSM_HOOK(int, path_truncate, const struct path *path)
+LSM_HOOK(int, path_symlink, const struct path *dir, struct dentry *dentry,
+ const char *old_name)
+LSM_HOOK(int, path_link, struct dentry *old_dentry, const struct path *new_dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, path_rename, const struct path *old_dir,
+ struct dentry *old_dentry, const struct path *new_dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, path_chmod, const struct path *path, umode_t mode)
+LSM_HOOK(int, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
+LSM_HOOK(int, path_chroot, const struct path *path)
+#endif
+
+/* Needed for inode based security check */
+LSM_HOOK(int, path_notify, const struct path *path, u64 mask,
+ unsigned int obj_type)
+LSM_HOOK(int, inode_alloc_security, struct inode *inode)
+LSM_HOOK(void, inode_free_security, struct inode *inode)
+LSM_HOOK(int, inode_init_security, struct inode *inode, struct inode *dir,
+ const struct qstr *qstr, const char **name, void **value, size_t *len)
+LSM_HOOK(int, inode_create, struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, inode_link, struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, inode_unlink, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, inode_symlink, struct inode *dir, struct dentry *dentry,
+ const char *old_name)
+LSM_HOOK(int, inode_mkdir, struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, inode_rmdir, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, inode_mknod, struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+LSM_HOOK(int, inode_rename, struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+LSM_HOOK(int, inode_readlink, struct dentry *dentry)
+LSM_HOOK(int, inode_follow_link, struct dentry *dentry, struct inode *inode,
+ bool rcu)
+LSM_HOOK(int, inode_permission, struct inode *inode, int mask)
+LSM_HOOK(int, inode_setattr, struct dentry *dentry, struct iattr *attr)
+LSM_HOOK(int, inode_getattr, const struct path *path)
+LSM_HOOK(int, inode_setxattr, struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(void, inode_post_setxattr, struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(int, inode_getxattr, struct dentry *dentry, const char *name)
+LSM_HOOK(int, inode_listxattr, struct dentry *dentry)
+LSM_HOOK(int, inode_removexattr, struct dentry *dentry, const char *name)
+LSM_HOOK(int, inode_need_killpriv, struct dentry *dentry)
+LSM_HOOK(int, inode_killpriv, struct dentry *dentry)
+LSM_HOOK(int, inode_getsecurity, struct inode *inode, const char *name,
+ void **buffer, bool alloc)
+LSM_HOOK(int, inode_setsecurity, struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(int, inode_listsecurity, struct inode *inode, char *buffer,
+ size_t buffer_size)
+LSM_HOOK(void, inode_getsecid, struct inode *inode, u32 *secid)
+LSM_HOOK(int, inode_copy_up, struct dentry *src, struct cred **new)
+LSM_HOOK(int, inode_copy_up_xattr, const char *name)
+LSM_HOOK(int, kernfs_init_security, struct kernfs_node *kn_dir,
+ struct kernfs_node *kn)
+LSM_HOOK(int, file_permission, struct file *file, int mask)
+LSM_HOOK(int, file_alloc_security, struct file *file)
+LSM_HOOK(void, file_free_security, struct file *file)
+LSM_HOOK(int, file_ioctl, struct file *file, unsigned int cmd,
+ unsigned long arg)
+LSM_HOOK(int, mmap_addr, unsigned long addr)
+LSM_HOOK(int, mmap_file, struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+LSM_HOOK(int, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
+LSM_HOOK(int, file_lock, struct file *file, unsigned int cmd)
+LSM_HOOK(int, file_fcntl, struct file *file, unsigned int cmd,
+ unsigned long arg)
+LSM_HOOK(void, file_set_fowner, struct file *file)
+LSM_HOOK(int, file_send_sigiotask, struct task_struct *tsk,
+ struct fown_struct *fown, int sig)
+LSM_HOOK(int, file_receive, struct file *file)
+LSM_HOOK(int, file_open, struct file *file)
+LSM_HOOK(int, task_alloc, struct task_struct *task, unsigned long clone_flags)
+LSM_HOOK(void, task_free, struct task_struct *task)
+LSM_HOOK(int, cred_alloc_blank, struct cred *cred, gfp_t gfp)
+LSM_HOOK(void, cred_free, struct cred *cred)
+LSM_HOOK(int, cred_prepare, struct cred *new, const struct cred *old, gfp_t gfp)
+LSM_HOOK(void, cred_transfer, struct cred *new, const struct cred *old)
+LSM_HOOK(void, cred_getsecid, const struct cred *c, u32 *secid)
+LSM_HOOK(int, kernel_act_as, struct cred *new, u32 secid)
+LSM_HOOK(int, kernel_create_files_as, struct cred *new, struct inode *inode)
+LSM_HOOK(int, kernel_module_request, char *kmod_name)
+LSM_HOOK(int, kernel_load_data, enum kernel_load_data_id id)
+LSM_HOOK(int, kernel_read_file, struct file *file, enum kernel_read_file_id id)
+LSM_HOOK(int, kernel_post_read_file, struct file *file, char *buf, loff_t size,
+ enum kernel_read_file_id id)
+LSM_HOOK(int, task_fix_setuid, struct cred *new, const struct cred *old,
+ int flags)
+LSM_HOOK(int, task_setpgid, struct task_struct *p, pid_t pgid)
+LSM_HOOK(int, task_getpgid, struct task_struct *p)
+LSM_HOOK(int, task_getsid, struct task_struct *p)
+LSM_HOOK(void, task_getsecid, struct task_struct *p, u32 *secid)
+LSM_HOOK(int, task_setnice, struct task_struct *p, int nice)
+LSM_HOOK(int, task_setioprio, struct task_struct *p, int ioprio)
+LSM_HOOK(int, task_getioprio, struct task_struct *p)
+LSM_HOOK(int, task_prlimit, const struct cred *cred, const struct cred *tcred,
+ unsigned int flags)
+LSM_HOOK(int, task_setrlimit, struct task_struct *p, unsigned int resource,
+ struct rlimit *new_rlim)
+LSM_HOOK(int, task_setscheduler, struct task_struct *p)
+LSM_HOOK(int, task_getscheduler, struct task_struct *p)
+LSM_HOOK(int, task_movememory, struct task_struct *p)
+LSM_HOOK(int, task_kill, struct task_struct *p, struct kernel_siginfo *info,
+ int sig, const struct cred *cred)
+LSM_HOOK(int, task_prctl, int option, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5)
+LSM_HOOK(void, task_to_inode, struct task_struct *p, struct inode *inode)
+LSM_HOOK(int, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
+LSM_HOOK(void, ipc_getsecid, struct kern_ipc_perm *ipcp, u32 *secid)
+LSM_HOOK(int, msg_msg_alloc_security, struct msg_msg *msg)
+LSM_HOOK(void, msg_msg_free_security, struct msg_msg *msg)
+LSM_HOOK(int, msg_queue_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, msg_queue_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg)
+LSM_HOOK(int, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, msg_queue_msgsnd, struct kern_ipc_perm *perm, struct msg_msg *msg,
+ int msqflg)
+LSM_HOOK(int, msg_queue_msgrcv, struct kern_ipc_perm *perm, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+LSM_HOOK(int, shm_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, shm_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, shm_associate, struct kern_ipc_perm *perm, int shmflg)
+LSM_HOOK(int, shm_shmctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, shm_shmat, struct kern_ipc_perm *perm, char __user *shmaddr,
+ int shmflg)
+LSM_HOOK(int, sem_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, sem_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, sem_associate, struct kern_ipc_perm *perm, int semflg)
+LSM_HOOK(int, sem_semctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops,
+ unsigned nsops, int alter)
+LSM_HOOK(int, netlink_send, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(void, d_instantiate, struct dentry *dentry, struct inode *inode)
+LSM_HOOK(int, getprocattr, struct task_struct *p, char *name, char **value)
+LSM_HOOK(int, setprocattr, const char *name, void *value, size_t size)
+LSM_HOOK(int, ismaclabel, const char *name)
+LSM_HOOK(int, secid_to_secctx, u32 secid, char **secdata, u32 *seclen)
+LSM_HOOK(int, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
+LSM_HOOK(void, release_secctx, char *secdata, u32 seclen)
+LSM_HOOK(void, inode_invalidate_secctx, struct inode *inode)
+LSM_HOOK(int, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
+LSM_HOOK(int, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen)
+LSM_HOOK(int, inode_getsecctx, struct inode *inode, void **ctx, u32 *ctxlen)
+#ifdef CONFIG_SECURITY_NETWORK
+LSM_HOOK(int, unix_stream_connect, struct sock *sock, struct sock *other,
+ struct sock *newsk)
+LSM_HOOK(int, unix_may_send, struct socket *sock, struct socket *other)
+LSM_HOOK(int, socket_create, int family, int type, int protocol, int kern)
+LSM_HOOK(int, socket_post_create, struct socket *sock, int family, int type,
+ int protocol, int kern)
+LSM_HOOK(int, socket_socketpair, struct socket *socka, struct socket *sockb)
+LSM_HOOK(int, socket_bind, struct socket *sock, struct sockaddr *address,
+ int addrlen)
+LSM_HOOK(int, socket_connect, struct socket *sock, struct sockaddr *address,
+ int addrlen)
+LSM_HOOK(int, socket_listen, struct socket *sock, int backlog)
+LSM_HOOK(int, socket_accept, struct socket *sock, struct socket *newsock)
+LSM_HOOK(int, socket_sendmsg, struct socket *sock, struct msghdr *msg, int size)
+LSM_HOOK(int, socket_recvmsg, struct socket *sock, struct msghdr *msg, int size,
+ int flags)
+LSM_HOOK(int, socket_getsockname, struct socket *sock)
+LSM_HOOK(int, socket_getpeername, struct socket *sock)
+LSM_HOOK(int, socket_getsockopt, struct socket *sock, int level, int optname)
+LSM_HOOK(int, socket_setsockopt, struct socket *sock, int level, int optname)
+LSM_HOOK(int, socket_shutdown, struct socket *sock, int how)
+LSM_HOOK(int, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(int, socket_getpeersec_stream, struct socket *sock,
+ char __user *optval, int __user *optlen, unsigned len)
+LSM_HOOK(int, socket_getpeersec_dgram, struct socket *sock, struct sk_buff *skb,
+ u32 *secid)
+LSM_HOOK(int, sk_alloc_security, struct sock *sk, int family, gfp_t priority)
+LSM_HOOK(void, sk_free_security, struct sock *sk)
+LSM_HOOK(void, sk_clone_security, const struct sock *sk, struct sock *newsk)
+LSM_HOOK(void, sk_getsecid, struct sock *sk, u32 *secid)
+LSM_HOOK(void, sock_graft, struct sock *sk, struct socket *parent)
+LSM_HOOK(int, inet_conn_request, struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+LSM_HOOK(void, inet_csk_clone, struct sock *newsk,
+ const struct request_sock *req)
+LSM_HOOK(void, inet_conn_established, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(int, secmark_relabel_packet, u32 secid)
+LSM_HOOK(void, secmark_refcount_inc, void)
+LSM_HOOK(void, secmark_refcount_dec, void)
+LSM_HOOK(void, req_classify_flow, const struct request_sock *req,
+ struct flowi *fl)
+LSM_HOOK(int, tun_dev_alloc_security, void **security)
+LSM_HOOK(void, tun_dev_free_security, void *security)
+LSM_HOOK(int, tun_dev_create, void)
+LSM_HOOK(int, tun_dev_attach_queue, void *security)
+LSM_HOOK(int, tun_dev_attach, struct sock *sk, void *security)
+LSM_HOOK(int, tun_dev_open, void *security)
+LSM_HOOK(int, sctp_assoc_request, struct sctp_endpoint *ep, struct sk_buff *skb)
+LSM_HOOK(int, sctp_bind_connect, struct sock *sk, int optname,
+ struct sockaddr *address, int addrlen)
+LSM_HOOK(void, sctp_sk_clone, struct sctp_endpoint *ep, struct sock *sk,
+ struct sock *newsk)
+#endif /* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_SECURITY_INFINIBAND
+LSM_HOOK(int, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey)
+LSM_HOOK(int, ib_endport_manage_subnet, void *sec, const char *dev_name,
+ u8 port_num)
+LSM_HOOK(int, ib_alloc_security, void **sec)
+LSM_HOOK(void, ib_free_security, void *sec)
+#endif /* CONFIG_SECURITY_INFINIBAND */
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+LSM_HOOK(int, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *sec_ctx, gfp_t gfp)
+LSM_HOOK(int, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctx)
+LSM_HOOK(void, xfrm_policy_free_security, struct xfrm_sec_ctx *ctx)
+LSM_HOOK(int, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx)
+LSM_HOOK(int, xfrm_state_alloc, struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx)
+LSM_HOOK(int, xfrm_state_alloc_acquire, struct xfrm_state *x,
+ struct xfrm_sec_ctx *polsec, u32 secid)
+LSM_HOOK(void, xfrm_state_free_security, struct xfrm_state *x)
+LSM_HOOK(int, xfrm_state_delete_security, struct xfrm_state *x)
+LSM_HOOK(int, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid,
+ u8 dir)
+LSM_HOOK(int, xfrm_state_pol_flow_match, struct xfrm_state *x,
+ struct xfrm_policy *xp, const struct flowi *fl)
+LSM_HOOK(int, xfrm_decode_session, struct sk_buff *skb, u32 *secid, int ckall)
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+
+/* key management security hooks */
+#ifdef CONFIG_KEYS
+LSM_HOOK(int, key_alloc, struct key *key, const struct cred *cred,
+ unsigned long flags)
+LSM_HOOK(void, key_free, struct key *key)
+LSM_HOOK(int, key_permission, key_ref_t key_ref, const struct cred *cred,
+ unsigned perm)
+LSM_HOOK(int, key_getsecurity, struct key *key, char **_buffer)
+#endif /* CONFIG_KEYS */
+
+#ifdef CONFIG_AUDIT
+LSM_HOOK(int, audit_rule_init, u32 field, u32 op, char *rulestr, void **lsmrule)
+LSM_HOOK(int, audit_rule_known, struct audit_krule *krule)
+LSM_HOOK(int, audit_rule_match, u32 secid, u32 field, u32 op, void *lsmrule)
+LSM_HOOK(void, audit_rule_free, void *lsmrule)
+#endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+LSM_HOOK(int, bpf, int cmd, union bpf_attr *attr, unsigned int size)
+LSM_HOOK(int, bpf_map, struct bpf_map *map, fmode_t fmode)
+LSM_HOOK(int, bpf_prog, struct bpf_prog *prog)
+LSM_HOOK(int, bpf_map_alloc_security, struct bpf_map *map)
+LSM_HOOK(void, bpf_map_free_security, struct bpf_map *map)
+LSM_HOOK(int, bpf_prog_alloc_security, struct bpf_prog_aux *aux)
+LSM_HOOK(void, bpf_prog_free_security, struct bpf_prog_aux *aux)
+#endif /* CONFIG_BPF_SYSCALL */
+
+LSM_HOOK(int, locked_down, enum lockdown_reason what)
+
+#ifdef CONFIG_PERF_EVENTS
+LSM_HOOK(int, perf_event_open, struct perf_event_attr *attr, int type)
+LSM_HOOK(int, perf_event_alloc, struct perf_event *event)
+LSM_HOOK(void, perf_event_free, struct perf_event *event)
+LSM_HOOK(int, perf_event_read, struct perf_event *event)
+LSM_HOOK(int, perf_event_write, struct perf_event *event)
+#endif
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 20d8cf194fb7..905954c650ff 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1456,625 +1456,15 @@
* @what: kernel feature being accessed
*/
union security_list_options {
- int (*binder_set_context_mgr)(struct task_struct *mgr);
- int (*binder_transaction)(struct task_struct *from,
- struct task_struct *to);
- int (*binder_transfer_binder)(struct task_struct *from,
- struct task_struct *to);
- int (*binder_transfer_file)(struct task_struct *from,
- struct task_struct *to,
- struct file *file);
-
- int (*ptrace_access_check)(struct task_struct *child,
- unsigned int mode);
- int (*ptrace_traceme)(struct task_struct *parent);
- int (*capget)(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted);
- int (*capset)(struct cred *new, const struct cred *old,
- const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted);
- int (*capable)(const struct cred *cred,
- struct user_namespace *ns,
- int cap,
- unsigned int opts);
- int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
- int (*quota_on)(struct dentry *dentry);
- int (*syslog)(int type);
- int (*settime)(const struct timespec64 *ts, const struct timezone *tz);
- int (*vm_enough_memory)(struct mm_struct *mm, long pages);
-
- int (*bprm_set_creds)(struct linux_binprm *bprm);
- int (*bprm_check_security)(struct linux_binprm *bprm);
- void (*bprm_committing_creds)(struct linux_binprm *bprm);
- void (*bprm_committed_creds)(struct linux_binprm *bprm);
-
- int (*fs_context_dup)(struct fs_context *fc, struct fs_context *src_sc);
- int (*fs_context_parse_param)(struct fs_context *fc, struct fs_parameter *param);
-
- int (*sb_alloc_security)(struct super_block *sb);
- void (*sb_free_security)(struct super_block *sb);
- void (*sb_free_mnt_opts)(void *mnt_opts);
- int (*sb_eat_lsm_opts)(char *orig, void **mnt_opts);
- int (*sb_remount)(struct super_block *sb, void *mnt_opts);
- int (*sb_kern_mount)(struct super_block *sb);
- int (*sb_show_options)(struct seq_file *m, struct super_block *sb);
- int (*sb_statfs)(struct dentry *dentry);
- int (*sb_mount)(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data);
- int (*sb_umount)(struct vfsmount *mnt, int flags);
- int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
- int (*sb_set_mnt_opts)(struct super_block *sb,
- void *mnt_opts,
- unsigned long kern_flags,
- unsigned long *set_kern_flags);
- int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
- struct super_block *newsb,
- unsigned long kern_flags,
- unsigned long *set_kern_flags);
- int (*sb_add_mnt_opt)(const char *option, const char *val, int len,
- void **mnt_opts);
- int (*move_mount)(const struct path *from_path, const struct path *to_path);
- int (*dentry_init_security)(struct dentry *dentry, int mode,
- const struct qstr *name, void **ctx,
- u32 *ctxlen);
- int (*dentry_create_files_as)(struct dentry *dentry, int mode,
- struct qstr *name,
- const struct cred *old,
- struct cred *new);
-
-
-#ifdef CONFIG_SECURITY_PATH
- int (*path_unlink)(const struct path *dir, struct dentry *dentry);
- int (*path_mkdir)(const struct path *dir, struct dentry *dentry,
- umode_t mode);
- int (*path_rmdir)(const struct path *dir, struct dentry *dentry);
- int (*path_mknod)(const struct path *dir, struct dentry *dentry,
- umode_t mode, unsigned int dev);
- int (*path_truncate)(const struct path *path);
- int (*path_symlink)(const struct path *dir, struct dentry *dentry,
- const char *old_name);
- int (*path_link)(struct dentry *old_dentry, const struct path *new_dir,
- struct dentry *new_dentry);
- int (*path_rename)(const struct path *old_dir, struct dentry *old_dentry,
- const struct path *new_dir,
- struct dentry *new_dentry);
- int (*path_chmod)(const struct path *path, umode_t mode);
- int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid);
- int (*path_chroot)(const struct path *path);
-#endif
- /* Needed for inode based security check */
- int (*path_notify)(const struct path *path, u64 mask,
- unsigned int obj_type);
- int (*inode_alloc_security)(struct inode *inode);
- void (*inode_free_security)(struct inode *inode);
- int (*inode_init_security)(struct inode *inode, struct inode *dir,
- const struct qstr *qstr,
- const char **name, void **value,
- size_t *len);
- int (*inode_create)(struct inode *dir, struct dentry *dentry,
- umode_t mode);
- int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
- struct dentry *new_dentry);
- int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
- int (*inode_symlink)(struct inode *dir, struct dentry *dentry,
- const char *old_name);
- int (*inode_mkdir)(struct inode *dir, struct dentry *dentry,
- umode_t mode);
- int (*inode_rmdir)(struct inode *dir, struct dentry *dentry);
- int (*inode_mknod)(struct inode *dir, struct dentry *dentry,
- umode_t mode, dev_t dev);
- int (*inode_rename)(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir,
- struct dentry *new_dentry);
- int (*inode_readlink)(struct dentry *dentry);
- int (*inode_follow_link)(struct dentry *dentry, struct inode *inode,
- bool rcu);
- int (*inode_permission)(struct inode *inode, int mask);
- int (*inode_setattr)(struct dentry *dentry, struct iattr *attr);
- int (*inode_getattr)(const struct path *path);
- int (*inode_setxattr)(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
- void (*inode_post_setxattr)(struct dentry *dentry, const char *name,
- const void *value, size_t size,
- int flags);
- int (*inode_getxattr)(struct dentry *dentry, const char *name);
- int (*inode_listxattr)(struct dentry *dentry);
- int (*inode_removexattr)(struct dentry *dentry, const char *name);
- int (*inode_need_killpriv)(struct dentry *dentry);
- int (*inode_killpriv)(struct dentry *dentry);
- int (*inode_getsecurity)(struct inode *inode, const char *name,
- void **buffer, bool alloc);
- int (*inode_setsecurity)(struct inode *inode, const char *name,
- const void *value, size_t size,
- int flags);
- int (*inode_listsecurity)(struct inode *inode, char *buffer,
- size_t buffer_size);
- void (*inode_getsecid)(struct inode *inode, u32 *secid);
- int (*inode_copy_up)(struct dentry *src, struct cred **new);
- int (*inode_copy_up_xattr)(const char *name);
-
- int (*kernfs_init_security)(struct kernfs_node *kn_dir,
- struct kernfs_node *kn);
-
- int (*file_permission)(struct file *file, int mask);
- int (*file_alloc_security)(struct file *file);
- void (*file_free_security)(struct file *file);
- int (*file_ioctl)(struct file *file, unsigned int cmd,
- unsigned long arg);
- int (*mmap_addr)(unsigned long addr);
- int (*mmap_file)(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags);
- int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
- unsigned long prot);
- int (*file_lock)(struct file *file, unsigned int cmd);
- int (*file_fcntl)(struct file *file, unsigned int cmd,
- unsigned long arg);
- void (*file_set_fowner)(struct file *file);
- int (*file_send_sigiotask)(struct task_struct *tsk,
- struct fown_struct *fown, int sig);
- int (*file_receive)(struct file *file);
- int (*file_open)(struct file *file);
-
- int (*task_alloc)(struct task_struct *task, unsigned long clone_flags);
- void (*task_free)(struct task_struct *task);
- int (*cred_alloc_blank)(struct cred *cred, gfp_t gfp);
- void (*cred_free)(struct cred *cred);
- int (*cred_prepare)(struct cred *new, const struct cred *old,
- gfp_t gfp);
- void (*cred_transfer)(struct cred *new, const struct cred *old);
- void (*cred_getsecid)(const struct cred *c, u32 *secid);
- int (*kernel_act_as)(struct cred *new, u32 secid);
- int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
- int (*kernel_module_request)(char *kmod_name);
- int (*kernel_load_data)(enum kernel_load_data_id id);
- int (*kernel_read_file)(struct file *file, enum kernel_read_file_id id);
- int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size,
- enum kernel_read_file_id id);
- int (*task_fix_setuid)(struct cred *new, const struct cred *old,
- int flags);
- int (*task_setpgid)(struct task_struct *p, pid_t pgid);
- int (*task_getpgid)(struct task_struct *p);
- int (*task_getsid)(struct task_struct *p);
- void (*task_getsecid)(struct task_struct *p, u32 *secid);
- int (*task_setnice)(struct task_struct *p, int nice);
- int (*task_setioprio)(struct task_struct *p, int ioprio);
- int (*task_getioprio)(struct task_struct *p);
- int (*task_prlimit)(const struct cred *cred, const struct cred *tcred,
- unsigned int flags);
- int (*task_setrlimit)(struct task_struct *p, unsigned int resource,
- struct rlimit *new_rlim);
- int (*task_setscheduler)(struct task_struct *p);
- int (*task_getscheduler)(struct task_struct *p);
- int (*task_movememory)(struct task_struct *p);
- int (*task_kill)(struct task_struct *p, struct kernel_siginfo *info,
- int sig, const struct cred *cred);
- int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5);
- void (*task_to_inode)(struct task_struct *p, struct inode *inode);
-
- int (*ipc_permission)(struct kern_ipc_perm *ipcp, short flag);
- void (*ipc_getsecid)(struct kern_ipc_perm *ipcp, u32 *secid);
-
- int (*msg_msg_alloc_security)(struct msg_msg *msg);
- void (*msg_msg_free_security)(struct msg_msg *msg);
-
- int (*msg_queue_alloc_security)(struct kern_ipc_perm *perm);
- void (*msg_queue_free_security)(struct kern_ipc_perm *perm);
- int (*msg_queue_associate)(struct kern_ipc_perm *perm, int msqflg);
- int (*msg_queue_msgctl)(struct kern_ipc_perm *perm, int cmd);
- int (*msg_queue_msgsnd)(struct kern_ipc_perm *perm, struct msg_msg *msg,
- int msqflg);
- int (*msg_queue_msgrcv)(struct kern_ipc_perm *perm, struct msg_msg *msg,
- struct task_struct *target, long type,
- int mode);
-
- int (*shm_alloc_security)(struct kern_ipc_perm *perm);
- void (*shm_free_security)(struct kern_ipc_perm *perm);
- int (*shm_associate)(struct kern_ipc_perm *perm, int shmflg);
- int (*shm_shmctl)(struct kern_ipc_perm *perm, int cmd);
- int (*shm_shmat)(struct kern_ipc_perm *perm, char __user *shmaddr,
- int shmflg);
-
- int (*sem_alloc_security)(struct kern_ipc_perm *perm);
- void (*sem_free_security)(struct kern_ipc_perm *perm);
- int (*sem_associate)(struct kern_ipc_perm *perm, int semflg);
- int (*sem_semctl)(struct kern_ipc_perm *perm, int cmd);
- int (*sem_semop)(struct kern_ipc_perm *perm, struct sembuf *sops,
- unsigned nsops, int alter);
-
- int (*netlink_send)(struct sock *sk, struct sk_buff *skb);
-
- void (*d_instantiate)(struct dentry *dentry, struct inode *inode);
-
- int (*getprocattr)(struct task_struct *p, char *name, char **value);
- int (*setprocattr)(const char *name, void *value, size_t size);
- int (*ismaclabel)(const char *name);
- int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen);
- int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid);
- void (*release_secctx)(char *secdata, u32 seclen);
-
- void (*inode_invalidate_secctx)(struct inode *inode);
- int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
- int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
- int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
-
-#ifdef CONFIG_SECURITY_NETWORK
- int (*unix_stream_connect)(struct sock *sock, struct sock *other,
- struct sock *newsk);
- int (*unix_may_send)(struct socket *sock, struct socket *other);
-
- int (*socket_create)(int family, int type, int protocol, int kern);
- int (*socket_post_create)(struct socket *sock, int family, int type,
- int protocol, int kern);
- int (*socket_socketpair)(struct socket *socka, struct socket *sockb);
- int (*socket_bind)(struct socket *sock, struct sockaddr *address,
- int addrlen);
- int (*socket_connect)(struct socket *sock, struct sockaddr *address,
- int addrlen);
- int (*socket_listen)(struct socket *sock, int backlog);
- int (*socket_accept)(struct socket *sock, struct socket *newsock);
- int (*socket_sendmsg)(struct socket *sock, struct msghdr *msg,
- int size);
- int (*socket_recvmsg)(struct socket *sock, struct msghdr *msg,
- int size, int flags);
- int (*socket_getsockname)(struct socket *sock);
- int (*socket_getpeername)(struct socket *sock);
- int (*socket_getsockopt)(struct socket *sock, int level, int optname);
- int (*socket_setsockopt)(struct socket *sock, int level, int optname);
- int (*socket_shutdown)(struct socket *sock, int how);
- int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
- int (*socket_getpeersec_stream)(struct socket *sock,
- char __user *optval,
- int __user *optlen, unsigned len);
- int (*socket_getpeersec_dgram)(struct socket *sock,
- struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security)(struct sock *sk, int family, gfp_t priority);
- void (*sk_free_security)(struct sock *sk);
- void (*sk_clone_security)(const struct sock *sk, struct sock *newsk);
- void (*sk_getsecid)(struct sock *sk, u32 *secid);
- void (*sock_graft)(struct sock *sk, struct socket *parent);
- int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
- struct request_sock *req);
- void (*inet_csk_clone)(struct sock *newsk,
- const struct request_sock *req);
- void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
- int (*secmark_relabel_packet)(u32 secid);
- void (*secmark_refcount_inc)(void);
- void (*secmark_refcount_dec)(void);
- void (*req_classify_flow)(const struct request_sock *req,
- struct flowi *fl);
- int (*tun_dev_alloc_security)(void **security);
- void (*tun_dev_free_security)(void *security);
- int (*tun_dev_create)(void);
- int (*tun_dev_attach_queue)(void *security);
- int (*tun_dev_attach)(struct sock *sk, void *security);
- int (*tun_dev_open)(void *security);
- int (*sctp_assoc_request)(struct sctp_endpoint *ep,
- struct sk_buff *skb);
- int (*sctp_bind_connect)(struct sock *sk, int optname,
- struct sockaddr *address, int addrlen);
- void (*sctp_sk_clone)(struct sctp_endpoint *ep, struct sock *sk,
- struct sock *newsk);
-#endif /* CONFIG_SECURITY_NETWORK */
-
-#ifdef CONFIG_SECURITY_INFINIBAND
- int (*ib_pkey_access)(void *sec, u64 subnet_prefix, u16 pkey);
- int (*ib_endport_manage_subnet)(void *sec, const char *dev_name,
- u8 port_num);
- int (*ib_alloc_security)(void **sec);
- void (*ib_free_security)(void *sec);
-#endif /* CONFIG_SECURITY_INFINIBAND */
-
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
- int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
- struct xfrm_user_sec_ctx *sec_ctx,
- gfp_t gfp);
- int (*xfrm_policy_clone_security)(struct xfrm_sec_ctx *old_ctx,
- struct xfrm_sec_ctx **new_ctx);
- void (*xfrm_policy_free_security)(struct xfrm_sec_ctx *ctx);
- int (*xfrm_policy_delete_security)(struct xfrm_sec_ctx *ctx);
- int (*xfrm_state_alloc)(struct xfrm_state *x,
- struct xfrm_user_sec_ctx *sec_ctx);
- int (*xfrm_state_alloc_acquire)(struct xfrm_state *x,
- struct xfrm_sec_ctx *polsec,
- u32 secid);
- void (*xfrm_state_free_security)(struct xfrm_state *x);
- int (*xfrm_state_delete_security)(struct xfrm_state *x);
- int (*xfrm_policy_lookup)(struct xfrm_sec_ctx *ctx, u32 fl_secid,
- u8 dir);
- int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
- struct xfrm_policy *xp,
- const struct flowi *fl);
- int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-
- /* key management security hooks */
-#ifdef CONFIG_KEYS
- int (*key_alloc)(struct key *key, const struct cred *cred,
- unsigned long flags);
- void (*key_free)(struct key *key);
- int (*key_permission)(key_ref_t key_ref, const struct cred *cred,
- unsigned perm);
- int (*key_getsecurity)(struct key *key, char **_buffer);
-#endif /* CONFIG_KEYS */
-
-#ifdef CONFIG_AUDIT
- int (*audit_rule_init)(u32 field, u32 op, char *rulestr,
- void **lsmrule);
- int (*audit_rule_known)(struct audit_krule *krule);
- int (*audit_rule_match)(u32 secid, u32 field, u32 op, void *lsmrule);
- void (*audit_rule_free)(void *lsmrule);
-#endif /* CONFIG_AUDIT */
-
-#ifdef CONFIG_BPF_SYSCALL
- int (*bpf)(int cmd, union bpf_attr *attr,
- unsigned int size);
- int (*bpf_map)(struct bpf_map *map, fmode_t fmode);
- int (*bpf_prog)(struct bpf_prog *prog);
- int (*bpf_map_alloc_security)(struct bpf_map *map);
- void (*bpf_map_free_security)(struct bpf_map *map);
- int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
- void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
-#endif /* CONFIG_BPF_SYSCALL */
- int (*locked_down)(enum lockdown_reason what);
-#ifdef CONFIG_PERF_EVENTS
- int (*perf_event_open)(struct perf_event_attr *attr, int type);
- int (*perf_event_alloc)(struct perf_event *event);
- void (*perf_event_free)(struct perf_event *event);
- int (*perf_event_read)(struct perf_event *event);
- int (*perf_event_write)(struct perf_event *event);
-
-#endif
+ #define LSM_HOOK(RET, NAME, ...) RET (*NAME)(__VA_ARGS__);
+ #include "lsm_hook_names.h"
+ #undef LSM_HOOK
};
struct security_hook_heads {
- struct hlist_head binder_set_context_mgr;
- struct hlist_head binder_transaction;
- struct hlist_head binder_transfer_binder;
- struct hlist_head binder_transfer_file;
- struct hlist_head ptrace_access_check;
- struct hlist_head ptrace_traceme;
- struct hlist_head capget;
- struct hlist_head capset;
- struct hlist_head capable;
- struct hlist_head quotactl;
- struct hlist_head quota_on;
- struct hlist_head syslog;
- struct hlist_head settime;
- struct hlist_head vm_enough_memory;
- struct hlist_head bprm_set_creds;
- struct hlist_head bprm_check_security;
- struct hlist_head bprm_committing_creds;
- struct hlist_head bprm_committed_creds;
- struct hlist_head fs_context_dup;
- struct hlist_head fs_context_parse_param;
- struct hlist_head sb_alloc_security;
- struct hlist_head sb_free_security;
- struct hlist_head sb_free_mnt_opts;
- struct hlist_head sb_eat_lsm_opts;
- struct hlist_head sb_remount;
- struct hlist_head sb_kern_mount;
- struct hlist_head sb_show_options;
- struct hlist_head sb_statfs;
- struct hlist_head sb_mount;
- struct hlist_head sb_umount;
- struct hlist_head sb_pivotroot;
- struct hlist_head sb_set_mnt_opts;
- struct hlist_head sb_clone_mnt_opts;
- struct hlist_head sb_add_mnt_opt;
- struct hlist_head move_mount;
- struct hlist_head dentry_init_security;
- struct hlist_head dentry_create_files_as;
-#ifdef CONFIG_SECURITY_PATH
- struct hlist_head path_unlink;
- struct hlist_head path_mkdir;
- struct hlist_head path_rmdir;
- struct hlist_head path_mknod;
- struct hlist_head path_truncate;
- struct hlist_head path_symlink;
- struct hlist_head path_link;
- struct hlist_head path_rename;
- struct hlist_head path_chmod;
- struct hlist_head path_chown;
- struct hlist_head path_chroot;
-#endif
- /* Needed for inode based modules as well */
- struct hlist_head path_notify;
- struct hlist_head inode_alloc_security;
- struct hlist_head inode_free_security;
- struct hlist_head inode_init_security;
- struct hlist_head inode_create;
- struct hlist_head inode_link;
- struct hlist_head inode_unlink;
- struct hlist_head inode_symlink;
- struct hlist_head inode_mkdir;
- struct hlist_head inode_rmdir;
- struct hlist_head inode_mknod;
- struct hlist_head inode_rename;
- struct hlist_head inode_readlink;
- struct hlist_head inode_follow_link;
- struct hlist_head inode_permission;
- struct hlist_head inode_setattr;
- struct hlist_head inode_getattr;
- struct hlist_head inode_setxattr;
- struct hlist_head inode_post_setxattr;
- struct hlist_head inode_getxattr;
- struct hlist_head inode_listxattr;
- struct hlist_head inode_removexattr;
- struct hlist_head inode_need_killpriv;
- struct hlist_head inode_killpriv;
- struct hlist_head inode_getsecurity;
- struct hlist_head inode_setsecurity;
- struct hlist_head inode_listsecurity;
- struct hlist_head inode_getsecid;
- struct hlist_head inode_copy_up;
- struct hlist_head inode_copy_up_xattr;
- struct hlist_head kernfs_init_security;
- struct hlist_head file_permission;
- struct hlist_head file_alloc_security;
- struct hlist_head file_free_security;
- struct hlist_head file_ioctl;
- struct hlist_head mmap_addr;
- struct hlist_head mmap_file;
- struct hlist_head file_mprotect;
- struct hlist_head file_lock;
- struct hlist_head file_fcntl;
- struct hlist_head file_set_fowner;
- struct hlist_head file_send_sigiotask;
- struct hlist_head file_receive;
- struct hlist_head file_open;
- struct hlist_head task_alloc;
- struct hlist_head task_free;
- struct hlist_head cred_alloc_blank;
- struct hlist_head cred_free;
- struct hlist_head cred_prepare;
- struct hlist_head cred_transfer;
- struct hlist_head cred_getsecid;
- struct hlist_head kernel_act_as;
- struct hlist_head kernel_create_files_as;
- struct hlist_head kernel_load_data;
- struct hlist_head kernel_read_file;
- struct hlist_head kernel_post_read_file;
- struct hlist_head kernel_module_request;
- struct hlist_head task_fix_setuid;
- struct hlist_head task_setpgid;
- struct hlist_head task_getpgid;
- struct hlist_head task_getsid;
- struct hlist_head task_getsecid;
- struct hlist_head task_setnice;
- struct hlist_head task_setioprio;
- struct hlist_head task_getioprio;
- struct hlist_head task_prlimit;
- struct hlist_head task_setrlimit;
- struct hlist_head task_setscheduler;
- struct hlist_head task_getscheduler;
- struct hlist_head task_movememory;
- struct hlist_head task_kill;
- struct hlist_head task_prctl;
- struct hlist_head task_to_inode;
- struct hlist_head ipc_permission;
- struct hlist_head ipc_getsecid;
- struct hlist_head msg_msg_alloc_security;
- struct hlist_head msg_msg_free_security;
- struct hlist_head msg_queue_alloc_security;
- struct hlist_head msg_queue_free_security;
- struct hlist_head msg_queue_associate;
- struct hlist_head msg_queue_msgctl;
- struct hlist_head msg_queue_msgsnd;
- struct hlist_head msg_queue_msgrcv;
- struct hlist_head shm_alloc_security;
- struct hlist_head shm_free_security;
- struct hlist_head shm_associate;
- struct hlist_head shm_shmctl;
- struct hlist_head shm_shmat;
- struct hlist_head sem_alloc_security;
- struct hlist_head sem_free_security;
- struct hlist_head sem_associate;
- struct hlist_head sem_semctl;
- struct hlist_head sem_semop;
- struct hlist_head netlink_send;
- struct hlist_head d_instantiate;
- struct hlist_head getprocattr;
- struct hlist_head setprocattr;
- struct hlist_head ismaclabel;
- struct hlist_head secid_to_secctx;
- struct hlist_head secctx_to_secid;
- struct hlist_head release_secctx;
- struct hlist_head inode_invalidate_secctx;
- struct hlist_head inode_notifysecctx;
- struct hlist_head inode_setsecctx;
- struct hlist_head inode_getsecctx;
-#ifdef CONFIG_SECURITY_NETWORK
- struct hlist_head unix_stream_connect;
- struct hlist_head unix_may_send;
- struct hlist_head socket_create;
- struct hlist_head socket_post_create;
- struct hlist_head socket_socketpair;
- struct hlist_head socket_bind;
- struct hlist_head socket_connect;
- struct hlist_head socket_listen;
- struct hlist_head socket_accept;
- struct hlist_head socket_sendmsg;
- struct hlist_head socket_recvmsg;
- struct hlist_head socket_getsockname;
- struct hlist_head socket_getpeername;
- struct hlist_head socket_getsockopt;
- struct hlist_head socket_setsockopt;
- struct hlist_head socket_shutdown;
- struct hlist_head socket_sock_rcv_skb;
- struct hlist_head socket_getpeersec_stream;
- struct hlist_head socket_getpeersec_dgram;
- struct hlist_head sk_alloc_security;
- struct hlist_head sk_free_security;
- struct hlist_head sk_clone_security;
- struct hlist_head sk_getsecid;
- struct hlist_head sock_graft;
- struct hlist_head inet_conn_request;
- struct hlist_head inet_csk_clone;
- struct hlist_head inet_conn_established;
- struct hlist_head secmark_relabel_packet;
- struct hlist_head secmark_refcount_inc;
- struct hlist_head secmark_refcount_dec;
- struct hlist_head req_classify_flow;
- struct hlist_head tun_dev_alloc_security;
- struct hlist_head tun_dev_free_security;
- struct hlist_head tun_dev_create;
- struct hlist_head tun_dev_attach_queue;
- struct hlist_head tun_dev_attach;
- struct hlist_head tun_dev_open;
- struct hlist_head sctp_assoc_request;
- struct hlist_head sctp_bind_connect;
- struct hlist_head sctp_sk_clone;
-#endif /* CONFIG_SECURITY_NETWORK */
-#ifdef CONFIG_SECURITY_INFINIBAND
- struct hlist_head ib_pkey_access;
- struct hlist_head ib_endport_manage_subnet;
- struct hlist_head ib_alloc_security;
- struct hlist_head ib_free_security;
-#endif /* CONFIG_SECURITY_INFINIBAND */
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
- struct hlist_head xfrm_policy_alloc_security;
- struct hlist_head xfrm_policy_clone_security;
- struct hlist_head xfrm_policy_free_security;
- struct hlist_head xfrm_policy_delete_security;
- struct hlist_head xfrm_state_alloc;
- struct hlist_head xfrm_state_alloc_acquire;
- struct hlist_head xfrm_state_free_security;
- struct hlist_head xfrm_state_delete_security;
- struct hlist_head xfrm_policy_lookup;
- struct hlist_head xfrm_state_pol_flow_match;
- struct hlist_head xfrm_decode_session;
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-#ifdef CONFIG_KEYS
- struct hlist_head key_alloc;
- struct hlist_head key_free;
- struct hlist_head key_permission;
- struct hlist_head key_getsecurity;
-#endif /* CONFIG_KEYS */
-#ifdef CONFIG_AUDIT
- struct hlist_head audit_rule_init;
- struct hlist_head audit_rule_known;
- struct hlist_head audit_rule_match;
- struct hlist_head audit_rule_free;
-#endif /* CONFIG_AUDIT */
-#ifdef CONFIG_BPF_SYSCALL
- struct hlist_head bpf;
- struct hlist_head bpf_map;
- struct hlist_head bpf_prog;
- struct hlist_head bpf_map_alloc_security;
- struct hlist_head bpf_map_free_security;
- struct hlist_head bpf_prog_alloc_security;
- struct hlist_head bpf_prog_free_security;
-#endif /* CONFIG_BPF_SYSCALL */
- struct hlist_head locked_down;
-#ifdef CONFIG_PERF_EVENTS
- struct hlist_head perf_event_open;
- struct hlist_head perf_event_alloc;
- struct hlist_head perf_event_free;
- struct hlist_head perf_event_read;
- struct hlist_head perf_event_write;
-#endif
+ #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
+ #include "lsm_hook_names.h"
+ #undef LSM_HOOK
} __randomize_layout;
/*
--
2.20.1
From: KP Singh <[email protected]>
JITed BPF programs are dynamically attached to the LSM hooks
using BPF trampolines. The trampoline prologue generates code to handle
conversion of the signature of the hook to the appropriate BPF context.
The allocated trampoline programs are attached to the nop functions
initialized as LSM hooks.
BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
and need CAP_SYS_ADMIN (required for loading eBPF programs).
Upon attachment:
* A BPF fexit trampoline is used for LSM hooks with a void return type.
* A BPF fmod_ret trampoline is used for LSM hooks which return an
int. The attached programs can override the return value of the
bpf LSM hook to indicate a MAC Policy decision.
Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
---
include/linux/bpf.h | 4 ++++
include/linux/bpf_lsm.h | 11 +++++++++++
kernel/bpf/bpf_lsm.c | 29 +++++++++++++++++++++++++++++
kernel/bpf/btf.c | 9 ++++++++-
kernel/bpf/syscall.c | 26 ++++++++++++++++++++++----
kernel/bpf/trampoline.c | 17 +++++++++++++----
kernel/bpf/verifier.c | 19 +++++++++++++++----
7 files changed, 102 insertions(+), 13 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index af81ec7b783c..adf2e5a6de4b 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -433,6 +433,10 @@ struct btf_func_model {
* programs only. Should not be used with normal calls and indirect calls.
*/
#define BPF_TRAMP_F_SKIP_FRAME BIT(2)
+/* Override the return value of the original function. This flag only makes
+ * sense for fexit trampolines.
+ */
+#define BPF_TRAMP_F_OVERRIDE_RETURN BIT(3)
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
* bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index c6423a140220..9bac0a11f303 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -16,6 +16,17 @@
#include <linux/lsm_hook_names.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 */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 530d137f7a84..2a8131b640b8 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -9,6 +9,9 @@
#include <linux/btf.h>
#include <linux/lsm_hooks.h>
#include <linux/bpf_lsm.h>
+#include <linux/jump_label.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 as an fexit trampoline.
@@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
#include <linux/lsm_hook_names.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)
+{
+ /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ 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,
+ strlen(BPF_LSM_SYM_PREFX))) {
+ 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 = {
};
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 6f397c4da05e..67466dd59a35 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3710,7 +3710,14 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
}
if (arg == nr_args) {
- if (prog->expected_attach_type == BPF_TRACE_FEXIT) {
+ /* BPF_LSM_MAC programs only have int and void functions they
+ * can be attached to. When they are attached to a void function
+ * they result in the creation of an FEXIT trampoline and when
+ * to a function that returns an int, a MODIFY_RETURN
+ * trampoline.
+ */
+ if (prog->expected_attach_type == BPF_TRACE_FEXIT ||
+ prog->expected_attach_type == BPF_LSM_MAC) {
if (!t)
return true;
t = btf_type_by_id(btf, t->type);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 85567a6ea5f9..845bdfb35852 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -25,6 +25,7 @@
#include <linux/nospec.h>
#include <linux/audit.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 || \
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
@@ -1935,6 +1936,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
switch (prog_type) {
case BPF_PROG_TYPE_TRACING:
+ case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
break;
@@ -2367,10 +2369,24 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
struct file *link_file;
int link_fd, err;
- if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
- prog->expected_attach_type != BPF_TRACE_FEXIT &&
- prog->expected_attach_type != BPF_MODIFY_RETURN &&
- prog->type != BPF_PROG_TYPE_EXT) {
+ switch (prog->type) {
+ case BPF_PROG_TYPE_TRACING:
+ if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
+ 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:
+ break;
+ case BPF_PROG_TYPE_LSM:
+ if (prog->expected_attach_type != BPF_LSM_MAC) {
+ err = -EINVAL;
+ goto out_put_prog;
+ }
+ break;
+ default:
err = -EINVAL;
goto out_put_prog;
}
@@ -2452,12 +2468,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_EXT &&
+ prog->type != BPF_PROG_TYPE_LSM &&
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
err = -EINVAL;
goto out_put_prog;
}
if (prog->type == BPF_PROG_TYPE_TRACING ||
+ prog->type == BPF_PROG_TYPE_LSM ||
prog->type == BPF_PROG_TYPE_EXT) {
if (attr->raw_tracepoint.name) {
/* The attach point for this category of programs
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index f30bca2a4d01..9be85aa4ec5f 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -6,6 +6,7 @@
#include <linux/ftrace.h>
#include <linux/rbtree_latch.h>
#include <linux/perf_event.h>
+#include <linux/btf.h>
/* dummy _ops. The verifier will operate on target program's ops. */
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
@@ -233,15 +234,23 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
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:
return BPF_TRAMP_FENTRY;
case BPF_MODIFY_RETURN:
return BPF_TRAMP_MODIFY_RETURN;
case BPF_TRACE_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:
return BPF_TRAMP_REPLACE;
}
@@ -255,7 +264,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
int cnt;
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);
if (tr->extension_prog) {
/* cannot attach fentry/fexit if extension prog is attached.
@@ -305,7 +314,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
int err;
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);
if (kind == BPF_TRAMP_REPLACE) {
WARN_ON_ONCE(!tr->extension_prog);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 745f3cfdf3b2..c5024499f86b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20,6 +20,7 @@
#include <linux/perf_event.h>
#include <linux/ctype.h>
#include <linux/error-injection.h>
+#include <linux/bpf_lsm.h>
#include "disasm.h"
@@ -6412,8 +6413,9 @@ static int check_return_code(struct bpf_verifier_env *env)
struct tnum range = tnum_range(0, 1);
int err;
- /* The struct_ops func-ptr's return type could be "void" */
- if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS &&
+ /* LSM and struct_ops func-ptr's return type could be "void" */
+ if ((env->prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
+ env->prog->type == BPF_PROG_TYPE_LSM) &&
!prog->aux->attach_func_proto->type)
return 0;
@@ -9843,7 +9845,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
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;
if (!btf_id) {
@@ -9974,8 +9978,16 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
return -EINVAL;
/* fallthrough */
case BPF_MODIFY_RETURN:
+ case BPF_LSM_MAC:
case BPF_TRACE_FENTRY:
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)) {
verbose(env, "attach_btf_id %u is not a function\n",
btf_id);
@@ -9990,7 +10002,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
tr = bpf_trampoline_lookup(key);
if (!tr)
return -ENOMEM;
- prog->aux->attach_func_name = tname;
/* t is either vmlinux type or another program's type */
prog->aux->attach_func_proto = t;
mutex_lock(&tr->mutex);
--
2.20.1
From: KP Singh <[email protected]>
When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
generated for each LSM hook. These nops are initialized as LSM hooks in
a subsequent patch.
Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
---
include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
2 files changed, 40 insertions(+)
create mode 100644 include/linux/bpf_lsm.h
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
new file mode 100644
index 000000000000..c6423a140220
--- /dev/null
+++ b/include/linux/bpf_lsm.h
@@ -0,0 +1,21 @@
+/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
+
+#endif /* CONFIG_BPF_LSM */
+
+#endif /* _LINUX_BPF_LSM_H */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 82875039ca90..530d137f7a84 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -7,6 +7,25 @@
#include <linux/filter.h>
#include <linux/bpf.h>
#include <linux/btf.h>
+#include <linux/lsm_hooks.h>
+#include <linux/bpf_lsm.h>
+
+/* For every LSM hook that allows attachment of BPF programs, declare a NOP
+ * function where a BPF program can be attached as an fexit trampoline.
+ */
+#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
+
+#define LSM_HOOK_int(NAME, ...) \
+noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
+{ \
+ return 0; \
+}
+
+#define LSM_HOOK_void(NAME, ...) \
+noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
+
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
const struct bpf_prog_ops lsm_prog_ops = {
};
--
2.20.1
From: KP Singh <[email protected]>
Since BPF_PROG_TYPE_LSM uses the same attaching mechanism as
BPF_PROG_TYPE_TRACING, the common logic is refactored into a static
function bpf_program__attach_btf.
A new API call bpf_program__attach_lsm is still added to avoid userspace
conflicts if this ever changes in the future.
Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Brendan Jackman <[email protected]>
Reviewed-by: Florent Revest <[email protected]>
---
tools/lib/bpf/bpf.c | 3 ++-
tools/lib/bpf/libbpf.c | 41 +++++++++++++++++++++++++++++++++++-----
tools/lib/bpf/libbpf.h | 4 ++++
tools/lib/bpf/libbpf.map | 3 +++
4 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index c6dafe563176..73220176728d 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
memset(&attr, 0, sizeof(attr));
attr.prog_type = load_attr->prog_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;
} else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
attr.prog_type == BPF_PROG_TYPE_EXT) {
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 085e41f9b68e..da8bee78e1ce 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2362,7 +2362,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
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;
/* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
@@ -4870,7 +4871,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.insns = insns;
load_attr.insns_cnt = insns_cnt;
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;
} else if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
@@ -4955,6 +4957,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
int err = 0, fd, i, btf_id;
if ((prog->type == BPF_PROG_TYPE_TRACING ||
+ prog->type == BPF_PROG_TYPE_LSM ||
prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
btf_id = libbpf_find_attach_btf_id(prog);
if (btf_id <= 0)
@@ -6194,6 +6197,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(lsm, BPF_PROG_TYPE_LSM);
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_act, BPF_PROG_TYPE_SCHED_ACT);
@@ -6260,6 +6264,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
struct bpf_program *prog);
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog);
+static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
+ struct bpf_program *prog);
struct bpf_sec_def {
const char *sec;
@@ -6310,6 +6316,10 @@ static const struct bpf_sec_def section_defs[] = {
SEC_DEF("freplace/", EXT,
.is_attach_btf = true,
.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("perf_event", BPF_PROG_TYPE_PERF_EVENT),
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
@@ -6572,6 +6582,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
}
#define BTF_TRACE_PREFIX "btf_trace_"
+#define BTF_LSM_PREFIX "bpf_lsm_"
#define BTF_MAX_NAME_SIZE 128
static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
@@ -6599,6 +6610,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,
if (attach_type == BPF_TRACE_RAW_TP)
err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,
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
err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
@@ -7452,7 +7466,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
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(struct bpf_program *prog)
{
char errmsg[STRERR_BUFSIZE];
struct bpf_link *link;
@@ -7474,7 +7489,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
if (pfd < 0) {
pfd = -errno;
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),
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
return ERR_PTR(pfd);
@@ -7483,10 +7498,26 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
return (struct bpf_link *)link;
}
+struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
+}
+
+struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
+}
+
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog)
{
- return bpf_program__attach_trace(prog);
+ return bpf_program__attach_btf(prog);
+}
+
+static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
+ struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
}
struct bpf_link *bpf_program__attach(struct bpf_program *prog)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index d38d7a629417..df1be44c8118 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -248,6 +248,8 @@ bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
LIBBPF_API struct bpf_link *
bpf_program__attach_trace(struct bpf_program *prog);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_lsm(struct bpf_program *prog);
struct bpf_map;
LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map);
struct bpf_insn;
@@ -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_raw_tracepoint(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_act(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);
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_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_act(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 5129283c0284..ec29d3aa2700 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -243,5 +243,8 @@ LIBBPF_0.0.8 {
bpf_link__pin;
bpf_link__pin_path;
bpf_link__unpin;
+ bpf_program__attach_lsm;
+ bpf_program__is_lsm;
bpf_program__set_attach_target;
+ bpf_program__set_lsm;
} LIBBPF_0.0.7;
--
2.20.1
On 3/23/20 9:44 AM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> generated for each LSM hook. These nops are initialized as LSM hooks in
> a subsequent patch.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
Acked-by: Yonghong Song <[email protected]>
On 3/23/20 9:44 AM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> JITed BPF programs are dynamically attached to the LSM hooks
> using BPF trampolines. The trampoline prologue generates code to handle
> conversion of the signature of the hook to the appropriate BPF context.
>
> The allocated trampoline programs are attached to the nop functions
> initialized as LSM hooks.
>
> BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> and need CAP_SYS_ADMIN (required for loading eBPF programs).
>
> Upon attachment:
>
> * A BPF fexit trampoline is used for LSM hooks with a void return type.
> * A BPF fmod_ret trampoline is used for LSM hooks which return an
> int. The attached programs can override the return value of the
> bpf LSM hook to indicate a MAC Policy decision.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/bpf.h | 4 ++++
> include/linux/bpf_lsm.h | 11 +++++++++++
> kernel/bpf/bpf_lsm.c | 29 +++++++++++++++++++++++++++++
> kernel/bpf/btf.c | 9 ++++++++-
> kernel/bpf/syscall.c | 26 ++++++++++++++++++++++----
> kernel/bpf/trampoline.c | 17 +++++++++++++----
> kernel/bpf/verifier.c | 19 +++++++++++++++----
> 7 files changed, 102 insertions(+), 13 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index af81ec7b783c..adf2e5a6de4b 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -433,6 +433,10 @@ struct btf_func_model {
> * programs only. Should not be used with normal calls and indirect calls.
> */
> #define BPF_TRAMP_F_SKIP_FRAME BIT(2)
> +/* Override the return value of the original function. This flag only makes
> + * sense for fexit trampolines.
> + */
> +#define BPF_TRAMP_F_OVERRIDE_RETURN BIT(3)
Whether the return value is overridable is determined by hook return
type as below. Do we still need this flag?
>
> /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
> * bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
[...]
On 3/23/20 9:44 AM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> Since BPF_PROG_TYPE_LSM uses the same attaching mechanism as
> BPF_PROG_TYPE_TRACING, the common logic is refactored into a static
> function bpf_program__attach_btf.
>
> A new API call bpf_program__attach_lsm is still added to avoid userspace
> conflicts if this ever changes in the future.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
Acked-by: Yonghong Song <[email protected]>
On Mon, Mar 23, 2020 at 05:44:10PM +0100, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> The information about the different types of LSM hooks is scattered
> in two locations i.e. union security_list_options and
> struct security_hook_heads. Rather than duplicating this information
> even further for BPF_PROG_TYPE_LSM, define all the hooks with the
> LSM_HOOK macro in lsm_hook_names.h which is then used to generate all
> the data structures required by the LSM framework.
>
> Signed-off-by: KP Singh <[email protected]>
The duplicate code removal provided here is alone worth landing this
patch. :)
Reviewed-by: Kees Cook <[email protected]>
-Kees
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/lsm_hook_names.h | 354 +++++++++++++++++++
> include/linux/lsm_hooks.h | 622 +--------------------------------
> 2 files changed, 360 insertions(+), 616 deletions(-)
> create mode 100644 include/linux/lsm_hook_names.h
>
> diff --git a/include/linux/lsm_hook_names.h b/include/linux/lsm_hook_names.h
> new file mode 100644
> index 000000000000..412e4ca24c9b
> --- /dev/null
> +++ b/include/linux/lsm_hook_names.h
> @@ -0,0 +1,354 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Linux Security Module Hook declarations.
> + *
> + * Copyright (C) 2001 WireX Communications, Inc <[email protected]>
> + * Copyright (C) 2001 Greg Kroah-Hartman <[email protected]>
> + * Copyright (C) 2001 Networks Associates Technology, Inc <[email protected]>
> + * Copyright (C) 2001 James Morris <[email protected]>
> + * Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
> + * Copyright (C) 2015 Intel Corporation.
> + * Copyright (C) 2015 Casey Schaufler <[email protected]>
> + * Copyright (C) 2016 Mellanox Techonologies
> + * Copyright (C) 2020 Google LLC.
> + */
> +
> +/* The macro LSM_HOOK is used to define the data structures required by the
> + * the LSM framework using the pattern:
> + *
> + * struct security_hook_heads {
> + * #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
> + * #include <linux/lsm_hook_names.h>
> + * #undef LSM_HOOK
> + * };
> + */
> +LSM_HOOK(int, binder_set_context_mgr, struct task_struct *mgr)
> +LSM_HOOK(int, binder_transaction, struct task_struct *from,
> + struct task_struct *to)
> +LSM_HOOK(int, binder_transfer_binder, struct task_struct *from,
> + struct task_struct *to)
> +LSM_HOOK(int, binder_transfer_file, struct task_struct *from,
> + struct task_struct *to, struct file *file)
> +LSM_HOOK(int, ptrace_access_check, struct task_struct *child, unsigned int mode)
> +LSM_HOOK(int, ptrace_traceme, struct task_struct *parent)
> +LSM_HOOK(int, capget, struct task_struct *target, kernel_cap_t *effective,
> + kernel_cap_t *inheritable, kernel_cap_t *permitted)
> +LSM_HOOK(int, capset, struct cred *new, const struct cred *old,
> + const kernel_cap_t *effective, const kernel_cap_t *inheritable,
> + const kernel_cap_t *permitted)
> +LSM_HOOK(int, capable, const struct cred *cred, struct user_namespace *ns,
> + int cap, unsigned int opts)
> +LSM_HOOK(int, quotactl, int cmds, int type, int id, struct super_block *sb)
> +LSM_HOOK(int, quota_on, struct dentry *dentry)
> +LSM_HOOK(int, syslog, int type)
> +LSM_HOOK(int, settime, const struct timespec64 *ts, const struct timezone *tz)
> +LSM_HOOK(int, vm_enough_memory, struct mm_struct *mm, long pages)
> +LSM_HOOK(int, bprm_set_creds, struct linux_binprm *bprm)
> +LSM_HOOK(int, bprm_check_security, struct linux_binprm *bprm)
> +LSM_HOOK(void, bprm_committing_creds, struct linux_binprm *bprm)
> +LSM_HOOK(void, bprm_committed_creds, struct linux_binprm *bprm)
> +LSM_HOOK(int, fs_context_dup, struct fs_context *fc, struct fs_context *src_sc)
> +LSM_HOOK(int, fs_context_parse_param, struct fs_context *fc,
> + struct fs_parameter *param)
> +LSM_HOOK(int, sb_alloc_security, struct super_block *sb)
> +LSM_HOOK(void, sb_free_security, struct super_block *sb)
> +LSM_HOOK(void, sb_free_mnt_opts, void *mnt_opts)
> +LSM_HOOK(int, sb_eat_lsm_opts, char *orig, void **mnt_opts)
> +LSM_HOOK(int, sb_remount, struct super_block *sb, void *mnt_opts)
> +LSM_HOOK(int, sb_kern_mount, struct super_block *sb)
> +LSM_HOOK(int, sb_show_options, struct seq_file *m, struct super_block *sb)
> +LSM_HOOK(int, sb_statfs, struct dentry *dentry)
> +LSM_HOOK(int, sb_mount, const char *dev_name, const struct path *path,
> + const char *type, unsigned long flags, void *data)
> +LSM_HOOK(int, sb_umount, struct vfsmount *mnt, int flags)
> +LSM_HOOK(int, sb_pivotroot, const struct path *old_path,
> + const struct path *new_path)
> +LSM_HOOK(int, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
> + unsigned long kern_flags, unsigned long *set_kern_flags)
> +LSM_HOOK(int, sb_clone_mnt_opts, const struct super_block *oldsb,
> + struct super_block *newsb, unsigned long kern_flags,
> + unsigned long *set_kern_flags)
> +LSM_HOOK(int, sb_add_mnt_opt, const char *option, const char *val, int len,
> + void **mnt_opts)
> +LSM_HOOK(int, move_mount, const struct path *from_path,
> + const struct path *to_path)
> +LSM_HOOK(int, dentry_init_security, struct dentry *dentry, int mode,
> + const struct qstr *name, void **ctx, u32 *ctxlen)
> +LSM_HOOK(int, dentry_create_files_as, struct dentry *dentry, int mode,
> + struct qstr *name, const struct cred *old, struct cred *new)
> +#ifdef CONFIG_SECURITY_PATH
> +LSM_HOOK(int, path_unlink, const struct path *dir, struct dentry *dentry)
> +LSM_HOOK(int, path_mkdir, const struct path *dir, struct dentry *dentry,
> + umode_t mode)
> +LSM_HOOK(int, path_rmdir, const struct path *dir, struct dentry *dentry)
> +LSM_HOOK(int, path_mknod, const struct path *dir, struct dentry *dentry,
> + umode_t mode, unsigned int dev)
> +LSM_HOOK(int, path_truncate, const struct path *path)
> +LSM_HOOK(int, path_symlink, const struct path *dir, struct dentry *dentry,
> + const char *old_name)
> +LSM_HOOK(int, path_link, struct dentry *old_dentry, const struct path *new_dir,
> + struct dentry *new_dentry)
> +LSM_HOOK(int, path_rename, const struct path *old_dir,
> + struct dentry *old_dentry, const struct path *new_dir,
> + struct dentry *new_dentry)
> +LSM_HOOK(int, path_chmod, const struct path *path, umode_t mode)
> +LSM_HOOK(int, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
> +LSM_HOOK(int, path_chroot, const struct path *path)
> +#endif
> +
> +/* Needed for inode based security check */
> +LSM_HOOK(int, path_notify, const struct path *path, u64 mask,
> + unsigned int obj_type)
> +LSM_HOOK(int, inode_alloc_security, struct inode *inode)
> +LSM_HOOK(void, inode_free_security, struct inode *inode)
> +LSM_HOOK(int, inode_init_security, struct inode *inode, struct inode *dir,
> + const struct qstr *qstr, const char **name, void **value, size_t *len)
> +LSM_HOOK(int, inode_create, struct inode *dir, struct dentry *dentry,
> + umode_t mode)
> +LSM_HOOK(int, inode_link, struct dentry *old_dentry, struct inode *dir,
> + struct dentry *new_dentry)
> +LSM_HOOK(int, inode_unlink, struct inode *dir, struct dentry *dentry)
> +LSM_HOOK(int, inode_symlink, struct inode *dir, struct dentry *dentry,
> + const char *old_name)
> +LSM_HOOK(int, inode_mkdir, struct inode *dir, struct dentry *dentry,
> + umode_t mode)
> +LSM_HOOK(int, inode_rmdir, struct inode *dir, struct dentry *dentry)
> +LSM_HOOK(int, inode_mknod, struct inode *dir, struct dentry *dentry,
> + umode_t mode, dev_t dev)
> +LSM_HOOK(int, inode_rename, struct inode *old_dir, struct dentry *old_dentry,
> + struct inode *new_dir, struct dentry *new_dentry)
> +LSM_HOOK(int, inode_readlink, struct dentry *dentry)
> +LSM_HOOK(int, inode_follow_link, struct dentry *dentry, struct inode *inode,
> + bool rcu)
> +LSM_HOOK(int, inode_permission, struct inode *inode, int mask)
> +LSM_HOOK(int, inode_setattr, struct dentry *dentry, struct iattr *attr)
> +LSM_HOOK(int, inode_getattr, const struct path *path)
> +LSM_HOOK(int, inode_setxattr, struct dentry *dentry, const char *name,
> + const void *value, size_t size, int flags)
> +LSM_HOOK(void, inode_post_setxattr, struct dentry *dentry, const char *name,
> + const void *value, size_t size, int flags)
> +LSM_HOOK(int, inode_getxattr, struct dentry *dentry, const char *name)
> +LSM_HOOK(int, inode_listxattr, struct dentry *dentry)
> +LSM_HOOK(int, inode_removexattr, struct dentry *dentry, const char *name)
> +LSM_HOOK(int, inode_need_killpriv, struct dentry *dentry)
> +LSM_HOOK(int, inode_killpriv, struct dentry *dentry)
> +LSM_HOOK(int, inode_getsecurity, struct inode *inode, const char *name,
> + void **buffer, bool alloc)
> +LSM_HOOK(int, inode_setsecurity, struct inode *inode, const char *name,
> + const void *value, size_t size, int flags)
> +LSM_HOOK(int, inode_listsecurity, struct inode *inode, char *buffer,
> + size_t buffer_size)
> +LSM_HOOK(void, inode_getsecid, struct inode *inode, u32 *secid)
> +LSM_HOOK(int, inode_copy_up, struct dentry *src, struct cred **new)
> +LSM_HOOK(int, inode_copy_up_xattr, const char *name)
> +LSM_HOOK(int, kernfs_init_security, struct kernfs_node *kn_dir,
> + struct kernfs_node *kn)
> +LSM_HOOK(int, file_permission, struct file *file, int mask)
> +LSM_HOOK(int, file_alloc_security, struct file *file)
> +LSM_HOOK(void, file_free_security, struct file *file)
> +LSM_HOOK(int, file_ioctl, struct file *file, unsigned int cmd,
> + unsigned long arg)
> +LSM_HOOK(int, mmap_addr, unsigned long addr)
> +LSM_HOOK(int, mmap_file, struct file *file, unsigned long reqprot,
> + unsigned long prot, unsigned long flags)
> +LSM_HOOK(int, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot,
> + unsigned long prot)
> +LSM_HOOK(int, file_lock, struct file *file, unsigned int cmd)
> +LSM_HOOK(int, file_fcntl, struct file *file, unsigned int cmd,
> + unsigned long arg)
> +LSM_HOOK(void, file_set_fowner, struct file *file)
> +LSM_HOOK(int, file_send_sigiotask, struct task_struct *tsk,
> + struct fown_struct *fown, int sig)
> +LSM_HOOK(int, file_receive, struct file *file)
> +LSM_HOOK(int, file_open, struct file *file)
> +LSM_HOOK(int, task_alloc, struct task_struct *task, unsigned long clone_flags)
> +LSM_HOOK(void, task_free, struct task_struct *task)
> +LSM_HOOK(int, cred_alloc_blank, struct cred *cred, gfp_t gfp)
> +LSM_HOOK(void, cred_free, struct cred *cred)
> +LSM_HOOK(int, cred_prepare, struct cred *new, const struct cred *old, gfp_t gfp)
> +LSM_HOOK(void, cred_transfer, struct cred *new, const struct cred *old)
> +LSM_HOOK(void, cred_getsecid, const struct cred *c, u32 *secid)
> +LSM_HOOK(int, kernel_act_as, struct cred *new, u32 secid)
> +LSM_HOOK(int, kernel_create_files_as, struct cred *new, struct inode *inode)
> +LSM_HOOK(int, kernel_module_request, char *kmod_name)
> +LSM_HOOK(int, kernel_load_data, enum kernel_load_data_id id)
> +LSM_HOOK(int, kernel_read_file, struct file *file, enum kernel_read_file_id id)
> +LSM_HOOK(int, kernel_post_read_file, struct file *file, char *buf, loff_t size,
> + enum kernel_read_file_id id)
> +LSM_HOOK(int, task_fix_setuid, struct cred *new, const struct cred *old,
> + int flags)
> +LSM_HOOK(int, task_setpgid, struct task_struct *p, pid_t pgid)
> +LSM_HOOK(int, task_getpgid, struct task_struct *p)
> +LSM_HOOK(int, task_getsid, struct task_struct *p)
> +LSM_HOOK(void, task_getsecid, struct task_struct *p, u32 *secid)
> +LSM_HOOK(int, task_setnice, struct task_struct *p, int nice)
> +LSM_HOOK(int, task_setioprio, struct task_struct *p, int ioprio)
> +LSM_HOOK(int, task_getioprio, struct task_struct *p)
> +LSM_HOOK(int, task_prlimit, const struct cred *cred, const struct cred *tcred,
> + unsigned int flags)
> +LSM_HOOK(int, task_setrlimit, struct task_struct *p, unsigned int resource,
> + struct rlimit *new_rlim)
> +LSM_HOOK(int, task_setscheduler, struct task_struct *p)
> +LSM_HOOK(int, task_getscheduler, struct task_struct *p)
> +LSM_HOOK(int, task_movememory, struct task_struct *p)
> +LSM_HOOK(int, task_kill, struct task_struct *p, struct kernel_siginfo *info,
> + int sig, const struct cred *cred)
> +LSM_HOOK(int, task_prctl, int option, unsigned long arg2, unsigned long arg3,
> + unsigned long arg4, unsigned long arg5)
> +LSM_HOOK(void, task_to_inode, struct task_struct *p, struct inode *inode)
> +LSM_HOOK(int, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
> +LSM_HOOK(void, ipc_getsecid, struct kern_ipc_perm *ipcp, u32 *secid)
> +LSM_HOOK(int, msg_msg_alloc_security, struct msg_msg *msg)
> +LSM_HOOK(void, msg_msg_free_security, struct msg_msg *msg)
> +LSM_HOOK(int, msg_queue_alloc_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(void, msg_queue_free_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(int, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg)
> +LSM_HOOK(int, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd)
> +LSM_HOOK(int, msg_queue_msgsnd, struct kern_ipc_perm *perm, struct msg_msg *msg,
> + int msqflg)
> +LSM_HOOK(int, msg_queue_msgrcv, struct kern_ipc_perm *perm, struct msg_msg *msg,
> + struct task_struct *target, long type, int mode)
> +LSM_HOOK(int, shm_alloc_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(void, shm_free_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(int, shm_associate, struct kern_ipc_perm *perm, int shmflg)
> +LSM_HOOK(int, shm_shmctl, struct kern_ipc_perm *perm, int cmd)
> +LSM_HOOK(int, shm_shmat, struct kern_ipc_perm *perm, char __user *shmaddr,
> + int shmflg)
> +LSM_HOOK(int, sem_alloc_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(void, sem_free_security, struct kern_ipc_perm *perm)
> +LSM_HOOK(int, sem_associate, struct kern_ipc_perm *perm, int semflg)
> +LSM_HOOK(int, sem_semctl, struct kern_ipc_perm *perm, int cmd)
> +LSM_HOOK(int, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops,
> + unsigned nsops, int alter)
> +LSM_HOOK(int, netlink_send, struct sock *sk, struct sk_buff *skb)
> +LSM_HOOK(void, d_instantiate, struct dentry *dentry, struct inode *inode)
> +LSM_HOOK(int, getprocattr, struct task_struct *p, char *name, char **value)
> +LSM_HOOK(int, setprocattr, const char *name, void *value, size_t size)
> +LSM_HOOK(int, ismaclabel, const char *name)
> +LSM_HOOK(int, secid_to_secctx, u32 secid, char **secdata, u32 *seclen)
> +LSM_HOOK(int, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
> +LSM_HOOK(void, release_secctx, char *secdata, u32 seclen)
> +LSM_HOOK(void, inode_invalidate_secctx, struct inode *inode)
> +LSM_HOOK(int, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
> +LSM_HOOK(int, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen)
> +LSM_HOOK(int, inode_getsecctx, struct inode *inode, void **ctx, u32 *ctxlen)
> +#ifdef CONFIG_SECURITY_NETWORK
> +LSM_HOOK(int, unix_stream_connect, struct sock *sock, struct sock *other,
> + struct sock *newsk)
> +LSM_HOOK(int, unix_may_send, struct socket *sock, struct socket *other)
> +LSM_HOOK(int, socket_create, int family, int type, int protocol, int kern)
> +LSM_HOOK(int, socket_post_create, struct socket *sock, int family, int type,
> + int protocol, int kern)
> +LSM_HOOK(int, socket_socketpair, struct socket *socka, struct socket *sockb)
> +LSM_HOOK(int, socket_bind, struct socket *sock, struct sockaddr *address,
> + int addrlen)
> +LSM_HOOK(int, socket_connect, struct socket *sock, struct sockaddr *address,
> + int addrlen)
> +LSM_HOOK(int, socket_listen, struct socket *sock, int backlog)
> +LSM_HOOK(int, socket_accept, struct socket *sock, struct socket *newsock)
> +LSM_HOOK(int, socket_sendmsg, struct socket *sock, struct msghdr *msg, int size)
> +LSM_HOOK(int, socket_recvmsg, struct socket *sock, struct msghdr *msg, int size,
> + int flags)
> +LSM_HOOK(int, socket_getsockname, struct socket *sock)
> +LSM_HOOK(int, socket_getpeername, struct socket *sock)
> +LSM_HOOK(int, socket_getsockopt, struct socket *sock, int level, int optname)
> +LSM_HOOK(int, socket_setsockopt, struct socket *sock, int level, int optname)
> +LSM_HOOK(int, socket_shutdown, struct socket *sock, int how)
> +LSM_HOOK(int, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb)
> +LSM_HOOK(int, socket_getpeersec_stream, struct socket *sock,
> + char __user *optval, int __user *optlen, unsigned len)
> +LSM_HOOK(int, socket_getpeersec_dgram, struct socket *sock, struct sk_buff *skb,
> + u32 *secid)
> +LSM_HOOK(int, sk_alloc_security, struct sock *sk, int family, gfp_t priority)
> +LSM_HOOK(void, sk_free_security, struct sock *sk)
> +LSM_HOOK(void, sk_clone_security, const struct sock *sk, struct sock *newsk)
> +LSM_HOOK(void, sk_getsecid, struct sock *sk, u32 *secid)
> +LSM_HOOK(void, sock_graft, struct sock *sk, struct socket *parent)
> +LSM_HOOK(int, inet_conn_request, struct sock *sk, struct sk_buff *skb,
> + struct request_sock *req)
> +LSM_HOOK(void, inet_csk_clone, struct sock *newsk,
> + const struct request_sock *req)
> +LSM_HOOK(void, inet_conn_established, struct sock *sk, struct sk_buff *skb)
> +LSM_HOOK(int, secmark_relabel_packet, u32 secid)
> +LSM_HOOK(void, secmark_refcount_inc, void)
> +LSM_HOOK(void, secmark_refcount_dec, void)
> +LSM_HOOK(void, req_classify_flow, const struct request_sock *req,
> + struct flowi *fl)
> +LSM_HOOK(int, tun_dev_alloc_security, void **security)
> +LSM_HOOK(void, tun_dev_free_security, void *security)
> +LSM_HOOK(int, tun_dev_create, void)
> +LSM_HOOK(int, tun_dev_attach_queue, void *security)
> +LSM_HOOK(int, tun_dev_attach, struct sock *sk, void *security)
> +LSM_HOOK(int, tun_dev_open, void *security)
> +LSM_HOOK(int, sctp_assoc_request, struct sctp_endpoint *ep, struct sk_buff *skb)
> +LSM_HOOK(int, sctp_bind_connect, struct sock *sk, int optname,
> + struct sockaddr *address, int addrlen)
> +LSM_HOOK(void, sctp_sk_clone, struct sctp_endpoint *ep, struct sock *sk,
> + struct sock *newsk)
> +#endif /* CONFIG_SECURITY_NETWORK */
> +
> +#ifdef CONFIG_SECURITY_INFINIBAND
> +LSM_HOOK(int, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey)
> +LSM_HOOK(int, ib_endport_manage_subnet, void *sec, const char *dev_name,
> + u8 port_num)
> +LSM_HOOK(int, ib_alloc_security, void **sec)
> +LSM_HOOK(void, ib_free_security, void *sec)
> +#endif /* CONFIG_SECURITY_INFINIBAND */
> +
> +#ifdef CONFIG_SECURITY_NETWORK_XFRM
> +LSM_HOOK(int, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp,
> + struct xfrm_user_sec_ctx *sec_ctx, gfp_t gfp)
> +LSM_HOOK(int, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx,
> + struct xfrm_sec_ctx **new_ctx)
> +LSM_HOOK(void, xfrm_policy_free_security, struct xfrm_sec_ctx *ctx)
> +LSM_HOOK(int, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx)
> +LSM_HOOK(int, xfrm_state_alloc, struct xfrm_state *x,
> + struct xfrm_user_sec_ctx *sec_ctx)
> +LSM_HOOK(int, xfrm_state_alloc_acquire, struct xfrm_state *x,
> + struct xfrm_sec_ctx *polsec, u32 secid)
> +LSM_HOOK(void, xfrm_state_free_security, struct xfrm_state *x)
> +LSM_HOOK(int, xfrm_state_delete_security, struct xfrm_state *x)
> +LSM_HOOK(int, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid,
> + u8 dir)
> +LSM_HOOK(int, xfrm_state_pol_flow_match, struct xfrm_state *x,
> + struct xfrm_policy *xp, const struct flowi *fl)
> +LSM_HOOK(int, xfrm_decode_session, struct sk_buff *skb, u32 *secid, int ckall)
> +#endif /* CONFIG_SECURITY_NETWORK_XFRM */
> +
> +/* key management security hooks */
> +#ifdef CONFIG_KEYS
> +LSM_HOOK(int, key_alloc, struct key *key, const struct cred *cred,
> + unsigned long flags)
> +LSM_HOOK(void, key_free, struct key *key)
> +LSM_HOOK(int, key_permission, key_ref_t key_ref, const struct cred *cred,
> + unsigned perm)
> +LSM_HOOK(int, key_getsecurity, struct key *key, char **_buffer)
> +#endif /* CONFIG_KEYS */
> +
> +#ifdef CONFIG_AUDIT
> +LSM_HOOK(int, audit_rule_init, u32 field, u32 op, char *rulestr, void **lsmrule)
> +LSM_HOOK(int, audit_rule_known, struct audit_krule *krule)
> +LSM_HOOK(int, audit_rule_match, u32 secid, u32 field, u32 op, void *lsmrule)
> +LSM_HOOK(void, audit_rule_free, void *lsmrule)
> +#endif /* CONFIG_AUDIT */
> +
> +#ifdef CONFIG_BPF_SYSCALL
> +LSM_HOOK(int, bpf, int cmd, union bpf_attr *attr, unsigned int size)
> +LSM_HOOK(int, bpf_map, struct bpf_map *map, fmode_t fmode)
> +LSM_HOOK(int, bpf_prog, struct bpf_prog *prog)
> +LSM_HOOK(int, bpf_map_alloc_security, struct bpf_map *map)
> +LSM_HOOK(void, bpf_map_free_security, struct bpf_map *map)
> +LSM_HOOK(int, bpf_prog_alloc_security, struct bpf_prog_aux *aux)
> +LSM_HOOK(void, bpf_prog_free_security, struct bpf_prog_aux *aux)
> +#endif /* CONFIG_BPF_SYSCALL */
> +
> +LSM_HOOK(int, locked_down, enum lockdown_reason what)
> +
> +#ifdef CONFIG_PERF_EVENTS
> +LSM_HOOK(int, perf_event_open, struct perf_event_attr *attr, int type)
> +LSM_HOOK(int, perf_event_alloc, struct perf_event *event)
> +LSM_HOOK(void, perf_event_free, struct perf_event *event)
> +LSM_HOOK(int, perf_event_read, struct perf_event *event)
> +LSM_HOOK(int, perf_event_write, struct perf_event *event)
> +#endif
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 20d8cf194fb7..905954c650ff 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1456,625 +1456,15 @@
> * @what: kernel feature being accessed
> */
> union security_list_options {
> - int (*binder_set_context_mgr)(struct task_struct *mgr);
> - int (*binder_transaction)(struct task_struct *from,
> - struct task_struct *to);
> - int (*binder_transfer_binder)(struct task_struct *from,
> - struct task_struct *to);
> - int (*binder_transfer_file)(struct task_struct *from,
> - struct task_struct *to,
> - struct file *file);
> -
> - int (*ptrace_access_check)(struct task_struct *child,
> - unsigned int mode);
> - int (*ptrace_traceme)(struct task_struct *parent);
> - int (*capget)(struct task_struct *target, kernel_cap_t *effective,
> - kernel_cap_t *inheritable, kernel_cap_t *permitted);
> - int (*capset)(struct cred *new, const struct cred *old,
> - const kernel_cap_t *effective,
> - const kernel_cap_t *inheritable,
> - const kernel_cap_t *permitted);
> - int (*capable)(const struct cred *cred,
> - struct user_namespace *ns,
> - int cap,
> - unsigned int opts);
> - int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
> - int (*quota_on)(struct dentry *dentry);
> - int (*syslog)(int type);
> - int (*settime)(const struct timespec64 *ts, const struct timezone *tz);
> - int (*vm_enough_memory)(struct mm_struct *mm, long pages);
> -
> - int (*bprm_set_creds)(struct linux_binprm *bprm);
> - int (*bprm_check_security)(struct linux_binprm *bprm);
> - void (*bprm_committing_creds)(struct linux_binprm *bprm);
> - void (*bprm_committed_creds)(struct linux_binprm *bprm);
> -
> - int (*fs_context_dup)(struct fs_context *fc, struct fs_context *src_sc);
> - int (*fs_context_parse_param)(struct fs_context *fc, struct fs_parameter *param);
> -
> - int (*sb_alloc_security)(struct super_block *sb);
> - void (*sb_free_security)(struct super_block *sb);
> - void (*sb_free_mnt_opts)(void *mnt_opts);
> - int (*sb_eat_lsm_opts)(char *orig, void **mnt_opts);
> - int (*sb_remount)(struct super_block *sb, void *mnt_opts);
> - int (*sb_kern_mount)(struct super_block *sb);
> - int (*sb_show_options)(struct seq_file *m, struct super_block *sb);
> - int (*sb_statfs)(struct dentry *dentry);
> - int (*sb_mount)(const char *dev_name, const struct path *path,
> - const char *type, unsigned long flags, void *data);
> - int (*sb_umount)(struct vfsmount *mnt, int flags);
> - int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
> - int (*sb_set_mnt_opts)(struct super_block *sb,
> - void *mnt_opts,
> - unsigned long kern_flags,
> - unsigned long *set_kern_flags);
> - int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
> - struct super_block *newsb,
> - unsigned long kern_flags,
> - unsigned long *set_kern_flags);
> - int (*sb_add_mnt_opt)(const char *option, const char *val, int len,
> - void **mnt_opts);
> - int (*move_mount)(const struct path *from_path, const struct path *to_path);
> - int (*dentry_init_security)(struct dentry *dentry, int mode,
> - const struct qstr *name, void **ctx,
> - u32 *ctxlen);
> - int (*dentry_create_files_as)(struct dentry *dentry, int mode,
> - struct qstr *name,
> - const struct cred *old,
> - struct cred *new);
> -
> -
> -#ifdef CONFIG_SECURITY_PATH
> - int (*path_unlink)(const struct path *dir, struct dentry *dentry);
> - int (*path_mkdir)(const struct path *dir, struct dentry *dentry,
> - umode_t mode);
> - int (*path_rmdir)(const struct path *dir, struct dentry *dentry);
> - int (*path_mknod)(const struct path *dir, struct dentry *dentry,
> - umode_t mode, unsigned int dev);
> - int (*path_truncate)(const struct path *path);
> - int (*path_symlink)(const struct path *dir, struct dentry *dentry,
> - const char *old_name);
> - int (*path_link)(struct dentry *old_dentry, const struct path *new_dir,
> - struct dentry *new_dentry);
> - int (*path_rename)(const struct path *old_dir, struct dentry *old_dentry,
> - const struct path *new_dir,
> - struct dentry *new_dentry);
> - int (*path_chmod)(const struct path *path, umode_t mode);
> - int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid);
> - int (*path_chroot)(const struct path *path);
> -#endif
> - /* Needed for inode based security check */
> - int (*path_notify)(const struct path *path, u64 mask,
> - unsigned int obj_type);
> - int (*inode_alloc_security)(struct inode *inode);
> - void (*inode_free_security)(struct inode *inode);
> - int (*inode_init_security)(struct inode *inode, struct inode *dir,
> - const struct qstr *qstr,
> - const char **name, void **value,
> - size_t *len);
> - int (*inode_create)(struct inode *dir, struct dentry *dentry,
> - umode_t mode);
> - int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
> - struct dentry *new_dentry);
> - int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
> - int (*inode_symlink)(struct inode *dir, struct dentry *dentry,
> - const char *old_name);
> - int (*inode_mkdir)(struct inode *dir, struct dentry *dentry,
> - umode_t mode);
> - int (*inode_rmdir)(struct inode *dir, struct dentry *dentry);
> - int (*inode_mknod)(struct inode *dir, struct dentry *dentry,
> - umode_t mode, dev_t dev);
> - int (*inode_rename)(struct inode *old_dir, struct dentry *old_dentry,
> - struct inode *new_dir,
> - struct dentry *new_dentry);
> - int (*inode_readlink)(struct dentry *dentry);
> - int (*inode_follow_link)(struct dentry *dentry, struct inode *inode,
> - bool rcu);
> - int (*inode_permission)(struct inode *inode, int mask);
> - int (*inode_setattr)(struct dentry *dentry, struct iattr *attr);
> - int (*inode_getattr)(const struct path *path);
> - int (*inode_setxattr)(struct dentry *dentry, const char *name,
> - const void *value, size_t size, int flags);
> - void (*inode_post_setxattr)(struct dentry *dentry, const char *name,
> - const void *value, size_t size,
> - int flags);
> - int (*inode_getxattr)(struct dentry *dentry, const char *name);
> - int (*inode_listxattr)(struct dentry *dentry);
> - int (*inode_removexattr)(struct dentry *dentry, const char *name);
> - int (*inode_need_killpriv)(struct dentry *dentry);
> - int (*inode_killpriv)(struct dentry *dentry);
> - int (*inode_getsecurity)(struct inode *inode, const char *name,
> - void **buffer, bool alloc);
> - int (*inode_setsecurity)(struct inode *inode, const char *name,
> - const void *value, size_t size,
> - int flags);
> - int (*inode_listsecurity)(struct inode *inode, char *buffer,
> - size_t buffer_size);
> - void (*inode_getsecid)(struct inode *inode, u32 *secid);
> - int (*inode_copy_up)(struct dentry *src, struct cred **new);
> - int (*inode_copy_up_xattr)(const char *name);
> -
> - int (*kernfs_init_security)(struct kernfs_node *kn_dir,
> - struct kernfs_node *kn);
> -
> - int (*file_permission)(struct file *file, int mask);
> - int (*file_alloc_security)(struct file *file);
> - void (*file_free_security)(struct file *file);
> - int (*file_ioctl)(struct file *file, unsigned int cmd,
> - unsigned long arg);
> - int (*mmap_addr)(unsigned long addr);
> - int (*mmap_file)(struct file *file, unsigned long reqprot,
> - unsigned long prot, unsigned long flags);
> - int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
> - unsigned long prot);
> - int (*file_lock)(struct file *file, unsigned int cmd);
> - int (*file_fcntl)(struct file *file, unsigned int cmd,
> - unsigned long arg);
> - void (*file_set_fowner)(struct file *file);
> - int (*file_send_sigiotask)(struct task_struct *tsk,
> - struct fown_struct *fown, int sig);
> - int (*file_receive)(struct file *file);
> - int (*file_open)(struct file *file);
> -
> - int (*task_alloc)(struct task_struct *task, unsigned long clone_flags);
> - void (*task_free)(struct task_struct *task);
> - int (*cred_alloc_blank)(struct cred *cred, gfp_t gfp);
> - void (*cred_free)(struct cred *cred);
> - int (*cred_prepare)(struct cred *new, const struct cred *old,
> - gfp_t gfp);
> - void (*cred_transfer)(struct cred *new, const struct cred *old);
> - void (*cred_getsecid)(const struct cred *c, u32 *secid);
> - int (*kernel_act_as)(struct cred *new, u32 secid);
> - int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
> - int (*kernel_module_request)(char *kmod_name);
> - int (*kernel_load_data)(enum kernel_load_data_id id);
> - int (*kernel_read_file)(struct file *file, enum kernel_read_file_id id);
> - int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size,
> - enum kernel_read_file_id id);
> - int (*task_fix_setuid)(struct cred *new, const struct cred *old,
> - int flags);
> - int (*task_setpgid)(struct task_struct *p, pid_t pgid);
> - int (*task_getpgid)(struct task_struct *p);
> - int (*task_getsid)(struct task_struct *p);
> - void (*task_getsecid)(struct task_struct *p, u32 *secid);
> - int (*task_setnice)(struct task_struct *p, int nice);
> - int (*task_setioprio)(struct task_struct *p, int ioprio);
> - int (*task_getioprio)(struct task_struct *p);
> - int (*task_prlimit)(const struct cred *cred, const struct cred *tcred,
> - unsigned int flags);
> - int (*task_setrlimit)(struct task_struct *p, unsigned int resource,
> - struct rlimit *new_rlim);
> - int (*task_setscheduler)(struct task_struct *p);
> - int (*task_getscheduler)(struct task_struct *p);
> - int (*task_movememory)(struct task_struct *p);
> - int (*task_kill)(struct task_struct *p, struct kernel_siginfo *info,
> - int sig, const struct cred *cred);
> - int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3,
> - unsigned long arg4, unsigned long arg5);
> - void (*task_to_inode)(struct task_struct *p, struct inode *inode);
> -
> - int (*ipc_permission)(struct kern_ipc_perm *ipcp, short flag);
> - void (*ipc_getsecid)(struct kern_ipc_perm *ipcp, u32 *secid);
> -
> - int (*msg_msg_alloc_security)(struct msg_msg *msg);
> - void (*msg_msg_free_security)(struct msg_msg *msg);
> -
> - int (*msg_queue_alloc_security)(struct kern_ipc_perm *perm);
> - void (*msg_queue_free_security)(struct kern_ipc_perm *perm);
> - int (*msg_queue_associate)(struct kern_ipc_perm *perm, int msqflg);
> - int (*msg_queue_msgctl)(struct kern_ipc_perm *perm, int cmd);
> - int (*msg_queue_msgsnd)(struct kern_ipc_perm *perm, struct msg_msg *msg,
> - int msqflg);
> - int (*msg_queue_msgrcv)(struct kern_ipc_perm *perm, struct msg_msg *msg,
> - struct task_struct *target, long type,
> - int mode);
> -
> - int (*shm_alloc_security)(struct kern_ipc_perm *perm);
> - void (*shm_free_security)(struct kern_ipc_perm *perm);
> - int (*shm_associate)(struct kern_ipc_perm *perm, int shmflg);
> - int (*shm_shmctl)(struct kern_ipc_perm *perm, int cmd);
> - int (*shm_shmat)(struct kern_ipc_perm *perm, char __user *shmaddr,
> - int shmflg);
> -
> - int (*sem_alloc_security)(struct kern_ipc_perm *perm);
> - void (*sem_free_security)(struct kern_ipc_perm *perm);
> - int (*sem_associate)(struct kern_ipc_perm *perm, int semflg);
> - int (*sem_semctl)(struct kern_ipc_perm *perm, int cmd);
> - int (*sem_semop)(struct kern_ipc_perm *perm, struct sembuf *sops,
> - unsigned nsops, int alter);
> -
> - int (*netlink_send)(struct sock *sk, struct sk_buff *skb);
> -
> - void (*d_instantiate)(struct dentry *dentry, struct inode *inode);
> -
> - int (*getprocattr)(struct task_struct *p, char *name, char **value);
> - int (*setprocattr)(const char *name, void *value, size_t size);
> - int (*ismaclabel)(const char *name);
> - int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen);
> - int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid);
> - void (*release_secctx)(char *secdata, u32 seclen);
> -
> - void (*inode_invalidate_secctx)(struct inode *inode);
> - int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
> - int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
> - int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
> -
> -#ifdef CONFIG_SECURITY_NETWORK
> - int (*unix_stream_connect)(struct sock *sock, struct sock *other,
> - struct sock *newsk);
> - int (*unix_may_send)(struct socket *sock, struct socket *other);
> -
> - int (*socket_create)(int family, int type, int protocol, int kern);
> - int (*socket_post_create)(struct socket *sock, int family, int type,
> - int protocol, int kern);
> - int (*socket_socketpair)(struct socket *socka, struct socket *sockb);
> - int (*socket_bind)(struct socket *sock, struct sockaddr *address,
> - int addrlen);
> - int (*socket_connect)(struct socket *sock, struct sockaddr *address,
> - int addrlen);
> - int (*socket_listen)(struct socket *sock, int backlog);
> - int (*socket_accept)(struct socket *sock, struct socket *newsock);
> - int (*socket_sendmsg)(struct socket *sock, struct msghdr *msg,
> - int size);
> - int (*socket_recvmsg)(struct socket *sock, struct msghdr *msg,
> - int size, int flags);
> - int (*socket_getsockname)(struct socket *sock);
> - int (*socket_getpeername)(struct socket *sock);
> - int (*socket_getsockopt)(struct socket *sock, int level, int optname);
> - int (*socket_setsockopt)(struct socket *sock, int level, int optname);
> - int (*socket_shutdown)(struct socket *sock, int how);
> - int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
> - int (*socket_getpeersec_stream)(struct socket *sock,
> - char __user *optval,
> - int __user *optlen, unsigned len);
> - int (*socket_getpeersec_dgram)(struct socket *sock,
> - struct sk_buff *skb, u32 *secid);
> - int (*sk_alloc_security)(struct sock *sk, int family, gfp_t priority);
> - void (*sk_free_security)(struct sock *sk);
> - void (*sk_clone_security)(const struct sock *sk, struct sock *newsk);
> - void (*sk_getsecid)(struct sock *sk, u32 *secid);
> - void (*sock_graft)(struct sock *sk, struct socket *parent);
> - int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
> - struct request_sock *req);
> - void (*inet_csk_clone)(struct sock *newsk,
> - const struct request_sock *req);
> - void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
> - int (*secmark_relabel_packet)(u32 secid);
> - void (*secmark_refcount_inc)(void);
> - void (*secmark_refcount_dec)(void);
> - void (*req_classify_flow)(const struct request_sock *req,
> - struct flowi *fl);
> - int (*tun_dev_alloc_security)(void **security);
> - void (*tun_dev_free_security)(void *security);
> - int (*tun_dev_create)(void);
> - int (*tun_dev_attach_queue)(void *security);
> - int (*tun_dev_attach)(struct sock *sk, void *security);
> - int (*tun_dev_open)(void *security);
> - int (*sctp_assoc_request)(struct sctp_endpoint *ep,
> - struct sk_buff *skb);
> - int (*sctp_bind_connect)(struct sock *sk, int optname,
> - struct sockaddr *address, int addrlen);
> - void (*sctp_sk_clone)(struct sctp_endpoint *ep, struct sock *sk,
> - struct sock *newsk);
> -#endif /* CONFIG_SECURITY_NETWORK */
> -
> -#ifdef CONFIG_SECURITY_INFINIBAND
> - int (*ib_pkey_access)(void *sec, u64 subnet_prefix, u16 pkey);
> - int (*ib_endport_manage_subnet)(void *sec, const char *dev_name,
> - u8 port_num);
> - int (*ib_alloc_security)(void **sec);
> - void (*ib_free_security)(void *sec);
> -#endif /* CONFIG_SECURITY_INFINIBAND */
> -
> -#ifdef CONFIG_SECURITY_NETWORK_XFRM
> - int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
> - struct xfrm_user_sec_ctx *sec_ctx,
> - gfp_t gfp);
> - int (*xfrm_policy_clone_security)(struct xfrm_sec_ctx *old_ctx,
> - struct xfrm_sec_ctx **new_ctx);
> - void (*xfrm_policy_free_security)(struct xfrm_sec_ctx *ctx);
> - int (*xfrm_policy_delete_security)(struct xfrm_sec_ctx *ctx);
> - int (*xfrm_state_alloc)(struct xfrm_state *x,
> - struct xfrm_user_sec_ctx *sec_ctx);
> - int (*xfrm_state_alloc_acquire)(struct xfrm_state *x,
> - struct xfrm_sec_ctx *polsec,
> - u32 secid);
> - void (*xfrm_state_free_security)(struct xfrm_state *x);
> - int (*xfrm_state_delete_security)(struct xfrm_state *x);
> - int (*xfrm_policy_lookup)(struct xfrm_sec_ctx *ctx, u32 fl_secid,
> - u8 dir);
> - int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
> - struct xfrm_policy *xp,
> - const struct flowi *fl);
> - int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
> -#endif /* CONFIG_SECURITY_NETWORK_XFRM */
> -
> - /* key management security hooks */
> -#ifdef CONFIG_KEYS
> - int (*key_alloc)(struct key *key, const struct cred *cred,
> - unsigned long flags);
> - void (*key_free)(struct key *key);
> - int (*key_permission)(key_ref_t key_ref, const struct cred *cred,
> - unsigned perm);
> - int (*key_getsecurity)(struct key *key, char **_buffer);
> -#endif /* CONFIG_KEYS */
> -
> -#ifdef CONFIG_AUDIT
> - int (*audit_rule_init)(u32 field, u32 op, char *rulestr,
> - void **lsmrule);
> - int (*audit_rule_known)(struct audit_krule *krule);
> - int (*audit_rule_match)(u32 secid, u32 field, u32 op, void *lsmrule);
> - void (*audit_rule_free)(void *lsmrule);
> -#endif /* CONFIG_AUDIT */
> -
> -#ifdef CONFIG_BPF_SYSCALL
> - int (*bpf)(int cmd, union bpf_attr *attr,
> - unsigned int size);
> - int (*bpf_map)(struct bpf_map *map, fmode_t fmode);
> - int (*bpf_prog)(struct bpf_prog *prog);
> - int (*bpf_map_alloc_security)(struct bpf_map *map);
> - void (*bpf_map_free_security)(struct bpf_map *map);
> - int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
> - void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
> -#endif /* CONFIG_BPF_SYSCALL */
> - int (*locked_down)(enum lockdown_reason what);
> -#ifdef CONFIG_PERF_EVENTS
> - int (*perf_event_open)(struct perf_event_attr *attr, int type);
> - int (*perf_event_alloc)(struct perf_event *event);
> - void (*perf_event_free)(struct perf_event *event);
> - int (*perf_event_read)(struct perf_event *event);
> - int (*perf_event_write)(struct perf_event *event);
> -
> -#endif
> + #define LSM_HOOK(RET, NAME, ...) RET (*NAME)(__VA_ARGS__);
> + #include "lsm_hook_names.h"
> + #undef LSM_HOOK
> };
>
> struct security_hook_heads {
> - struct hlist_head binder_set_context_mgr;
> - struct hlist_head binder_transaction;
> - struct hlist_head binder_transfer_binder;
> - struct hlist_head binder_transfer_file;
> - struct hlist_head ptrace_access_check;
> - struct hlist_head ptrace_traceme;
> - struct hlist_head capget;
> - struct hlist_head capset;
> - struct hlist_head capable;
> - struct hlist_head quotactl;
> - struct hlist_head quota_on;
> - struct hlist_head syslog;
> - struct hlist_head settime;
> - struct hlist_head vm_enough_memory;
> - struct hlist_head bprm_set_creds;
> - struct hlist_head bprm_check_security;
> - struct hlist_head bprm_committing_creds;
> - struct hlist_head bprm_committed_creds;
> - struct hlist_head fs_context_dup;
> - struct hlist_head fs_context_parse_param;
> - struct hlist_head sb_alloc_security;
> - struct hlist_head sb_free_security;
> - struct hlist_head sb_free_mnt_opts;
> - struct hlist_head sb_eat_lsm_opts;
> - struct hlist_head sb_remount;
> - struct hlist_head sb_kern_mount;
> - struct hlist_head sb_show_options;
> - struct hlist_head sb_statfs;
> - struct hlist_head sb_mount;
> - struct hlist_head sb_umount;
> - struct hlist_head sb_pivotroot;
> - struct hlist_head sb_set_mnt_opts;
> - struct hlist_head sb_clone_mnt_opts;
> - struct hlist_head sb_add_mnt_opt;
> - struct hlist_head move_mount;
> - struct hlist_head dentry_init_security;
> - struct hlist_head dentry_create_files_as;
> -#ifdef CONFIG_SECURITY_PATH
> - struct hlist_head path_unlink;
> - struct hlist_head path_mkdir;
> - struct hlist_head path_rmdir;
> - struct hlist_head path_mknod;
> - struct hlist_head path_truncate;
> - struct hlist_head path_symlink;
> - struct hlist_head path_link;
> - struct hlist_head path_rename;
> - struct hlist_head path_chmod;
> - struct hlist_head path_chown;
> - struct hlist_head path_chroot;
> -#endif
> - /* Needed for inode based modules as well */
> - struct hlist_head path_notify;
> - struct hlist_head inode_alloc_security;
> - struct hlist_head inode_free_security;
> - struct hlist_head inode_init_security;
> - struct hlist_head inode_create;
> - struct hlist_head inode_link;
> - struct hlist_head inode_unlink;
> - struct hlist_head inode_symlink;
> - struct hlist_head inode_mkdir;
> - struct hlist_head inode_rmdir;
> - struct hlist_head inode_mknod;
> - struct hlist_head inode_rename;
> - struct hlist_head inode_readlink;
> - struct hlist_head inode_follow_link;
> - struct hlist_head inode_permission;
> - struct hlist_head inode_setattr;
> - struct hlist_head inode_getattr;
> - struct hlist_head inode_setxattr;
> - struct hlist_head inode_post_setxattr;
> - struct hlist_head inode_getxattr;
> - struct hlist_head inode_listxattr;
> - struct hlist_head inode_removexattr;
> - struct hlist_head inode_need_killpriv;
> - struct hlist_head inode_killpriv;
> - struct hlist_head inode_getsecurity;
> - struct hlist_head inode_setsecurity;
> - struct hlist_head inode_listsecurity;
> - struct hlist_head inode_getsecid;
> - struct hlist_head inode_copy_up;
> - struct hlist_head inode_copy_up_xattr;
> - struct hlist_head kernfs_init_security;
> - struct hlist_head file_permission;
> - struct hlist_head file_alloc_security;
> - struct hlist_head file_free_security;
> - struct hlist_head file_ioctl;
> - struct hlist_head mmap_addr;
> - struct hlist_head mmap_file;
> - struct hlist_head file_mprotect;
> - struct hlist_head file_lock;
> - struct hlist_head file_fcntl;
> - struct hlist_head file_set_fowner;
> - struct hlist_head file_send_sigiotask;
> - struct hlist_head file_receive;
> - struct hlist_head file_open;
> - struct hlist_head task_alloc;
> - struct hlist_head task_free;
> - struct hlist_head cred_alloc_blank;
> - struct hlist_head cred_free;
> - struct hlist_head cred_prepare;
> - struct hlist_head cred_transfer;
> - struct hlist_head cred_getsecid;
> - struct hlist_head kernel_act_as;
> - struct hlist_head kernel_create_files_as;
> - struct hlist_head kernel_load_data;
> - struct hlist_head kernel_read_file;
> - struct hlist_head kernel_post_read_file;
> - struct hlist_head kernel_module_request;
> - struct hlist_head task_fix_setuid;
> - struct hlist_head task_setpgid;
> - struct hlist_head task_getpgid;
> - struct hlist_head task_getsid;
> - struct hlist_head task_getsecid;
> - struct hlist_head task_setnice;
> - struct hlist_head task_setioprio;
> - struct hlist_head task_getioprio;
> - struct hlist_head task_prlimit;
> - struct hlist_head task_setrlimit;
> - struct hlist_head task_setscheduler;
> - struct hlist_head task_getscheduler;
> - struct hlist_head task_movememory;
> - struct hlist_head task_kill;
> - struct hlist_head task_prctl;
> - struct hlist_head task_to_inode;
> - struct hlist_head ipc_permission;
> - struct hlist_head ipc_getsecid;
> - struct hlist_head msg_msg_alloc_security;
> - struct hlist_head msg_msg_free_security;
> - struct hlist_head msg_queue_alloc_security;
> - struct hlist_head msg_queue_free_security;
> - struct hlist_head msg_queue_associate;
> - struct hlist_head msg_queue_msgctl;
> - struct hlist_head msg_queue_msgsnd;
> - struct hlist_head msg_queue_msgrcv;
> - struct hlist_head shm_alloc_security;
> - struct hlist_head shm_free_security;
> - struct hlist_head shm_associate;
> - struct hlist_head shm_shmctl;
> - struct hlist_head shm_shmat;
> - struct hlist_head sem_alloc_security;
> - struct hlist_head sem_free_security;
> - struct hlist_head sem_associate;
> - struct hlist_head sem_semctl;
> - struct hlist_head sem_semop;
> - struct hlist_head netlink_send;
> - struct hlist_head d_instantiate;
> - struct hlist_head getprocattr;
> - struct hlist_head setprocattr;
> - struct hlist_head ismaclabel;
> - struct hlist_head secid_to_secctx;
> - struct hlist_head secctx_to_secid;
> - struct hlist_head release_secctx;
> - struct hlist_head inode_invalidate_secctx;
> - struct hlist_head inode_notifysecctx;
> - struct hlist_head inode_setsecctx;
> - struct hlist_head inode_getsecctx;
> -#ifdef CONFIG_SECURITY_NETWORK
> - struct hlist_head unix_stream_connect;
> - struct hlist_head unix_may_send;
> - struct hlist_head socket_create;
> - struct hlist_head socket_post_create;
> - struct hlist_head socket_socketpair;
> - struct hlist_head socket_bind;
> - struct hlist_head socket_connect;
> - struct hlist_head socket_listen;
> - struct hlist_head socket_accept;
> - struct hlist_head socket_sendmsg;
> - struct hlist_head socket_recvmsg;
> - struct hlist_head socket_getsockname;
> - struct hlist_head socket_getpeername;
> - struct hlist_head socket_getsockopt;
> - struct hlist_head socket_setsockopt;
> - struct hlist_head socket_shutdown;
> - struct hlist_head socket_sock_rcv_skb;
> - struct hlist_head socket_getpeersec_stream;
> - struct hlist_head socket_getpeersec_dgram;
> - struct hlist_head sk_alloc_security;
> - struct hlist_head sk_free_security;
> - struct hlist_head sk_clone_security;
> - struct hlist_head sk_getsecid;
> - struct hlist_head sock_graft;
> - struct hlist_head inet_conn_request;
> - struct hlist_head inet_csk_clone;
> - struct hlist_head inet_conn_established;
> - struct hlist_head secmark_relabel_packet;
> - struct hlist_head secmark_refcount_inc;
> - struct hlist_head secmark_refcount_dec;
> - struct hlist_head req_classify_flow;
> - struct hlist_head tun_dev_alloc_security;
> - struct hlist_head tun_dev_free_security;
> - struct hlist_head tun_dev_create;
> - struct hlist_head tun_dev_attach_queue;
> - struct hlist_head tun_dev_attach;
> - struct hlist_head tun_dev_open;
> - struct hlist_head sctp_assoc_request;
> - struct hlist_head sctp_bind_connect;
> - struct hlist_head sctp_sk_clone;
> -#endif /* CONFIG_SECURITY_NETWORK */
> -#ifdef CONFIG_SECURITY_INFINIBAND
> - struct hlist_head ib_pkey_access;
> - struct hlist_head ib_endport_manage_subnet;
> - struct hlist_head ib_alloc_security;
> - struct hlist_head ib_free_security;
> -#endif /* CONFIG_SECURITY_INFINIBAND */
> -#ifdef CONFIG_SECURITY_NETWORK_XFRM
> - struct hlist_head xfrm_policy_alloc_security;
> - struct hlist_head xfrm_policy_clone_security;
> - struct hlist_head xfrm_policy_free_security;
> - struct hlist_head xfrm_policy_delete_security;
> - struct hlist_head xfrm_state_alloc;
> - struct hlist_head xfrm_state_alloc_acquire;
> - struct hlist_head xfrm_state_free_security;
> - struct hlist_head xfrm_state_delete_security;
> - struct hlist_head xfrm_policy_lookup;
> - struct hlist_head xfrm_state_pol_flow_match;
> - struct hlist_head xfrm_decode_session;
> -#endif /* CONFIG_SECURITY_NETWORK_XFRM */
> -#ifdef CONFIG_KEYS
> - struct hlist_head key_alloc;
> - struct hlist_head key_free;
> - struct hlist_head key_permission;
> - struct hlist_head key_getsecurity;
> -#endif /* CONFIG_KEYS */
> -#ifdef CONFIG_AUDIT
> - struct hlist_head audit_rule_init;
> - struct hlist_head audit_rule_known;
> - struct hlist_head audit_rule_match;
> - struct hlist_head audit_rule_free;
> -#endif /* CONFIG_AUDIT */
> -#ifdef CONFIG_BPF_SYSCALL
> - struct hlist_head bpf;
> - struct hlist_head bpf_map;
> - struct hlist_head bpf_prog;
> - struct hlist_head bpf_map_alloc_security;
> - struct hlist_head bpf_map_free_security;
> - struct hlist_head bpf_prog_alloc_security;
> - struct hlist_head bpf_prog_free_security;
> -#endif /* CONFIG_BPF_SYSCALL */
> - struct hlist_head locked_down;
> -#ifdef CONFIG_PERF_EVENTS
> - struct hlist_head perf_event_open;
> - struct hlist_head perf_event_alloc;
> - struct hlist_head perf_event_free;
> - struct hlist_head perf_event_read;
> - struct hlist_head perf_event_write;
> -#endif
> + #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
> + #include "lsm_hook_names.h"
> + #undef LSM_HOOK
> } __randomize_layout;
>
> /*
> --
> 2.20.1
>
--
Kees Cook
On Mon, Mar 23, 2020 at 05:44:11PM +0100, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> generated for each LSM hook. These nops are initialized as LSM hooks in
> a subsequent patch.
>
> Signed-off-by: KP Singh <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
-Kees
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> 2 files changed, 40 insertions(+)
> create mode 100644 include/linux/bpf_lsm.h
>
> diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> new file mode 100644
> index 000000000000..c6423a140220
> --- /dev/null
> +++ b/include/linux/bpf_lsm.h
> @@ -0,0 +1,21 @@
> +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> +#include <linux/lsm_hook_names.h>
> +#undef LSM_HOOK
> +
> +#endif /* CONFIG_BPF_LSM */
> +
> +#endif /* _LINUX_BPF_LSM_H */
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 82875039ca90..530d137f7a84 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -7,6 +7,25 @@
> #include <linux/filter.h>
> #include <linux/bpf.h>
> #include <linux/btf.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/bpf_lsm.h>
> +
> +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> + * function where a BPF program can be attached as an fexit trampoline.
> + */
> +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> +
> +#define LSM_HOOK_int(NAME, ...) \
> +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> +{ \
> + return 0; \
> +}
> +
> +#define LSM_HOOK_void(NAME, ...) \
> +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> +
> +#include <linux/lsm_hook_names.h>
> +#undef LSM_HOOK
>
> const struct bpf_prog_ops lsm_prog_ops = {
> };
> --
> 2.20.1
>
--
Kees Cook
On 23-M?r 12:16, Yonghong Song wrote:
>
>
> On 3/23/20 9:44 AM, KP Singh wrote:
> > From: KP Singh <[email protected]>
> >
> > JITed BPF programs are dynamically attached to the LSM hooks
> > using BPF trampolines. The trampoline prologue generates code to handle
> > conversion of the signature of the hook to the appropriate BPF context.
> >
> > The allocated trampoline programs are attached to the nop functions
> > initialized as LSM hooks.
> >
> > BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> > and need CAP_SYS_ADMIN (required for loading eBPF programs).
> >
> > Upon attachment:
> >
> > * A BPF fexit trampoline is used for LSM hooks with a void return type.
> > * A BPF fmod_ret trampoline is used for LSM hooks which return an
> > int. The attached programs can override the return value of the
> > bpf LSM hook to indicate a MAC Policy decision.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
> > include/linux/bpf.h | 4 ++++
> > include/linux/bpf_lsm.h | 11 +++++++++++
> > kernel/bpf/bpf_lsm.c | 29 +++++++++++++++++++++++++++++
> > kernel/bpf/btf.c | 9 ++++++++-
> > kernel/bpf/syscall.c | 26 ++++++++++++++++++++++----
> > kernel/bpf/trampoline.c | 17 +++++++++++++----
> > kernel/bpf/verifier.c | 19 +++++++++++++++----
> > 7 files changed, 102 insertions(+), 13 deletions(-)
> >
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index af81ec7b783c..adf2e5a6de4b 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -433,6 +433,10 @@ struct btf_func_model {
> > * programs only. Should not be used with normal calls and indirect calls.
> > */
> > #define BPF_TRAMP_F_SKIP_FRAME BIT(2)
> > +/* Override the return value of the original function. This flag only makes
> > + * sense for fexit trampolines.
> > + */
> > +#define BPF_TRAMP_F_OVERRIDE_RETURN BIT(3)
>
> Whether the return value is overridable is determined by hook return type as
> below. Do we still need this flag?
Apologies, this is a relic and should not have been there, will send a
new revision with this removed.
- KP
>
> > /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
> > * bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2
> [...]
On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> The bpf_lsm_ nops are initialized into the LSM framework like any other
> LSM. Some LSM hooks do not have 0 as their default return value. The
> __weak symbol for these hooks is overridden by a corresponding
> definition in security/bpf/hooks.c
>
> The LSM can be enabled / disabled with CONFIG_LSM.
>
> Signed-off-by: KP Singh <[email protected]>
Nice! This is super clean on the LSM side of things. :)
One note below...
> 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 | 55 +++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 67 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..68e5824868f9
> --- /dev/null
> +++ b/security/bpf/hooks.c
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +#include <linux/lsm_hooks.h>
> +#include <linux/bpf_lsm.h>
> +
> +/* Some LSM hooks do not have 0 as their default return values. Override the
> + * __weak definitons generated by default for these hooks
If you wanted to avoid this, couldn't you make the default return value
part of lsm_hooks.h?
e.g.:
LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
const char *name, void **buffer, bool alloc)
...
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
LSM_HOOK_##RET(NAME, DEFAULT, __VA_ARGS__)
...
#define LSM_HOOK_int(NAME, DEFAULT, ...) \
noinline int bpf_lsm_##NAME(__VA_ARGS__) \
{ \
return (DEFAULT); \
}
Then all the __weak stuff is gone, and the following 4 functions don't
need to be written out, and the information is available to the macros
if anyone else might ever want it.
-Kees
> + */
> +noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
> + void **buffer, bool alloc)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +noinline int bpf_lsm_inode_setsecurity(struct inode *inode, const char *name,
> + const void *value, size_t size,
> + int flags)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +noinline int bpf_lsm_task_prctl(int option, unsigned long arg2,
> + unsigned long arg3, unsigned long arg4,
> + unsigned long arg5)
> +{
> + return -ENOSYS;
> +}
> +
> +noinline int bpf_lsm_xfrm_state_pol_flow_match(struct xfrm_state *x,
> + struct xfrm_policy *xp,
> + const struct flowi *fl)
> +{
> + return 1;
> +}
> +
> +static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {
> + #define LSM_HOOK(RET, NAME, ...) LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
> + #include <linux/lsm_hook_names.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
>
--
Kees Cook
On 23-M?r 12:44, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
> > From: KP Singh <[email protected]>
> >
> > The bpf_lsm_ nops are initialized into the LSM framework like any other
> > LSM. Some LSM hooks do not have 0 as their default return value. The
> > __weak symbol for these hooks is overridden by a corresponding
> > definition in security/bpf/hooks.c
> >
> > The LSM can be enabled / disabled with CONFIG_LSM.
> >
> > Signed-off-by: KP Singh <[email protected]>
>
> Nice! This is super clean on the LSM side of things. :)
>
> One note below...
>
> > Reviewed-by: Brendan Jackman <[email protected]>
[...]
> > +
> > +/*
> > + * Copyright (C) 2020 Google LLC.
> > + */
> > +#include <linux/lsm_hooks.h>
> > +#include <linux/bpf_lsm.h>
> > +
> > +/* Some LSM hooks do not have 0 as their default return values. Override the
> > + * __weak definitons generated by default for these hooks
>
> If you wanted to avoid this, couldn't you make the default return value
> part of lsm_hooks.h?
>
> e.g.:
>
> LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
> const char *name, void **buffer, bool alloc)
>
> ...
>
> #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
> LSM_HOOK_##RET(NAME, DEFAULT, __VA_ARGS__)
> ...
> #define LSM_HOOK_int(NAME, DEFAULT, ...) \
> noinline int bpf_lsm_##NAME(__VA_ARGS__) \
> { \
> return (DEFAULT); \
> }
>
> Then all the __weak stuff is gone, and the following 4 functions don't
> need to be written out, and the information is available to the macros
> if anyone else might ever want it.
Thanks, I like it!
If no-one objects, I will update it in the next revision.
- KP
>
> -Kees
>
> > + */
> > +noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
> > + void **buffer, bool alloc)
> > +};
[...]
> > --
> > 2.20.1
> >
>
> --
> Kees Cook
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> The information about the different types of LSM hooks is scattered
> in two locations i.e. union security_list_options and
> struct security_hook_heads. Rather than duplicating this information
> even further for BPF_PROG_TYPE_LSM, define all the hooks with the
> LSM_HOOK macro in lsm_hook_names.h which is then used to generate all
> the data structures required by the LSM framework.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/lsm_hook_names.h | 354 +++++++++++++++++++
> include/linux/lsm_hooks.h | 622 +--------------------------------
> 2 files changed, 360 insertions(+), 616 deletions(-)
> create mode 100644 include/linux/lsm_hook_names.h
>
> diff --git a/include/linux/lsm_hook_names.h b/include/linux/lsm_hook_names.h
> new file mode 100644
> index 000000000000..412e4ca24c9b
> --- /dev/null
> +++ b/include/linux/lsm_hook_names.h
It's not really just hook names, it's full hook definitions, no? So
lsm_hook_defs.h seems a bit more appropriate. Just for consideration,
not that I care that strongly :)
[...]
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> generated for each LSM hook. These nops are initialized as LSM hooks in
> a subsequent patch.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> 2 files changed, 40 insertions(+)
> create mode 100644 include/linux/bpf_lsm.h
>
> diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> new file mode 100644
> index 000000000000..c6423a140220
> --- /dev/null
> +++ b/include/linux/bpf_lsm.h
> @@ -0,0 +1,21 @@
> +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> +#include <linux/lsm_hook_names.h>
> +#undef LSM_HOOK
> +
> +#endif /* CONFIG_BPF_LSM */
> +
> +#endif /* _LINUX_BPF_LSM_H */
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 82875039ca90..530d137f7a84 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -7,6 +7,25 @@
> #include <linux/filter.h>
> #include <linux/bpf.h>
> #include <linux/btf.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/bpf_lsm.h>
> +
> +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> + * function where a BPF program can be attached as an fexit trampoline.
> + */
> +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> +
> +#define LSM_HOOK_int(NAME, ...) \
> +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> +{ \
> + return 0; \
> +}
> +
> +#define LSM_HOOK_void(NAME, ...) \
> +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> +
Could unify with:
#define LSM_HOOK(RET, NAME, ...) noinline __weak RET bpf_lsm_##NAME(__VA_ARGS__)
{
return (RET)0;
}
then you don't need LSM_HOOK_int and LSM_HOOK_void.
> +#include <linux/lsm_hook_names.h>
> +#undef LSM_HOOK
>
> const struct bpf_prog_ops lsm_prog_ops = {
> };
> --
> 2.20.1
>
On 3/23/20 9:44 AM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> * Load/attach a BPF program to the file_mprotect (int) and
> bprm_committed_creds (void) LSM hooks.
> * Perform an action that triggers the hook.
> * Verify if the audit event was received using a shared global
> result variable.
>
> 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]>
> ---
> tools/testing/selftests/bpf/lsm_helpers.h | 19 +++
> .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++
> .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++
> .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++
> 4 files changed, 226 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
> create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
> create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
>
> diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
> new file mode 100644
> index 000000000000..3de230df93db
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/lsm_helpers.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +#ifndef _LSM_HELPERS_H
> +#define _LSM_HELPERS_H
> +
> +struct lsm_prog_result {
> + /* This ensures that the LSM Hook only monitors the PID requested
> + * by the loader
> + */
> + __u32 monitored_pid;
> + /* The number of calls to the prog for the monitored PID.
> + */
> + __u32 count;
> +};
> +
> +#endif /* _LSM_HELPERS_H */
> diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> new file mode 100644
> index 000000000000..5fd6b8f569f7
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> @@ -0,0 +1,112 @@
> +// 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_helpers.h"
> +#include "lsm_void_hook.skel.h"
> +#include "lsm_int_hook.skel.h"
> +
> +char *LS_ARGS[] = {"true", NULL};
> +
> +int heap_mprotect(void)
> +{
> + void *buf;
> + long sz;
> +
> + sz = sysconf(_SC_PAGESIZE);
> + if (sz < 0)
> + return sz;
> +
> + buf = memalign(sz, 2 * sz);
> + if (buf == NULL)
> + return -ENOMEM;
> +
> + return mprotect(buf, sz, PROT_READ | PROT_EXEC);
"buf" is leaking memory here.
> +}
> +
> +int exec_ls(struct lsm_prog_result *result)
> +{
> + int child_pid;
> +
> + child_pid = fork();
> + if (child_pid == 0) {
> + result->monitored_pid = getpid();
> + execvp(LS_ARGS[0], LS_ARGS);
> + return -EINVAL;
> + } else if (child_pid > 0)
> + return wait(NULL);
> +
> + return -EINVAL;
> +}
> +
> +void test_lsm_void_hook(void)
> +{
> + struct lsm_prog_result *result;
> + struct lsm_void_hook *skel = NULL;
> + int err, duration = 0;
> +
> + skel = lsm_void_hook__open_and_load();
> + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n"))
> + goto close_prog;
> +
> + err = lsm_void_hook__attach(skel);
> + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err))
> + goto close_prog;
> +
> + result = &skel->bss->result;
> +
> + err = exec_ls(result);
> + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno))
> + goto close_prog;
> +
> + if (CHECK(result->count != 1, "count", "count = %d", result->count))
> + goto close_prog;
> +
> + CHECK_FAIL(result->count != 1);
I think the above
if (CHECK(result->count != 1, "count", "count = %d", result->count))
goto close_prog;
CHECK_FAIL(result->count != 1);
can be replaced with
CHECK(result->count != 1, "count", "count = %d", result->count);
> +
> +close_prog:
> + lsm_void_hook__destroy(skel);
> +}
> +
> +void test_lsm_int_hook(void)
> +{
> + struct lsm_prog_result *result;
> + struct lsm_int_hook *skel = NULL;
> + int err, duration = 0;
> +
> + skel = lsm_int_hook__open_and_load();
> + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n"))
> + goto close_prog;
> +
> + err = lsm_int_hook__attach(skel);
> + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err))
> + goto close_prog;
> +
> + result = &skel->bss->result;
> + result->monitored_pid = getpid();
> +
> + err = heap_mprotect();
> + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
> + errno))
> + goto close_prog;
> +
> + CHECK_FAIL(result->count != 1);
> +
> +close_prog:
> + lsm_int_hook__destroy(skel);
> +}
> +
> +void test_lsm_test(void)
> +{
> + test_lsm_void_hook();
> + test_lsm_int_hook();
> +}
> diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> new file mode 100644
> index 000000000000..1c5028ddca61
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> @@ -0,0 +1,54 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright 2020 Google LLC.
> + */
> +
> +#include <linux/bpf.h>
> +#include <stdbool.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <errno.h>
> +#include "lsm_helpers.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +struct lsm_prog_result result = {
> + .monitored_pid = 0,
> + .count = 0,
> +};
> +
> +/*
> + * Define some of the structs used in the BPF program.
> + * Only the field names and their sizes need to be the
> + * same as the kernel type, the order is irrelevant.
> + */
> +struct mm_struct {
> + unsigned long start_brk, brk;
> +} __attribute__((preserve_access_index));
> +
> +struct vm_area_struct {
> + unsigned long vm_start, vm_end;
> + struct mm_struct *vm_mm;
> +} __attribute__((preserve_access_index));
> +
> +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();
In user space, we assign monitored_pid with getpid()
which is the process pid. Here
pid = bpf_get_current_pid_tgid()
actually got tid in the kernel.
Although it does not matter in this particular example,
maybe still use
bpf_get_current_pid_tgid() >> 32
to get process pid to be consistent.
The same for lsm_void_hook.c.
> + int is_heap = 0;
> +
> + is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
> + vma->vm_end <= vma->vm_mm->brk);
> +
> + if (is_heap && result.monitored_pid == pid) {
> + result.count++;
> + ret = -EPERM;
> + }
> +
> + return ret;
> +}
> diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> new file mode 100644
> index 000000000000..4d01a8536413
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +
> +#include <linux/bpf.h>
> +#include <stdbool.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <errno.h>
> +#include "lsm_helpers.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +struct lsm_prog_result result = {
> + .monitored_pid = 0,
> + .count = 0,
> +};
> +
> +/*
> + * Define some of the structs used in the BPF program.
> + * Only the field names and their sizes need to be the
> + * same as the kernel type, the order is irrelevant.
> + */
> +struct linux_binprm {
> + const char *filename;
> +} __attribute__((preserve_access_index));
> +
> +SEC("lsm/bprm_committed_creds")
> +int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
> +{
> + __u32 pid = bpf_get_current_pid_tgid();
> + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n";
> +
> + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename);
> + if (result.monitored_pid == pid)
> + result.count++;
> +
> + return 0;
> +}
>
Could you also upddate tools/testing/selftests/bpf/config file
so people will know what config options are needed to run the
self tests properly?
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> JITed BPF programs are dynamically attached to the LSM hooks
> using BPF trampolines. The trampoline prologue generates code to handle
> conversion of the signature of the hook to the appropriate BPF context.
>
> The allocated trampoline programs are attached to the nop functions
> initialized as LSM hooks.
>
> BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> and need CAP_SYS_ADMIN (required for loading eBPF programs).
>
> Upon attachment:
>
> * A BPF fexit trampoline is used for LSM hooks with a void return type.
> * A BPF fmod_ret trampoline is used for LSM hooks which return an
> int. The attached programs can override the return value of the
> bpf LSM hook to indicate a MAC Policy decision.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> include/linux/bpf.h | 4 ++++
> include/linux/bpf_lsm.h | 11 +++++++++++
> kernel/bpf/bpf_lsm.c | 29 +++++++++++++++++++++++++++++
> kernel/bpf/btf.c | 9 ++++++++-
> kernel/bpf/syscall.c | 26 ++++++++++++++++++++++----
> kernel/bpf/trampoline.c | 17 +++++++++++++----
> kernel/bpf/verifier.c | 19 +++++++++++++++----
> 7 files changed, 102 insertions(+), 13 deletions(-)
>
[...]
>
> +#define BPF_LSM_SYM_PREFX "bpf_lsm_"
> +
> +int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> + const struct bpf_prog *prog)
> +{
> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> + */
> + if (!capable(CAP_MAC_ADMIN))
> + return -EPERM;
> +
> + 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,
> + strlen(BPF_LSM_SYM_PREFX))) {
sizeof(BPF_LSM_SYM_PREFIX) - 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;
> +}
> +
[...]
> @@ -2367,10 +2369,24 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
> struct file *link_file;
> int link_fd, err;
>
> - if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
> - prog->expected_attach_type != BPF_TRACE_FEXIT &&
> - prog->expected_attach_type != BPF_MODIFY_RETURN &&
> - prog->type != BPF_PROG_TYPE_EXT) {
> + switch (prog->type) {
> + case BPF_PROG_TYPE_TRACING:
> + if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
> + 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:
It looks like an omission that we don't enforce expected_attach_type
to be 0 here. Should we fix it until it's too late?
> + break;
> + case BPF_PROG_TYPE_LSM:
> + if (prog->expected_attach_type != BPF_LSM_MAC) {
> + err = -EINVAL;
> + goto out_put_prog;
> + }
> + break;
> + default:
> err = -EINVAL;
> goto out_put_prog;
> }
> @@ -2452,12 +2468,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
> if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
> prog->type != BPF_PROG_TYPE_TRACING &&
> prog->type != BPF_PROG_TYPE_EXT &&
> + prog->type != BPF_PROG_TYPE_LSM &&
> prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
> err = -EINVAL;
> goto out_put_prog;
> }
>
> if (prog->type == BPF_PROG_TYPE_TRACING ||
> + prog->type == BPF_PROG_TYPE_LSM ||
> prog->type == BPF_PROG_TYPE_EXT) {
can you please refactor this into a nicer explicit switch instead of
combination of if/elses?
> if (attr->raw_tracepoint.name) {
> /* The attach point for this category of programs
> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index f30bca2a4d01..9be85aa4ec5f 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -6,6 +6,7 @@
> #include <linux/ftrace.h>
> #include <linux/rbtree_latch.h>
> #include <linux/perf_event.h>
> +#include <linux/btf.h>
>
[...]
On Mon, Mar 23, 2020 at 12:48 PM KP Singh <[email protected]> wrote:
>
> On 23-Mär 12:44, Kees Cook wrote:
> > On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
> > > From: KP Singh <[email protected]>
> > >
> > > The bpf_lsm_ nops are initialized into the LSM framework like any other
> > > LSM. Some LSM hooks do not have 0 as their default return value. The
> > > __weak symbol for these hooks is overridden by a corresponding
> > > definition in security/bpf/hooks.c
> > >
> > > The LSM can be enabled / disabled with CONFIG_LSM.
> > >
> > > Signed-off-by: KP Singh <[email protected]>
> >
> > Nice! This is super clean on the LSM side of things. :)
> >
> > One note below...
> >
> > > Reviewed-by: Brendan Jackman <[email protected]>
>
> [...]
>
> > > +
> > > +/*
> > > + * Copyright (C) 2020 Google LLC.
> > > + */
> > > +#include <linux/lsm_hooks.h>
> > > +#include <linux/bpf_lsm.h>
> > > +
> > > +/* Some LSM hooks do not have 0 as their default return values. Override the
> > > + * __weak definitons generated by default for these hooks
> >
> > If you wanted to avoid this, couldn't you make the default return value
> > part of lsm_hooks.h?
> >
> > e.g.:
> >
> > LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
> > const char *name, void **buffer, bool alloc)
> >
> > ...
> >
> > #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
> > LSM_HOOK_##RET(NAME, DEFAULT, __VA_ARGS__)
> > ...
> > #define LSM_HOOK_int(NAME, DEFAULT, ...) \
> > noinline int bpf_lsm_##NAME(__VA_ARGS__) \
> > { \
> > return (DEFAULT); \
> > }
> >
> > Then all the __weak stuff is gone, and the following 4 functions don't
> > need to be written out, and the information is available to the macros
> > if anyone else might ever want it.
>
> Thanks, I like it!
>
> If no-one objects, I will update it in the next revision.
>
I was about to propose the same, explicit default value seems like a
much cleaner and more straightforward way to do this.
> - KP
>
> >
> > -Kees
> >
> > > + */
> > > +noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
> > > + void **buffer, bool alloc)
> > > +};
>
> [...]
>
> > > --
> > > 2.20.1
> > >
> >
> > --
> > Kees Cook
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> Since BPF_PROG_TYPE_LSM uses the same attaching mechanism as
> BPF_PROG_TYPE_TRACING, the common logic is refactored into a static
> function bpf_program__attach_btf.
>
> A new API call bpf_program__attach_lsm is still added to avoid userspace
> conflicts if this ever changes in the future.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> tools/lib/bpf/bpf.c | 3 ++-
> tools/lib/bpf/libbpf.c | 41 +++++++++++++++++++++++++++++++++++-----
> tools/lib/bpf/libbpf.h | 4 ++++
> tools/lib/bpf/libbpf.map | 3 +++
> 4 files changed, 45 insertions(+), 6 deletions(-)
>
> diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> index c6dafe563176..73220176728d 100644
> --- a/tools/lib/bpf/bpf.c
> +++ b/tools/lib/bpf/bpf.c
> @@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
> memset(&attr, 0, sizeof(attr));
> attr.prog_type = load_attr->prog_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;
> } else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
> attr.prog_type == BPF_PROG_TYPE_EXT) {
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 085e41f9b68e..da8bee78e1ce 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -2362,7 +2362,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
>
> 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;
>
> /* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
> @@ -4870,7 +4871,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
> load_attr.insns = insns;
> load_attr.insns_cnt = insns_cnt;
> 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;
> } else if (prog->type == BPF_PROG_TYPE_TRACING ||
> prog->type == BPF_PROG_TYPE_EXT) {
> @@ -4955,6 +4957,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
> int err = 0, fd, i, btf_id;
>
> if ((prog->type == BPF_PROG_TYPE_TRACING ||
> + prog->type == BPF_PROG_TYPE_LSM ||
> prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
> btf_id = libbpf_find_attach_btf_id(prog);
> if (btf_id <= 0)
> @@ -6194,6 +6197,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(lsm, BPF_PROG_TYPE_LSM);
> 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_act, BPF_PROG_TYPE_SCHED_ACT);
> @@ -6260,6 +6264,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
> struct bpf_program *prog);
> static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
> struct bpf_program *prog);
> +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
> + struct bpf_program *prog);
>
> struct bpf_sec_def {
> const char *sec;
> @@ -6310,6 +6316,10 @@ static const struct bpf_sec_def section_defs[] = {
> SEC_DEF("freplace/", EXT,
> .is_attach_btf = true,
> .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("perf_event", BPF_PROG_TYPE_PERF_EVENT),
> BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
> @@ -6572,6 +6582,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
> }
>
> #define BTF_TRACE_PREFIX "btf_trace_"
> +#define BTF_LSM_PREFIX "bpf_lsm_"
> #define BTF_MAX_NAME_SIZE 128
>
> static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
> @@ -6599,6 +6610,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,
> if (attach_type == BPF_TRACE_RAW_TP)
> err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,
> 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
> err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
>
> @@ -7452,7 +7466,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
> 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(struct bpf_program *prog)
bpf_program__attach_btf_id() would be a bit more precise name
> {
> char errmsg[STRERR_BUFSIZE];
> struct bpf_link *link;
> @@ -7474,7 +7489,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> if (pfd < 0) {
> pfd = -errno;
> 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),
> libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
> return ERR_PTR(pfd);
> @@ -7483,10 +7498,26 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> return (struct bpf_link *)link;
> }
>
> +struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> +{
> + return bpf_program__attach_btf(prog);
> +}
> +
> +struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog)
> +{
> + return bpf_program__attach_btf(prog);
> +}
> +
> static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
> struct bpf_program *prog)
> {
> - return bpf_program__attach_trace(prog);
> + return bpf_program__attach_btf(prog);
well, no, it should call bpf_program__attach_trace()
> +}
> +
> +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
> + struct bpf_program *prog)
> +{
> + return bpf_program__attach_btf(prog);
and bpf_program__attach_lsm() here, don't shortcut invocation (you
argued yourself above, what if something about LSM changes, why
updating this invocation as well?)
> }
>
> struct bpf_link *bpf_program__attach(struct bpf_program *prog)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index d38d7a629417..df1be44c8118 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -248,6 +248,8 @@ bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
>
> LIBBPF_API struct bpf_link *
> bpf_program__attach_trace(struct bpf_program *prog);
> +LIBBPF_API struct bpf_link *
> +bpf_program__attach_lsm(struct bpf_program *prog);
> struct bpf_map;
> LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map);
> struct bpf_insn;
> @@ -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_raw_tracepoint(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_act(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);
> 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_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_act(const struct bpf_program *prog);
> LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 5129283c0284..ec29d3aa2700 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -243,5 +243,8 @@ LIBBPF_0.0.8 {
> bpf_link__pin;
> bpf_link__pin_path;
> bpf_link__unpin;
> + bpf_program__attach_lsm;
> + bpf_program__is_lsm;
> bpf_program__set_attach_target;
> + bpf_program__set_lsm;
> } LIBBPF_0.0.7;
> --
> 2.20.1
>
On 3/23/2020 12:44 PM, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
>> From: KP Singh <[email protected]>
>>
>> The bpf_lsm_ nops are initialized into the LSM framework like any other
>> LSM. Some LSM hooks do not have 0 as their default return value. The
>> __weak symbol for these hooks is overridden by a corresponding
>> definition in security/bpf/hooks.c
>>
>> The LSM can be enabled / disabled with CONFIG_LSM.
>>
>> Signed-off-by: KP Singh <[email protected]>
> Nice! This is super clean on the LSM side of things. :)
>
> One note below...
>
>> 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 | 55 +++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 67 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..68e5824868f9
>> --- /dev/null
>> +++ b/security/bpf/hooks.c
>> @@ -0,0 +1,55 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +/*
>> + * Copyright (C) 2020 Google LLC.
>> + */
>> +#include <linux/lsm_hooks.h>
>> +#include <linux/bpf_lsm.h>
>> +
>> +/* Some LSM hooks do not have 0 as their default return values. Override the
>> + * __weak definitons generated by default for these hooks
> If you wanted to avoid this, couldn't you make the default return value
> part of lsm_hooks.h?
>
> e.g.:
>
> LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
> const char *name, void **buffer, bool alloc)
If you're going to do that you'll have to keep lsm_hooks.h and security.c
default values in sync somehow. Note that the four functions you've called
out won't be using call_int_hook() after the next round of stacking. I'm not
nixing the idea, I just don't want the default return for the security_
functions defined in two places.
>
> ...
>
> #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
> LSM_HOOK_##RET(NAME, DEFAULT, __VA_ARGS__)
> ...
> #define LSM_HOOK_int(NAME, DEFAULT, ...) \
> noinline int bpf_lsm_##NAME(__VA_ARGS__) \
> { \
> return (DEFAULT); \
> }
>
> Then all the __weak stuff is gone, and the following 4 functions don't
> need to be written out, and the information is available to the macros
> if anyone else might ever want it.
>
> -Kees
>
>> + */
>> +noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
>> + void **buffer, bool alloc)
>> +{
>> + return -EOPNOTSUPP;
>> +}
>> +
>> +noinline int bpf_lsm_inode_setsecurity(struct inode *inode, const char *name,
>> + const void *value, size_t size,
>> + int flags)
>> +{
>> + return -EOPNOTSUPP;
>> +}
>> +
>> +noinline int bpf_lsm_task_prctl(int option, unsigned long arg2,
>> + unsigned long arg3, unsigned long arg4,
>> + unsigned long arg5)
>> +{
>> + return -ENOSYS;
>> +}
>> +
>> +noinline int bpf_lsm_xfrm_state_pol_flow_match(struct xfrm_state *x,
>> + struct xfrm_policy *xp,
>> + const struct flowi *fl)
>> +{
>> + return 1;
>> +}
>> +
>> +static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {
>> + #define LSM_HOOK(RET, NAME, ...) LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
>> + #include <linux/lsm_hook_names.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
>>
On Mon, Mar 23, 2020 at 01:47:29PM -0700, Casey Schaufler wrote:
> On 3/23/2020 12:44 PM, Kees Cook wrote:
> > On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
> >> +/* Some LSM hooks do not have 0 as their default return values. Override the
> >> + * __weak definitons generated by default for these hooks
> > If you wanted to avoid this, couldn't you make the default return value
> > part of lsm_hooks.h?
> >
> > e.g.:
> >
> > LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
> > const char *name, void **buffer, bool alloc)
>
> If you're going to do that you'll have to keep lsm_hooks.h and security.c
> default values in sync somehow. Note that the four functions you've called
> out won't be using call_int_hook() after the next round of stacking. I'm not
> nixing the idea, I just don't want the default return for the security_
> functions defined in two places.
Yeah, I actually went looking for this after I sent the email, realizing
that the defaults were also used in security.c. I've been pondering how
to keep them from being duplicated. I'm working on some ideas.
The four are:
inode_getsecurity
inode_setsecurity
task_prctl
xfrm_state_pol_flow_match
None of these are already just calling call_int_hook(), but I assume
they'll need further tweaks in the coming stacking.
To leave things as open-code-able as possible while still benefiting
from the macro consolidation, how about something like this:
lsm_hook_names.h:
LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity,
struct inode *inode, const char *name, void **buffer, bool alloc)
...
security.c:
#define LSM_RET_DEFAULT_void(DEFAULT, NAME) /* */
#define LSM_RET_DEFAULT_int(DEFAULT, NAME)
static const int NAME#_default = (DEFAULT);
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
LSM_RET_DEFAULT_#RET(DEFAULT, NAME)
#include <linux/lsm_hook_names.h>
#undef LSM_HOOK
...
Then -EOPNOTSUPP is available as "inode_getsecurity_default":
int security_inode_getsecurity(struct inode *inode, const char *name,
void **buffer, bool alloc)
{
struct security_hook_list *hp;
int rc;
if (unlikely(IS_PRIVATE(inode)))
return inode_getsecurity_default;
/*
* Only one module will provide an attribute with a given name.
*/
hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
if (rc != inode_getsecurity_default)
return rc;
}
return inode_getsecurity_default;
}
On the other hand, it's only 4 non-default return codes, so maybe the
sync burden isn't very high?
--
Kees Cook
On 3/23/2020 2:44 PM, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 01:47:29PM -0700, Casey Schaufler wrote:
>> On 3/23/2020 12:44 PM, Kees Cook wrote:
>>> On Mon, Mar 23, 2020 at 05:44:13PM +0100, KP Singh wrote:
>>>> +/* Some LSM hooks do not have 0 as their default return values. Override the
>>>> + * __weak definitons generated by default for these hooks
>>> If you wanted to avoid this, couldn't you make the default return value
>>> part of lsm_hooks.h?
>>>
>>> e.g.:
>>>
>>> LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct inode *inode,
>>> const char *name, void **buffer, bool alloc)
>> If you're going to do that you'll have to keep lsm_hooks.h and security.c
>> default values in sync somehow. Note that the four functions you've called
>> out won't be using call_int_hook() after the next round of stacking. I'm not
>> nixing the idea, I just don't want the default return for the security_
>> functions defined in two places.
> Yeah, I actually went looking for this after I sent the email, realizing
> that the defaults were also used in security.c. I've been pondering how
> to keep them from being duplicated. I'm working on some ideas.
>
> The four are:
>
> inode_getsecurity
> inode_setsecurity
> task_prctl
> xfrm_state_pol_flow_match
>
> None of these are already just calling call_int_hook(), but I assume
> they'll need further tweaks in the coming stacking.
>
> To leave things as open-code-able as possible while still benefiting
> from the macro consolidation, how about something like this:
>
> lsm_hook_names.h:
>
> LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity,
> struct inode *inode, const char *name, void **buffer, bool alloc)
>
> ...
>
> security.c:
>
> #define LSM_RET_DEFAULT_void(DEFAULT, NAME) /* */
> #define LSM_RET_DEFAULT_int(DEFAULT, NAME)
> static const int NAME#_default = (DEFAULT);
>
> #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
> LSM_RET_DEFAULT_#RET(DEFAULT, NAME)
> #include <linux/lsm_hook_names.h>
> #undef LSM_HOOK
> ...
>
> Then -EOPNOTSUPP is available as "inode_getsecurity_default":
>
> int security_inode_getsecurity(struct inode *inode, const char *name,
> void **buffer, bool alloc)
> {
> struct security_hook_list *hp;
> int rc;
>
> if (unlikely(IS_PRIVATE(inode)))
> return inode_getsecurity_default;
> /*
> * Only one module will provide an attribute with a given name.
> */
> hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
> rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
> if (rc != inode_getsecurity_default)
> return rc;
> }
> return inode_getsecurity_default;
> }
>
>
> On the other hand, it's only 4 non-default return codes, so maybe the
> sync burden isn't very high?
That's not too terrible, I suppose. What would you be thinking for
the calls that do use call_int_hook()?
rc = call_int_hook(something, something_default, goodnesses);
or embedded in the macro:
rc = call_int_hook(something, goodnesses);
On Mon, Mar 23, 2020 at 02:58:18PM -0700, Casey Schaufler wrote:
> That's not too terrible, I suppose. What would you be thinking for
> the calls that do use call_int_hook()?
>
> rc = call_int_hook(something, something_default, goodnesses);
>
> or embedded in the macro:
>
> rc = call_int_hook(something, goodnesses);
Oh yes, good point. The hook call already knows the name, so:
#define call_int_hook(FUNC, ...) ({ \
int RC = FUNC#_default; \
...
--
Kees Cook
On 3/23/2020 3:12 PM, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 02:58:18PM -0700, Casey Schaufler wrote:
>> That's not too terrible, I suppose. What would you be thinking for
>> the calls that do use call_int_hook()?
>>
>> rc = call_int_hook(something, something_default, goodnesses);
>>
>> or embedded in the macro:
>>
>> rc = call_int_hook(something, goodnesses);
> Oh yes, good point. The hook call already knows the name, so:
>
> #define call_int_hook(FUNC, ...) ({ \
> int RC = FUNC#_default; \
> ...
That makes the most sense, I think. It's getting a little heavy on
hidden magic, but we do tend to have a pretty good set of eyes watching
when a new hook is proposed. I would expect the changes to call_int_hook()
and its callers should be made when default is added to LSM_HOOK, not
after.
On 3/23/2020 9:44 AM, KP Singh wrote:
> From: KP Singh <[email protected]>
>
> The bpf_lsm_ nops are initialized into the LSM framework like any other
> LSM. Some LSM hooks do not have 0 as their default return value. The
> __weak symbol for these hooks is overridden by a corresponding
> definition in security/bpf/hooks.c
>
> The LSM can be enabled / disabled with CONFIG_LSM.
>
> Signed-off-by: KP Singh <[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 | 55 +++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 67 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..68e5824868f9
> --- /dev/null
> +++ b/security/bpf/hooks.c
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +#include <linux/lsm_hooks.h>
> +#include <linux/bpf_lsm.h>
> +
> +/* Some LSM hooks do not have 0 as their default return values. Override the
> + * __weak definitons generated by default for these hooks
> + */
> +noinline int bpf_lsm_inode_getsecurity(struct inode *inode, const char *name,
> + void **buffer, bool alloc)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +noinline int bpf_lsm_inode_setsecurity(struct inode *inode, const char *name,
> + const void *value, size_t size,
> + int flags)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +noinline int bpf_lsm_task_prctl(int option, unsigned long arg2,
> + unsigned long arg3, unsigned long arg4,
> + unsigned long arg5)
> +{
> + return -ENOSYS;
> +}
> +
> +noinline int bpf_lsm_xfrm_state_pol_flow_match(struct xfrm_state *x,
> + struct xfrm_policy *xp,
> + const struct flowi *fl)
> +{
> + return 1;
> +}
> +
> +static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {
> + #define LSM_HOOK(RET, NAME, ...) LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
> + #include <linux/lsm_hook_names.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,
Have you given up on the "BPF must be last" requirement?
> +};
On 23-M?r 18:13, Casey Schaufler wrote:
> On 3/23/2020 9:44 AM, KP Singh wrote:
> > From: KP Singh <[email protected]>
> >
> > The bpf_lsm_ nops are initialized into the LSM framework like any other
> > LSM. Some LSM hooks do not have 0 as their default return value. The
> > __weak symbol for these hooks is overridden by a corresponding
> > definition in security/bpf/hooks.c
> >
> > + return 0;
[...]
> > +}
> > +
> > +DEFINE_LSM(bpf) = {
> > + .name = "bpf",
> > + .init = bpf_lsm_init,
>
> Have you given up on the "BPF must be last" requirement?
Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
anwyays so the position ~shouldn't~ matter. (based on some of the
discussions we had on the BPF_MODIFY_RETURN patches).
However, This can be added later (in a separate patch) if really
deemed necessary.
- KP
>
> > +};
On 23-M?r 15:12, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 02:58:18PM -0700, Casey Schaufler wrote:
> > That's not too terrible, I suppose. What would you be thinking for
> > the calls that do use call_int_hook()?
> >
> > rc = call_int_hook(something, something_default, goodnesses);
> >
> > or embedded in the macro:
> >
> > rc = call_int_hook(something, goodnesses);
>
> Oh yes, good point. The hook call already knows the name, so:
>
> #define call_int_hook(FUNC, ...) ({ \
> int RC = FUNC#_default; \
Excellent ideas, I will spin up the next revision with these changes.
- KP
> ...
>
>
> --
> Kees Cook
>
>
On 23-M?r 13:25, Andrii Nakryiko wrote:
> On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > Since BPF_PROG_TYPE_LSM uses the same attaching mechanism as
> > BPF_PROG_TYPE_TRACING, the common logic is refactored into a static
> > function bpf_program__attach_btf.
> >
> > A new API call bpf_program__attach_lsm is still added to avoid userspace
> > conflicts if this ever changes in the future.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
> > tools/lib/bpf/bpf.c | 3 ++-
> > tools/lib/bpf/libbpf.c | 41 +++++++++++++++++++++++++++++++++++-----
> > tools/lib/bpf/libbpf.h | 4 ++++
> > tools/lib/bpf/libbpf.map | 3 +++
> > 4 files changed, 45 insertions(+), 6 deletions(-)
> >
> > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> > index c6dafe563176..73220176728d 100644
> > --- a/tools/lib/bpf/bpf.c
> > +++ b/tools/lib/bpf/bpf.c
> > @@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
> > memset(&attr, 0, sizeof(attr));
> > attr.prog_type = load_attr->prog_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;
> > } else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
> > attr.prog_type == BPF_PROG_TYPE_EXT) {
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 085e41f9b68e..da8bee78e1ce 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -2362,7 +2362,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
> >
> > 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;
> >
> > /* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
> > @@ -4870,7 +4871,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
> > load_attr.insns = insns;
> > load_attr.insns_cnt = insns_cnt;
> > 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;
> > } else if (prog->type == BPF_PROG_TYPE_TRACING ||
> > prog->type == BPF_PROG_TYPE_EXT) {
> > @@ -4955,6 +4957,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
> > int err = 0, fd, i, btf_id;
> >
> > if ((prog->type == BPF_PROG_TYPE_TRACING ||
> > + prog->type == BPF_PROG_TYPE_LSM ||
> > prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
> > btf_id = libbpf_find_attach_btf_id(prog);
> > if (btf_id <= 0)
> > @@ -6194,6 +6197,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(lsm, BPF_PROG_TYPE_LSM);
> > 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_act, BPF_PROG_TYPE_SCHED_ACT);
> > @@ -6260,6 +6264,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
> > struct bpf_program *prog);
> > static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
> > struct bpf_program *prog);
> > +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
> > + struct bpf_program *prog);
> >
> > struct bpf_sec_def {
> > const char *sec;
> > @@ -6310,6 +6316,10 @@ static const struct bpf_sec_def section_defs[] = {
> > SEC_DEF("freplace/", EXT,
> > .is_attach_btf = true,
> > .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("perf_event", BPF_PROG_TYPE_PERF_EVENT),
> > BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
> > @@ -6572,6 +6582,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
> > }
> >
> > #define BTF_TRACE_PREFIX "btf_trace_"
> > +#define BTF_LSM_PREFIX "bpf_lsm_"
> > #define BTF_MAX_NAME_SIZE 128
> >
> > static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
> > @@ -6599,6 +6610,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,
> > if (attach_type == BPF_TRACE_RAW_TP)
> > err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,
> > 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
> > err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
> >
> > @@ -7452,7 +7466,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
> > 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(struct bpf_program *prog)
>
> bpf_program__attach_btf_id() would be a bit more precise name
Agreed, Updated.
>
> > {
> > char errmsg[STRERR_BUFSIZE];
> > struct bpf_link *link;
> > @@ -7474,7 +7489,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> > if (pfd < 0) {
> > pfd = -errno;
> > 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),
> > libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
> > return ERR_PTR(pfd);
> > @@ -7483,10 +7498,26 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> > return (struct bpf_link *)link;
> > }
> >
> > +struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
> > +{
> > + return bpf_program__attach_btf(prog);
> > +}
> > +
> > +struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog)
> > +{
> > + return bpf_program__attach_btf(prog);
> > +}
> > +
> > static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
> > struct bpf_program *prog)
> > {
> > - return bpf_program__attach_trace(prog);
> > + return bpf_program__attach_btf(prog);
>
> well, no, it should call bpf_program__attach_trace()
You are right, the static helper should not be called directly.
Updated this and the LSM call to call their respective functions.
>
> > +}
> > +
> > +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
> > + struct bpf_program *prog)
> > +{
> > + return bpf_program__attach_btf(prog);
>
> and bpf_program__attach_lsm() here, don't shortcut invocation (you
> argued yourself above, what if something about LSM changes, why
> updating this invocation as well?)
Makes sense.
- KP
>
> > }
> >
> > struct bpf_link *bpf_program__attach(struct bpf_program *prog)
[...]
> > + bpf_program__is_lsm;
> > bpf_program__set_attach_target;
> > + bpf_program__set_lsm;
> > } LIBBPF_0.0.7;
> > --
> > 2.20.1
> >
On 23-M?r 12:59, Andrii Nakryiko wrote:
> On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> > generated for each LSM hook. These nops are initialized as LSM hooks in
> > a subsequent patch.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
> > include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> > kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> > 2 files changed, 40 insertions(+)
> > create mode 100644 include/linux/bpf_lsm.h
> >
> > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> > new file mode 100644
> > index 000000000000..c6423a140220
> > --- /dev/null
> > +++ b/include/linux/bpf_lsm.h
> > @@ -0,0 +1,21 @@
> > +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> > +#include <linux/lsm_hook_names.h>
> > +#undef LSM_HOOK
> > +
> > +#endif /* CONFIG_BPF_LSM */
> > +
> > +#endif /* _LINUX_BPF_LSM_H */
> > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > index 82875039ca90..530d137f7a84 100644
> > --- a/kernel/bpf/bpf_lsm.c
> > +++ b/kernel/bpf/bpf_lsm.c
> > @@ -7,6 +7,25 @@
> > #include <linux/filter.h>
> > #include <linux/bpf.h>
> > #include <linux/btf.h>
> > +#include <linux/lsm_hooks.h>
> > +#include <linux/bpf_lsm.h>
> > +
> > +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> > + * function where a BPF program can be attached as an fexit trampoline.
> > + */
> > +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> > +
> > +#define LSM_HOOK_int(NAME, ...) \
> > +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> > +{ \
> > + return 0; \
> > +}
> > +
> > +#define LSM_HOOK_void(NAME, ...) \
> > +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > +
>
> Could unify with:
>
> #define LSM_HOOK(RET, NAME, ...) noinline __weak RET bpf_lsm_##NAME(__VA_ARGS__)
> {
> return (RET)0;
> }
>
> then you don't need LSM_HOOK_int and LSM_HOOK_void.
Nice.
But, given that we are adding default values and that
they are only needed for int hooks, we will need to keep the macros
separate for int and void. Or, Am I missing a trick here?
- KP
>
> > +#include <linux/lsm_hook_names.h>
> > +#undef LSM_HOOK
> >
> > const struct bpf_prog_ops lsm_prog_ops = {
> > };
> > --
> > 2.20.1
> >
On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> JITed BPF programs are dynamically attached to the LSM hooks
> using BPF trampolines. The trampoline prologue generates code to handle
> conversion of the signature of the hook to the appropriate BPF context.
>
> The allocated trampoline programs are attached to the nop functions
> initialized as LSM hooks.
>
> BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> and need CAP_SYS_ADMIN (required for loading eBPF programs).
>
> Upon attachment:
>
> * A BPF fexit trampoline is used for LSM hooks with a void return type.
> * A BPF fmod_ret trampoline is used for LSM hooks which return an
> int. The attached programs can override the return value of the
> bpf LSM hook to indicate a MAC Policy decision.
>
> Signed-off-by: KP Singh <[email protected]>
> Reviewed-by: Brendan Jackman <[email protected]>
> Reviewed-by: Florent Revest <[email protected]>
> ---
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 530d137f7a84..2a8131b640b8 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -9,6 +9,9 @@
> #include <linux/btf.h>
> #include <linux/lsm_hooks.h>
> #include <linux/bpf_lsm.h>
> +#include <linux/jump_label.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 as an fexit trampoline.
> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> #include <linux/lsm_hook_names.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)
> +{
> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> + */
> + if (!capable(CAP_MAC_ADMIN))
> + return -EPERM;
I had asked before, and will ask again: please provide an explicit LSM
hook for mediating whether one can make changes to the LSM hooks.
Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
On Mon, Mar 23, 2020 at 9:52 PM KP Singh <[email protected]> wrote:
>
> On 23-Mär 18:13, Casey Schaufler wrote:
> > On 3/23/2020 9:44 AM, KP Singh wrote:
> > > From: KP Singh <[email protected]>
> > >
> > > The bpf_lsm_ nops are initialized into the LSM framework like any other
> > > LSM. Some LSM hooks do not have 0 as their default return value. The
> > > __weak symbol for these hooks is overridden by a corresponding
> > > definition in security/bpf/hooks.c
> > >
> > > + return 0;
>
> [...]
>
> > > +}
> > > +
> > > +DEFINE_LSM(bpf) = {
> > > + .name = "bpf",
> > > + .init = bpf_lsm_init,
> >
> > Have you given up on the "BPF must be last" requirement?
>
> Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
> anwyays so the position ~shouldn't~ matter. (based on some of the
> discussions we had on the BPF_MODIFY_RETURN patches).
>
> However, This can be added later (in a separate patch) if really
> deemed necessary.
It matters for SELinux, as I previously explained. A process that has
CAP_SYS_ADMIN is not assumed to be able to circumvent MAC policy.
And executing prior to SELinux allows the bpf program to access and
potentially leak to userspace information that wouldn't be visible to
the
process itself. However, I thought you were handling the order issue
by putting it last in the list of lsms?
On 24-M?r 10:37, Stephen Smalley wrote:
> On Mon, Mar 23, 2020 at 9:52 PM KP Singh <[email protected]> wrote:
> >
> > On 23-M?r 18:13, Casey Schaufler wrote:
> > > On 3/23/2020 9:44 AM, KP Singh wrote:
> > > > From: KP Singh <[email protected]>
> > > >
> > > > The bpf_lsm_ nops are initialized into the LSM framework like any other
> > > > LSM. Some LSM hooks do not have 0 as their default return value. The
> > > > __weak symbol for these hooks is overridden by a corresponding
> > > > definition in security/bpf/hooks.c
> > > >
> > > > + return 0;
> >
> > [...]
> >
> > > > +}
> > > > +
> > > > +DEFINE_LSM(bpf) = {
> > > > + .name = "bpf",
> > > > + .init = bpf_lsm_init,
> > >
> > > Have you given up on the "BPF must be last" requirement?
> >
> > Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
> > anwyays so the position ~shouldn't~ matter. (based on some of the
> > discussions we had on the BPF_MODIFY_RETURN patches).
> >
> > However, This can be added later (in a separate patch) if really
> > deemed necessary.
>
> It matters for SELinux, as I previously explained. A process that has
> CAP_SYS_ADMIN is not assumed to be able to circumvent MAC policy.
> And executing prior to SELinux allows the bpf program to access and
> potentially leak to userspace information that wouldn't be visible to
> the
> process itself. However, I thought you were handling the order issue
> by putting it last in the list of lsms?
We can still do that if it does not work for SELinux.
Would it be okay to add bpf as LSM_ORDER_LAST?
LSMs like Landlock can then add LSM_ORDER_UNPRIVILEGED to even end up
after bpf?
- KP
On 24-M?r 10:35, Stephen Smalley wrote:
> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > JITed BPF programs are dynamically attached to the LSM hooks
> > using BPF trampolines. The trampoline prologue generates code to handle
> > conversion of the signature of the hook to the appropriate BPF context.
> >
> > The allocated trampoline programs are attached to the nop functions
> > initialized as LSM hooks.
> >
> > BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> > and need CAP_SYS_ADMIN (required for loading eBPF programs).
> >
> > Upon attachment:
> >
> > * A BPF fexit trampoline is used for LSM hooks with a void return type.
> > * A BPF fmod_ret trampoline is used for LSM hooks which return an
> > int. The attached programs can override the return value of the
> > bpf LSM hook to indicate a MAC Policy decision.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
>
> > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > index 530d137f7a84..2a8131b640b8 100644
> > --- a/kernel/bpf/bpf_lsm.c
> > +++ b/kernel/bpf/bpf_lsm.c
> > @@ -9,6 +9,9 @@
> > #include <linux/btf.h>
> > #include <linux/lsm_hooks.h>
> > #include <linux/bpf_lsm.h>
> > +#include <linux/jump_label.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 as an fexit trampoline.
> > @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > #include <linux/lsm_hook_names.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)
> > +{
> > + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > + */
> > + if (!capable(CAP_MAC_ADMIN))
> > + return -EPERM;
>
> I had asked before, and will ask again: please provide an explicit LSM
> hook for mediating whether one can make changes to the LSM hooks.
> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
What do you think about:
int security_check_mutable_hooks(void)
Do you have any suggestions on the signature of this hook? Does this
hook need to be BPF specific?
- KP
On Tue, Mar 24, 2020 at 10:42 AM KP Singh <[email protected]> wrote:
>
> On 24-Mär 10:37, Stephen Smalley wrote:
> > On Mon, Mar 23, 2020 at 9:52 PM KP Singh <[email protected]> wrote:
> > >
> > > On 23-Mär 18:13, Casey Schaufler wrote:
> > > > Have you given up on the "BPF must be last" requirement?
> > >
> > > Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
> > > anwyays so the position ~shouldn't~ matter. (based on some of the
> > > discussions we had on the BPF_MODIFY_RETURN patches).
> > >
> > > However, This can be added later (in a separate patch) if really
> > > deemed necessary.
> >
> > It matters for SELinux, as I previously explained. A process that has
> > CAP_SYS_ADMIN is not assumed to be able to circumvent MAC policy.
> > And executing prior to SELinux allows the bpf program to access and
> > potentially leak to userspace information that wouldn't be visible to
> > the
> > process itself. However, I thought you were handling the order issue
> > by putting it last in the list of lsms?
>
> We can still do that if it does not work for SELinux.
>
> Would it be okay to add bpf as LSM_ORDER_LAST?
>
> LSMs like Landlock can then add LSM_ORDER_UNPRIVILEGED to even end up
> after bpf?
I guess the question is whether we need an explicit LSM_ORDER_LAST or
can just handle it via the default
values for the lsm= parameter, where you are already placing bpf last
IIUC? If someone can mess with the kernel boot
parameters, they already have options to mess with SELinux, so it is no worse...
On 24-M?r 10:51, Stephen Smalley wrote:
> On Tue, Mar 24, 2020 at 10:42 AM KP Singh <[email protected]> wrote:
> >
> > On 24-M?r 10:37, Stephen Smalley wrote:
> > > On Mon, Mar 23, 2020 at 9:52 PM KP Singh <[email protected]> wrote:
> > > >
> > > > On 23-M?r 18:13, Casey Schaufler wrote:
> > > > > Have you given up on the "BPF must be last" requirement?
> > > >
> > > > Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
> > > > anwyays so the position ~shouldn't~ matter. (based on some of the
> > > > discussions we had on the BPF_MODIFY_RETURN patches).
> > > >
> > > > However, This can be added later (in a separate patch) if really
> > > > deemed necessary.
> > >
> > > It matters for SELinux, as I previously explained. A process that has
> > > CAP_SYS_ADMIN is not assumed to be able to circumvent MAC policy.
> > > And executing prior to SELinux allows the bpf program to access and
> > > potentially leak to userspace information that wouldn't be visible to
> > > the
> > > process itself. However, I thought you were handling the order issue
> > > by putting it last in the list of lsms?
> >
> > We can still do that if it does not work for SELinux.
> >
> > Would it be okay to add bpf as LSM_ORDER_LAST?
> >
> > LSMs like Landlock can then add LSM_ORDER_UNPRIVILEGED to even end up
> > after bpf?
>
> I guess the question is whether we need an explicit LSM_ORDER_LAST or
> can just handle it via the default
> values for the lsm= parameter, where you are already placing bpf last
> IIUC? If someone can mess with the kernel boot
> parameters, they already have options to mess with SELinux, so it is no worse...
Yeah, we do add BPF as the last LSM in the default list. So, I will
avoid adding LSM_ORDER_LAST for now.
- KP
On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
>
> On 24-Mär 10:35, Stephen Smalley wrote:
> > On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > >
> > > From: KP Singh <[email protected]>
> > > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > index 530d137f7a84..2a8131b640b8 100644
> > > --- a/kernel/bpf/bpf_lsm.c
> > > +++ b/kernel/bpf/bpf_lsm.c
> > > @@ -9,6 +9,9 @@
> > > #include <linux/btf.h>
> > > #include <linux/lsm_hooks.h>
> > > #include <linux/bpf_lsm.h>
> > > +#include <linux/jump_label.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 as an fexit trampoline.
> > > @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > #include <linux/lsm_hook_names.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)
> > > +{
> > > + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > > + */
> > > + if (!capable(CAP_MAC_ADMIN))
> > > + return -EPERM;
> >
> > I had asked before, and will ask again: please provide an explicit LSM
> > hook for mediating whether one can make changes to the LSM hooks.
> > Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
>
> What do you think about:
>
> int security_check_mutable_hooks(void)
>
> Do you have any suggestions on the signature of this hook? Does this
> hook need to be BPF specific?
I'd do something like int security_bpf_prog_attach_security(const
struct bpf_prog *prog) or similar.
Then the security module can do a check based on the current task
and/or the prog. We already have some bpf-specific hooks.
On 23-M?r 12:56, Andrii Nakryiko wrote:
> On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > The information about the different types of LSM hooks is scattered
> > in two locations i.e. union security_list_options and
> > struct security_hook_heads. Rather than duplicating this information
> > even further for BPF_PROG_TYPE_LSM, define all the hooks with the
> > LSM_HOOK macro in lsm_hook_names.h which is then used to generate all
> > the data structures required by the LSM framework.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
> > include/linux/lsm_hook_names.h | 354 +++++++++++++++++++
> > include/linux/lsm_hooks.h | 622 +--------------------------------
> > 2 files changed, 360 insertions(+), 616 deletions(-)
> > create mode 100644 include/linux/lsm_hook_names.h
> >
> > diff --git a/include/linux/lsm_hook_names.h b/include/linux/lsm_hook_names.h
> > new file mode 100644
> > index 000000000000..412e4ca24c9b
> > --- /dev/null
> > +++ b/include/linux/lsm_hook_names.h
>
> It's not really just hook names, it's full hook definitions, no? So
> lsm_hook_defs.h seems a bit more appropriate. Just for consideration,
> not that I care that strongly :)
I like lsm_hook_defs.h better too :) Updated.
- KP
>
>
> [...]
On 24-M?r 11:39, KP Singh wrote:
> On 23-M?r 12:59, Andrii Nakryiko wrote:
> > On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> > >
> > > From: KP Singh <[email protected]>
> > >
> > > When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> > > generated for each LSM hook. These nops are initialized as LSM hooks in
> > > a subsequent patch.
> > >
> > > Signed-off-by: KP Singh <[email protected]>
> > > Reviewed-by: Brendan Jackman <[email protected]>
> > > Reviewed-by: Florent Revest <[email protected]>
> > > ---
> > > include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> > > kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> > > 2 files changed, 40 insertions(+)
> > > create mode 100644 include/linux/bpf_lsm.h
> > >
> > > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> > > new file mode 100644
> > > index 000000000000..c6423a140220
> > > --- /dev/null
> > > +++ b/include/linux/bpf_lsm.h
> > > @@ -0,0 +1,21 @@
> > > +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> > > +#include <linux/lsm_hook_names.h>
> > > +#undef LSM_HOOK
> > > +
> > > +#endif /* CONFIG_BPF_LSM */
> > > +
> > > +#endif /* _LINUX_BPF_LSM_H */
> > > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > index 82875039ca90..530d137f7a84 100644
> > > --- a/kernel/bpf/bpf_lsm.c
> > > +++ b/kernel/bpf/bpf_lsm.c
> > > @@ -7,6 +7,25 @@
> > > #include <linux/filter.h>
> > > #include <linux/bpf.h>
> > > #include <linux/btf.h>
> > > +#include <linux/lsm_hooks.h>
> > > +#include <linux/bpf_lsm.h>
> > > +
> > > +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> > > + * function where a BPF program can be attached as an fexit trampoline.
> > > + */
> > > +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> > > +
> > > +#define LSM_HOOK_int(NAME, ...) \
> > > +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> > > +{ \
> > > + return 0; \
> > > +}
> > > +
> > > +#define LSM_HOOK_void(NAME, ...) \
> > > +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > +
> >
> > Could unify with:
> >
> > #define LSM_HOOK(RET, NAME, ...) noinline __weak RET bpf_lsm_##NAME(__VA_ARGS__)
> > {
> > return (RET)0;
> > }
> >
> > then you don't need LSM_HOOK_int and LSM_HOOK_void.
>
> Nice.
>
> But, given that we are adding default values and that
> they are only needed for int hooks, we will need to keep the macros
> separate for int and void. Or, Am I missing a trick here?
>
> - KP
Actually, was able to get it work. not setting a default for void
hooks makes the macros messier. So i just set it void. For example:
LSM_HOOK(void, void, bprm_committing_creds, struct linux_binprm *bprm)
This also allows me to use the cleanup you suggested and not having
to split every usage into int and void.
- KP
>
> >
> > > +#include <linux/lsm_hook_names.h>
> > > +#undef LSM_HOOK
> > >
> > > const struct bpf_prog_ops lsm_prog_ops = {
> > > };
> > > --
> > > 2.20.1
> > >
On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
>> On 24-Mär 10:35, Stephen Smalley wrote:
>>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
>>>> From: KP Singh <[email protected]>
>>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
>>>> index 530d137f7a84..2a8131b640b8 100644
>>>> --- a/kernel/bpf/bpf_lsm.c
>>>> +++ b/kernel/bpf/bpf_lsm.c
>>>> @@ -9,6 +9,9 @@
>>>> #include <linux/btf.h>
>>>> #include <linux/lsm_hooks.h>
>>>> #include <linux/bpf_lsm.h>
>>>> +#include <linux/jump_label.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 as an fexit trampoline.
>>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
>>>> #include <linux/lsm_hook_names.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)
>>>> +{
>>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
>>>> + */
>>>> + if (!capable(CAP_MAC_ADMIN))
>>>> + return -EPERM;
>>> I had asked before, and will ask again: please provide an explicit LSM
>>> hook for mediating whether one can make changes to the LSM hooks.
>>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
>> What do you think about:
>>
>> int security_check_mutable_hooks(void)
>>
>> Do you have any suggestions on the signature of this hook? Does this
>> hook need to be BPF specific?
> I'd do something like int security_bpf_prog_attach_security(const
> struct bpf_prog *prog) or similar.
> Then the security module can do a check based on the current task
> and/or the prog. We already have some bpf-specific hooks.
I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
Just as Yama policy is independent of SELinux policy so KRSI policy should
be independent of SELinux policy. I understand the argument that BDF programs
ought to be constrained by SELinux, but I don't think it's right. Further,
we've got unholy layering when security modules call security_ functions.
I'm not saying there is no case where it would be appropriate, but this is not
one of them.
On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
>
> On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> >> On 24-Mär 10:35, Stephen Smalley wrote:
> >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> >>>> From: KP Singh <[email protected]>
> >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> >>>> index 530d137f7a84..2a8131b640b8 100644
> >>>> --- a/kernel/bpf/bpf_lsm.c
> >>>> +++ b/kernel/bpf/bpf_lsm.c
> >>>> @@ -9,6 +9,9 @@
> >>>> #include <linux/btf.h>
> >>>> #include <linux/lsm_hooks.h>
> >>>> #include <linux/bpf_lsm.h>
> >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> >>>> #include <linux/lsm_hook_names.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)
> >>>> +{
> >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> >>>> + */
> >>>> + if (!capable(CAP_MAC_ADMIN))
> >>>> + return -EPERM;
> >>> I had asked before, and will ask again: please provide an explicit LSM
> >>> hook for mediating whether one can make changes to the LSM hooks.
> >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> >> What do you think about:
> >>
> >> int security_check_mutable_hooks(void)
> >>
> >> Do you have any suggestions on the signature of this hook? Does this
> >> hook need to be BPF specific?
> > I'd do something like int security_bpf_prog_attach_security(const
> > struct bpf_prog *prog) or similar.
> > Then the security module can do a check based on the current task
> > and/or the prog. We already have some bpf-specific hooks.
>
> I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> Just as Yama policy is independent of SELinux policy so KRSI policy should
> be independent of SELinux policy. I understand the argument that BDF programs
> ought to be constrained by SELinux, but I don't think it's right. Further,
> we've got unholy layering when security modules call security_ functions.
> I'm not saying there is no case where it would be appropriate, but this is not
> one of them.
I explained this previously. The difference is that the BPF programs
are loaded from a userspace
process, not a kernel-resident module. They already recognize there
is a difference here or
they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
problem with that
check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
privileged with respect to
SELinux, which is why I want an explicit hook. This gets a NAK from
me until there is such a hook.
On Tue, Mar 24, 2020 at 03:51:55PM +0100, KP Singh wrote:
> On 24-M?r 10:51, Stephen Smalley wrote:
> > On Tue, Mar 24, 2020 at 10:42 AM KP Singh <[email protected]> wrote:
> > >
> > > On 24-M?r 10:37, Stephen Smalley wrote:
> > > > On Mon, Mar 23, 2020 at 9:52 PM KP Singh <[email protected]> wrote:
> > > > >
> > > > > On 23-M?r 18:13, Casey Schaufler wrote:
> > > > > > Have you given up on the "BPF must be last" requirement?
> > > > >
> > > > > Yes, we dropped it for as the BPF programs require CAP_SYS_ADMIN
> > > > > anwyays so the position ~shouldn't~ matter. (based on some of the
> > > > > discussions we had on the BPF_MODIFY_RETURN patches).
> > > > >
> > > > > However, This can be added later (in a separate patch) if really
> > > > > deemed necessary.
> > > >
> > > > It matters for SELinux, as I previously explained. A process that has
> > > > CAP_SYS_ADMIN is not assumed to be able to circumvent MAC policy.
> > > > And executing prior to SELinux allows the bpf program to access and
> > > > potentially leak to userspace information that wouldn't be visible to
> > > > the
> > > > process itself. However, I thought you were handling the order issue
> > > > by putting it last in the list of lsms?
> > >
> > > We can still do that if it does not work for SELinux.
> > >
> > > Would it be okay to add bpf as LSM_ORDER_LAST?
> > >
> > > LSMs like Landlock can then add LSM_ORDER_UNPRIVILEGED to even end up
> > > after bpf?
> >
> > I guess the question is whether we need an explicit LSM_ORDER_LAST or
> > can just handle it via the default
> > values for the lsm= parameter, where you are already placing bpf last
> > IIUC? If someone can mess with the kernel boot
> > parameters, they already have options to mess with SELinux, so it is no worse...
>
> Yeah, we do add BPF as the last LSM in the default list. So, I will
> avoid adding LSM_ORDER_LAST for now.
FWIW, this is my preference as well. If there ends up being a stronger
need, then we can implement LSM_ORDER_LAST at that time.
--
Kees Cook
On Tue, Mar 24, 2020 at 01:49:34PM -0400, Stephen Smalley wrote:
> On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
> >
> > On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> > >> On 24-M?r 10:35, Stephen Smalley wrote:
> > >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > >>>> From: KP Singh <[email protected]>
> > >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > >>>> index 530d137f7a84..2a8131b640b8 100644
> > >>>> --- a/kernel/bpf/bpf_lsm.c
> > >>>> +++ b/kernel/bpf/bpf_lsm.c
> > >>>> @@ -9,6 +9,9 @@
> > >>>> #include <linux/btf.h>
> > >>>> #include <linux/lsm_hooks.h>
> > >>>> #include <linux/bpf_lsm.h>
> > >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> > >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > >>>> #include <linux/lsm_hook_names.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)
> > >>>> +{
> > >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > >>>> + */
> > >>>> + if (!capable(CAP_MAC_ADMIN))
> > >>>> + return -EPERM;
> > >>> I had asked before, and will ask again: please provide an explicit LSM
> > >>> hook for mediating whether one can make changes to the LSM hooks.
> > >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> > >> What do you think about:
> > >>
> > >> int security_check_mutable_hooks(void)
> > >>
> > >> Do you have any suggestions on the signature of this hook? Does this
> > >> hook need to be BPF specific?
> > > I'd do something like int security_bpf_prog_attach_security(const
> > > struct bpf_prog *prog) or similar.
> > > Then the security module can do a check based on the current task
> > > and/or the prog. We already have some bpf-specific hooks.
> >
> > I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> > Just as Yama policy is independent of SELinux policy so KRSI policy should
> > be independent of SELinux policy. I understand the argument that BDF programs
> > ought to be constrained by SELinux, but I don't think it's right. Further,
> > we've got unholy layering when security modules call security_ functions.
> > I'm not saying there is no case where it would be appropriate, but this is not
> > one of them.
>
> I explained this previously. The difference is that the BPF programs
> are loaded from a userspace
> process, not a kernel-resident module. They already recognize there
> is a difference here or
> they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
> problem with that
> check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
> privileged with respect to
> SELinux, which is why I want an explicit hook. This gets a NAK from
> me until there is such a hook.
Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
SELinux's need here? I.e. it can already examine that a hook is being
created for the LSM (since it has a distinct type, etc)?
--
Kees Cook
On 24-M?r 11:01, Kees Cook wrote:
> On Tue, Mar 24, 2020 at 01:49:34PM -0400, Stephen Smalley wrote:
> > On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
> > >
> > > On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > > > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> > > >> On 24-M?r 10:35, Stephen Smalley wrote:
> > > >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > > >>>> From: KP Singh <[email protected]>
> > > >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > >>>> index 530d137f7a84..2a8131b640b8 100644
> > > >>>> --- a/kernel/bpf/bpf_lsm.c
> > > >>>> +++ b/kernel/bpf/bpf_lsm.c
> > > >>>> @@ -9,6 +9,9 @@
> > > >>>> #include <linux/btf.h>
> > > >>>> #include <linux/lsm_hooks.h>
> > > >>>> #include <linux/bpf_lsm.h>
> > > >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> > > >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > >>>> #include <linux/lsm_hook_names.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)
> > > >>>> +{
> > > >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > > >>>> + */
> > > >>>> + if (!capable(CAP_MAC_ADMIN))
> > > >>>> + return -EPERM;
> > > >>> I had asked before, and will ask again: please provide an explicit LSM
> > > >>> hook for mediating whether one can make changes to the LSM hooks.
> > > >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> > > >> What do you think about:
> > > >>
> > > >> int security_check_mutable_hooks(void)
> > > >>
> > > >> Do you have any suggestions on the signature of this hook? Does this
> > > >> hook need to be BPF specific?
> > > > I'd do something like int security_bpf_prog_attach_security(const
> > > > struct bpf_prog *prog) or similar.
> > > > Then the security module can do a check based on the current task
> > > > and/or the prog. We already have some bpf-specific hooks.
> > >
> > > I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> > > Just as Yama policy is independent of SELinux policy so KRSI policy should
> > > be independent of SELinux policy. I understand the argument that BDF programs
> > > ought to be constrained by SELinux, but I don't think it's right. Further,
> > > we've got unholy layering when security modules call security_ functions.
> > > I'm not saying there is no case where it would be appropriate, but this is not
> > > one of them.
> >
> > I explained this previously. The difference is that the BPF programs
> > are loaded from a userspace
> > process, not a kernel-resident module. They already recognize there
> > is a difference here or
> > they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
> > problem with that
> > check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
> > privileged with respect to
> > SELinux, which is why I want an explicit hook. This gets a NAK from
> > me until there is such a hook.
>
> Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
> SELinux's need here? I.e. it can already examine that a hook is being
> created for the LSM (since it has a distinct type, etc)?
I was about to say the same, specifically for the BPF use-case, we do
have the "bpf_prog" i.e. :
"Do a check when the kernel generate and return a file descriptor for
eBPF programs."
SELinux can implement its policy logic for BPF_PROG_TYPE_LSM by
providing a callback for this hook.
- KP
>
> --
> Kees Cook
On Tue, Mar 24, 2020 at 2:06 PM KP Singh <[email protected]> wrote:
>
> On 24-Mär 11:01, Kees Cook wrote:
> > On Tue, Mar 24, 2020 at 01:49:34PM -0400, Stephen Smalley wrote:
> > > On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
> > > >
> > > > On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > > > > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> > > > >> On 24-Mär 10:35, Stephen Smalley wrote:
> > > > >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > > > >>>> From: KP Singh <[email protected]>
> > > > >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > > >>>> index 530d137f7a84..2a8131b640b8 100644
> > > > >>>> --- a/kernel/bpf/bpf_lsm.c
> > > > >>>> +++ b/kernel/bpf/bpf_lsm.c
> > > > >>>> @@ -9,6 +9,9 @@
> > > > >>>> #include <linux/btf.h>
> > > > >>>> #include <linux/lsm_hooks.h>
> > > > >>>> #include <linux/bpf_lsm.h>
> > > > >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> > > > >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > > >>>> #include <linux/lsm_hook_names.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)
> > > > >>>> +{
> > > > >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > > > >>>> + */
> > > > >>>> + if (!capable(CAP_MAC_ADMIN))
> > > > >>>> + return -EPERM;
> > > > >>> I had asked before, and will ask again: please provide an explicit LSM
> > > > >>> hook for mediating whether one can make changes to the LSM hooks.
> > > > >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> > > > >> What do you think about:
> > > > >>
> > > > >> int security_check_mutable_hooks(void)
> > > > >>
> > > > >> Do you have any suggestions on the signature of this hook? Does this
> > > > >> hook need to be BPF specific?
> > > > > I'd do something like int security_bpf_prog_attach_security(const
> > > > > struct bpf_prog *prog) or similar.
> > > > > Then the security module can do a check based on the current task
> > > > > and/or the prog. We already have some bpf-specific hooks.
> > > >
> > > > I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> > > > Just as Yama policy is independent of SELinux policy so KRSI policy should
> > > > be independent of SELinux policy. I understand the argument that BDF programs
> > > > ought to be constrained by SELinux, but I don't think it's right. Further,
> > > > we've got unholy layering when security modules call security_ functions.
> > > > I'm not saying there is no case where it would be appropriate, but this is not
> > > > one of them.
> > >
> > > I explained this previously. The difference is that the BPF programs
> > > are loaded from a userspace
> > > process, not a kernel-resident module. They already recognize there
> > > is a difference here or
> > > they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
> > > problem with that
> > > check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
> > > privileged with respect to
> > > SELinux, which is why I want an explicit hook. This gets a NAK from
> > > me until there is such a hook.
> >
> > Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
> > SELinux's need here? I.e. it can already examine that a hook is being
> > created for the LSM (since it has a distinct type, etc)?
>
> I was about to say the same, specifically for the BPF use-case, we do
> have the "bpf_prog" i.e. :
>
> "Do a check when the kernel generate and return a file descriptor for
> eBPF programs."
>
> SELinux can implement its policy logic for BPF_PROG_TYPE_LSM by
> providing a callback for this hook.
Ok. In that case do we really need the capable() check here at all?
On 24-M?r 14:21, Stephen Smalley wrote:
> On Tue, Mar 24, 2020 at 2:06 PM KP Singh <[email protected]> wrote:
> >
> > On 24-M?r 11:01, Kees Cook wrote:
> > > On Tue, Mar 24, 2020 at 01:49:34PM -0400, Stephen Smalley wrote:
> > > > On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
> > > > >
> > > > > On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > > > > > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> > > > > >> On 24-M?r 10:35, Stephen Smalley wrote:
> > > > > >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > > > > >>>> From: KP Singh <[email protected]>
> > > > > >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > > > >>>> index 530d137f7a84..2a8131b640b8 100644
> > > > > >>>> --- a/kernel/bpf/bpf_lsm.c
> > > > > >>>> +++ b/kernel/bpf/bpf_lsm.c
> > > > > >>>> @@ -9,6 +9,9 @@
> > > > > >>>> #include <linux/btf.h>
> > > > > >>>> #include <linux/lsm_hooks.h>
> > > > > >>>> #include <linux/bpf_lsm.h>
> > > > > >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> > > > > >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > > > >>>> #include <linux/lsm_hook_names.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)
> > > > > >>>> +{
> > > > > >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > > > > >>>> + */
> > > > > >>>> + if (!capable(CAP_MAC_ADMIN))
> > > > > >>>> + return -EPERM;
> > > > > >>> I had asked before, and will ask again: please provide an explicit LSM
> > > > > >>> hook for mediating whether one can make changes to the LSM hooks.
> > > > > >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> > > > > >> What do you think about:
> > > > > >>
> > > > > >> int security_check_mutable_hooks(void)
> > > > > >>
> > > > > >> Do you have any suggestions on the signature of this hook? Does this
> > > > > >> hook need to be BPF specific?
> > > > > > I'd do something like int security_bpf_prog_attach_security(const
> > > > > > struct bpf_prog *prog) or similar.
> > > > > > Then the security module can do a check based on the current task
> > > > > > and/or the prog. We already have some bpf-specific hooks.
> > > > >
> > > > > I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> > > > > Just as Yama policy is independent of SELinux policy so KRSI policy should
> > > > > be independent of SELinux policy. I understand the argument that BDF programs
> > > > > ought to be constrained by SELinux, but I don't think it's right. Further,
> > > > > we've got unholy layering when security modules call security_ functions.
> > > > > I'm not saying there is no case where it would be appropriate, but this is not
> > > > > one of them.
> > > >
> > > > I explained this previously. The difference is that the BPF programs
> > > > are loaded from a userspace
> > > > process, not a kernel-resident module. They already recognize there
> > > > is a difference here or
> > > > they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
> > > > problem with that
> > > > check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
> > > > privileged with respect to
> > > > SELinux, which is why I want an explicit hook. This gets a NAK from
> > > > me until there is such a hook.
> > >
> > > Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
> > > SELinux's need here? I.e. it can already examine that a hook is being
> > > created for the LSM (since it has a distinct type, etc)?
> >
> > I was about to say the same, specifically for the BPF use-case, we do
> > have the "bpf_prog" i.e. :
> >
> > "Do a check when the kernel generate and return a file descriptor for
> > eBPF programs."
> >
> > SELinux can implement its policy logic for BPF_PROG_TYPE_LSM by
> > providing a callback for this hook.
>
> Ok. In that case do we really need the capable() check here at all?
We do not have a specific capable check for BPF_PROG_TYPE_LSM programs
now. There is a general check which requires CAP_SYS_ADMIN when
unprivileged BPF is disabled:
in kernel/bpf/sycall.c:
if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
return -EPERM;
AFAIK, Most distros disable unprivileged eBPF.
Now that I look at this, I think we might need a CAP_MAC_ADMIN check
though as unprivileged BPF being enabled will result in an
unprivileged user being able to load MAC policies.
- KP
On 24-M?r 19:27, KP Singh wrote:
> On 24-M?r 14:21, Stephen Smalley wrote:
> > On Tue, Mar 24, 2020 at 2:06 PM KP Singh <[email protected]> wrote:
> > >
> > > On 24-M?r 11:01, Kees Cook wrote:
> > > > On Tue, Mar 24, 2020 at 01:49:34PM -0400, Stephen Smalley wrote:
> > > > > On Tue, Mar 24, 2020 at 12:25 PM Casey Schaufler <[email protected]> wrote:
> > > > > >
> > > > > > On 3/24/2020 7:58 AM, Stephen Smalley wrote:
> > > > > > > On Tue, Mar 24, 2020 at 10:50 AM KP Singh <[email protected]> wrote:
> > > > > > >> On 24-M?r 10:35, Stephen Smalley wrote:
> > > > > > >>> On Mon, Mar 23, 2020 at 12:46 PM KP Singh <[email protected]> wrote:
> > > > > > >>>> From: KP Singh <[email protected]>
> > > > > > >>>> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > > > > >>>> index 530d137f7a84..2a8131b640b8 100644
> > > > > > >>>> --- a/kernel/bpf/bpf_lsm.c
> > > > > > >>>> +++ b/kernel/bpf/bpf_lsm.c
> > > > > > >>>> @@ -9,6 +9,9 @@
> > > > > > >>>> #include <linux/btf.h>
> > > > > > >>>> #include <linux/lsm_hooks.h>
> > > > > > >>>> #include <linux/bpf_lsm.h>
> > > > > > >>>> +#include <linux/jump_label.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 as an fexit trampoline.
> > > > > > >>>> @@ -27,6 +30,32 @@ noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > > > > >>>> #include <linux/lsm_hook_names.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)
> > > > > > >>>> +{
> > > > > > >>>> + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > > > > > >>>> + */
> > > > > > >>>> + if (!capable(CAP_MAC_ADMIN))
> > > > > > >>>> + return -EPERM;
> > > > > > >>> I had asked before, and will ask again: please provide an explicit LSM
> > > > > > >>> hook for mediating whether one can make changes to the LSM hooks.
> > > > > > >>> Neither CAP_MAC_ADMIN nor CAP_SYS_ADMIN suffices to check this for SELinux.
> > > > > > >> What do you think about:
> > > > > > >>
> > > > > > >> int security_check_mutable_hooks(void)
> > > > > > >>
> > > > > > >> Do you have any suggestions on the signature of this hook? Does this
> > > > > > >> hook need to be BPF specific?
> > > > > > > I'd do something like int security_bpf_prog_attach_security(const
> > > > > > > struct bpf_prog *prog) or similar.
> > > > > > > Then the security module can do a check based on the current task
> > > > > > > and/or the prog. We already have some bpf-specific hooks.
> > > > > >
> > > > > > I *strongly* disagree with Stephen on this. KRSI and SELinux are peers.
> > > > > > Just as Yama policy is independent of SELinux policy so KRSI policy should
> > > > > > be independent of SELinux policy. I understand the argument that BDF programs
> > > > > > ought to be constrained by SELinux, but I don't think it's right. Further,
> > > > > > we've got unholy layering when security modules call security_ functions.
> > > > > > I'm not saying there is no case where it would be appropriate, but this is not
> > > > > > one of them.
> > > > >
> > > > > I explained this previously. The difference is that the BPF programs
> > > > > are loaded from a userspace
> > > > > process, not a kernel-resident module. They already recognize there
> > > > > is a difference here or
> > > > > they wouldn't have the CAP_MAC_ADMIN check above in their patch. The
> > > > > problem with that
> > > > > check is just that CAP_MAC_ADMIN doesn't necessarily mean fully
> > > > > privileged with respect to
> > > > > SELinux, which is why I want an explicit hook. This gets a NAK from
> > > > > me until there is such a hook.
> > > >
> > > > Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
> > > > SELinux's need here? I.e. it can already examine that a hook is being
> > > > created for the LSM (since it has a distinct type, etc)?
> > >
> > > I was about to say the same, specifically for the BPF use-case, we do
> > > have the "bpf_prog" i.e. :
> > >
> > > "Do a check when the kernel generate and return a file descriptor for
> > > eBPF programs."
> > >
> > > SELinux can implement its policy logic for BPF_PROG_TYPE_LSM by
> > > providing a callback for this hook.
> >
> > Ok. In that case do we really need the capable() check here at all?
>
> We do not have a specific capable check for BPF_PROG_TYPE_LSM programs
> now. There is a general check which requires CAP_SYS_ADMIN when
> unprivileged BPF is disabled:
>
> in kernel/bpf/sycall.c:
>
> if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
> return -EPERM;
>
> AFAIK, Most distros disable unprivileged eBPF.
>
> Now that I look at this, I think we might need a CAP_MAC_ADMIN check
> though as unprivileged BPF being enabled will result in an
> unprivileged user being able to load MAC policies.
Actually we do have an extra check for loading BPF programs:
in kernel/bpf/syscall.c:bpf_prog_load
if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
type != BPF_PROG_TYPE_CGROUP_SKB &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
Do you think we still need a CAP_MAC_ADMIN check for LSM programs?
- KP
>
> - KP
On Tue, Mar 24, 2020 at 02:21:30PM -0400, Stephen Smalley wrote:
> On Tue, Mar 24, 2020 at 2:06 PM KP Singh <[email protected]> wrote:
> >
> > On 24-M?r 11:01, Kees Cook wrote:
> > > Doesn't the existing int (*bpf_prog)(struct bpf_prog *prog); cover
> > > SELinux's need here? I.e. it can already examine that a hook is being
> > > created for the LSM (since it has a distinct type, etc)?
> >
> > I was about to say the same, specifically for the BPF use-case, we do
> > have the "bpf_prog" i.e. :
> >
> > "Do a check when the kernel generate and return a file descriptor for
> > eBPF programs."
> >
> > SELinux can implement its policy logic for BPF_PROG_TYPE_LSM by
> > providing a callback for this hook.
>
> Ok. In that case do we really need the capable() check here at all?
IMO, this is for systems without SELinux, where they're using the
capabilities as the basic policy for MAC management.
--
Kees Cook
On Tue, Mar 24, 2020 at 07:31:30PM +0100, KP Singh wrote:
> On 24-M?r 19:27, KP Singh wrote:
> > We do not have a specific capable check for BPF_PROG_TYPE_LSM programs
> > now. There is a general check which requires CAP_SYS_ADMIN when
> > unprivileged BPF is disabled:
> >
> > in kernel/bpf/sycall.c:
> >
> > if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
> > return -EPERM;
> >
> > AFAIK, Most distros disable unprivileged eBPF.
> >
> > Now that I look at this, I think we might need a CAP_MAC_ADMIN check
> > though as unprivileged BPF being enabled will result in an
> > unprivileged user being able to load MAC policies.
>
> Actually we do have an extra check for loading BPF programs:
>
>
> in kernel/bpf/syscall.c:bpf_prog_load
>
> if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
> type != BPF_PROG_TYPE_CGROUP_SKB &&
> !capable(CAP_SYS_ADMIN))
> return -EPERM;
>
> Do you think we still need a CAP_MAC_ADMIN check for LSM programs?
IMO, these are distinct privileges on the non-SELinux system. I think
your patch is fine as-is.
--
Kees Cook
On 23-M?r 13:18, Andrii Nakryiko wrote:
> On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > JITed BPF programs are dynamically attached to the LSM hooks
> > using BPF trampolines. The trampoline prologue generates code to handle
> > conversion of the signature of the hook to the appropriate BPF context.
> >
> > The allocated trampoline programs are attached to the nop functions
> > initialized as LSM hooks.
> >
> > BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
> > and need CAP_SYS_ADMIN (required for loading eBPF programs).
> >
> > Upon attachment:
> >
> > * A BPF fexit trampoline is used for LSM hooks with a void return type.
> > * A BPF fmod_ret trampoline is used for LSM hooks which return an
> > int. The attached programs can override the return value of the
> > bpf LSM hook to indicate a MAC Policy decision.
> >
> > Signed-off-by: KP Singh <[email protected]>
> > Reviewed-by: Brendan Jackman <[email protected]>
> > Reviewed-by: Florent Revest <[email protected]>
> > ---
> > include/linux/bpf.h | 4 ++++
> > include/linux/bpf_lsm.h | 11 +++++++++++
> > kernel/bpf/bpf_lsm.c | 29 +++++++++++++++++++++++++++++
> > kernel/bpf/btf.c | 9 ++++++++-
> > kernel/bpf/syscall.c | 26 ++++++++++++++++++++++----
> > kernel/bpf/trampoline.c | 17 +++++++++++++----
> > kernel/bpf/verifier.c | 19 +++++++++++++++----
> > 7 files changed, 102 insertions(+), 13 deletions(-)
> >
>
> [...]
>
> >
> > +#define BPF_LSM_SYM_PREFX "bpf_lsm_"
> > +
> > +int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> > + const struct bpf_prog *prog)
> > +{
> > + /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
> > + */
> > + if (!capable(CAP_MAC_ADMIN))
> > + return -EPERM;
> > +
> > + 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,
> > + strlen(BPF_LSM_SYM_PREFX))) {
>
> sizeof(BPF_LSM_SYM_PREFIX) - 1?
Thanks, done.
>
> > + 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;
> > +}
> > +
>
> [...]
>
> > @@ -2367,10 +2369,24 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
> > struct file *link_file;
> > int link_fd, err;
> >
> > - if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
> > - prog->expected_attach_type != BPF_TRACE_FEXIT &&
> > - prog->expected_attach_type != BPF_MODIFY_RETURN &&
> > - prog->type != BPF_PROG_TYPE_EXT) {
> > + switch (prog->type) {
> > + case BPF_PROG_TYPE_TRACING:
> > + if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
> > + 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:
>
> It looks like an omission that we don't enforce expected_attach_type
> to be 0 here. Should we fix it until it's too late?
Done.
>
> > + break;
> > + case BPF_PROG_TYPE_LSM:
> > + if (prog->expected_attach_type != BPF_LSM_MAC) {
> > + err = -EINVAL;
> > + goto out_put_prog;
> > + }
> > + break;
> > + default:
> > err = -EINVAL;
> > goto out_put_prog;
> > }
> > @@ -2452,12 +2468,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
> > if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
> > prog->type != BPF_PROG_TYPE_TRACING &&
> > prog->type != BPF_PROG_TYPE_EXT &&
> > + prog->type != BPF_PROG_TYPE_LSM &&
> > prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
> > err = -EINVAL;
> > goto out_put_prog;
> > }
> >
> > if (prog->type == BPF_PROG_TYPE_TRACING ||
> > + prog->type == BPF_PROG_TYPE_LSM ||
> > prog->type == BPF_PROG_TYPE_EXT) {
>
>
> can you please refactor this into a nicer explicit switch instead of
> combination of if/elses?
Done.
- KP
>
> > if (attr->raw_tracepoint.name) {
> > /* The attach point for this category of programs
> > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > index f30bca2a4d01..9be85aa4ec5f 100644
> > --- a/kernel/bpf/trampoline.c
> > +++ b/kernel/bpf/trampoline.c
> > @@ -6,6 +6,7 @@
> > #include <linux/ftrace.h>
> > #include <linux/rbtree_latch.h>
> > #include <linux/perf_event.h>
> > +#include <linux/btf.h>
> >
>
> [...]
On 23-M?r 13:04, Yonghong Song wrote:
>
>
> On 3/23/20 9:44 AM, KP Singh wrote:
> > From: KP Singh <[email protected]>
> >
> > * Load/attach a BPF program to the file_mprotect (int) and
> > bprm_committed_creds (void) LSM hooks.
> > * Perform an action that triggers the hook.
> > * Verify if the audit event was received using a shared global
> > result variable.
> >
> > 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]>
> > ---
> > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++
> > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++
> > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++
> > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++
> > 4 files changed, 226 insertions(+)
> > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
> > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
> >
> > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
> > new file mode 100644
> > index 000000000000..3de230df93db
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/lsm_helpers.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +/*
> > + * Copyright (C) 2020 Google LLC.
> > + */
> > +#ifndef _LSM_HELPERS_H
> > +#define _LSM_HELPERS_H
> > +
> > +struct lsm_prog_result {
> > + /* This ensures that the LSM Hook only monitors the PID requested
> > + * by the loader
> > + */
> > + __u32 monitored_pid;
> > + /* The number of calls to the prog for the monitored PID.
> > + */
> > + __u32 count;
> > +};
> > +
> > +#endif /* _LSM_HELPERS_H */
> > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > new file mode 100644
> > index 000000000000..5fd6b8f569f7
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > @@ -0,0 +1,112 @@
> > +// 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_helpers.h"
> > +#include "lsm_void_hook.skel.h"
> > +#include "lsm_int_hook.skel.h"
> > +
> > +char *LS_ARGS[] = {"true", NULL};
> > +
> > +int heap_mprotect(void)
> > +{
> > + void *buf;
> > + long sz;
> > +
> > + sz = sysconf(_SC_PAGESIZE);
> > + if (sz < 0)
> > + return sz;
> > +
> > + buf = memalign(sz, 2 * sz);
> > + if (buf == NULL)
> > + return -ENOMEM;
> > +
> > + return mprotect(buf, sz, PROT_READ | PROT_EXEC);
>
> "buf" is leaking memory here.
>
> > +}
> > +
> > +int exec_ls(struct lsm_prog_result *result)
> > +{
> > + int child_pid;
> > +
> > + child_pid = fork();
> > + if (child_pid == 0) {
> > + result->monitored_pid = getpid();
> > + execvp(LS_ARGS[0], LS_ARGS);
> > + return -EINVAL;
> > + } else if (child_pid > 0)
> > + return wait(NULL);
> > +
> > + return -EINVAL;
> > +}
> > +
> > +void test_lsm_void_hook(void)
> > +{
> > + struct lsm_prog_result *result;
> > + struct lsm_void_hook *skel = NULL;
> > + int err, duration = 0;
> > +
> > + skel = lsm_void_hook__open_and_load();
> > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n"))
> > + goto close_prog;
> > +
> > + err = lsm_void_hook__attach(skel);
> > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err))
> > + goto close_prog;
> > +
> > + result = &skel->bss->result;
> > +
> > + err = exec_ls(result);
> > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno))
> > + goto close_prog;
> > +
> > + if (CHECK(result->count != 1, "count", "count = %d", result->count))
> > + goto close_prog;
> > +
> > + CHECK_FAIL(result->count != 1);
>
> I think the above
> if (CHECK(result->count != 1, "count", "count = %d", result->count))
> goto close_prog;
>
> CHECK_FAIL(result->count != 1);
> can be replaced with
> CHECK(result->count != 1, "count", "count = %d", result->count);
Thanks, and updated for test_lsm_int_hook as well.
>
> > +
> > +close_prog:
> > + lsm_void_hook__destroy(skel);
> > +}
> > +
> > +void test_lsm_int_hook(void)
> > +{
> > + struct lsm_prog_result *result;
> > + struct lsm_int_hook *skel = NULL;
> > + int err, duration = 0;
> > +
> > + skel = lsm_int_hook__open_and_load();
> > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n"))
> > + goto close_prog;
> > +
> > + err = lsm_int_hook__attach(skel);
> > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err))
> > + goto close_prog;
> > +
> > + result = &skel->bss->result;
> > + result->monitored_pid = getpid();
> > +
> > + err = heap_mprotect();
> > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
> > + errno))
> > + goto close_prog;
> > +
> > + CHECK_FAIL(result->count != 1);
> > +
> > +close_prog:
> > + lsm_int_hook__destroy(skel);
> > +}
> > +
> > +void test_lsm_test(void)
> > +{
> > + test_lsm_void_hook();
> > + test_lsm_int_hook();
> > +}
> > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> > new file mode 100644
> > index 000000000000..1c5028ddca61
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> > @@ -0,0 +1,54 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +/*
> > + * Copyright 2020 Google LLC.
> > + */
> > +
> > +#include <linux/bpf.h>
> > +#include <stdbool.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <errno.h>
> > +#include "lsm_helpers.h"
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +struct lsm_prog_result result = {
> > + .monitored_pid = 0,
> > + .count = 0,
> > +};
> > +
> > +/*
> > + * Define some of the structs used in the BPF program.
> > + * Only the field names and their sizes need to be the
> > + * same as the kernel type, the order is irrelevant.
> > + */
> > +struct mm_struct {
> > + unsigned long start_brk, brk;
> > +} __attribute__((preserve_access_index));
> > +
> > +struct vm_area_struct {
> > + unsigned long vm_start, vm_end;
> > + struct mm_struct *vm_mm;
> > +} __attribute__((preserve_access_index));
> > +
> > +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();
>
> In user space, we assign monitored_pid with getpid()
> which is the process pid. Here
> pid = bpf_get_current_pid_tgid()
> actually got tid in the kernel.
>
> Although it does not matter in this particular example,
> maybe still use
> bpf_get_current_pid_tgid() >> 32
> to get process pid to be consistent.
>
> The same for lsm_void_hook.c.
Done. Thanks!
>
> > + int is_heap = 0;
> > +
> > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
> > + vma->vm_end <= vma->vm_mm->brk);
> > +
> > + if (is_heap && result.monitored_pid == pid) {
> > + result.count++;
> > + ret = -EPERM;
> > + }
> > +
> > + return ret;
> > +}
> > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> > new file mode 100644
> > index 000000000000..4d01a8536413
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> > @@ -0,0 +1,41 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +/*
> > + * Copyright (C) 2020 Google LLC.
> > + */
> > +
> > +#include <linux/bpf.h>
> > +#include <stdbool.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <errno.h>
> > +#include "lsm_helpers.h"
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +struct lsm_prog_result result = {
> > + .monitored_pid = 0,
> > + .count = 0,
> > +};
> > +
> > +/*
> > + * Define some of the structs used in the BPF program.
> > + * Only the field names and their sizes need to be the
> > + * same as the kernel type, the order is irrelevant.
> > + */
> > +struct linux_binprm {
> > + const char *filename;
> > +} __attribute__((preserve_access_index));
> > +
> > +SEC("lsm/bprm_committed_creds")
> > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
> > +{
> > + __u32 pid = bpf_get_current_pid_tgid();
> > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n";
> > +
> > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename);
> > + if (result.monitored_pid == pid)
> > + result.count++;
> > +
> > + return 0;
> > +}
> >
>
> Could you also upddate tools/testing/selftests/bpf/config file
> so people will know what config options are needed to run the
> self tests properly?
Added CONFIG_BPF_LSM and CONFIG_SECURITY to the list.
- KP
On Tue, Mar 24, 2020 at 9:12 AM KP Singh <[email protected]> wrote:
>
> On 24-Mär 11:39, KP Singh wrote:
> > On 23-Mär 12:59, Andrii Nakryiko wrote:
> > > On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> > > >
> > > > From: KP Singh <[email protected]>
> > > >
> > > > When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> > > > generated for each LSM hook. These nops are initialized as LSM hooks in
> > > > a subsequent patch.
> > > >
> > > > Signed-off-by: KP Singh <[email protected]>
> > > > Reviewed-by: Brendan Jackman <[email protected]>
> > > > Reviewed-by: Florent Revest <[email protected]>
> > > > ---
> > > > include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> > > > kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> > > > 2 files changed, 40 insertions(+)
> > > > create mode 100644 include/linux/bpf_lsm.h
> > > >
> > > > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> > > > new file mode 100644
> > > > index 000000000000..c6423a140220
> > > > --- /dev/null
> > > > +++ b/include/linux/bpf_lsm.h
> > > > @@ -0,0 +1,21 @@
> > > > +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> > > > +#include <linux/lsm_hook_names.h>
> > > > +#undef LSM_HOOK
> > > > +
> > > > +#endif /* CONFIG_BPF_LSM */
> > > > +
> > > > +#endif /* _LINUX_BPF_LSM_H */
> > > > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > > index 82875039ca90..530d137f7a84 100644
> > > > --- a/kernel/bpf/bpf_lsm.c
> > > > +++ b/kernel/bpf/bpf_lsm.c
> > > > @@ -7,6 +7,25 @@
> > > > #include <linux/filter.h>
> > > > #include <linux/bpf.h>
> > > > #include <linux/btf.h>
> > > > +#include <linux/lsm_hooks.h>
> > > > +#include <linux/bpf_lsm.h>
> > > > +
> > > > +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> > > > + * function where a BPF program can be attached as an fexit trampoline.
> > > > + */
> > > > +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> > > > +
> > > > +#define LSM_HOOK_int(NAME, ...) \
> > > > +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> > > > +{ \
> > > > + return 0; \
> > > > +}
> > > > +
> > > > +#define LSM_HOOK_void(NAME, ...) \
> > > > +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > > +
> > >
> > > Could unify with:
> > >
> > > #define LSM_HOOK(RET, NAME, ...) noinline __weak RET bpf_lsm_##NAME(__VA_ARGS__)
> > > {
> > > return (RET)0;
> > > }
> > >
> > > then you don't need LSM_HOOK_int and LSM_HOOK_void.
> >
> > Nice.
> >
> > But, given that we are adding default values and that
> > they are only needed for int hooks, we will need to keep the macros
> > separate for int and void. Or, Am I missing a trick here?
> >
> > - KP
>
> Actually, was able to get it work. not setting a default for void
> hooks makes the macros messier. So i just set it void. For example:
>
> LSM_HOOK(void, void, bprm_committing_creds, struct linux_binprm *bprm)
surprised this works, was going to propose to specify `(void)0` as
default value :)
>
> This also allows me to use the cleanup you suggested and not having
> to split every usage into int and void.
>
Nice, one of the reasons for proposing this.
> - KP
>
> >
> > >
> > > > +#include <linux/lsm_hook_names.h>
> > > > +#undef LSM_HOOK
> > > >
> > > > const struct bpf_prog_ops lsm_prog_ops = {
> > > > };
> > > > --
> > > > 2.20.1
> > > >
On 24-M?r 14:26, Andrii Nakryiko wrote:
> On Tue, Mar 24, 2020 at 9:12 AM KP Singh <[email protected]> wrote:
> >
> > On 24-M?r 11:39, KP Singh wrote:
> > > On 23-M?r 12:59, Andrii Nakryiko wrote:
> > > > On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> > > > >
> > > > > From: KP Singh <[email protected]>
> > > > >
> > > > > When CONFIG_BPF_LSM is enabled, nops functions, bpf_lsm_<hook_name>, are
> > > > > generated for each LSM hook. These nops are initialized as LSM hooks in
> > > > > a subsequent patch.
> > > > >
> > > > > Signed-off-by: KP Singh <[email protected]>
> > > > > Reviewed-by: Brendan Jackman <[email protected]>
> > > > > Reviewed-by: Florent Revest <[email protected]>
> > > > > ---
> > > > > include/linux/bpf_lsm.h | 21 +++++++++++++++++++++
> > > > > kernel/bpf/bpf_lsm.c | 19 +++++++++++++++++++
> > > > > 2 files changed, 40 insertions(+)
> > > > > create mode 100644 include/linux/bpf_lsm.h
> > > > >
> > > > > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> > > > > new file mode 100644
> > > > > index 000000000000..c6423a140220
> > > > > --- /dev/null
> > > > > +++ b/include/linux/bpf_lsm.h
> > > > > @@ -0,0 +1,21 @@
> > > > > +/* 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, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
> > > > > +#include <linux/lsm_hook_names.h>
> > > > > +#undef LSM_HOOK
> > > > > +
> > > > > +#endif /* CONFIG_BPF_LSM */
> > > > > +
> > > > > +#endif /* _LINUX_BPF_LSM_H */
> > > > > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > > > > index 82875039ca90..530d137f7a84 100644
> > > > > --- a/kernel/bpf/bpf_lsm.c
> > > > > +++ b/kernel/bpf/bpf_lsm.c
> > > > > @@ -7,6 +7,25 @@
> > > > > #include <linux/filter.h>
> > > > > #include <linux/bpf.h>
> > > > > #include <linux/btf.h>
> > > > > +#include <linux/lsm_hooks.h>
> > > > > +#include <linux/bpf_lsm.h>
> > > > > +
> > > > > +/* For every LSM hook that allows attachment of BPF programs, declare a NOP
> > > > > + * function where a BPF program can be attached as an fexit trampoline.
> > > > > + */
> > > > > +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
> > > > > +
> > > > > +#define LSM_HOOK_int(NAME, ...) \
> > > > > +noinline __weak int bpf_lsm_##NAME(__VA_ARGS__) \
> > > > > +{ \
> > > > > + return 0; \
> > > > > +}
> > > > > +
> > > > > +#define LSM_HOOK_void(NAME, ...) \
> > > > > +noinline __weak void bpf_lsm_##NAME(__VA_ARGS__) {}
> > > > > +
> > > >
> > > > Could unify with:
> > > >
> > > > #define LSM_HOOK(RET, NAME, ...) noinline __weak RET bpf_lsm_##NAME(__VA_ARGS__)
> > > > {
> > > > return (RET)0;
> > > > }
> > > >
> > > > then you don't need LSM_HOOK_int and LSM_HOOK_void.
> > >
> > > Nice.
> > >
> > > But, given that we are adding default values and that
> > > they are only needed for int hooks, we will need to keep the macros
> > > separate for int and void. Or, Am I missing a trick here?
> > >
> > > - KP
> >
> > Actually, was able to get it work. not setting a default for void
> > hooks makes the macros messier. So i just set it void. For example:
> >
> > LSM_HOOK(void, void, bprm_committing_creds, struct linux_binprm *bprm)
>
> surprised this works, was going to propose to specify `(void)0` as
> default value :)
Yeah, you are right that does not work. so I added:
LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)
and as you suggested defined LSM_RET_VOID in lsm_hooks.h:
/* LSM_RET_VOID is used as the default value in LSM_HOOK definitions for void
* for void LSM hooks (in include/linux/lsm_hook_defs.h).
*/
#define LSM_RET_VOID ((void) 0)
I also noticed a few other hooks that were passing an initial return
value to call_int_hook which were missed in this revision. Have fixed
these for the next one.
- KP
>
> >
> > This also allows me to use the cleanup you suggested and not having
> > to split every usage into int and void.
> >
>
> Nice, one of the reasons for proposing this.
>
> > - KP
> >
> > >
> > > >
> > > > > +#include <linux/lsm_hook_names.h>
> > > > > +#undef LSM_HOOK
> > > > >
> > > > > const struct bpf_prog_ops lsm_prog_ops = {
> > > > > };
> > > > > --
> > > > > 2.20.1
> > > > >
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
>
> From: KP Singh <[email protected]>
>
> * Load/attach a BPF program to the file_mprotect (int) and
> bprm_committed_creds (void) LSM hooks.
> * Perform an action that triggers the hook.
> * Verify if the audit event was received using a shared global
> result variable.
>
> 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]>
> ---
> tools/testing/selftests/bpf/lsm_helpers.h | 19 +++
> .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++
> .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++
> .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++
> 4 files changed, 226 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
> create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
> create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
> create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
>
> diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
> new file mode 100644
> index 000000000000..3de230df93db
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/lsm_helpers.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +#ifndef _LSM_HELPERS_H
> +#define _LSM_HELPERS_H
> +
> +struct lsm_prog_result {
> + /* This ensures that the LSM Hook only monitors the PID requested
> + * by the loader
> + */
> + __u32 monitored_pid;
> + /* The number of calls to the prog for the monitored PID.
> + */
> + __u32 count;
> +};
> +
Having this extra header just for this simple struct... On BPF side
it's easier and nicer to just use global variables. Can you please
drop helper and just pass two variables in prog_test part?
> +#endif /* _LSM_HELPERS_H */
> diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> new file mode 100644
> index 000000000000..5fd6b8f569f7
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> @@ -0,0 +1,112 @@
> +// 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_helpers.h"
> +#include "lsm_void_hook.skel.h"
> +#include "lsm_int_hook.skel.h"
> +
> +char *LS_ARGS[] = {"true", NULL};
> +
> +int heap_mprotect(void)
> +{
> + void *buf;
> + long sz;
> +
> + sz = sysconf(_SC_PAGESIZE);
> + if (sz < 0)
> + return sz;
> +
> + buf = memalign(sz, 2 * sz);
> + if (buf == NULL)
> + return -ENOMEM;
> +
> + return mprotect(buf, sz, PROT_READ | PROT_EXEC);
> +}
> +
> +int exec_ls(struct lsm_prog_result *result)
> +{
> + int child_pid;
> +
> + child_pid = fork();
> + if (child_pid == 0) {
> + result->monitored_pid = getpid();
monitored_pid needed here only
> + execvp(LS_ARGS[0], LS_ARGS);
> + return -EINVAL;
> + } else if (child_pid > 0)
> + return wait(NULL);
> +
> + return -EINVAL;
> +}
> +
> +void test_lsm_void_hook(void)
> +{
> + struct lsm_prog_result *result;
> + struct lsm_void_hook *skel = NULL;
> + int err, duration = 0;
> +
> + skel = lsm_void_hook__open_and_load();
> + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n"))
> + goto close_prog;
> +
> + err = lsm_void_hook__attach(skel);
> + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err))
> + goto close_prog;
> +
> + result = &skel->bss->result;
if you define variables directly, you'll access them easily as
skel->bss->monitored_pid and skel->bss->count, no problem, right?
> +
> + err = exec_ls(result);
> + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno))
> + goto close_prog;
> +
> + if (CHECK(result->count != 1, "count", "count = %d", result->count))
> + goto close_prog;
> +
> + CHECK_FAIL(result->count != 1);
> +
> +close_prog:
> + lsm_void_hook__destroy(skel);
> +}
> +
> +void test_lsm_int_hook(void)
> +{
> + struct lsm_prog_result *result;
> + struct lsm_int_hook *skel = NULL;
> + int err, duration = 0;
> +
> + skel = lsm_int_hook__open_and_load();
> + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n"))
> + goto close_prog;
> +
> + err = lsm_int_hook__attach(skel);
> + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err))
> + goto close_prog;
> +
> + result = &skel->bss->result;
> + result->monitored_pid = getpid();
> +
> + err = heap_mprotect();
> + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
> + errno))
> + goto close_prog;
> +
> + CHECK_FAIL(result->count != 1);
> +
> +close_prog:
> + lsm_int_hook__destroy(skel);
> +}
> +
> +void test_lsm_test(void)
> +{
> + test_lsm_void_hook();
> + test_lsm_int_hook();
These should be subtests (see test__start_subtest() usage). Also, I'm
not sure why you need two separate BPF programs, why not create one
and use it for two subtests?
> +}
> diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> new file mode 100644
> index 000000000000..1c5028ddca61
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
consider it a nit because not every test follows this, but using
progs/test_whatever.c for BPF side and prog_test/whatever.c makes my
life a bit easier.
> @@ -0,0 +1,54 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright 2020 Google LLC.
> + */
> +
> +#include <linux/bpf.h>
> +#include <stdbool.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <errno.h>
> +#include "lsm_helpers.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +struct lsm_prog_result result = {
> + .monitored_pid = 0,
> + .count = 0,
> +};
> +
> +/*
> + * Define some of the structs used in the BPF program.
> + * Only the field names and their sizes need to be the
> + * same as the kernel type, the order is irrelevant.
> + */
> +struct mm_struct {
> + unsigned long start_brk, brk;
> +} __attribute__((preserve_access_index));
> +
> +struct vm_area_struct {
> + unsigned long vm_start, vm_end;
> + struct mm_struct *vm_mm;
> +} __attribute__((preserve_access_index));
Why not just using vmlinux.h instead?
> +
> +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();
> + int is_heap = 0;
> +
> + is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
> + vma->vm_end <= vma->vm_mm->brk);
> +
> + if (is_heap && result.monitored_pid == pid) {
> + result.count++;
> + ret = -EPERM;
> + }
> +
> + return ret;
> +}
> diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> new file mode 100644
> index 000000000000..4d01a8536413
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2020 Google LLC.
> + */
> +
> +#include <linux/bpf.h>
> +#include <stdbool.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <errno.h>
> +#include "lsm_helpers.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +struct lsm_prog_result result = {
> + .monitored_pid = 0,
> + .count = 0,
> +};
> +
> +/*
> + * Define some of the structs used in the BPF program.
> + * Only the field names and their sizes need to be the
> + * same as the kernel type, the order is irrelevant.
> + */
> +struct linux_binprm {
> + const char *filename;
> +} __attribute__((preserve_access_index));
> +
> +SEC("lsm/bprm_committed_creds")
> +int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
> +{
> + __u32 pid = bpf_get_current_pid_tgid();
> + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n";
Try static char fmt[] = "..." instead and then compare BPF assembly
before and after, you'll be amazed ;)
> +
> + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename);
is this part of test?
> + if (result.monitored_pid == pid)
> + result.count++;
> +
> + return 0;
> +}
> --
> 2.20.1
>
On 24-M?r 16:54, Andrii Nakryiko wrote:
> On Mon, Mar 23, 2020 at 9:45 AM KP Singh <[email protected]> wrote:
> >
> > From: KP Singh <[email protected]>
> >
> > * Load/attach a BPF program to the file_mprotect (int) and
> > bprm_committed_creds (void) LSM hooks.
> > * Perform an action that triggers the hook.
> > * Verify if the audit event was received using a shared global
> > result variable.
> >
> > 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]>
> > ---
> > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++
> > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++
> > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++
> > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++
> > 4 files changed, 226 insertions(+)
> > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c
> > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c
> >
> > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
> > new file mode 100644
> > index 000000000000..3de230df93db
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/lsm_helpers.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +/*
> > + * Copyright (C) 2020 Google LLC.
> > + */
> > +#ifndef _LSM_HELPERS_H
> > +#define _LSM_HELPERS_H
> > +
> > +struct lsm_prog_result {
> > + /* This ensures that the LSM Hook only monitors the PID requested
> > + * by the loader
> > + */
> > + __u32 monitored_pid;
> > + /* The number of calls to the prog for the monitored PID.
> > + */
> > + __u32 count;
> > +};
> > +
>
> Having this extra header just for this simple struct... On BPF side
> it's easier and nicer to just use global variables. Can you please
> drop helper and just pass two variables in prog_test part?
Removed the header and moved to global variables. One less file to
worry about :)
>
> > +#endif /* _LSM_HELPERS_H */
> > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > new file mode 100644
> > index 000000000000..5fd6b8f569f7
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c
> > @@ -0,0 +1,112 @@
> > +// 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_helpers.h"
> > +#include "lsm_void_hook.skel.h"
> > +#include "lsm_int_hook.skel.h"
> > +
> > +char *LS_ARGS[] = {"true", NULL};
> > +
> > +int heap_mprotect(void)
> > +{
> > + void *buf;
> > + long sz;
> > +
> > + sz = sysconf(_SC_PAGESIZE);
> > + if (sz < 0)
> > + return sz;
> > +
> > + buf = memalign(sz, 2 * sz);
> > + if (buf == NULL)
> > + return -ENOMEM;
> > +
> > + return mprotect(buf, sz, PROT_READ | PROT_EXEC);
> > +}
> > +
> > +int exec_ls(struct lsm_prog_result *result)
> > +{
> > + int child_pid;
> > +
> > + child_pid = fork();
> > + if (child_pid == 0) {
> > + result->monitored_pid = getpid();
>
> monitored_pid needed here only
>
> > + execvp(LS_ARGS[0], LS_ARGS);
> > + return -EINVAL;
> > + } else if (child_pid > 0)
> > + return wait(NULL);
> > +
> > + return -EINVAL;
> > +}
> > +
> > +void test_lsm_void_hook(void)
> > +{
> > + struct lsm_prog_result *result;
> > + struct lsm_void_hook *skel = NULL;
> > + int err, duration = 0;
> > +
> > + skel = lsm_void_hook__open_and_load();
> > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n"))
> > + goto close_prog;
> > +
> > + err = lsm_void_hook__attach(skel);
> > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err))
> > + goto close_prog;
> > +
> > + result = &skel->bss->result;
>
> if you define variables directly, you'll access them easily as
> skel->bss->monitored_pid and skel->bss->count, no problem, right?
Yes. Updated.
>
> > +
> > + err = exec_ls(result);
> > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno))
> > + goto close_prog;
> > +
> > + if (CHECK(result->count != 1, "count", "count = %d", result->count))
> > + goto close_prog;
> > +
> > + CHECK_FAIL(result->count != 1);
> > +
> > +close_prog:
> > + lsm_void_hook__destroy(skel);
> > +}
> > +
> > +void test_lsm_int_hook(void)
> > +{
> > + struct lsm_prog_result *result;
> > + struct lsm_int_hook *skel = NULL;
> > + int err, duration = 0;
> > +
> > + skel = lsm_int_hook__open_and_load();
> > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n"))
> > + goto close_prog;
> > +
> > + err = lsm_int_hook__attach(skel);
> > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err))
> > + goto close_prog;
> > +
> > + result = &skel->bss->result;
> > + result->monitored_pid = getpid();
> > +
> > + err = heap_mprotect();
> > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
> > + errno))
> > + goto close_prog;
> > +
> > + CHECK_FAIL(result->count != 1);
> > +
> > +close_prog:
> > + lsm_int_hook__destroy(skel);
> > +}
> > +
> > +void test_lsm_test(void)
> > +{
> > + test_lsm_void_hook();
> > + test_lsm_int_hook();
>
> These should be subtests (see test__start_subtest() usage). Also, I'm
> not sure why you need two separate BPF programs, why not create one
> and use it for two subtests?
Thanks! I simplified it much more based on your feedback.
Now we just have two files:
* prog_tests/test_lsm.c which defines "test_lsm_test which
does an exec and an mprotect and verifies the global variables and
the return of the mprotect.
These are fairly simple, so we can drop the need for subtests.
* progs/lsm.c which has both bprm_committed_creds and file_mprotect.
>
>
> > +}
> > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
> > new file mode 100644
> > index 000000000000..1c5028ddca61
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c
>
> consider it a nit because not every test follows this, but using
> progs/test_whatever.c for BPF side and prog_test/whatever.c makes my
> life a bit easier.
I am all for uniformity :) Updated.
>
>
> > @@ -0,0 +1,54 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +/*
> > + * Copyright 2020 Google LLC.
> > + */
> > +
> > +#include <linux/bpf.h>
> > +#include <stdbool.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <errno.h>
> > +#include "lsm_helpers.h"
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +struct lsm_prog_result result = {
> > + .monitored_pid = 0,
> > + .count = 0,
> > +};
> > +
> > +/*
> > + * Define some of the structs used in the BPF program.
> > + * Only the field names and their sizes need to be the
> > + * same as the kernel type, the order is irrelevant.
> > + */
> > +struct mm_struct {
> > + unsigned long start_brk, brk;
> > +} __attribute__((preserve_access_index));
> > +
> > +struct vm_area_struct {
> > + unsigned long vm_start, vm_end;
> > + struct mm_struct *vm_mm;
> > +} __attribute__((preserve_access_index));
>
> Why not just using vmlinux.h instead?
Thanks, updated.
>
> > +
> > +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();
> > + int is_heap = 0;
> > +
> > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
> > + vma->vm_end <= vma->vm_mm->brk);
> > +
> > + if (is_heap && result.monitored_pid == pid) {
> > + result.count++;
> > + ret = -EPERM;
> > + }
> > +
> > + return ret;
> > +}
> > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> > new file mode 100644
> > index 000000000000..4d01a8536413
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c
> > @@ -0,0 +1,41 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +/*
> > + * Copyright (C) 2020 Google LLC.
> > + */
> > +
> > +#include <linux/bpf.h>
> > +#include <stdbool.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <errno.h>
> > +#include "lsm_helpers.h"
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +struct lsm_prog_result result = {
> > + .monitored_pid = 0,
> > + .count = 0,
> > +};
> > +
> > +/*
> > + * Define some of the structs used in the BPF program.
> > + * Only the field names and their sizes need to be the
> > + * same as the kernel type, the order is irrelevant.
> > + */
> > +struct linux_binprm {
> > + const char *filename;
> > +} __attribute__((preserve_access_index));
> > +
> > +SEC("lsm/bprm_committed_creds")
> > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
> > +{
> > + __u32 pid = bpf_get_current_pid_tgid();
> > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n";
>
> Try static char fmt[] = "..." instead and then compare BPF assembly
> before and after, you'll be amazed ;)
>
> > +
> > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename);
>
> is this part of test?
Not really, removed.
- KP
>
> > + if (result.monitored_pid == pid)
> > + result.count++;
> > +
> > + return 0;
> > +}
> > --
> > 2.20.1
> >
On 23-M?r 15:12, Kees Cook wrote:
> On Mon, Mar 23, 2020 at 02:58:18PM -0700, Casey Schaufler wrote:
> > That's not too terrible, I suppose. What would you be thinking for
> > the calls that do use call_int_hook()?
> >
> > rc = call_int_hook(something, something_default, goodnesses);
> >
> > or embedded in the macro:
> >
> > rc = call_int_hook(something, goodnesses);
>
> Oh yes, good point. The hook call already knows the name, so:
I learnt this the hard way that IRC that is passed to the
call_int_hook macro is not the same as the default value for a hook
call_int_hook accomdates for a different return value when no hook is
implemented, but it does expect the default value of the hook to be 0
as it compares the return value of the callbacks to 0 instead of the
default value whereas these special cases compare it with the default
value.
For example:
If we define the default_value of the secid_to_secctx to
-EOPNOTSUPP, it changes the behaviour and the BPF hook, which
returns this default value always results in a failure.
I noticed this when I saw a bunch of messages on my VM:
audit: error in audit_log_task_context
which comes from audit_log_task_context and calls
security_secid_to_secctx which ends up being always denied by BPF.
In anycase, I am still adding the default value in LSM_HOOK and using
them in the following hooks:
getprocattr -EINVAL
inode_getsecurity -EOPNOTSUPP
inode_setsecurity -EOPNOTSUPP
setprocattr -EINVAL
task_prctl -ENOSYS
xfrm_state_pol_flow_match 1
Will send v6 out with these changes.
- KP
>
> #define call_int_hook(FUNC, ...) ({ \
> int RC = FUNC#_default; \
> ...
>
>
> --
> Kees Cook
>
>