mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-08-12 03:51:47 +00:00
Backport from Linux v4.11-rc3-812-gc6bf33827b7d to Linux 4.9.20: https://github.com/landlock-lsm/linux/commits/landlock-v6-linux-v4.9.20 Do not include documentation nor tests. See built documentation here: https://landlock-lsm.github.io/linux-doc/landlock-v6/security/landlock/index.html Signed-off-by: Mickaël Salaün <mic@digikod.net> Link: https://lkml.kernel.org/r/20170328234650.19695-1-mic@digikod.net
1053 lines
29 KiB
Diff
1053 lines
29 KiB
Diff
From 4cddce4529373b7e903b140220473183f8a813ff Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net>
|
|
Date: Wed, 29 Mar 2017 01:30:33 +0200
|
|
Subject: [PATCH 08/12] landlock: Add LSM hooks related to filesystem
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Handle 33 filesystem-related LSM hooks for the Landlock filesystem
|
|
event: LANDLOCK_SUBTYPE_EVENT_FS.
|
|
|
|
A Landlock event wrap LSM hooks for similar kernel object types (e.g.
|
|
struct file, struct path...). Multiple LSM hooks can trigger the same
|
|
Landlock event.
|
|
|
|
Landlock handle nine coarse-grained actions: read, write, execute, new,
|
|
get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
|
|
access control in a way that can be extended in the future.
|
|
|
|
The Landlock LSM hook registration is done after other LSM to only run
|
|
actions from user-space, via eBPF programs, if the access was granted by
|
|
major (privileged) LSMs.
|
|
|
|
Changes since v5:
|
|
* split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
|
|
* add more documentation
|
|
* cosmetic fixes
|
|
|
|
Changes since v4:
|
|
* add LSM hook abstraction called Landlock event
|
|
* use the compiler type checking to verify hooks use by an event
|
|
* handle all filesystem related LSM hooks (e.g. file_permission,
|
|
mmap_file, sb_mount...)
|
|
* register BPF programs for Landlock just after LSM hooks registration
|
|
* move hooks registration after other LSMs
|
|
* add failsafes to check if a hook is not used by the kernel
|
|
* allow partial raw value access form the context (needed for programs
|
|
generated by LLVM)
|
|
|
|
Changes since v3:
|
|
* split commit
|
|
* add hooks dealing with struct inode and struct path pointers:
|
|
inode_permission and inode_getattr
|
|
* add abstraction over eBPF helper arguments thanks to wrapping structs
|
|
|
|
Signed-off-by: Mickaël Salaün <mic@digikod.net>
|
|
Cc: Alexei Starovoitov <ast@kernel.org>
|
|
Cc: Andy Lutomirski <luto@amacapital.net>
|
|
Cc: Daniel Borkmann <daniel@iogearbox.net>
|
|
Cc: David S. Miller <davem@davemloft.net>
|
|
Cc: James Morris <james.l.morris@oracle.com>
|
|
Cc: Kees Cook <keescook@chromium.org>
|
|
Cc: Serge E. Hallyn <serge@hallyn.com>
|
|
(cherry picked from commit 3a41a0423f66fbb5e68f655bd80cb575a7de3321)
|
|
---
|
|
include/linux/lsm_hooks.h | 5 +
|
|
security/landlock/Makefile | 4 +-
|
|
security/landlock/hooks.c | 115 +++++++++
|
|
security/landlock/hooks.h | 177 ++++++++++++++
|
|
security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
|
|
security/landlock/hooks_fs.h | 19 ++
|
|
security/landlock/init.c | 13 +
|
|
security/security.c | 7 +-
|
|
8 files changed, 901 insertions(+), 2 deletions(-)
|
|
create mode 100644 security/landlock/hooks.c
|
|
create mode 100644 security/landlock/hooks.h
|
|
create mode 100644 security/landlock/hooks_fs.c
|
|
create mode 100644 security/landlock/hooks_fs.h
|
|
|
|
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
|
|
index 558adfa5c8a8..069af34301d4 100644
|
|
--- a/include/linux/lsm_hooks.h
|
|
+++ b/include/linux/lsm_hooks.h
|
|
@@ -1933,5 +1933,10 @@ void __init loadpin_add_hooks(void);
|
|
#else
|
|
static inline void loadpin_add_hooks(void) { };
|
|
#endif
|
|
+#ifdef CONFIG_SECURITY_LANDLOCK
|
|
+extern void __init landlock_add_hooks(void);
|
|
+#else
|
|
+static inline void __init landlock_add_hooks(void) { }
|
|
+#endif /* CONFIG_SECURITY_LANDLOCK */
|
|
|
|
#endif /* ! __LINUX_LSM_HOOKS_H */
|
|
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
|
|
index 7205f9a7a2ee..c0db504a6335 100644
|
|
--- a/security/landlock/Makefile
|
|
+++ b/security/landlock/Makefile
|
|
@@ -1,3 +1,5 @@
|
|
+ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
|
|
+
|
|
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
|
|
|
|
-landlock-y := init.o
|
|
+landlock-y := init.o hooks.o hooks_fs.o
|
|
diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
|
|
new file mode 100644
|
|
index 000000000000..eaee8162ff70
|
|
--- /dev/null
|
|
+++ b/security/landlock/hooks.c
|
|
@@ -0,0 +1,115 @@
|
|
+/*
|
|
+ * Landlock LSM - hooks helpers
|
|
+ *
|
|
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2, as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <asm/current.h>
|
|
+#include <asm/processor.h> /* task_pt_regs() */
|
|
+#include <asm/syscall.h> /* syscall_get_nr(), syscall_get_arch() */
|
|
+#include <linux/bpf.h> /* enum bpf_access_type, struct landlock_context */
|
|
+#include <linux/err.h> /* EPERM */
|
|
+#include <linux/filter.h> /* BPF_PROG_RUN() */
|
|
+#include <linux/landlock.h> /* struct landlock_rule */
|
|
+#include <linux/lsm_hooks.h>
|
|
+#include <linux/rculist.h> /* list_add_tail_rcu */
|
|
+#include <linux/stddef.h> /* offsetof */
|
|
+
|
|
+#include "common.h" /* get_index() */
|
|
+#include "hooks.h" /* CTX_ARG_NB */
|
|
+
|
|
+
|
|
+__init void landlock_register_hooks(struct security_hook_list *hooks, int count)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ hooks[i].lsm = "landlock";
|
|
+ list_add_tail_rcu(&hooks[i].list, hooks[i].head);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
|
|
+ enum bpf_reg_type *reg_type,
|
|
+ enum bpf_reg_type ctx_types[CTX_ARG_NB],
|
|
+ union bpf_prog_subtype *prog_subtype)
|
|
+{
|
|
+ int max_size;
|
|
+
|
|
+ if (type != BPF_READ)
|
|
+ return false;
|
|
+ if (off < 0 || off >= sizeof(struct landlock_context))
|
|
+ return false;
|
|
+ if (size <= 0 || size > sizeof(__u64))
|
|
+ return false;
|
|
+
|
|
+ /* set max size */
|
|
+ switch (off) {
|
|
+ case offsetof(struct landlock_context, arch):
|
|
+ case offsetof(struct landlock_context, syscall_nr):
|
|
+ case offsetof(struct landlock_context, syscall_cmd):
|
|
+ case offsetof(struct landlock_context, event):
|
|
+ max_size = sizeof(__u32);
|
|
+ break;
|
|
+ case offsetof(struct landlock_context, status):
|
|
+ case offsetof(struct landlock_context, arg1):
|
|
+ case offsetof(struct landlock_context, arg2):
|
|
+ max_size = sizeof(__u64);
|
|
+ break;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* set register type */
|
|
+ switch (off) {
|
|
+ case offsetof(struct landlock_context, arg1):
|
|
+ *reg_type = ctx_types[0];
|
|
+ break;
|
|
+ case offsetof(struct landlock_context, arg2):
|
|
+ *reg_type = ctx_types[1];
|
|
+ break;
|
|
+ default:
|
|
+ *reg_type = UNKNOWN_VALUE;
|
|
+ }
|
|
+
|
|
+ /* check memory range access */
|
|
+ switch (*reg_type) {
|
|
+ case NOT_INIT:
|
|
+ return false;
|
|
+ case UNKNOWN_VALUE:
|
|
+ case CONST_IMM:
|
|
+ /* allow partial raw value */
|
|
+ if (size > max_size)
|
|
+ return false;
|
|
+ break;
|
|
+ default:
|
|
+ /* deny partial pointer */
|
|
+ if (size != max_size)
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+int landlock_decide(enum landlock_subtype_event event,
|
|
+ __u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook)
|
|
+{
|
|
+ bool deny = false;
|
|
+ u32 event_idx = get_index(event);
|
|
+
|
|
+ struct landlock_context ctx = {
|
|
+ .status = 0,
|
|
+ .arch = syscall_get_arch(),
|
|
+ .syscall_nr = syscall_get_nr(current, task_pt_regs(current)),
|
|
+ .syscall_cmd = cmd,
|
|
+ .event = event,
|
|
+ .arg1 = ctx_values[0],
|
|
+ .arg2 = ctx_values[1],
|
|
+ };
|
|
+
|
|
+ return deny ? -EPERM : 0;
|
|
+}
|
|
diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
|
|
new file mode 100644
|
|
index 000000000000..2e180f6ed86b
|
|
--- /dev/null
|
|
+++ b/security/landlock/hooks.h
|
|
@@ -0,0 +1,177 @@
|
|
+/*
|
|
+ * Landlock LSM - hooks helpers
|
|
+ *
|
|
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2, as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <asm/current.h>
|
|
+#include <linux/bpf.h> /* enum bpf_access_type */
|
|
+#include <linux/lsm_hooks.h>
|
|
+#include <linux/sched.h> /* struct task_struct */
|
|
+
|
|
+/* separators */
|
|
+#define SEP_COMMA() ,
|
|
+#define SEP_SPACE()
|
|
+#define SEP_AND() &&
|
|
+
|
|
+#define MAP2x1(s, m, x1, x2, ...) m(x1, x2)
|
|
+#define MAP2x2(s, m, x1, x2, ...) m(x1, x2) s() MAP2x1(s, m, __VA_ARGS__)
|
|
+#define MAP2x3(s, m, x1, x2, ...) m(x1, x2) s() MAP2x2(s, m, __VA_ARGS__)
|
|
+#define MAP2x4(s, m, x1, x2, ...) m(x1, x2) s() MAP2x3(s, m, __VA_ARGS__)
|
|
+#define MAP2x5(s, m, x1, x2, ...) m(x1, x2) s() MAP2x4(s, m, __VA_ARGS__)
|
|
+#define MAP2x6(s, m, x1, x2, ...) m(x1, x2) s() MAP2x5(s, m, __VA_ARGS__)
|
|
+#define MAP2x(n, ...) MAP2x##n(__VA_ARGS__)
|
|
+
|
|
+#define MAP1x1(s, m, x1, ...) m(x1)
|
|
+#define MAP1x2(s, m, x1, ...) m(x1) s() MAP1x1(s, m, __VA_ARGS__)
|
|
+#define MAP1x(n, ...) MAP1x##n(__VA_ARGS__)
|
|
+
|
|
+#define SKIP2x1(x1, x2, ...) __VA_ARGS__
|
|
+#define SKIP2x2(x1, x2, ...) SKIP2x1(__VA_ARGS__)
|
|
+#define SKIP2x3(x1, x2, ...) SKIP2x2(__VA_ARGS__)
|
|
+#define SKIP2x4(x1, x2, ...) SKIP2x3(__VA_ARGS__)
|
|
+#define SKIP2x5(x1, x2, ...) SKIP2x4(__VA_ARGS__)
|
|
+#define SKIP2x6(x1, x2, ...) SKIP2x5(__VA_ARGS__)
|
|
+#define SKIP2x(n, ...) SKIP2x##n(__VA_ARGS__)
|
|
+
|
|
+/* LSM hook argument helpers */
|
|
+#define MAP_HOOK_COMMA(n, ...) MAP2x(n, SEP_COMMA, __VA_ARGS__)
|
|
+
|
|
+#define GET_HOOK_TA(t, a) t a
|
|
+
|
|
+/* Landlock event argument helpers */
|
|
+#define MAP_EVENT_COMMA(h, n, m, ...) MAP2x(n, SEP_COMMA, m, SKIP2x(h, __VA_ARGS__))
|
|
+#define MAP_EVENT_SPACE(h, n, m, ...) MAP2x(n, SEP_SPACE, m, SKIP2x(h, __VA_ARGS__))
|
|
+#define MAP_EVENT_AND(h, n, m, ...) MAP2x(n, SEP_AND, m, SKIP2x(h, __VA_ARGS__))
|
|
+
|
|
+#define GET_CMD(h, n, ...) SKIP2x(n, SKIP2x(h, __VA_ARGS__))
|
|
+
|
|
+#define EXPAND_TYPE(d) d##_TYPE
|
|
+#define EXPAND_BPF(d) d##_BPF
|
|
+#define EXPAND_C(d) d##_C
|
|
+
|
|
+#define GET_TYPE_BPF(t) EXPAND_BPF(t)
|
|
+#define GET_TYPE_C(t) EXPAND_C(t) *
|
|
+
|
|
+#define GET_EVENT_C(d, a) GET_TYPE_C(EXPAND_TYPE(d))
|
|
+#define GET_EVENT_U64(d, a) ((u64)(d##_VAL(a)))
|
|
+#define GET_EVENT_DEC(d, a) d##_DEC(a)
|
|
+#define GET_EVENT_OK(d, a) d##_OK(a)
|
|
+
|
|
+/**
|
|
+ * HOOK_ACCESS
|
|
+ *
|
|
+ * @EVENT: Landlock event name
|
|
+ * @NA: number of event arguments
|
|
+ *
|
|
+ * The __consistent_##EVENT() extern functions and __wrapcheck_* types are
|
|
+ * useful to catch inconsistencies in LSM hook definitions thanks to the
|
|
+ * compiler type checking.
|
|
+ */
|
|
+#define HOOK_ACCESS(EVENT, NA, ...) \
|
|
+ inline bool landlock_is_valid_access_event_##EVENT( \
|
|
+ int off, int size, enum bpf_access_type type, \
|
|
+ enum bpf_reg_type *reg_type, \
|
|
+ union bpf_prog_subtype *prog_subtype) \
|
|
+ { \
|
|
+ enum bpf_reg_type _ctx_types[CTX_ARG_NB] = { \
|
|
+ MAP1x(NA, SEP_COMMA, GET_TYPE_BPF, __VA_ARGS__) \
|
|
+ }; \
|
|
+ return landlock_is_valid_access(off, size, type, \
|
|
+ reg_type, _ctx_types, prog_subtype); \
|
|
+ } \
|
|
+ extern void __consistent_##EVENT( \
|
|
+ MAP1x(NA, SEP_COMMA, GET_TYPE_C, __VA_ARGS__))
|
|
+
|
|
+/**
|
|
+ * HOOK_NEW
|
|
+ *
|
|
+ * @INST: event instance for this hook
|
|
+ * @EVENT: Landlock event name
|
|
+ * @NE: number of event arguments
|
|
+ * @HOOK: LSM hook name
|
|
+ * @NH: number of hook arguments
|
|
+ */
|
|
+#define HOOK_NEW(INST, EVENT, NE, HOOK, NH, ...) \
|
|
+ static int landlock_hook_##EVENT##_##HOOK##_##INST( \
|
|
+ MAP_HOOK_COMMA(NH, GET_HOOK_TA, __VA_ARGS__)) \
|
|
+ { \
|
|
+ if (!landlocked(current)) \
|
|
+ return 0; \
|
|
+ if (!(MAP_EVENT_AND(NH, NE, GET_EVENT_OK, \
|
|
+ __VA_ARGS__))) \
|
|
+ return 0; \
|
|
+ { \
|
|
+ MAP_EVENT_SPACE(NH, NE, GET_EVENT_DEC, __VA_ARGS__) \
|
|
+ __u64 _ctx_values[CTX_ARG_NB] = { \
|
|
+ MAP_EVENT_COMMA(NH, NE, GET_EVENT_U64, \
|
|
+ __VA_ARGS__) \
|
|
+ }; \
|
|
+ u32 _cmd = GET_CMD(NH, NE, __VA_ARGS__); \
|
|
+ return landlock_decide(LANDLOCK_SUBTYPE_EVENT_##EVENT, \
|
|
+ _ctx_values, _cmd, #HOOK); \
|
|
+ } \
|
|
+ } \
|
|
+ extern void __consistent_##EVENT(MAP_EVENT_COMMA( \
|
|
+ NH, NE, GET_EVENT_C, __VA_ARGS__))
|
|
+
|
|
+/*
|
|
+ * The WRAP_TYPE_* definitions group the bpf_reg_type enum value and the C
|
|
+ * type. This C type may remains unused except to catch inconsistencies in LSM
|
|
+ * hook definitions thanks to the compiler type checking.
|
|
+ */
|
|
+
|
|
+/* WRAP_TYPE_NONE */
|
|
+#define WRAP_TYPE_NONE_BPF NOT_INIT
|
|
+#define WRAP_TYPE_NONE_C struct __wrapcheck_none
|
|
+WRAP_TYPE_NONE_C;
|
|
+
|
|
+/* WRAP_TYPE_RAW */
|
|
+#define WRAP_TYPE_RAW_BPF UNKNOWN_VALUE
|
|
+#define WRAP_TYPE_RAW_C struct __wrapcheck_raw
|
|
+WRAP_TYPE_RAW_C;
|
|
+
|
|
+/*
|
|
+ * The WRAP_ARG_* definitions group the LSM hook argument type (C and BPF), the
|
|
+ * wrapping struct declaration (if any) and the value to copy to the BPF
|
|
+ * context. This definitions may be used thanks to the EXPAND_* helpers.
|
|
+ *
|
|
+ * WRAP_ARG_*_TYPE: type for BPF and C (cf. WRAP_TYPE_*)
|
|
+ * WRAP_ARG_*_DEC: declare a wrapper
|
|
+ * WRAP_ARG_*_VAL: get this wrapper's address
|
|
+ * WRAP_ARG_*_OK: check if the argument is usable
|
|
+ */
|
|
+
|
|
+/* WRAP_ARG_NONE */
|
|
+#define WRAP_ARG_NONE_TYPE WRAP_TYPE_NONE
|
|
+#define WRAP_ARG_NONE_DEC(arg)
|
|
+#define WRAP_ARG_NONE_VAL(arg) 0
|
|
+#define WRAP_ARG_NONE_OK(arg) (!WARN_ON(true))
|
|
+
|
|
+/* WRAP_ARG_RAW */
|
|
+#define WRAP_ARG_RAW_TYPE WRAP_TYPE_RAW
|
|
+#define WRAP_ARG_RAW_DEC(arg)
|
|
+#define WRAP_ARG_RAW_VAL(arg) arg
|
|
+#define WRAP_ARG_RAW_OK(arg) (true)
|
|
+
|
|
+
|
|
+#define CTX_ARG_NB 2
|
|
+
|
|
+static inline bool landlocked(const struct task_struct *task)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+
|
|
+__init void landlock_register_hooks(struct security_hook_list *hooks, int count);
|
|
+
|
|
+bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
|
|
+ enum bpf_reg_type *reg_type,
|
|
+ enum bpf_reg_type ctx_types[CTX_ARG_NB],
|
|
+ union bpf_prog_subtype *prog_subtype);
|
|
+
|
|
+int landlock_decide(enum landlock_subtype_event event,
|
|
+ __u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook);
|
|
diff --git a/security/landlock/hooks_fs.c b/security/landlock/hooks_fs.c
|
|
new file mode 100644
|
|
index 000000000000..6578f21783c7
|
|
--- /dev/null
|
|
+++ b/security/landlock/hooks_fs.c
|
|
@@ -0,0 +1,563 @@
|
|
+/*
|
|
+ * Landlock LSM - filesystem hooks
|
|
+ *
|
|
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2, as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h> /* ARRAY_SIZE */
|
|
+#include <linux/lsm_hooks.h>
|
|
+#include <linux/types.h> /* uintptr_t */
|
|
+
|
|
+/* permissions translation */
|
|
+#include <linux/fs.h> /* MAY_* */
|
|
+#include <linux/mman.h> /* PROT_* */
|
|
+
|
|
+/* hook arguments */
|
|
+#include <linux/cred.h>
|
|
+#include <linux/dcache.h> /* struct dentry */
|
|
+#include <linux/fs.h> /* struct inode, struct iattr */
|
|
+#include <linux/mm_types.h> /* struct vm_area_struct */
|
|
+#include <linux/mount.h> /* struct vfsmount */
|
|
+#include <linux/path.h> /* struct path */
|
|
+#include <linux/sched.h> /* struct task_struct */
|
|
+#include <linux/time.h> /* struct timespec */
|
|
+
|
|
+#include "hooks.h"
|
|
+
|
|
+#include "hooks_fs.h"
|
|
+
|
|
+
|
|
+#define HOOK_NEW_FS(...) HOOK_NEW(1, FS, 2, __VA_ARGS__, 0)
|
|
+#define HOOK_NEW_FS2(...) HOOK_NEW(2, FS, 2, __VA_ARGS__, 0)
|
|
+#define HOOK_NEW_FS3(...) HOOK_NEW(3, FS, 2, __VA_ARGS__, 0)
|
|
+#define HOOK_NEW_FS4(...) HOOK_NEW(4, FS, 2, __VA_ARGS__, 0)
|
|
+#define HOOK_NEW_FS_CMD(...) HOOK_NEW(1, FS, 2, __VA_ARGS__)
|
|
+#define HOOK_INIT_FS(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_1)
|
|
+#define HOOK_INIT_FS2(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_2)
|
|
+#define HOOK_INIT_FS3(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_3)
|
|
+#define HOOK_INIT_FS4(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_4)
|
|
+
|
|
+/* WRAP_TYPE_FS */
|
|
+#define WRAP_TYPE_FS_BPF CONST_PTR_TO_HANDLE_FS
|
|
+#define WRAP_TYPE_FS_C const struct bpf_handle_fs
|
|
+
|
|
+/* WRAP_ARG_FILE */
|
|
+#define WRAP_ARG_FILE_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_FILE_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_FILE, .file = arg };
|
|
+#define WRAP_ARG_FILE_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_FILE_OK(arg) (arg)
|
|
+
|
|
+/* WRAP_ARG_VMAF */
|
|
+#define WRAP_ARG_VMAF_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_VMAF_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_FILE, .file = arg->vm_file };
|
|
+#define WRAP_ARG_VMAF_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_VMAF_OK(arg) (arg && arg->vm_file)
|
|
+
|
|
+/* WRAP_ARG_INODE */
|
|
+#define WRAP_ARG_INODE_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_INODE_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_INODE, .inode = arg };
|
|
+#define WRAP_ARG_INODE_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_INODE_OK(arg) (arg)
|
|
+
|
|
+/* WRAP_ARG_PATH */
|
|
+#define WRAP_ARG_PATH_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_PATH_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_PATH, .path = arg };
|
|
+#define WRAP_ARG_PATH_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_PATH_OK(arg) (arg)
|
|
+
|
|
+/* WRAP_ARG_DENTRY */
|
|
+#define WRAP_ARG_DENTRY_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_DENTRY_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg };
|
|
+#define WRAP_ARG_DENTRY_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_DENTRY_OK(arg) (arg)
|
|
+
|
|
+/* WRAP_ARG_SB */
|
|
+#define WRAP_ARG_SB_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_SB_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg->s_root };
|
|
+#define WRAP_ARG_SB_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_SB_OK(arg) (arg && arg->s_root)
|
|
+
|
|
+/* WRAP_ARG_MNTROOT */
|
|
+#define WRAP_ARG_MNTROOT_TYPE WRAP_TYPE_FS
|
|
+#define WRAP_ARG_MNTROOT_DEC(arg) \
|
|
+ EXPAND_C(WRAP_TYPE_FS) wrap_##arg = \
|
|
+ { .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg->mnt_root };
|
|
+#define WRAP_ARG_MNTROOT_VAL(arg) ((uintptr_t)&wrap_##arg)
|
|
+#define WRAP_ARG_MNTROOT_OK(arg) (arg && arg->mnt_root)
|
|
+
|
|
+
|
|
+static inline u64 fs_may_to_access(int fs_may)
|
|
+{
|
|
+ u64 ret = 0;
|
|
+
|
|
+ if (fs_may & MAY_EXEC)
|
|
+ ret |= LANDLOCK_ACTION_FS_EXEC;
|
|
+ if (fs_may & MAY_READ)
|
|
+ ret |= LANDLOCK_ACTION_FS_READ;
|
|
+ if (fs_may & MAY_WRITE)
|
|
+ ret |= LANDLOCK_ACTION_FS_WRITE;
|
|
+ if (fs_may & MAY_APPEND)
|
|
+ ret |= LANDLOCK_ACTION_FS_WRITE;
|
|
+ if (fs_may & MAY_OPEN)
|
|
+ ret |= LANDLOCK_ACTION_FS_GET;
|
|
+ /* ignore MAY_CHDIR and MAY_ACCESS */
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u64 mem_prot_to_access(unsigned long prot, bool private)
|
|
+{
|
|
+ u64 ret = 0;
|
|
+
|
|
+ /* private mapping do not write to files */
|
|
+ if (!private && (prot & PROT_WRITE))
|
|
+ ret |= LANDLOCK_ACTION_FS_WRITE;
|
|
+ if (prot & PROT_READ)
|
|
+ ret |= LANDLOCK_ACTION_FS_READ;
|
|
+ if (prot & PROT_EXEC)
|
|
+ ret |= LANDLOCK_ACTION_FS_EXEC;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* hook definitions */
|
|
+
|
|
+HOOK_ACCESS(FS, 2, WRAP_TYPE_FS, WRAP_TYPE_RAW);
|
|
+
|
|
+/* binder_* hooks */
|
|
+
|
|
+HOOK_NEW_FS(binder_transfer_file, 3,
|
|
+ struct task_struct *, from,
|
|
+ struct task_struct *, to,
|
|
+ struct file *, file,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+/* sb_* hooks */
|
|
+
|
|
+HOOK_NEW_FS(sb_statfs, 1,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+/*
|
|
+ * Being able to mount on a path means being able to override the underlying
|
|
+ * filesystem view of this path, hence the need for a write access right.
|
|
+ */
|
|
+HOOK_NEW_FS(sb_mount, 5,
|
|
+ const char *, dev_name,
|
|
+ const struct path *, path,
|
|
+ const char *, type,
|
|
+ unsigned long, flags,
|
|
+ void *, data,
|
|
+ WRAP_ARG_PATH, path,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(sb_remount, 2,
|
|
+ struct super_block *, sb,
|
|
+ void *, data,
|
|
+ WRAP_ARG_SB, sb,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(sb_umount, 2,
|
|
+ struct vfsmount *, mnt,
|
|
+ int, flags,
|
|
+ WRAP_ARG_MNTROOT, mnt,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+/*
|
|
+ * The old_path is similar to a destination mount point.
|
|
+ */
|
|
+HOOK_NEW_FS(sb_pivotroot, 2,
|
|
+ const struct path *, old_path,
|
|
+ const struct path *, new_path,
|
|
+ WRAP_ARG_PATH, old_path,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+/* inode_* hooks */
|
|
+
|
|
+/* a directory inode contains only one dentry */
|
|
+HOOK_NEW_FS(inode_create, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_create, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_link, 3,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_DENTRY, old_dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_link, 3,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS3(inode_link, 3,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_DENTRY, new_dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_unlink, 2,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_unlink, 2,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_symlink, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ const char *, old_name,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_symlink, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ const char *, old_name,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_mkdir, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_mkdir, 3,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_rmdir, 2,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_rmdir, 2,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_mknod, 4,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ dev_t, dev,
|
|
+ WRAP_ARG_INODE, dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_mknod, 4,
|
|
+ struct inode *, dir,
|
|
+ struct dentry *, dentry,
|
|
+ umode_t, mode,
|
|
+ dev_t, dev,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_rename, 4,
|
|
+ struct inode *, old_dir,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, new_dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_INODE, old_dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS2(inode_rename, 4,
|
|
+ struct inode *, old_dir,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, new_dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_DENTRY, old_dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS3(inode_rename, 4,
|
|
+ struct inode *, old_dir,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, new_dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_INODE, new_dir,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS4(inode_rename, 4,
|
|
+ struct inode *, old_dir,
|
|
+ struct dentry *, old_dentry,
|
|
+ struct inode *, new_dir,
|
|
+ struct dentry *, new_dentry,
|
|
+ WRAP_ARG_DENTRY, new_dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_readlink, 1,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+// XXX: handle inode?
|
|
+HOOK_NEW_FS(inode_follow_link, 3,
|
|
+ struct dentry *, dentry,
|
|
+ struct inode *, inode,
|
|
+ bool, rcu,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_permission, 2,
|
|
+ struct inode *, inode,
|
|
+ int, mask,
|
|
+ WRAP_ARG_INODE, inode,
|
|
+ WRAP_ARG_RAW, fs_may_to_access(mask)
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_setattr, 2,
|
|
+ struct dentry *, dentry,
|
|
+ struct iattr *, attr,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_getattr, 1,
|
|
+ const struct path *, path,
|
|
+ WRAP_ARG_PATH, path,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_setxattr, 5,
|
|
+ struct dentry *, dentry,
|
|
+ const char *, name,
|
|
+ const void *, value,
|
|
+ size_t, size,
|
|
+ int, flags,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_getxattr, 2,
|
|
+ struct dentry *, dentry,
|
|
+ const char *, name,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_listxattr, 1,
|
|
+ struct dentry *, dentry,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_removexattr, 2,
|
|
+ struct dentry *, dentry,
|
|
+ const char *, name,
|
|
+ WRAP_ARG_DENTRY, dentry,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_getsecurity, 4,
|
|
+ struct inode *, inode,
|
|
+ const char *, name,
|
|
+ void **, buffer,
|
|
+ bool, alloc,
|
|
+ WRAP_ARG_INODE, inode,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(inode_setsecurity, 5,
|
|
+ struct inode *, inode,
|
|
+ const char *, name,
|
|
+ const void *, value,
|
|
+ size_t, size,
|
|
+ int, flag,
|
|
+ WRAP_ARG_INODE, inode,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
|
|
+);
|
|
+
|
|
+/* file_* hooks */
|
|
+
|
|
+HOOK_NEW_FS(file_permission, 2,
|
|
+ struct file *, file,
|
|
+ int, mask,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, fs_may_to_access(mask)
|
|
+);
|
|
+
|
|
+/*
|
|
+ * An ioctl command can be a read or a write. This can be checked with _IOC*()
|
|
+ * for some commands but a Landlock rule should check the ioctl command to
|
|
+ * whitelist them.
|
|
+ */
|
|
+HOOK_NEW_FS_CMD(file_ioctl, 3,
|
|
+ struct file *, file,
|
|
+ unsigned int, cmd,
|
|
+ unsigned long, arg,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_IOCTL,
|
|
+ cmd
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS_CMD(file_lock, 2,
|
|
+ struct file *, file,
|
|
+ unsigned int, cmd,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_LOCK,
|
|
+ cmd
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS_CMD(file_fcntl, 3,
|
|
+ struct file *, file,
|
|
+ unsigned int, cmd,
|
|
+ unsigned long, arg,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_FCNTL,
|
|
+ cmd
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(mmap_file, 4,
|
|
+ struct file *, file,
|
|
+ unsigned long, reqprot,
|
|
+ unsigned long, prot,
|
|
+ unsigned long, flags,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, mem_prot_to_access(prot, flags & MAP_PRIVATE)
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(file_mprotect, 3,
|
|
+ struct vm_area_struct *, vma,
|
|
+ unsigned long, reqprot,
|
|
+ unsigned long, prot,
|
|
+ WRAP_ARG_VMAF, vma,
|
|
+ WRAP_ARG_RAW, mem_prot_to_access(prot, !(vma->vm_flags & VM_SHARED))
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(file_receive, 1,
|
|
+ struct file *, file,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_GET
|
|
+);
|
|
+
|
|
+HOOK_NEW_FS(file_open, 2,
|
|
+ struct file *, file,
|
|
+ const struct cred *, cred,
|
|
+ WRAP_ARG_FILE, file,
|
|
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_GET
|
|
+);
|
|
+
|
|
+static struct security_hook_list landlock_hooks[] = {
|
|
+ HOOK_INIT_FS(binder_transfer_file),
|
|
+
|
|
+ HOOK_INIT_FS(sb_statfs),
|
|
+ HOOK_INIT_FS(sb_mount),
|
|
+ HOOK_INIT_FS(sb_remount),
|
|
+ HOOK_INIT_FS(sb_umount),
|
|
+ HOOK_INIT_FS(sb_pivotroot),
|
|
+
|
|
+ HOOK_INIT_FS(inode_create),
|
|
+ HOOK_INIT_FS2(inode_create),
|
|
+ HOOK_INIT_FS(inode_link),
|
|
+ HOOK_INIT_FS2(inode_link),
|
|
+ HOOK_INIT_FS3(inode_link),
|
|
+ HOOK_INIT_FS(inode_unlink),
|
|
+ HOOK_INIT_FS2(inode_unlink),
|
|
+ HOOK_INIT_FS(inode_symlink),
|
|
+ HOOK_INIT_FS2(inode_symlink),
|
|
+ HOOK_INIT_FS(inode_mkdir),
|
|
+ HOOK_INIT_FS2(inode_mkdir),
|
|
+ HOOK_INIT_FS(inode_rmdir),
|
|
+ HOOK_INIT_FS2(inode_rmdir),
|
|
+ HOOK_INIT_FS(inode_mknod),
|
|
+ HOOK_INIT_FS2(inode_mknod),
|
|
+ HOOK_INIT_FS(inode_rename),
|
|
+ HOOK_INIT_FS2(inode_rename),
|
|
+ HOOK_INIT_FS3(inode_rename),
|
|
+ HOOK_INIT_FS4(inode_rename),
|
|
+ HOOK_INIT_FS(inode_readlink),
|
|
+ HOOK_INIT_FS(inode_follow_link),
|
|
+ HOOK_INIT_FS(inode_permission),
|
|
+ HOOK_INIT_FS(inode_setattr),
|
|
+ HOOK_INIT_FS(inode_getattr),
|
|
+ HOOK_INIT_FS(inode_setxattr),
|
|
+ HOOK_INIT_FS(inode_getxattr),
|
|
+ HOOK_INIT_FS(inode_listxattr),
|
|
+ HOOK_INIT_FS(inode_removexattr),
|
|
+ HOOK_INIT_FS(inode_getsecurity),
|
|
+ HOOK_INIT_FS(inode_setsecurity),
|
|
+
|
|
+ HOOK_INIT_FS(file_permission),
|
|
+ HOOK_INIT_FS(file_ioctl),
|
|
+ HOOK_INIT_FS(file_lock),
|
|
+ HOOK_INIT_FS(file_fcntl),
|
|
+ HOOK_INIT_FS(mmap_file),
|
|
+ HOOK_INIT_FS(file_mprotect),
|
|
+ HOOK_INIT_FS(file_receive),
|
|
+ HOOK_INIT_FS(file_open),
|
|
+};
|
|
+
|
|
+__init void landlock_add_hooks_fs(void)
|
|
+{
|
|
+ landlock_register_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks));
|
|
+}
|
|
diff --git a/security/landlock/hooks_fs.h b/security/landlock/hooks_fs.h
|
|
new file mode 100644
|
|
index 000000000000..093c72bb91dc
|
|
--- /dev/null
|
|
+++ b/security/landlock/hooks_fs.h
|
|
@@ -0,0 +1,19 @@
|
|
+/*
|
|
+ * Landlock LSM - filesystem hooks
|
|
+ *
|
|
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2, as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/bpf.h> /* enum bpf_access_type */
|
|
+
|
|
+
|
|
+bool landlock_is_valid_access_event_FS(
|
|
+ int off, int size, enum bpf_access_type type,
|
|
+ enum bpf_reg_type *reg_type,
|
|
+ union bpf_prog_subtype *prog_subtype);
|
|
+
|
|
+__init void landlock_add_hooks_fs(void);
|
|
diff --git a/security/landlock/init.c b/security/landlock/init.c
|
|
index 914895d08320..1c2750e12dfa 100644
|
|
--- a/security/landlock/init.c
|
|
+++ b/security/landlock/init.c
|
|
@@ -11,6 +11,9 @@
|
|
#include <linux/bpf.h> /* enum bpf_access_type */
|
|
#include <linux/capability.h> /* capable */
|
|
#include <linux/landlock.h> /* LANDLOCK_VERSION */
|
|
+#include <linux/lsm_hooks.h>
|
|
+
|
|
+#include "hooks_fs.h"
|
|
|
|
|
|
static inline bool bpf_landlock_is_valid_access(int off, int size,
|
|
@@ -22,6 +25,8 @@ static inline bool bpf_landlock_is_valid_access(int off, int size,
|
|
|
|
switch (prog_subtype->landlock_rule.event) {
|
|
case LANDLOCK_SUBTYPE_EVENT_FS:
|
|
+ return landlock_is_valid_access_event_FS(off, size, type,
|
|
+ reg_type, prog_subtype);
|
|
case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
|
|
default:
|
|
return false;
|
|
@@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
|
|
.ops = &bpf_landlock_ops,
|
|
.type = BPF_PROG_TYPE_LANDLOCK,
|
|
};
|
|
+
|
|
+void __init landlock_add_hooks(void)
|
|
+{
|
|
+ pr_info("landlock: Version %u", LANDLOCK_VERSION);
|
|
+ landlock_add_hooks_fs();
|
|
+ security_add_hooks(NULL, 0, "landlock");
|
|
+ bpf_register_prog_type(&bpf_landlock_type);
|
|
+}
|
|
diff --git a/security/security.c b/security/security.c
|
|
index f825304f04a7..74d2bf057f30 100644
|
|
--- a/security/security.c
|
|
+++ b/security/security.c
|
|
@@ -63,10 +63,15 @@ int __init security_init(void)
|
|
loadpin_add_hooks();
|
|
|
|
/*
|
|
- * Load all the remaining security modules.
|
|
+ * Load all remaining privileged security modules.
|
|
*/
|
|
do_security_initcalls();
|
|
|
|
+ /*
|
|
+ * Load potentially-unprivileged security modules at the end.
|
|
+ */
|
|
+ landlock_add_hooks();
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
--
|
|
2.11.0
|
|
|