2017-08-21 00:13:38

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 00/10] Landlock LSM: Toward unprivileged sandboxing

Hi,

This seventh series add some changes to the previous one [1], including a
simplified landlock_context, architecture-independent rules, more documentation
and multiples fixes.

As planed [6], I simplified and make the FS event more generic for the IOCTL,
LOCK or FCNTL actions. The action flags for the LANDLOCK_SUBTYPE_EVENT_FS event
remains the same but the syscall_cmd field is removed from struct
landlock_context. Instead, one of three dedicated events is triggered in
addition to one of this three multiplexed actions. The aim is to trigger the
LANDLOCK_SUBTYPE_EVENT_FS for all file system events (still including
IOCTL/LOCK/FCNTL actions). This should avoid a developer/user to forget such
actions. However, when this kind of action is triggered, a
LANDLOCK_SUBTYPE_EVENT_FS_{IOCTL,LOCK,FCNTL} event will follow. This enable to
simplify the struct landlock_context while still having it as generic as
possible. The difference will be that the arg2 field for one of the
LANDLOCK_SUBTYPE_EVENT_FS_{IOCTL,LOCK,FCNTL} events will contain a custom
IOCTL, LOCK or FCNTL command (previously in the syscall_cmd field) instead of a
LANDLOCK_ACTION_FS_* value. The same logic could be used to tighten other
actions in the future (e.g. add a LANDLOCK_SUBTYPE_EVENT_FS_RENAME).

I also removed the arch and syscall_nr fields, which result in a more simple
and architecture-independent landlock_context.

The documentation patch contains some kernel documentation and explanations on
how to use Landlock. The compiled documentation can be found here:
https://landlock-lsm.github.io/linux-doc/landlock-v7/security/landlock/index.html

This is the first step of the roadmap discussed at LPC [2] (with the
inheritance feature included). While the intended final goal is to allow
unprivileged users to use Landlock, this series allows only a process with
global CAP_SYS_ADMIN to load and enforce a rule. This may help to get feedback
and avoid unexpected behaviors.

This series can be applied on top of net-next, commit d6e1e46f69fb ("bpf:
linux/bpf.h needs linux/numa.h"). This can be tested with
CONFIG_SECCOMP_FILTER and CONFIG_SECURITY_LANDLOCK. I would really appreciate
constructive comments on the usability, architecture, code, userland API or use
cases.


# Landlock LSM

The goal of this new stackable Linux Security Module (LSM) called Landlock is
to allow any process, including unprivileged ones, to create powerful security
sandboxes comparable to XNU Sandbox or OpenBSD Pledge. This kind of sandbox is
expected to help mitigate the security impact of bugs or unexpected/malicious
behaviors in user-space applications.

The approach taken is to add the minimum amount of code while still allowing
the user-space application to create quite complex access rules. A dedicated
security policy language such as the one used by SELinux, AppArmor and other
major LSMs involves a lot of code and is usually permitted to only a trusted
user (i.e. root). On the contrary, eBPF programs already exist and are
designed to be safely loaded by unprivileged user-space.

This design does not seem too intrusive but is flexible enough to allow a
powerful sandbox mechanism accessible by any process on Linux. The use of
seccomp and Landlock is more suitable with the help of a user-space library
(e.g. libseccomp) that could help to specify a high-level language to express
a security policy instead of raw eBPF programs. Moreover, thanks to the LLVM
front-end, it is quite easy to write an eBPF program with a subset of the C
language.


# Landlock events and rule enforcement

Unlike syscalls, LSM hooks are security checkpoints and are not architecture
dependent. They are designed to match a security need associated with a
security policy (e.g. access to a file). The approach taken for Landlock is to
abstract these hooks with Landlock events such as a generic filesystem event
(LANDLOCK_SUBTYPE_EVENT_FS). Further explanations can be found in the
documentation.

This series uses seccomp(2) only as an entry point to apply a rule to the
calling process and its future children. It is planed to restore the ability
to use cgroup as an alternative way to enforce a Landlock rule.

There is as yet no way to allow a process to access only a subset of the
filesystem where the subset is specified via a path or a file descriptor. This
feature is intentionally left out so as to minimize the amount of code of this
patch series but will come in a following series. However, it is possible to
check the file type, as done in the following example.


# Sandbox example with a read-only filesystem

This example is provided in the samples/bpf directory. It creates a read-only
environment for all kind of file access except for character devices such as a
TTY.

# :> X
# echo $?
0
# ./samples/bpf/landlock1 /bin/sh -i
Launching a new sandboxed process.
# :> Y
cannot create Y: Operation not permitted


# Warning on read-only filesystems

Other than owing a mount namespace and remounting every accessible mounts
points as read-only, which may not be possible for an unprivileged security
sandbox, there is no way of preventing a process to change the access time of a
file, including anonymous inodes. This provides a trivial way to leak
information from a sandboxed environment. A new LSM hook has been proposed to
allow an LSM to enforce a real read-only filesystem view, but it did not get
strong support so far [5].


# Frequently asked questions

## Why is seccomp-bpf not enough?

A seccomp filter can access only raw syscall arguments (i.e. the register
values) which means that it is not possible to filter according to the value
pointed to by an argument, such as a file pathname. As an embryonic Landlock
version demonstrated, filtering at the syscall level is complicated (e.g. need
to take care of race conditions). This is mainly because the access control
checkpoints of the kernel are not at this high-level but more underneath, at
the LSM-hook level. The LSM hooks are designed to handle this kind of checks.
Landlock abstracts this approach to leverage the ability of unprivileged users
to limit themselves.

Cf. section "What it isn't?" in Documentation/prctl/seccomp_filter.txt


## Why use the seccomp(2) syscall?

Landlock use the same semantic as seccomp to apply access rule restrictions. It
add a new layer of security for the current process which is inherited by its
children. It makes sense to use an unique access-restricting syscall (that
should be allowed by seccomp filters) which can only drop privileges. Moreover,
a Landlock rule could come from outside a process (e.g. passed through a UNIX
socket). It is then useful to differentiate the creation/load of Landlock eBPF
programs via bpf(2), from rule enforcement via seccomp(2).


## Why a new LSM? Are SELinux, AppArmor, Smack and Tomoyo not good enough?

The current access control LSMs are fine for their purpose which is to give the
*root* the ability to enforce a security policy for the *system*. What is
missing is a way to enforce a security policy for any application by its
developer and *unprivileged user* as seccomp can do for raw syscall filtering.

Differences from other (access control) LSMs:
* not only dedicated to administrators (i.e. no_new_priv);
* limited kernel attack surface (e.g. policy parsing);
* constrained policy rules (no DoS: deterministic execution time);
* do not leak more information than the loader process can legitimately have
access to (minimize metadata inference).


# Changes since v6
* upstreamed patches:
* commit 752ba56fb130 ("bpf: Extend check_uarg_tail_zero() checks")
* commit 0b40808a1084 ("selftests: Make test_harness.h more generally
available") and related ones
* commit 3bb857e47e49 ("LSM: Enable multiple calls to security_add_hooks()
for the same LSM")
* pending patch to add a step mechanism to test_harness.h (kselftest tree) [7]
* simplify the landlock_context (remove syscall_* fields) and add three FS
sub-events: IOCTL, LOCK, FCNTL
* minimize the number of callable BPF functions from a Landlock rule
* do not split put_seccomp_filter() with put_seccomp()
* rename Landlock version to Landlock ABI
* miscellaneous fixes
* rebase on net-next


# Changes since v5

* eBPF program subtype:
* use a prog_subtype pointer instead of inlining it into bpf_attr
* enable a future-proof behavior (reject unhandled data/size)
* add tests
* use a simple rule hierarchy (similar to seccomp-bpf)
* add a ptrace scope protection
* add more tests
* add more documentation
* rename some files
* miscellaneous fixes
* rebase on net-next


# Changes since v4

* upstreamed patches:
* commit d498f8719a09 ("bpf: Rebuild bpf.o for any dependency update")
* commit a734fb5d6006 ("samples/bpf: Reset global variables") and related
ones
* commit f4874d01beba ("bpf: Use bpf_create_map() from the library") and
related ones
* commit d02d8986a768 ("bpf: Always test unprivileged programs")
* commit 640eb7e7b524 ("fs: Constify path_is_under()'s arguments")
* commit 535e7b4b5ef2 ("bpf: Use u64_to_user_ptr()")
* revamp Landlock to not expose an LSM hook interface but wrap and abstract
them with Landlock events (currently one for all filesystem related
operations: LANDLOCK_SUBTYPE_EVENT_FS)
* wrap all filesystem kernel objects through the same FS handle (struct
landlock_handle_fs): struct file, struct inode, struct path and struct dentry
* a rule don't return an errno code but only a boolean to allow or deny an
access request
* handle all filesystem related LSM hooks
* add some tests and a sample:
* BPF context tests
* Landlock sandboxing tests and sample
* write Landlock rules in C and compile them with LLVM
* change field names of eBPF program subtype
* remove arraymap of handles for now (will be replaced with a revamped map)
* remove cgroup handling for now
* add user and kernel documentation
* rebase on net-next


# Changes since v3

* upstreamed patch:
* commit 1955351da41c ("bpf: Set register type according to
is_valid_access()")
* use abstract LSM hook arguments with custom types (e.g. *_LANDLOCK_ARG_FS for
struct file, struct inode and struct path)
* add more LSM hooks to support full filesystem access control
* improve the sandbox example
* fix races and RCU issues:
* eBPF program execution and eBPF helpers
* revamp the arraymap of handles to cleanly deal with update/delete
* eBPF program subtype for Landlock:
* remove the "origin" field
* add an "option" field
* rebase onto Daniel Mack's patches v7 [3]
* remove merged commit 1955351da41c ("bpf: Set register type according to
is_valid_access()")
* fix spelling mistakes
* cleanup some type and variable names
* split patches
* for now, remove cgroup delegation handling for unprivileged user
* remove extra access check for cgroup_get_from_fd()
* remove unused example code dealing with skb
* remove seccomp-bpf link:
* no more seccomp cookie
* for now, it is no more possible to check the current syscall properties


# Changes since v2

* revamp cgroup handling:
* use Daniel Mack's patches "Add eBPF hooks for cgroups" v5
* remove bpf_landlock_cmp_cgroup_beneath()
* make BPF_PROG_ATTACH usable with delegated cgroups
* add a new CGRP_NO_NEW_PRIVS flag for safe cgroups
* handle Landlock sandboxing for cgroups hierarchy
* allow unprivileged processes to attach Landlock eBPF program to cgroups
* add subtype to eBPF programs:
* replace Landlock hook identification by custom eBPF program types with a
dedicated subtype field
* manage fine-grained privileged Landlock programs
* register Landlock programs for dedicated trigger origins (e.g. syscall,
return from seccomp filter and/or interruption)
* performance and memory optimizations: use an array to access Landlock hooks
directly but do not duplicated it for each thread (seccomp-based)
* allow running Landlock programs without seccomp filter
* fix seccomp-related issues
* remove extra errno bounding check for Landlock programs
* add some examples for optional eBPF functions or context access (network
related) according to security checks to allow more features for privileged
programs (e.g. Checmate)


# Changes since v1

* focus on the LSM hooks, not the syscalls:
* much more simple implementation
* does not need audit cache tricks to avoid race conditions
* more simple to use and more generic because using the LSM hook abstraction
directly
* more efficient because only checking in LSM hooks
* architecture agnostic
* switch from cBPF to eBPF:
* new eBPF program types dedicated to Landlock
* custom functions used by the eBPF program
* gain some new features (e.g. 10 registers, can load values of different
size, LLVM translator) but only a few functions allowed and a dedicated map
type
* new context: LSM hook ID, cookie and LSM hook arguments
* need to set the sysctl kernel.unprivileged_bpf_disable to 0 (default value)
to be able to load hook filters as unprivileged users
* smaller and simpler:
* no more checker groups but dedicated arraymap of handles
* simpler userland structs thanks to eBPF functions
* distinctive name: Landlock


[1] https://lkml.kernel.org/r/[email protected]
[2] https://lkml.kernel.org/r/[email protected]
[3] https://lkml.kernel.org/r/[email protected]
[4] https://lkml.kernel.org/r/[email protected]
[5] https://lkml.kernel.org/r/[email protected]
[6] https://lkml.kernel.org/r/[email protected]
[7] https://lkml.kernel.org/r/[email protected]

Regards,

Mickaël Salaün (10):
selftest: Enhance kselftest_harness.h with a step mechanism
bpf: Add eBPF program subtype and is_valid_subtype() verifier
bpf,landlock: Define an eBPF program type for a Landlock rule
bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode()
landlock: Add LSM hooks related to filesystem
seccomp,landlock: Handle Landlock events per process hierarchy
landlock: Add ptrace restrictions
bpf: Add a Landlock sandbox example
bpf,landlock: Add tests for Landlock
landlock: Add user and kernel documentation for Landlock

Documentation/security/index.rst | 1 +
Documentation/security/landlock/index.rst | 19 +
Documentation/security/landlock/kernel.rst | 132 +++++
Documentation/security/landlock/user.rst | 313 +++++++++++
include/linux/bpf.h | 38 +-
include/linux/bpf_types.h | 3 +
include/linux/filter.h | 2 +
include/linux/landlock.h | 42 ++
include/linux/lsm_hooks.h | 5 +
include/linux/seccomp.h | 5 +
include/uapi/linux/bpf.h | 116 ++++
include/uapi/linux/seccomp.h | 1 +
kernel/bpf/Makefile | 2 +-
kernel/bpf/helpers_fs.c | 52 ++
kernel/bpf/syscall.c | 22 +-
kernel/bpf/verifier.c | 23 +-
kernel/fork.c | 8 +-
kernel/seccomp.c | 3 +
kernel/trace/bpf_trace.c | 15 +-
net/core/filter.c | 71 ++-
samples/bpf/Makefile | 4 +
samples/bpf/bpf_load.c | 31 +-
samples/bpf/cookie_uid_helper_example.c | 2 +-
samples/bpf/fds_example.c | 2 +-
samples/bpf/landlock1_kern.c | 100 ++++
samples/bpf/landlock1_user.c | 100 ++++
samples/bpf/sock_example.c | 3 +-
samples/bpf/test_cgrp2_attach.c | 2 +-
samples/bpf/test_cgrp2_attach2.c | 2 +-
samples/bpf/test_cgrp2_sock.c | 2 +-
security/Kconfig | 1 +
security/Makefile | 2 +
security/landlock/Kconfig | 18 +
security/landlock/Makefile | 8 +
security/landlock/common.h | 65 +++
security/landlock/hooks.c | 129 +++++
security/landlock/hooks.h | 182 +++++++
security/landlock/hooks_fs.c | 586 +++++++++++++++++++++
security/landlock/hooks_fs.h | 19 +
security/landlock/hooks_ptrace.c | 123 +++++
security/landlock/hooks_ptrace.h | 11 +
security/landlock/init.c | 128 +++++
security/landlock/providers.c | 261 +++++++++
security/security.c | 12 +-
tools/include/uapi/linux/bpf.h | 118 ++++-
tools/lib/bpf/bpf.c | 10 +-
tools/lib/bpf/bpf.h | 5 +-
tools/lib/bpf/libbpf.c | 4 +-
tools/perf/tests/bpf.c | 2 +-
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/bpf/bpf_helpers.h | 2 +
tools/testing/selftests/bpf/test_align.c | 2 +-
tools/testing/selftests/bpf/test_tag.c | 2 +-
tools/testing/selftests/bpf/test_verifier.c | 72 ++-
tools/testing/selftests/kselftest_harness.h | 31 +-
tools/testing/selftests/landlock/.gitignore | 5 +
tools/testing/selftests/landlock/Makefile | 48 ++
tools/testing/selftests/landlock/bpf/Makefile | 55 ++
tools/testing/selftests/landlock/bpf/README.rst | 1 +
.../selftests/landlock/bpf/rule_fs_no_open.c | 32 ++
.../selftests/landlock/bpf/rule_fs_read_only.c | 32 ++
tools/testing/selftests/landlock/test.h | 28 +
tools/testing/selftests/landlock/test_base.c | 27 +
tools/testing/selftests/landlock/test_fs.c | 296 +++++++++++
tools/testing/selftests/landlock/test_ptrace.c | 158 ++++++
tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
66 files changed, 3530 insertions(+), 69 deletions(-)
create mode 100644 Documentation/security/landlock/index.rst
create mode 100644 Documentation/security/landlock/kernel.rst
create mode 100644 Documentation/security/landlock/user.rst
create mode 100644 include/linux/landlock.h
create mode 100644 kernel/bpf/helpers_fs.c
create mode 100644 samples/bpf/landlock1_kern.c
create mode 100644 samples/bpf/landlock1_user.c
create mode 100644 security/landlock/Kconfig
create mode 100644 security/landlock/Makefile
create mode 100644 security/landlock/common.h
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
create mode 100644 security/landlock/hooks_ptrace.c
create mode 100644 security/landlock/hooks_ptrace.h
create mode 100644 security/landlock/init.c
create mode 100644 security/landlock/providers.c
create mode 100644 tools/testing/selftests/landlock/.gitignore
create mode 100644 tools/testing/selftests/landlock/Makefile
create mode 100644 tools/testing/selftests/landlock/bpf/Makefile
create mode 120000 tools/testing/selftests/landlock/bpf/README.rst
create mode 100644 tools/testing/selftests/landlock/bpf/rule_fs_no_open.c
create mode 100644 tools/testing/selftests/landlock/bpf/rule_fs_read_only.c
create mode 100644 tools/testing/selftests/landlock/test.h
create mode 100644 tools/testing/selftests/landlock/test_base.c
create mode 100644 tools/testing/selftests/landlock/test_fs.c
create mode 100644 tools/testing/selftests/landlock/test_ptrace.c

--
2.14.1


2017-08-21 00:11:01

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 04/10] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode()

Add an eBPF function bpf_handle_fs_get_mode(handle_fs) to get the mode
of a an abstract object wrapping either a file, a dentry, a path, or an
inode.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
Cc: Jann Horn <[email protected]>
---

Changes since v6:
* remove WARN_ON() for missing dentry->d_inode
* refactor bpf_landlock_func_proto() (suggested by Kees Cook)

Changes since v5:
* cosmetic fixes and rebase

Changes since v4:
* use a file abstraction (handle) to wrap inode, dentry, path and file
structs
* remove bpf_landlock_cmp_fs_beneath()
* rename the BPF helper and move it to kernel/bpf/
* tighten helpers accessible by a Landlock rule

Changes since v3:
* remove bpf_landlock_cmp_fs_prop() (suggested by Alexie Starovoitov)
* 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
* add bpf_landlock_get_fs_mode() helper to check file type and mode
* merge WARN_ON() (suggested by Kees Cook)
* fix and update bpf_helpers.h
* use BPF_CALL_* for eBPF helpers (suggested by Alexie Starovoitov)
* make handle arraymap safe (RCU) and remove buggy synchronize_rcu()
* factor out the arraymay walk
* use size_t to index array (suggested by Jann Horn)

Changes since v2:
* add MNT_INTERNAL check to only add file handle from user-visible FS
(e.g. no anonymous inode)
* replace struct file* with struct path* in map_landlock_handle
* add BPF protos
* fix bpf_landlock_cmp_fs_prop_with_struct_file()
---
include/linux/bpf.h | 31 ++++++++++++++++++
include/uapi/linux/bpf.h | 8 +++++
kernel/bpf/Makefile | 2 +-
kernel/bpf/helpers_fs.c | 52 +++++++++++++++++++++++++++++++
kernel/bpf/verifier.c | 6 ++++
security/landlock/init.c | 17 ++++++++++
tools/include/uapi/linux/bpf.h | 10 +++++-
tools/testing/selftests/bpf/bpf_helpers.h | 2 ++
8 files changed, 126 insertions(+), 2 deletions(-)
create mode 100644 kernel/bpf/helpers_fs.c

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index aef2e6f6d763..5316393150e1 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -16,6 +16,11 @@
#include <linux/rbtree_latch.h>
#include <linux/numa.h>

+/* FS helpers */
+#include <linux/dcache.h> /* struct dentry */
+#include <linux/fs.h> /* struct file, struct inode */
+#include <linux/path.h> /* struct path */
+
struct perf_event;
struct bpf_prog;
struct bpf_map;
@@ -85,6 +90,8 @@ enum bpf_arg_type {

ARG_PTR_TO_CTX, /* pointer to context */
ARG_ANYTHING, /* any (initialized) argument is ok */
+
+ ARG_CONST_PTR_TO_HANDLE_FS, /* pointer to an abstract FS struct */
};

/* type of values returned from helper functions */
@@ -141,6 +148,7 @@ enum bpf_reg_type {
PTR_TO_STACK, /* reg == frame_pointer + offset */
PTR_TO_PACKET, /* reg points to skb->data */
PTR_TO_PACKET_END, /* skb->data + headlen */
+ CONST_PTR_TO_HANDLE_FS, /* FS helpers */
};

/* The information passed from prog-specific *_is_valid_access
@@ -223,6 +231,26 @@ struct bpf_event_entry {
struct rcu_head rcu;
};

+/* FS helpers */
+enum bpf_handle_fs_type {
+ BPF_HANDLE_FS_TYPE_NONE,
+ BPF_HANDLE_FS_TYPE_FILE,
+ BPF_HANDLE_FS_TYPE_INODE,
+ BPF_HANDLE_FS_TYPE_PATH,
+ BPF_HANDLE_FS_TYPE_DENTRY,
+};
+
+struct bpf_handle_fs {
+ enum bpf_handle_fs_type type;
+ union {
+ struct file *file;
+ struct inode *inode;
+ const struct path *path;
+ struct dentry *dentry;
+ };
+};
+
+
u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5);
u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);

@@ -415,6 +443,9 @@ extern const struct bpf_func_proto bpf_skb_vlan_pop_proto;
extern const struct bpf_func_proto bpf_get_stackid_proto;
extern const struct bpf_func_proto bpf_sock_map_update_proto;

+/* FS helpers */
+extern const struct bpf_func_proto bpf_handle_fs_get_mode_proto;
+
/* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void);
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 20da634da941..1624c0bbdf33 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -600,6 +600,13 @@ union bpf_attr {
* @map_flags: sock map specific flags
* bit 1: Enable strparser
* other bits: reserved
+ *
+ * s64 bpf_handle_fs_get_mode(handle_fs)
+ * Get the mode of a struct bpf_handle_fs
+ * fs: struct bpf_handle_fs address
+ * Return:
+ * >= 0 file mode
+ * < 0 error
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -656,6 +663,7 @@ union bpf_attr {
FN(redirect_map), \
FN(sk_redirect_map), \
FN(sock_map_update), \
+ FN(handle_fs_get_mode), \

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 897daa005b23..41e2e4b9f80c 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -1,6 +1,6 @@
obj-y := core.o

-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o helpers_fs.o
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
diff --git a/kernel/bpf/helpers_fs.c b/kernel/bpf/helpers_fs.c
new file mode 100644
index 000000000000..c79313979957
--- /dev/null
+++ b/kernel/bpf/helpers_fs.c
@@ -0,0 +1,52 @@
+/*
+ * BPF filesystem helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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> /* struct bpf_handle_fs */
+#include <linux/errno.h>
+#include <linux/filter.h> /* BPF_CALL*() */
+
+BPF_CALL_1(bpf_handle_fs_get_mode, struct bpf_handle_fs *, handle_fs)
+{
+ if (WARN_ON(!handle_fs))
+ return -EFAULT;
+ if (!handle_fs->file) {
+ /* file can be null for anonymous mmap */
+ WARN_ON(handle_fs->type != BPF_HANDLE_FS_TYPE_FILE);
+ return -ENOENT;
+ }
+ switch (handle_fs->type) {
+ case BPF_HANDLE_FS_TYPE_FILE:
+ if (WARN_ON(!handle_fs->file->f_inode))
+ return -ENOENT;
+ return handle_fs->file->f_inode->i_mode;
+ case BPF_HANDLE_FS_TYPE_INODE:
+ return handle_fs->inode->i_mode;
+ case BPF_HANDLE_FS_TYPE_PATH:
+ if (WARN_ON(!handle_fs->path->dentry ||
+ !handle_fs->path->dentry->d_inode))
+ return -ENOENT;
+ return handle_fs->path->dentry->d_inode->i_mode;
+ case BPF_HANDLE_FS_TYPE_DENTRY:
+ if (!handle_fs->dentry->d_inode)
+ return -ENOENT;
+ return handle_fs->dentry->d_inode->i_mode;
+ case BPF_HANDLE_FS_TYPE_NONE:
+ default:
+ WARN_ON(1);
+ return -EFAULT;
+ }
+}
+
+const struct bpf_func_proto bpf_handle_fs_get_mode_proto = {
+ .func = bpf_handle_fs_get_mode,
+ .gpl_only = true,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_CONST_PTR_TO_HANDLE_FS,
+};
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 74933f5aba0e..258369a056a9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -188,6 +188,7 @@ static const char * const reg_type_str[] = {
[PTR_TO_STACK] = "fp",
[PTR_TO_PACKET] = "pkt",
[PTR_TO_PACKET_END] = "pkt_end",
+ [CONST_PTR_TO_HANDLE_FS] = "handle_fs",
};

#define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
@@ -704,6 +705,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case PTR_TO_PACKET:
case PTR_TO_PACKET_END:
case CONST_PTR_TO_MAP:
+ case CONST_PTR_TO_HANDLE_FS:
return true;
default:
return false;
@@ -1371,6 +1373,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
expected_type = PTR_TO_CTX;
if (type != expected_type)
goto err_type;
+ } else if (arg_type == ARG_CONST_PTR_TO_HANDLE_FS) {
+ expected_type = CONST_PTR_TO_HANDLE_FS;
+ if (type != expected_type)
+ goto err_type;
} else if (arg_type == ARG_PTR_TO_MEM ||
arg_type == ARG_PTR_TO_UNINIT_MEM) {
expected_type = PTR_TO_STACK;
diff --git a/security/landlock/init.c b/security/landlock/init.c
index c7922a91aa57..09acbc74abd6 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -37,6 +37,9 @@ static inline bool bpf_landlock_is_valid_subtype(

switch (prog_subtype->landlock_rule.event) {
case LANDLOCK_SUBTYPE_EVENT_FS:
+ case LANDLOCK_SUBTYPE_EVENT_FS_IOCTL:
+ case LANDLOCK_SUBTYPE_EVENT_FS_LOCK:
+ case LANDLOCK_SUBTYPE_EVENT_FS_FCNTL:
break;
case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
default:
@@ -72,6 +75,20 @@ static inline const struct bpf_func_proto *bpf_landlock_func_proto(
enum bpf_func_id func_id,
const union bpf_prog_subtype *prog_subtype)
{
+ /* context-dependant functions */
+ switch (prog_subtype->landlock_rule.event) {
+ case LANDLOCK_SUBTYPE_EVENT_FS:
+ case LANDLOCK_SUBTYPE_EVENT_FS_IOCTL:
+ case LANDLOCK_SUBTYPE_EVENT_FS_LOCK:
+ case LANDLOCK_SUBTYPE_EVENT_FS_FCNTL:
+ switch (func_id) {
+ case BPF_FUNC_handle_fs_get_mode:
+ return &bpf_handle_fs_get_mode_proto;
+ default:
+ break;
+ }
+ }
+
/* generic functions */
if (prog_subtype->landlock_rule.ability &
LANDLOCK_SUBTYPE_ABILITY_DEBUG) {
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index e83bdecf9d27..f844e38ee10b 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -592,6 +592,13 @@ union bpf_attr {
* @map_flags: sock map specific flags
* bit 1: Enable strparser
* other bits: reserved
+ *
+ * s64 bpf_handle_fs_get_mode(handle_fs)
+ * Get the mode of a struct bpf_handle_fs
+ * fs: struct bpf_handle_fs address
+ * Return:
+ * >= 0 file mode
+ * < 0 error
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -647,7 +654,8 @@ union bpf_attr {
FN(skb_adjust_room), \
FN(redirect_map), \
FN(sk_redirect_map), \
- FN(sock_map_update),
+ FN(sock_map_update), \
+ FN(handle_fs_get_mode), \

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index 98f3be26d390..809cc0b70333 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -72,6 +72,8 @@ static int (*bpf_sock_map_update)(void *map, void *key, void *value,
unsigned long long map_lags) =
(void *) BPF_FUNC_sock_map_update;

+static long long (*bpf_handle_fs_get_mode)(void *handle_fs) =
+ (void *) BPF_FUNC_handle_fs_get_mode;

/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
--
2.14.1

2017-08-21 00:11:19

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 06/10] seccomp,landlock: Handle Landlock events per process hierarchy

The seccomp(2) syscall can be used by a task to apply a Landlock rule to
itself. As a seccomp filter, a Landlock rule is enforced for the current
task and all its future children. A rule is immutable and a task can
only add new restricting rules to itself, forming a chain of rules.

A Landlock rule is tied to a Landlock event. If the action on a kernel
object is allowed by the other Linux security mechanisms (e.g. DAC,
capabilities, other LSM), then a Landlock event related to this kind of
object is triggered. The chain of rules for this event is then
evaluated. Each rule return a 32-bit value which can deny the action on
a kernel object with a non-zero value. If every rules of the chain
return zero, then the action on the object is allowed.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
Cc: Will Drewry <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
---

Changes since v6:
* rename some functions with more accurate names to reflect that an eBPF
program for Landlock could be used for something else than a rule
* reword rule "appending" to "prepending" and explain it
* remove the superfluous no_new_privs check, only check global
CAP_SYS_ADMIN when prepending a Landlock rule (needed for containers)
* create and use {get,put}_seccomp_landlock() (suggested by Kees Cook)
* replace ifdef with static inlined function (suggested by Kees Cook)
* use get_user() (suggested by Kees Cook)
* replace atomic_t with refcount_t (requested by Kees Cook)
* move struct landlock_{rule,events} from landlock.h to common.h
* cleanup headers

Changes since v5:
* remove struct landlock_node and use a similar inheritance mechanisme
as seccomp-bpf (requested by Andy Lutomirski)
* rename SECCOMP_ADD_LANDLOCK_RULE to SECCOMP_APPEND_LANDLOCK_RULE
* rename file manager.c to providers.c
* add comments
* typo and cosmetic fixes

Changes since v4:
* merge manager and seccomp patches
* return -EFAULT in seccomp(2) when user_bpf_fd is null to easely check
if Landlock is supported
* only allow a process with the global CAP_SYS_ADMIN to use Landlock
(will be lifted in the future)
* add an early check to exit as soon as possible if the current process
does not have Landlock rules

Changes since v3:
* remove the hard link with seccomp (suggested by Andy Lutomirski and
Kees Cook):
* remove the cookie which could imply multiple evaluation of Landlock
rules
* remove the origin field in struct landlock_data
* remove documentation fix (merged upstream)
* rename the new seccomp command to SECCOMP_ADD_LANDLOCK_RULE
* internal renaming
* split commit
* new design to be able to inherit on the fly the parent rules

Changes since v2:
* Landlock programs can now be run without seccomp filter but for any
syscall (from the process) or interruption
* move Landlock related functions and structs into security/landlock/*
(to manage cgroups as well)
* fix seccomp filter handling: run Landlock programs for each of their
legitimate seccomp filter
* properly clean up all seccomp results
* cosmetic changes to ease the understanding
* fix some ifdef
---
include/linux/landlock.h | 42 +++++++
include/linux/seccomp.h | 5 +
include/uapi/linux/seccomp.h | 1 +
kernel/fork.c | 8 +-
kernel/seccomp.c | 3 +
security/landlock/Makefile | 2 +-
security/landlock/common.h | 42 +++++++
security/landlock/hooks.c | 46 ++++++++
security/landlock/hooks.h | 5 +
security/landlock/init.c | 3 +-
security/landlock/providers.c | 261 ++++++++++++++++++++++++++++++++++++++++++
11 files changed, 415 insertions(+), 3 deletions(-)
create mode 100644 include/linux/landlock.h
create mode 100644 security/landlock/providers.c

diff --git a/include/linux/landlock.h b/include/linux/landlock.h
new file mode 100644
index 000000000000..c5c929931a1f
--- /dev/null
+++ b/include/linux/landlock.h
@@ -0,0 +1,42 @@
+/*
+ * Landlock LSM - public kernel headers
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_LANDLOCK_H
+#define _LINUX_LANDLOCK_H
+
+#include <linux/errno.h>
+#include <linux/sched.h> /* task_struct */
+
+#ifdef CONFIG_SECURITY_LANDLOCK
+struct landlock_events;
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+extern int landlock_seccomp_prepend_rule(unsigned int flags,
+ const char __user *user_bpf_fd);
+extern void put_seccomp_landlock(struct task_struct *tsk);
+extern void get_seccomp_landlock(struct task_struct *tsk);
+#else /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
+static inline int landlock_seccomp_prepend_rule(unsigned int flags,
+ const char __user *user_bpf_fd)
+{
+ return -EINVAL;
+}
+static inline void put_seccomp_landlock(struct task_struct *tsk)
+{
+ return;
+}
+static inline void get_seccomp_landlock(struct task_struct *tsk)
+{
+ return;
+}
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
+
+#endif /* _LINUX_LANDLOCK_H */
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index ecc296c137cd..4c9010b40fd5 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -7,6 +7,7 @@

#ifdef CONFIG_SECCOMP

+#include <linux/landlock.h>
#include <linux/thread_info.h>
#include <asm/seccomp.h>

@@ -18,6 +19,7 @@ struct seccomp_filter;
* system calls available to a process.
* @filter: must always point to a valid seccomp-filter or NULL as it is
* accessed without locking during system call entry.
+ * @landlock_events: contains an array of Landlock rules.
*
* @filter must only be accessed from the context of current as there
* is no read locking.
@@ -25,6 +27,9 @@ struct seccomp_filter;
struct seccomp {
int mode;
struct seccomp_filter *filter;
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+ struct landlock_events *landlock_events;
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
};

#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index 0f238a43ff1e..c1355805a06d 100644
--- a/include/uapi/linux/seccomp.h
+++ b/include/uapi/linux/seccomp.h
@@ -13,6 +13,7 @@
/* Valid operations for seccomp syscall. */
#define SECCOMP_SET_MODE_STRICT 0
#define SECCOMP_SET_MODE_FILTER 1
+#define SECCOMP_PREPEND_LANDLOCK_RULE 2

/* Valid flags for SECCOMP_SET_MODE_FILTER */
#define SECCOMP_FILTER_FLAG_TSYNC 1
diff --git a/kernel/fork.c b/kernel/fork.c
index e075b7780421..f1ad3694cd8a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -47,6 +47,7 @@
#include <linux/security.h>
#include <linux/hugetlb.h>
#include <linux/seccomp.h>
+#include <linux/landlock.h>
#include <linux/swap.h>
#include <linux/syscalls.h>
#include <linux/jiffies.h>
@@ -377,6 +378,7 @@ void free_task(struct task_struct *tsk)
rt_mutex_debug_task_free(tsk);
ftrace_graph_exit_task(tsk);
put_seccomp_filter(tsk);
+ put_seccomp_landlock(tsk);
arch_release_task_struct(tsk);
if (tsk->flags & PF_KTHREAD)
free_kthread_struct(tsk);
@@ -546,7 +548,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
* the usage counts on the error path calling free_task.
*/
tsk->seccomp.filter = NULL;
-#endif
+#ifdef CONFIG_SECURITY_LANDLOCK
+ tsk->seccomp.landlock_events = NULL;
+#endif /* CONFIG_SECURITY_LANDLOCK */
+#endif /* CONFIG_SECCOMP */

setup_thread_stack(tsk, orig);
clear_user_return_notifier(tsk);
@@ -1427,6 +1432,7 @@ static void copy_seccomp(struct task_struct *p)

/* Ref-count the new filter user, and assign it. */
get_seccomp_filter(current);
+ get_seccomp_landlock(current);
p->seccomp = current->seccomp;

/*
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 98b59b5db90b..0c65a61aa756 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -34,6 +34,7 @@
#include <linux/security.h>
#include <linux/tracehook.h>
#include <linux/uaccess.h>
+#include <linux/landlock.h>

/**
* struct seccomp_filter - container for seccomp BPF programs
@@ -805,6 +806,8 @@ static long do_seccomp(unsigned int op, unsigned int flags,
return seccomp_set_mode_strict();
case SECCOMP_SET_MODE_FILTER:
return seccomp_set_mode_filter(flags, uargs);
+ case SECCOMP_PREPEND_LANDLOCK_RULE:
+ return landlock_seccomp_prepend_rule(flags, uargs);
default:
return -EINVAL;
}
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index b382be409b3b..8153b024ffd7 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -5,4 +5,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function

obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

-landlock-y := init.o hooks.o hooks_fs.o
+landlock-y := init.o providers.o hooks.o hooks_fs.o
diff --git a/security/landlock/common.h b/security/landlock/common.h
index a69c35231d35..2a50a71d9954 100644
--- a/security/landlock/common.h
+++ b/security/landlock/common.h
@@ -11,6 +11,9 @@
#ifndef _SECURITY_LANDLOCK_COMMON_H
#define _SECURITY_LANDLOCK_COMMON_H

+#include <linux/bpf.h> /* enum landlock_subtype_event */
+#include <linux/refcount.h> /* refcount_t */
+
/*
* This is not intended for the UAPI headers. Each userland software should use
* a static minimal ABI for the required features as explained in the
@@ -20,4 +23,43 @@

#define LANDLOCK_NAME "landlock"

+// TODO: change name to not collide with UAPI
+struct landlock_rule {
+ refcount_t usage;
+ struct landlock_rule *prev;
+ struct bpf_prog *prog;
+};
+
+/**
+ * struct landlock_events - Landlock event rules enforced on a thread
+ *
+ * This is used for low performance impact when forking a process. Instead of
+ * copying the full array and incrementing the usage of each entries, only
+ * create a pointer to &struct landlock_events and increments its usage. When
+ * prepending a new rule, if &struct landlock_events is shared with other
+ * tasks, then duplicate it and prepend the rule to this new &struct
+ * landlock_events.
+ *
+ * @usage: reference count to manage the object lifetime. When a thread need to
+ * add Landlock rules and if @usage is greater than 1, then the thread
+ * must duplicate &struct landlock_events to not change the children's
+ * rules as well.
+ * @rules: array of non-NULL &struct landlock_rule pointers
+ */
+struct landlock_events {
+ refcount_t usage;
+ struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST];
+};
+
+/**
+ * get_index - get an index for the rules of struct landlock_events
+ *
+ * @event: a Landlock event type
+ */
+static inline int get_index(enum landlock_subtype_event event)
+{
+ /* event ID > 0 for loaded programs */
+ return event - 1;
+}
+
#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
index b48caeb0a49a..444927e72ff1 100644
--- a/security/landlock/hooks.c
+++ b/security/landlock/hooks.c
@@ -15,6 +15,7 @@
#include <linux/rculist.h> /* list_add_tail_rcu */
#include <linux/stddef.h> /* offsetof */

+#include "common.h" /* struct landlock_rule, get_index() */
#include "hooks.h" /* CTX_ARG_NB */


@@ -74,10 +75,55 @@ bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
return true;
}

+/**
+ * landlock_event_deny - run Landlock rules tied to an event
+ *
+ * @event_idx: event index in the rules array
+ * @ctx: non-NULL eBPF context
+ * @events: Landlock events pointer
+ *
+ * Return true if at least one rule deny the event.
+ */
+static bool landlock_event_deny(u32 event_idx, const struct landlock_context *ctx,
+ struct landlock_events *events)
+{
+ struct landlock_rule *rule;
+
+ if (!events)
+ return false;
+
+ for (rule = events->rules[event_idx]; rule; rule = rule->prev) {
+ u32 ret;
+
+ if (WARN_ON(!rule->prog))
+ continue;
+ rcu_read_lock();
+ ret = BPF_PROG_RUN(rule->prog, (void *)ctx);
+ rcu_read_unlock();
+ /* deny access if a program returns a value different than 0 */
+ if (ret)
+ return true;
+ }
+ return false;
+}
+
int landlock_decide(enum landlock_subtype_event event,
__u64 ctx_values[CTX_ARG_NB], const char *hook)
{
bool deny = false;
+ u32 event_idx = get_index(event);
+
+ struct landlock_context ctx = {
+ .status = 0,
+ .event = event,
+ .arg1 = ctx_values[0],
+ .arg2 = ctx_values[1],
+ };
+
+#ifdef CONFIG_SECCOMP_FILTER
+ deny = landlock_event_deny(event_idx, &ctx,
+ current->seccomp.landlock_events);
+#endif /* CONFIG_SECCOMP_FILTER */

return deny ? -EPERM : 0;
}
diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
index 51957211b67d..ad1cc967b06e 100644
--- a/security/landlock/hooks.h
+++ b/security/landlock/hooks.h
@@ -12,6 +12,7 @@
#include <linux/bpf.h> /* enum bpf_access_type */
#include <linux/lsm_hooks.h>
#include <linux/sched.h> /* struct task_struct */
+#include <linux/seccomp.h>

/* separators */
#define SEP_COMMA() ,
@@ -163,7 +164,11 @@ WRAP_TYPE_RAW_C;

static inline bool landlocked(const struct task_struct *task)
{
+#ifdef CONFIG_SECCOMP_FILTER
+ return !!(task->seccomp.landlock_events);
+#else
return false;
+#endif /* CONFIG_SECCOMP_FILTER */
}

__init void landlock_register_hooks(struct security_hook_list *hooks, int count);
diff --git a/security/landlock/init.c b/security/landlock/init.c
index 1e6660fed697..81f373f7cc52 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -120,6 +120,7 @@ const struct bpf_verifier_ops bpf_landlock_ops = {

void __init landlock_add_hooks(void)
{
- pr_info("%s: ABI %u", LANDLOCK_NAME, LANDLOCK_ABI);
+ pr_info("%s: ABI %u, ready to sandbox with %s\n",
+ LANDLOCK_NAME, LANDLOCK_ABI, "seccomp");
landlock_add_hooks_fs();
}
diff --git a/security/landlock/providers.c b/security/landlock/providers.c
new file mode 100644
index 000000000000..e37458f984bc
--- /dev/null
+++ b/security/landlock/providers.c
@@ -0,0 +1,261 @@
+/*
+ * Landlock LSM - seccomp provider
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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/barrier.h> /* smp_store_release() */
+#include <asm/page.h> /* PAGE_SIZE */
+#include <linux/bpf.h> /* bpf_prog_put() */
+#include <linux/err.h> /* ERR_PTR() */
+#include <linux/errno.h>
+#include <linux/filter.h> /* struct bpf_prog */
+#include <linux/kernel.h> /* round_up() */
+#include <linux/landlock.h>
+#include <linux/refcount.h> /* refcount_t() */
+#include <linux/sched.h> /* current_cred(), task_no_new_privs() */
+#include <linux/security.h> /* security_capable_noaudit() */
+#include <linux/slab.h> /* alloc(), kfree() */
+#include <linux/uaccess.h> /* get_user() */
+
+#include "common.h" /* struct landlock_rule */
+
+static void put_landlock_rule(struct landlock_rule *rule)
+{
+ struct landlock_rule *orig = rule;
+
+ /* clean up single-reference branches iteratively */
+ while (orig && refcount_dec_and_test(&orig->usage)) {
+ struct landlock_rule *freeme = orig;
+
+ bpf_prog_put(orig->prog);
+ orig = orig->prev;
+ kfree(freeme);
+ }
+}
+
+static void put_landlock_events(struct landlock_events *events)
+{
+ if (events && refcount_dec_and_test(&events->usage)) {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(events->rules); i++)
+ /* XXX: Do we need to use lockless_dereference() here? */
+ put_landlock_rule(events->rules[i]);
+ kfree(events);
+ }
+}
+
+static struct landlock_events *new_landlock_events(void)
+{
+ struct landlock_events *ret;
+
+ /* array filled with NULL values */
+ ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+ refcount_set(&ret->usage, 1);
+ return ret;
+}
+
+static void add_landlock_rule(struct landlock_events *events,
+ struct landlock_rule *rule)
+{
+ /* subtype.landlock_rule.event > 0 for loaded programs */
+ u32 event_idx = get_index(rule->prog->subtype.landlock_rule.event);
+
+ rule->prev = events->rules[event_idx];
+ WARN_ON(refcount_read(&rule->usage));
+ refcount_set(&rule->usage, 1);
+ /* do not increment the previous rule usage */
+ smp_store_release(&events->rules[event_idx], rule);
+}
+
+/* limit Landlock events to 256KB */
+#define LANDLOCK_EVENTS_MAX_PAGES (1 << 6)
+
+/**
+ * landlock_prepend_rule - attach a Landlock rule to @current_events
+ *
+ * @current_events: landlock_events pointer, must be locked (if needed) to
+ * prevent a concurrent put/free. This pointer must not be
+ * freed after the call.
+ * @prog: non-NULL Landlock rule to prepend to @current_events. @prog will be
+ * owned by landlock_prepend_rule() and freed if an error happened.
+ *
+ * Return @current_events or a new pointer when OK. Return a pointer error
+ * otherwise.
+ */
+static struct landlock_events *landlock_prepend_rule(
+ struct landlock_events *current_events, struct bpf_prog *prog)
+{
+ struct landlock_events *new_events = current_events;
+ unsigned long pages;
+ struct landlock_rule *rule;
+ u32 event_idx;
+
+ if (prog->type != BPF_PROG_TYPE_LANDLOCK_RULE) {
+ new_events = ERR_PTR(-EINVAL);
+ goto put_prog;
+ }
+
+ /* validate memory size allocation */
+ pages = prog->pages;
+ if (current_events) {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(current_events->rules); i++) {
+ struct landlock_rule *walker_r;
+
+ for (walker_r = current_events->rules[i]; walker_r;
+ walker_r = walker_r->prev)
+ pages += walker_r->prog->pages;
+ }
+ /* count a struct landlock_events if we need to allocate one */
+ if (refcount_read(&current_events->usage) != 1)
+ pages += round_up(sizeof(*current_events), PAGE_SIZE) /
+ PAGE_SIZE;
+ }
+ if (pages > LANDLOCK_EVENTS_MAX_PAGES) {
+ new_events = ERR_PTR(-E2BIG);
+ goto put_prog;
+ }
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (!rule) {
+ new_events = ERR_PTR(-ENOMEM);
+ goto put_prog;
+ }
+ rule->prog = prog;
+
+ /* subtype.landlock_rule.event > 0 for loaded programs */
+ event_idx = get_index(rule->prog->subtype.landlock_rule.event);
+
+ /*
+ * Each task_struct points to an array of rule list pointers. These
+ * tables are duplicated when additions are made (which means each
+ * table needs to be refcounted for the processes using it). When a new
+ * table is created, all the refcounters on the rules are bumped (to
+ * track each table that references the rule). When a new rule is
+ * added, it's just prepended to the list for the new table to point
+ * at.
+ */
+ if (!new_events) {
+ /*
+ * If there is no Landlock events used by the current task,
+ * then create a new one.
+ */
+ new_events = new_landlock_events();
+ if (IS_ERR(new_events))
+ goto put_rule;
+ } else if (refcount_read(&current_events->usage) > 1) {
+ /*
+ * If the current task is not the sole user of its Landlock
+ * events, then duplicate them.
+ */
+ size_t i;
+
+ new_events = new_landlock_events();
+ if (IS_ERR(new_events))
+ goto put_rule;
+ for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) {
+ new_events->rules[i] =
+ lockless_dereference(current_events->rules[i]);
+ if (new_events->rules[i])
+ refcount_inc(&new_events->rules[i]->usage);
+ }
+
+ /*
+ * Landlock events from the current task will not be freed here
+ * because the usage is strictly greater than 1. It is only
+ * prevented to be freed by another subject thanks to the
+ * caller of landlock_prepend_rule() which should be locked if
+ * needed.
+ */
+ put_landlock_events(current_events);
+ }
+ add_landlock_rule(new_events, rule);
+ return new_events;
+
+put_prog:
+ bpf_prog_put(prog);
+ return new_events;
+
+put_rule:
+ put_landlock_rule(rule);
+ return new_events;
+}
+
+#ifdef CONFIG_SECCOMP_FILTER
+
+/**
+ * landlock_seccomp_prepend_rule - attach a Landlock rule to the current
+ * process
+ *
+ * current->seccomp.landlock_events is lazily allocated. When a process fork,
+ * only a pointer is copied. When a new event is added by a process, if there
+ * is other references to this process' landlock_events, then a new allocation
+ * is made to contain an array pointing to Landlock rule lists. This design
+ * enable low-performance impact and is memory efficient while keeping the
+ * property of prepend-only rules.
+ *
+ * For now, installing a Landlock rule requires that the requesting task has
+ * the global CAP_SYS_ADMIN. We cannot force the use of no_new_privs to not
+ * exclude containers where a process may legitimately acquire more privileges
+ * thanks to an SUID binary.
+ *
+ * @flags: not used for now, but could be used for TSYNC
+ * @user_bpf_fd: file descriptor pointing to a loaded Landlock rule
+ */
+int landlock_seccomp_prepend_rule(unsigned int flags,
+ const char __user *user_bpf_fd)
+{
+ struct landlock_events *new_events;
+ struct bpf_prog *prog;
+ int bpf_fd, err;
+
+ /* planned to be replaced with a no_new_privs check to allow
+ * unprivileged tasks */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ /* enable to check if Landlock is supported with early EFAULT */
+ if (!user_bpf_fd)
+ return -EFAULT;
+ if (flags)
+ return -EINVAL;
+ err = get_user(bpf_fd, user_bpf_fd);
+ if (err)
+ return err;
+ prog = bpf_prog_get(bpf_fd);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ /*
+ * We don't need to lock anything for the current process hierarchy,
+ * everything is guarded by the atomic counters.
+ */
+ new_events = landlock_prepend_rule(current->seccomp.landlock_events,
+ prog);
+ /* @prog is managed/freed by landlock_prepend_rule() */
+ if (IS_ERR(new_events))
+ return PTR_ERR(new_events);
+ current->seccomp.landlock_events = new_events;
+ return 0;
+}
+
+void put_seccomp_landlock(struct task_struct *tsk)
+{
+ put_landlock_events(tsk->seccomp.landlock_events);
+}
+
+void get_seccomp_landlock(struct task_struct *tsk)
+{
+ if (tsk->seccomp.landlock_events)
+ refcount_inc(&tsk->seccomp.landlock_events->usage);
+}
+
+#endif /* CONFIG_SECCOMP_FILTER */
--
2.14.1

2017-08-21 00:10:58

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 08/10] bpf: Add a Landlock sandbox example

Add a basic sandbox tool to create a process isolated from some part of
the system. This sandbox create a read-only environment. It is only
allowed to write to a character device such as a TTY:

# :> X
# echo $?
0
# ./samples/bpf/landlock1 /bin/sh -i
Launching a new sandboxed process.
# :> Y
cannot create Y: Operation not permitted

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
---

Changes since v6:
* check return value of load_and_attach()
* allow to write on pipes
* rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE
* rename Landlock version to ABI to better reflect its purpose
* use const variable (suggested by Kees Cook)
* remove useless definitions (suggested by Kees Cook)
* add detailed explanations (suggested by Kees Cook)

Changes since v5:
* cosmetic fixes
* rebase

Changes since v4:
* write Landlock rule in C and compiled it with LLVM
* remove cgroup handling
* remove path handling: only handle a read-only environment
* remove errno return codes

Changes since v3:
* remove seccomp and origin field: completely free from seccomp programs
* handle more FS-related hooks
* handle inode hooks and directory traversal
* add faked but consistent view thanks to ENOENT
* add /lib64 in the example
* fix spelling
* rename some types and definitions (e.g. SECCOMP_ADD_LANDLOCK_RULE)

Changes since v2:
* use BPF_PROG_ATTACH for cgroup handling
---
samples/bpf/Makefile | 4 ++
samples/bpf/bpf_load.c | 28 ++++++++++--
samples/bpf/landlock1_kern.c | 100 +++++++++++++++++++++++++++++++++++++++++++
samples/bpf/landlock1_user.c | 100 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 229 insertions(+), 3 deletions(-)
create mode 100644 samples/bpf/landlock1_kern.c
create mode 100644 samples/bpf/landlock1_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index f1010fe759fe..08d5d728e3e0 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -40,6 +40,7 @@ hostprogs-y += load_sock_ops
hostprogs-y += xdp_redirect
hostprogs-y += xdp_redirect_map
hostprogs-y += syscall_tp
+hostprogs-y += landlock1

# Libbpf dependencies
LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -84,6 +85,7 @@ per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o
xdp_redirect-objs := bpf_load.o $(LIBBPF) xdp_redirect_user.o
xdp_redirect_map-objs := bpf_load.o $(LIBBPF) xdp_redirect_map_user.o
syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
+landlock1-objs := bpf_load.o $(LIBBPF) landlock1_user.o

# Tell kbuild to always build the programs
always := $(hostprogs-y)
@@ -128,6 +130,7 @@ always += tcp_clamp_kern.o
always += xdp_redirect_kern.o
always += xdp_redirect_map_kern.o
always += syscall_tp_kern.o
+always += landlock1_kern.o

HOSTCFLAGS += -I$(objtree)/usr/include
HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -167,6 +170,7 @@ HOSTLOADLIBES_test_map_in_map += -lelf
HOSTLOADLIBES_xdp_redirect += -lelf
HOSTLOADLIBES_xdp_redirect_map += -lelf
HOSTLOADLIBES_syscall_tp += -lelf
+HOSTLOADLIBES_landlock1 += -lelf

# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 01a506f768da..30fcddda8b81 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -31,6 +31,8 @@

static char license[128];
static int kern_version;
+static union bpf_prog_subtype subtype = {};
+static bool has_subtype;
static bool processed_sec[128];
char bpf_log_buf[BPF_LOG_BUF_SIZE];
int map_fd[MAX_MAPS];
@@ -66,6 +68,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0;
bool is_sockops = strncmp(event, "sockops", 7) == 0;
bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
+ bool is_landlock = strncmp(event, "landlock", 8) == 0;
size_t insns_cnt = size / sizeof(struct bpf_insn);
enum bpf_prog_type prog_type;
char buf[256];
@@ -96,6 +99,13 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
prog_type = BPF_PROG_TYPE_SOCK_OPS;
} else if (is_sk_skb) {
prog_type = BPF_PROG_TYPE_SK_SKB;
+ } else if (is_landlock) {
+ prog_type = BPF_PROG_TYPE_LANDLOCK_RULE;
+ if (!has_subtype) {
+ printf("No subtype\n");
+ return -1;
+ }
+ st = &subtype;
} else {
printf("Unknown event '%s'\n", event);
return -1;
@@ -110,7 +120,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)

prog_fd[prog_cnt++] = fd;

- if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
+ if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
+ is_landlock)
return 0;

if (is_socket || is_sockops || is_sk_skb) {
@@ -454,6 +465,7 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
kern_version = 0;
memset(license, 0, sizeof(license));
memset(processed_sec, 0, sizeof(processed_sec));
+ has_subtype = false;

if (elf_version(EV_CURRENT) == EV_NONE)
return 1;
@@ -502,6 +514,16 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
data_maps = data;
for (j = 0; j < MAX_MAPS; j++)
map_data[j].fd = -1;
+ } else if (strcmp(shname, "subtype") == 0) {
+ processed_sec[i] = true;
+ if (data->d_size != sizeof(union bpf_prog_subtype)) {
+ printf("invalid size of subtype section %zd\n",
+ data->d_size);
+ return 1;
+ }
+ memcpy(&subtype, data->d_buf,
+ sizeof(union bpf_prog_subtype));
+ has_subtype = true;
} else if (shdr.sh_type == SHT_SYMTAB) {
strtabidx = shdr.sh_link;
symbols = data;
@@ -562,7 +584,6 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)

/* load programs */
for (i = 1; i < ehdr.e_shnum; i++) {
-
if (processed_sec[i])
continue;

@@ -577,7 +598,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
memcmp(shname, "socket", 6) == 0 ||
memcmp(shname, "cgroup/", 7) == 0 ||
memcmp(shname, "sockops", 7) == 0 ||
- memcmp(shname, "sk_skb", 6) == 0) {
+ memcmp(shname, "sk_skb", 6) == 0 ||
+ memcmp(shname, "landlock", 8) == 0) {
ret = load_and_attach(shname, data->d_buf,
data->d_size);
if (ret != 0)
diff --git a/samples/bpf/landlock1_kern.c b/samples/bpf/landlock1_kern.c
new file mode 100644
index 000000000000..4ed80d8eee4e
--- /dev/null
+++ b/samples/bpf/landlock1_kern.c
@@ -0,0 +1,100 @@
+/*
+ * Landlock rule - partial read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+/*
+ * This file contains a function that will be compiled to eBPF bytecode thanks
+ * to LLVM/Clang.
+ *
+ * Each SEC() means that the following function or variable will be part of a
+ * custom ELF section. This sections are then processed by the userspace part
+ * (see landlock1_user.c) to extract eBPF bytecode and take into account
+ * variables describing the eBPF program subtype or its license.
+ */
+
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/stat.h> /* S_ISCHR() */
+#include "bpf_helpers.h"
+
+/*
+ * The function landlock_fs_prog1() is a simple Landlock rule enforced on a set
+ * of processes. This rule will be run for each file-system operations and will
+ * then forbid any write on a file-descriptor except if this file-descriptor
+ * point to a pipe. Hence, it will not be possible to create new files nor to
+ * modify a regular file.
+ *
+ * The argument ctx contains the context of the rule when it is run, which
+ * enable to check which action on which file is requested. This context can
+ * change for each run of the rule.
+ */
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+ char fmt_error_mode[] = "landlock1: error: get_mode:%lld\n";
+ char fmt_error_access[] = "landlock1: error: access denied\n";
+ long long ret;
+
+ /*
+ * The argument ctx->arg2 contains bitflags of actions for which the
+ * rule is run. The flag LANDLOCK_ACTION_FS_WRITE means that a write
+ * is requested by one of the userspace processes restricted by this
+ * rule. The following test allows any actions which does not include a
+ * write.
+ */
+ if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
+ return 0;
+
+ /*
+ * The argument ctx->arg1 is a file handle for which the process want
+ * to access. The function bpf_handle_fs_get_mode() return the mode of
+ * a file (e.g. S_IFBLK, S_IFDIR, S_IFREG...). If there is an error,
+ * for example if the argument is not a file handle, then an
+ * -errno value is returned. Otherwise the caller get the file mode as
+ * with stat(2).
+ */
+ ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
+ if (ret < 0) {
+
+ /*
+ * The bpf_trace_printk() function enable to write in the
+ * kernel eBPF debug log, accessible through
+ * /sys/kernel/debug/tracing/trace_pipe . To be allowed to call
+ * this function, a Landlock rule must have the
+ * LANDLOCK_SUBTYPE_ABILITY_DEBUG ability, which is only
+ * allowed for CAP_SYS_ADMIN.
+ */
+ bpf_trace_printk(fmt_error_mode, sizeof(fmt_error_mode), ret);
+ return 1;
+ }
+
+ /*
+ * This check allows the action on the file if it is a directory or a
+ * pipe. Otherwise, a message is printed to the eBPF log.
+ */
+ if (S_ISCHR(ret) || S_ISFIFO(ret))
+ return 0;
+ bpf_trace_printk(fmt_error_access, sizeof(fmt_error_access));
+ return 1;
+}
+
+/*
+ * This subtype enable to set the ABI, which ensure that the eBPF context and
+ * program behavior will be compatible with this Landlock rule.
+ */
+SEC("subtype")
+static const union bpf_prog_subtype _subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ .ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
+ }
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/samples/bpf/landlock1_user.c b/samples/bpf/landlock1_user.c
new file mode 100644
index 000000000000..27435dc4acb1
--- /dev/null
+++ b/samples/bpf/landlock1_user.c
@@ -0,0 +1,100 @@
+/*
+ * Landlock sandbox - partial read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 "bpf_load.h"
+#include "libbpf.h"
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h> /* open() */
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/prctl.h>
+#include <linux/seccomp.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#ifndef seccomp
+static int seccomp(unsigned int op, unsigned int flags, void *args)
+{
+ errno = 0;
+ return syscall(__NR_seccomp, op, flags, args);
+}
+#endif
+
+
+struct landlock_rule {
+ enum landlock_subtype_event event;
+ struct bpf_insn *bpf;
+ size_t size;
+};
+
+static int apply_sandbox(int prog_fd)
+{
+ int ret = 0;
+
+ /* safer to set no_new_privs */
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ perror("prctl(no_new_priv)");
+ return 1;
+ }
+ /* set up the test sandbox */
+ if (seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &prog_fd)) {
+ perror("seccomp(set_hook)");
+ ret = 1;
+ }
+ close(prog_fd);
+
+ return ret;
+}
+
+int main(int argc, char * const argv[], char * const *envp)
+{
+ char filename[256];
+ char *cmd_path;
+ char * const *cmd_argv;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]);
+ fprintf(stderr, "Launch a command in a read-only environment "
+ "(except for character devices).\n");
+ fprintf(stderr, "Display debug with: "
+ "cat /sys/kernel/debug/tracing/trace_pipe &\n");
+ return 1;
+ }
+
+ snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+ if (load_bpf_file(filename)) {
+ printf("%s", bpf_log_buf);
+ return 1;
+ }
+ if (!prog_fd[0]) {
+ if (errno) {
+ printf("load_bpf_file: %s\n", strerror(errno));
+ } else {
+ printf("load_bpf_file: Error\n");
+ }
+ return 1;
+ }
+
+ if (apply_sandbox(prog_fd[0]))
+ return 1;
+ cmd_path = argv[1];
+ cmd_argv = argv + 1;
+ fprintf(stderr, "Launching a new sandboxed process.\n");
+ execve(cmd_path, cmd_argv, envp);
+ perror("execve");
+ return 1;
+}
--
2.14.1

2017-08-21 00:11:46

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem

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.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
---

Changes since v6:
* add 3 more sub-events: IOCTL, LOCK, FCNTL
https://lkml.kernel.org/r/[email protected]
* use the new security_add_hooks()
* explain the -Werror=unused-function
* constify pointers
* cleanup headers

Changes since v5:
* split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
* add more documentation
* cosmetic fixes
* rebase (SCALAR_VALUE)

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
---
include/linux/lsm_hooks.h | 5 +
security/landlock/Makefile | 7 +-
security/landlock/common.h | 2 +
security/landlock/hooks.c | 83 ++++++
security/landlock/hooks.h | 177 +++++++++++++
security/landlock/hooks_fs.c | 586 +++++++++++++++++++++++++++++++++++++++++++
security/landlock/hooks_fs.h | 19 ++
security/landlock/init.c | 10 +
security/security.c | 12 +-
9 files changed, 899 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 3a90febadbe2..7614c3d66265 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1982,5 +1982,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..b382be409b3b 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,3 +1,8 @@
+# Catch defined but unused hooks, e.g. error out if a HOOK_NEW_FS(foo) is not
+# used with a HOOK_INIT_FS(foo) in the struct security_hook_list
+# landlock_hooks.
+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/common.h b/security/landlock/common.h
index c82cbd3fb640..a69c35231d35 100644
--- a/security/landlock/common.h
+++ b/security/landlock/common.h
@@ -18,4 +18,6 @@
*/
#define LANDLOCK_ABI 1

+#define LANDLOCK_NAME "landlock"
+
#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
new file mode 100644
index 000000000000..b48caeb0a49a
--- /dev/null
+++ b/security/landlock/hooks.c
@@ -0,0 +1,83 @@
+/*
+ * Landlock LSM - hook helpers
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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, struct landlock_context */
+#include <linux/errno.h>
+#include <linux/filter.h> /* BPF_PROG_RUN() */
+#include <linux/rculist.h> /* list_add_tail_rcu */
+#include <linux/stddef.h> /* offsetof */
+
+#include "hooks.h" /* CTX_ARG_NB */
+
+
+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],
+ const 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, status):
+ case offsetof(struct landlock_context, event):
+ 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 = SCALAR_VALUE;
+ }
+
+ /* check memory range access */
+ switch (*reg_type) {
+ case NOT_INIT:
+ return false;
+ case SCALAR_VALUE:
+ /* 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], const char *hook)
+{
+ bool deny = false;
+
+ return deny ? -EPERM : 0;
+}
diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
new file mode 100644
index 000000000000..51957211b67d
--- /dev/null
+++ b/security/landlock/hooks.h
@@ -0,0 +1,177 @@
+/*
+ * Landlock LSM - hooks helpers
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 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, \
+ const 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__) \
+ }; \
+ return landlock_decide(LANDLOCK_SUBTYPE_EVENT_##EVENT, \
+ _ctx_values, #HOOK); \
+ } \
+ } \
+ extern void __consistent_##EVENT(MAP_EVENT_COMMA( \
+ NH, NE, GET_EVENT_C, __VA_ARGS__))
+
+#define HOOK_INIT(EVENT, HOOK, ID) \
+ LSM_HOOK_INIT(HOOK, landlock_hook_##EVENT##_##HOOK##_##ID)
+
+/*
+ * 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 SCALAR_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],
+ const union bpf_prog_subtype *prog_subtype);
+
+int landlock_decide(enum landlock_subtype_event event,
+ __u64 ctx_values[CTX_ARG_NB], const char *hook);
diff --git a/security/landlock/hooks_fs.c b/security/landlock/hooks_fs.c
new file mode 100644
index 000000000000..fa80b35a269d
--- /dev/null
+++ b/security/landlock/hooks_fs.c
@@ -0,0 +1,586 @@
+/*
+ * Landlock LSM - filesystem hooks
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 "common.h"
+#include "hooks.h"
+#include "hooks_fs.h"
+
+
+#define HOOK_NEW_FS(...) HOOK_NEW(1, FS, 2, __VA_ARGS__)
+#define HOOK_NEW_FS2(...) HOOK_NEW(2, FS, 2, __VA_ARGS__)
+#define HOOK_NEW_FS3(...) HOOK_NEW(3, FS, 2, __VA_ARGS__)
+#define HOOK_NEW_FS4(...) HOOK_NEW(4, FS, 2, __VA_ARGS__)
+
+#define HOOK_INIT_FS(HOOK) HOOK_INIT(FS, HOOK, 1)
+#define HOOK_INIT_FS2(HOOK) HOOK_INIT(FS, HOOK, 2)
+#define HOOK_INIT_FS3(HOOK) HOOK_INIT(FS, HOOK, 3)
+#define HOOK_INIT_FS4(HOOK) HOOK_INIT(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(file_ioctl, 3,
+ struct file *, file,
+ unsigned int, cmd,
+ unsigned long, arg,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_IOCTL
+);
+
+HOOK_NEW(1, FS_IOCTL, 2, file_ioctl, 3,
+ struct file *, file,
+ unsigned int, cmd,
+ unsigned long, arg,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, cmd
+);
+
+HOOK_NEW_FS(file_lock, 2,
+ struct file *, file,
+ unsigned int, cmd,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_LOCK
+);
+
+HOOK_NEW(1, FS_LOCK, 2, file_lock, 2,
+ struct file *, file,
+ unsigned int, cmd,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, cmd
+);
+
+HOOK_NEW_FS(file_fcntl, 3,
+ struct file *, file,
+ unsigned int, cmd,
+ unsigned long, arg,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, LANDLOCK_ACTION_FS_FCNTL
+);
+
+HOOK_NEW(1, FS_FCNTL, 2, file_fcntl, 3,
+ struct file *, file,
+ unsigned int, cmd,
+ unsigned long, arg,
+ WRAP_ARG_FILE, file,
+ WRAP_ARG_RAW, 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_IOCTL, file_ioctl, 1),
+ HOOK_INIT_FS(file_lock),
+ HOOK_INIT(FS_LOCK, file_lock, 1),
+ HOOK_INIT_FS(file_fcntl),
+ HOOK_INIT(FS_FCNTL, file_fcntl, 1),
+ 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)
+{
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), LANDLOCK_NAME);
+}
diff --git a/security/landlock/hooks_fs.h b/security/landlock/hooks_fs.h
new file mode 100644
index 000000000000..aab07c018f38
--- /dev/null
+++ b/security/landlock/hooks_fs.h
@@ -0,0 +1,19 @@
+/*
+ * Landlock LSM - filesystem hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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,
+ const 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 09acbc74abd6..1e6660fed697 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -10,8 +10,10 @@

#include <linux/bpf.h> /* enum bpf_access_type */
#include <linux/capability.h> /* capable */
+#include <linux/lsm_hooks.h>

#include "common.h" /* LANDLOCK_* */
+#include "hooks_fs.h"


static inline bool bpf_landlock_is_valid_access(int off, int size,
@@ -23,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,
+ &info->reg_type, prog_subtype);
case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
default:
return false;
@@ -113,3 +117,9 @@ const struct bpf_verifier_ops bpf_landlock_ops = {
.is_valid_access = bpf_landlock_is_valid_access,
.is_valid_subtype = bpf_landlock_is_valid_subtype,
};
+
+void __init landlock_add_hooks(void)
+{
+ pr_info("%s: ABI %u", LANDLOCK_NAME, LANDLOCK_ABI);
+ landlock_add_hooks_fs();
+}
diff --git a/security/security.c b/security/security.c
index 30132378d103..000d95d53902 100644
--- a/security/security.c
+++ b/security/security.c
@@ -75,10 +75,20 @@ 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.
+ *
+ * For an unprivileged access-control, we don't want to give the
+ * ability to any process to do some checks (e.g. through an eBPF
+ * program) on kernel objects (e.g. files) if a privileged security
+ * policy forbid their access.
+ */
+ landlock_add_hooks();
+
return 0;
}

--
2.14.1

2017-08-21 00:12:31

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier

The goal of the program subtype is to be able to have different static
fine-grained verifications for a unique program type.

The struct bpf_verifier_ops gets a new optional function:
is_valid_subtype(). This new verifier is called at the beginning of the
eBPF program verification to check if the (optional) program subtype is
valid.

For now, only Landlock eBPF programs are using a program subtype (see
next commit) but this could be used by other program types in the future.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
---

Changes since v6:
* rename Landlock version to ABI to better reflect its purpose
* fix unsigned integer checks
* fix pointer cast
* constify pointers
* rebase

Changes since v5:
* use a prog_subtype pointer and make it future-proof
* add subtype test
* constify bpf_load_program()'s subtype argument
* cleanup subtype initialization
* rebase

Changes since v4:
* replace the "status" field with "version" (more generic)
* replace the "access" field with "ability" (less confusing)

Changes since v3:
* remove the "origin" field
* add an "option" field
* cleanup comments
---
include/linux/bpf.h | 7 ++-
include/linux/filter.h | 2 +
include/uapi/linux/bpf.h | 11 +++++
kernel/bpf/syscall.c | 22 ++++++++-
kernel/bpf/verifier.c | 17 +++++--
kernel/trace/bpf_trace.c | 15 ++++--
net/core/filter.c | 71 ++++++++++++++++++-----------
samples/bpf/bpf_load.c | 3 +-
samples/bpf/cookie_uid_helper_example.c | 2 +-
samples/bpf/fds_example.c | 2 +-
samples/bpf/sock_example.c | 3 +-
samples/bpf/test_cgrp2_attach.c | 2 +-
samples/bpf/test_cgrp2_attach2.c | 2 +-
samples/bpf/test_cgrp2_sock.c | 2 +-
tools/include/uapi/linux/bpf.h | 11 +++++
tools/lib/bpf/bpf.c | 10 +++-
tools/lib/bpf/bpf.h | 5 +-
tools/lib/bpf/libbpf.c | 4 +-
tools/perf/tests/bpf.c | 2 +-
tools/testing/selftests/bpf/test_align.c | 2 +-
tools/testing/selftests/bpf/test_tag.c | 2 +-
tools/testing/selftests/bpf/test_verifier.c | 17 ++++++-
22 files changed, 158 insertions(+), 56 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 830f472d8df5..aef2e6f6d763 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -159,13 +159,15 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)

struct bpf_verifier_ops {
/* return eBPF function prototype for verification */
- const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id);
+ const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype);

/* return true if 'size' wide access at offset 'off' within bpf_context
* with 'type' (read or write) is allowed
*/
bool (*is_valid_access)(int off, int size, enum bpf_access_type type,
- struct bpf_insn_access_aux *info);
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype);
int (*gen_prologue)(struct bpf_insn *insn, bool direct_write,
const struct bpf_prog *prog);
u32 (*convert_ctx_access)(enum bpf_access_type type,
@@ -174,6 +176,7 @@ struct bpf_verifier_ops {
struct bpf_prog *prog, u32 *target_size);
int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr,
union bpf_attr __user *uattr);
+ bool (*is_valid_subtype)(const union bpf_prog_subtype *prog_subtype);
};

struct bpf_prog_aux {
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 7015116331af..0c3fadbb5a58 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -464,6 +464,8 @@ struct bpf_prog {
u32 len; /* Number of filter blocks */
u32 jited_len; /* Size of jited insns in bytes */
u8 tag[BPF_TAG_SIZE];
+ u8 has_subtype;
+ union bpf_prog_subtype subtype; /* Fine-grained verifications */
struct bpf_prog_aux *aux; /* Auxiliary fields */
struct sock_fprog_kern *orig_prog; /* Original BPF program */
unsigned int (*bpf_func)(const void *ctx,
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 843818dff96d..8541ab85e432 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -177,6 +177,15 @@ enum bpf_attach_type {
/* Specify numa node during map creation */
#define BPF_F_NUMA_NODE (1U << 2)

+union bpf_prog_subtype {
+ struct {
+ __u32 abi; /* minimal ABI version, cf. user doc */
+ __u32 event; /* enum landlock_subtype_event */
+ __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+ __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
+ } landlock_rule;
+} __attribute__((aligned(8)));
+
union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */
@@ -212,6 +221,8 @@ union bpf_attr {
__aligned_u64 log_buf; /* user supplied buffer */
__u32 kern_version; /* checked when prog_type=kprobe */
__u32 prog_flags;
+ __aligned_u64 prog_subtype; /* bpf_prog_subtype address */
+ __u32 prog_subtype_size;
};

struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 9378f3ba2cbf..6b663dd786c6 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -970,7 +970,7 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type)
EXPORT_SYMBOL_GPL(bpf_prog_get_type);

/* last field in 'union bpf_attr' used by this command */
-#define BPF_PROG_LOAD_LAST_FIELD prog_flags
+#define BPF_PROG_LOAD_LAST_FIELD prog_subtype_size

static int bpf_prog_load(union bpf_attr *attr)
{
@@ -1034,6 +1034,26 @@ static int bpf_prog_load(union bpf_attr *attr)
if (err < 0)
goto free_prog;

+ /* copy eBPF program subtype from user space */
+ if (attr->prog_subtype) {
+ u32 size;
+
+ err = check_uarg_tail_zero(u64_to_user_ptr(attr->prog_subtype),
+ sizeof(prog->subtype),
+ attr->prog_subtype_size);
+ if (err)
+ goto free_prog;
+ size = min_t(u32, attr->prog_subtype_size, sizeof(prog->subtype));
+
+ /* prog->subtype is __GFP_ZERO */
+ if (copy_from_user(&prog->subtype,
+ u64_to_user_ptr(attr->prog_subtype), size)
+ != 0)
+ return -EFAULT;
+ prog->has_subtype = 1;
+ } else if (attr->prog_subtype_size != 0)
+ return -EINVAL;
+
/* run eBPF verifier */
err = bpf_check(&prog, attr);
if (err < 0)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e42c096ba20d..74933f5aba0e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -952,7 +952,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
return 0;

if (env->prog->aux->ops->is_valid_access &&
- env->prog->aux->ops->is_valid_access(off, size, t, &info)) {
+ env->prog->aux->ops->is_valid_access(off, size, t, &info,
+ &env->prog->subtype)) {
/* A non zero info.ctx_field_size indicates that this field is a
* candidate for later verifier transformation to load the whole
* field and then apply a mask when accessed with a narrower
@@ -962,7 +963,6 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
*/
env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
*reg_type = info.reg_type;
-
/* remember the offset of last byte accessed in ctx */
if (env->prog->aux->max_ctx_offset < off + size)
env->prog->aux->max_ctx_offset = off + size;
@@ -1636,7 +1636,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
}

if (env->prog->aux->ops->get_func_proto)
- fn = env->prog->aux->ops->get_func_proto(func_id);
+ fn = env->prog->aux->ops->get_func_proto(func_id,
+ &env->prog->subtype);

if (!fn) {
verbose("unknown func %s#%d\n", func_id_name(func_id), func_id);
@@ -4190,7 +4191,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
}

patch_call_imm:
- fn = prog->aux->ops->get_func_proto(insn->imm);
+ fn = prog->aux->ops->get_func_proto(insn->imm, &prog->subtype);
/* all functions that have prototype and verifier allowed
* programs to call them, must be real in-kernel functions
*/
@@ -4233,6 +4234,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
struct bpf_verifier_env *env;
int ret = -EINVAL;

+ if ((*prog)->aux->ops->is_valid_subtype) {
+ if (!(*prog)->aux->ops->is_valid_subtype(&(*prog)->subtype))
+ return -EINVAL;
+ } else if ((*prog)->has_subtype) {
+ /* do not accept a subtype if the program does not handle it */
+ return -EINVAL;
+ }
+
/* 'struct bpf_verifier_env' can be global, but since it's not small,
* allocate/free it every time bpf_check() is called
*/
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index dc498b605d5d..33e9e05a3220 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -492,7 +492,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
}
}

-static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -506,7 +507,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func

/* bpf+kprobe programs can access fields of 'struct pt_regs' */
static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (off < 0 || off >= sizeof(struct pt_regs))
return false;
@@ -576,7 +578,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
.arg3_type = ARG_ANYTHING,
};

-static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -589,7 +592,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
}

static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
return false;
@@ -608,7 +612,8 @@ const struct bpf_verifier_ops tracepoint_prog_ops = {
};

static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
const int size_sp = FIELD_SIZEOF(struct bpf_perf_event_data,
sample_period);
diff --git a/net/core/filter.c b/net/core/filter.c
index fa2115695037..dc4a75aa635f 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3111,7 +3111,8 @@ static const struct bpf_func_proto bpf_setsockopt_proto = {
};

static const struct bpf_func_proto *
-bpf_base_func_proto(enum bpf_func_id func_id)
+bpf_base_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
@@ -3139,7 +3140,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
}

static const struct bpf_func_proto *
-sk_filter_func_proto(enum bpf_func_id func_id)
+sk_filter_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_load_bytes:
@@ -3149,12 +3151,13 @@ sk_filter_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_get_socket_uid:
return &bpf_get_socket_uid_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

static const struct bpf_func_proto *
-tc_cls_act_func_proto(enum bpf_func_id func_id)
+tc_cls_act_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
@@ -3216,12 +3219,13 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_get_socket_uid:
return &bpf_get_socket_uid_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

static const struct bpf_func_proto *
-xdp_func_proto(enum bpf_func_id func_id)
+xdp_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -3235,12 +3239,13 @@ xdp_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_redirect_map:
return &bpf_redirect_map_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

static const struct bpf_func_proto *
-lwt_inout_func_proto(enum bpf_func_id func_id)
+lwt_inout_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_load_bytes:
@@ -3262,12 +3267,13 @@ lwt_inout_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_skb_under_cgroup:
return &bpf_skb_under_cgroup_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

static const struct bpf_func_proto *
- sock_ops_func_proto(enum bpf_func_id func_id)
+ sock_ops_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_setsockopt:
@@ -3275,11 +3281,13 @@ static const struct bpf_func_proto *
case BPF_FUNC_sock_map_update:
return &bpf_sock_map_update_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

-static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *
+sk_skb_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
@@ -3299,12 +3307,13 @@ static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_sk_redirect_map:
return &bpf_sk_redirect_map_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}

static const struct bpf_func_proto *
-lwt_xmit_func_proto(enum bpf_func_id func_id)
+lwt_xmit_func_proto(enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_get_tunnel_key:
@@ -3334,12 +3343,13 @@ lwt_xmit_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_set_hash_invalid:
return &bpf_set_hash_invalid_proto;
default:
- return lwt_inout_func_proto(func_id);
+ return lwt_inout_func_proto(func_id, prog_subtype);
}
}

static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
const int size_default = sizeof(__u32);

@@ -3381,7 +3391,8 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type

static bool sk_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (off) {
case bpf_ctx_range(struct __sk_buff, tc_classid):
@@ -3400,12 +3411,13 @@ static bool sk_filter_is_valid_access(int off, int size,
}
}

- return bpf_skb_is_valid_access(off, size, type, info);
+ return bpf_skb_is_valid_access(off, size, type, info, prog_subtype);
}

static bool lwt_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
switch (off) {
case bpf_ctx_range(struct __sk_buff, tc_classid):
@@ -3433,12 +3445,13 @@ static bool lwt_is_valid_access(int off, int size,
break;
}

- return bpf_skb_is_valid_access(off, size, type, info);
+ return bpf_skb_is_valid_access(off, size, type, info, prog_subtype);
}

static bool sock_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3507,7 +3520,8 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write,

static bool tc_cls_act_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3533,7 +3547,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
return false;
}

- return bpf_skb_is_valid_access(off, size, type, info);
+ return bpf_skb_is_valid_access(off, size, type, info, prog_subtype);
}

static bool __is_valid_xdp_access(int off, int size)
@@ -3550,7 +3564,8 @@ static bool __is_valid_xdp_access(int off, int size)

static bool xdp_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE)
return false;
@@ -3593,7 +3608,8 @@ static bool __is_valid_sock_ops_access(int off, int size)

static bool sock_ops_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3616,7 +3632,8 @@ static int sk_skb_prologue(struct bpf_insn *insn_buf, bool direct_write,

static bool sk_skb_is_valid_access(int off, int size,
enum bpf_access_type type,
- struct bpf_insn_access_aux *info)
+ struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3640,7 +3657,7 @@ static bool sk_skb_is_valid_access(int off, int size,
break;
}

- return bpf_skb_is_valid_access(off, size, type, info);
+ return bpf_skb_is_valid_access(off, size, type, info, prog_subtype);
}

static u32 bpf_convert_ctx_access(enum bpf_access_type type,
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 6aa50098dfb8..01a506f768da 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -71,6 +71,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
char buf[256];
int fd, efd, err, id;
struct perf_event_attr attr = {};
+ union bpf_prog_subtype *st = NULL;

attr.type = PERF_TYPE_TRACEPOINT;
attr.sample_type = PERF_SAMPLE_RAW;
@@ -101,7 +102,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
}

fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, st);
if (fd < 0) {
printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf);
return -1;
diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c
index 9d751e209f31..df457c07d35d 100644
--- a/samples/bpf/cookie_uid_helper_example.c
+++ b/samples/bpf/cookie_uid_helper_example.c
@@ -159,7 +159,7 @@ static void prog_load(void)
};
prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
ARRAY_SIZE(prog), "GPL", 0,
- log_buf, sizeof(log_buf));
+ log_buf, sizeof(log_buf), NULL);
if (prog_fd < 0)
error(1, errno, "failed to load prog\n%s\n", log_buf);
}
diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c
index e29bd52ff9e8..0f4f5f6a9f9f 100644
--- a/samples/bpf/fds_example.c
+++ b/samples/bpf/fds_example.c
@@ -62,7 +62,7 @@ static int bpf_prog_create(const char *object)
} else {
return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
insns, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}
}

diff --git a/samples/bpf/sock_example.c b/samples/bpf/sock_example.c
index 6fc6e193ef1b..3778f66deb76 100644
--- a/samples/bpf/sock_example.c
+++ b/samples/bpf/sock_example.c
@@ -60,7 +60,8 @@ static int test_sock(void)
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);

prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt,
- "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+ "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE,
+ NULL);
if (prog_fd < 0) {
printf("failed to load prog '%s'\n", strerror(errno));
goto cleanup;
diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c
index 4bfcaf93fcf3..f8a91d2b7896 100644
--- a/samples/bpf/test_cgrp2_attach.c
+++ b/samples/bpf/test_cgrp2_attach.c
@@ -72,7 +72,7 @@ static int prog_load(int map_fd, int verdict)

return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
prog, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}

static int usage(const char *argv0)
diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c
index 3049b1f26267..31a0f4bd665f 100644
--- a/samples/bpf/test_cgrp2_attach2.c
+++ b/samples/bpf/test_cgrp2_attach2.c
@@ -45,7 +45,7 @@ static int prog_load(int verdict)

ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
prog, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);

if (ret < 0) {
log_err("Loading program");
diff --git a/samples/bpf/test_cgrp2_sock.c b/samples/bpf/test_cgrp2_sock.c
index c3cfb23e23b5..697f2db30e6a 100644
--- a/samples/bpf/test_cgrp2_sock.c
+++ b/samples/bpf/test_cgrp2_sock.c
@@ -38,7 +38,7 @@ static int prog_load(int idx)
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);

return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt,
- "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+ "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}

static int usage(const char *argv0)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index f8f6377fd541..2c0dc9d58ea0 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -180,6 +180,15 @@ enum bpf_sockmap_flags {
/* Specify numa node during map creation */
#define BPF_F_NUMA_NODE (1U << 2)

+union bpf_prog_subtype {
+ struct {
+ __u32 abi; /* minimal ABI version, cf. user doc */
+ __u32 event; /* enum landlock_subtype_event */
+ __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+ __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
+ } landlock_rule;
+} __attribute__((aligned(8)));
+
union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */
@@ -215,6 +224,8 @@ union bpf_attr {
__aligned_u64 log_buf; /* user supplied buffer */
__u32 kern_version; /* checked when prog_type=kprobe */
__u32 prog_flags;
+ __aligned_u64 prog_subtype; /* bpf_prog_subtype address */
+ __u32 prog_subtype_size;
};

struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index a0717610b116..a11cace703f3 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -116,7 +116,8 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,

int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
- __u32 kern_version, char *log_buf, size_t log_buf_sz)
+ __u32 kern_version, char *log_buf, size_t log_buf_sz,
+ const union bpf_prog_subtype *subtype)
{
int fd;
union bpf_attr attr;
@@ -130,6 +131,8 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
attr.log_size = 0;
attr.log_level = 0;
attr.kern_version = kern_version;
+ attr.prog_subtype = ptr_to_u64(subtype);
+ attr.prog_subtype_size = subtype ? sizeof(*subtype) : 0;

fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd >= 0 || !log_buf || !log_buf_sz)
@@ -146,7 +149,8 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, int strict_alignment,
const char *license, __u32 kern_version,
- char *log_buf, size_t log_buf_sz, int log_level)
+ char *log_buf, size_t log_buf_sz, int log_level,
+ const union bpf_prog_subtype *subtype)
{
union bpf_attr attr;

@@ -160,6 +164,8 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
attr.log_level = log_level;
log_buf[0] = 0;
attr.kern_version = kern_version;
+ attr.prog_subtype = ptr_to_u64(subtype);
+ attr.prog_subtype_size = subtype ? sizeof(*subtype) : 0;
attr.prog_flags = strict_alignment ? BPF_F_STRICT_ALIGNMENT : 0;

return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 90e9d4e85d08..bc66469cd1a4 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -40,11 +40,12 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
__u32 kern_version, char *log_buf,
- size_t log_buf_sz);
+ size_t log_buf_sz, const union bpf_prog_subtype *subtype);
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, int strict_alignment,
const char *license, __u32 kern_version,
- char *log_buf, size_t log_buf_sz, int log_level);
+ char *log_buf, size_t log_buf_sz, int log_level,
+ const union bpf_prog_subtype *subtype);

int bpf_map_update_elem(int fd, const void *key, const void *value,
__u64 flags);
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1cc3ea0ffdc3..ff59af4c1282 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -995,7 +995,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
pr_warning("Alloc log buffer for bpf loader error, continue without log\n");

ret = bpf_load_program(type, insns, insns_cnt, license,
- kern_version, log_buf, BPF_LOG_BUF_SIZE);
+ kern_version, log_buf, BPF_LOG_BUF_SIZE, NULL);

if (ret >= 0) {
*pfd = ret;
@@ -1022,7 +1022,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,

fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
insns_cnt, license, kern_version,
- NULL, 0);
+ NULL, 0, NULL);
if (fd >= 0) {
close(fd);
ret = -LIBBPF_ERRNO__PROGTYPE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 5876da126b58..a9585470275e 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -310,7 +310,7 @@ static int check_env(void)

err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
sizeof(insns) / sizeof(insns[0]),
- license, kver_int, NULL, 0);
+ license, kver_int, NULL, 0, NULL);
if (err < 0) {
pr_err("Missing basic BPF support, skip this test: %s\n",
strerror(errno));
diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c
index 8591c89c0828..46d528936b04 100644
--- a/tools/testing/selftests/bpf/test_align.c
+++ b/tools/testing/selftests/bpf/test_align.c
@@ -629,7 +629,7 @@ static int do_test_single(struct bpf_align_test *test)
prog_len = probe_filter_length(prog);
fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
prog, prog_len, 1, "GPL", 0,
- bpf_vlog, sizeof(bpf_vlog), 2);
+ bpf_vlog, sizeof(bpf_vlog), 2, NULL);
if (fd_prog < 0 && test->result != REJECT) {
printf("Failed to load program.\n");
printf("%s", bpf_vlog);
diff --git a/tools/testing/selftests/bpf/test_tag.c b/tools/testing/selftests/bpf/test_tag.c
index de409fc50c35..cf7892c87b5a 100644
--- a/tools/testing/selftests/bpf/test_tag.c
+++ b/tools/testing/selftests/bpf/test_tag.c
@@ -57,7 +57,7 @@ static int bpf_try_load_prog(int insns, int fd_map,

bpf_filler(insns, fd_map);
fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0,
- NULL, 0);
+ NULL, 0, NULL);
assert(fd_prog > 0);
if (fd_map > 0)
bpf_filler(insns, 0);
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index c03542c417db..3146839a51bf 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -68,6 +68,8 @@ struct bpf_test {
} result, result_unpriv;
enum bpf_prog_type prog_type;
uint8_t flags;
+ bool has_prog_subtype;
+ union bpf_prog_subtype prog_subtype;
};

/* Note we want this to be 64 bit aligned so that the end of our array is
@@ -6487,6 +6489,16 @@ static struct bpf_test tests[] = {
.result = REJECT,
.prog_type = BPF_PROG_TYPE_LWT_IN,
},
+ {
+ "superfluous subtype",
+ .insns = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .errstr = "",
+ .result = REJECT,
+ .has_prog_subtype = true,
+ },
};

static int probe_filter_length(const struct bpf_insn *fp)
@@ -6602,6 +6614,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
int map_fds[MAX_NR_MAPS];
const char *expected_err;
int i;
+ union bpf_prog_subtype *prog_subtype =
+ test->has_prog_subtype ? &test->prog_subtype : NULL;

for (i = 0; i < MAX_NR_MAPS; i++)
map_fds[i] = -1;
@@ -6610,7 +6624,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,

fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
prog, prog_len, test->flags & F_LOAD_WITH_STRICT_ALIGNMENT,
- "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 1);
+ "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 1,
+ prog_subtype);

expected_ret = unpriv && test->result_unpriv != UNDEF ?
test->result_unpriv : test->result;
--
2.14.1

2017-08-21 00:12:55

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 10/10] landlock: Add user and kernel documentation for Landlock

This documentation can be built with the Sphinx framework.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
---

Changes since v6:
* add a check for ctx->event
* rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE
* rename Landlock version to ABI to better reflect its purpose and add a
dedicated changelog section
* update tables
* relax no_new_privs recommendations
* remove ABILITY_WRITE related functions
* reword rule "appending" to "prepending" and explain it
* cosmetic fixes

Changes since v5:
* update the rule hierarchy inheritance explanation
* briefly explain ctx->arg2
* add ptrace restrictions
* explain EPERM
* update example (subtype)
* use ":manpage:"
---
Documentation/security/index.rst | 1 +
Documentation/security/landlock/index.rst | 19 ++
Documentation/security/landlock/kernel.rst | 132 ++++++++++++
Documentation/security/landlock/user.rst | 313 +++++++++++++++++++++++++++++
4 files changed, 465 insertions(+)
create mode 100644 Documentation/security/landlock/index.rst
create mode 100644 Documentation/security/landlock/kernel.rst
create mode 100644 Documentation/security/landlock/user.rst

diff --git a/Documentation/security/index.rst b/Documentation/security/index.rst
index 298a94a33f05..1db294025d0f 100644
--- a/Documentation/security/index.rst
+++ b/Documentation/security/index.rst
@@ -11,3 +11,4 @@ Security Documentation
LSM
self-protection
tpm/index
+ landlock/index
diff --git a/Documentation/security/landlock/index.rst b/Documentation/security/landlock/index.rst
new file mode 100644
index 000000000000..8afde6a5805c
--- /dev/null
+++ b/Documentation/security/landlock/index.rst
@@ -0,0 +1,19 @@
+=========================================
+Landlock LSM: programmatic access control
+=========================================
+
+Landlock is a stackable Linux Security Module (LSM) that makes it possible to
+create security sandboxes. This kind of sandbox is expected to help mitigate
+the security impact of bugs or unexpected/malicious behaviors in user-space
+applications. The current version allows only a process with the global
+CAP_SYS_ADMIN capability to create such sandboxes but the ultimate goal of
+Landlock is to empower any process, including unprivileged ones, to securely
+restrict themselves. Landlock is inspired by seccomp-bpf but instead of
+filtering syscalls and their raw arguments, a Landlock rule can inspect the use
+of kernel objects like files and hence make a decision according to the kernel
+semantic.
+
+.. toctree::
+
+ user
+ kernel
diff --git a/Documentation/security/landlock/kernel.rst b/Documentation/security/landlock/kernel.rst
new file mode 100644
index 000000000000..560711835ce8
--- /dev/null
+++ b/Documentation/security/landlock/kernel.rst
@@ -0,0 +1,132 @@
+==============================
+Landlock: kernel documentation
+==============================
+
+eBPF properties
+===============
+
+To get an expressive language while still being safe and small, Landlock is
+based on eBPF. Landlock should be usable by untrusted processes and must
+therefore expose a minimal attack surface. The eBPF bytecode is minimal,
+powerful, widely used and designed to be used by untrusted applications. Thus,
+reusing the eBPF support in the kernel enables a generic approach while
+minimizing new code.
+
+An eBPF program has access to an eBPF context containing some fields including
+event arguments (i.e. arg1 and arg2). These arguments can be used directly or
+passed to helper functions according to their types. It is then possible to do
+complex access checks without race conditions or inconsistent evaluation (i.e.
+`incorrect mirroring of the OS code and state
+<https://www.internetsociety.org/doc/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools>`_).
+
+A Landlock event describes a particular access type. For now, there is only
+one event type dedicated to filesystem related operations:
+LANDLOCK_SUBTYPE_EVENT_FS. A Landlock rule is tied to one event type. This
+makes it possible to statically check context accesses, potentially performed
+by such rule, and hence prevents kernel address leaks and ensure the right use
+of event arguments with eBPF functions. Any user can add multiple Landlock
+rules per Landlock event. They are stacked and evaluated one after the other,
+starting from the most recent rule, as seccomp-bpf does with its filters.
+Underneath, an event is an abstraction over a set of LSM hooks.
+
+
+Guiding principles
+==================
+
+Unprivileged use
+----------------
+
+* Everything potentially security sensitive which is exposed to a Landlock
+ rule, through functions or context, shall have an associated ability flag to
+ specify which kind of privilege a process must have to load such a rule.
+* Every ability flag expresses a semantic goal (e.g. debug, process
+ introspection, process modification) potentially tied to a set of
+ capabilities.
+* Landlock helpers and context should be usable by any unprivileged and
+ untrusted rule while following the system security policy enforced by other
+ access control mechanisms (e.g. DAC, LSM).
+
+
+Landlock event and context
+--------------------------
+
+* A Landlock event shall be focused on access control on kernel objects instead
+ of syscall filtering (i.e. syscall arguments), which is the purpose of
+ seccomp-bpf.
+* A Landlock context provided by an event shall express the minimal interface
+ to control an access for a kernel object. This can be achieved by wrapping
+ this raw object (e.g. file, inode, path, dentry) with an abstract
+ representation (i.e. handle) for userland/bpfland.
+* An evolution of a context's field (e.g. new flags in the status field) shall
+ only be activated for a rule if the ABI specified by the loading thread imply
+ this behavior. This makes it possible to ensure that the rule code make
+ sense (e.g. only watch flags which may be activated).
+* An event type shall guaranty that all the BPF function calls from a rule are
+ safe. Thus, the related Landlock context arguments shall always be of the
+ same type for a particular event type. For example, a network event could
+ share helpers with a file event because of UNIX socket. However, the same
+ helpers may not be compatible for a FS handle and a net handle.
+* Multiple event types may use the same context interface.
+
+
+Landlock helpers
+----------------
+
+* Landlock helpers shall be as generic as possible (i.e. using handles) while
+ at the same time being as simple as possible and following the syscall
+ creation principles (cf. *Documentation/adding-syscalls.txt*).
+* The only behavior change allowed on a helper is to fix a (logical) bug to
+ match the initial semantic.
+* Helpers shall be reentrant, i.e. only take inputs from arguments (e.g. from
+ the BPF context) or from the current thread, to allow an event type to use a
+ cache. Future rule options might change this cache behavior (e.g. invalidate
+ cache after some time).
+* It is quite easy to add new helpers to extend Landlock. The main concern
+ should be about the possibility to leak information from a landlocked process
+ to another (e.g. through maps) to not reproduce the same security sensitive
+ behavior as :manpage:`ptrace(2).`
+
+
+Rule addition and propagation
+=============================
+
+See :ref:`Documentation/security/landlock/user <inherited_rules>` for the
+intended goal of rule propagation.
+
+Structure definitions
+---------------------
+
+.. kernel-doc:: security/landlock/common.h
+
+
+Functions for rule addition
+---------------------------
+
+.. kernel-doc:: security/landlock/providers.c
+
+
+Questions and answers
+=====================
+
+Why not create a custom event type for each kind of action?
+-----------------------------------------------------------
+
+Landlock rules can handle these checks. Adding more exceptions to the kernel
+code would lead to more code complexity. A decision to ignore a kind of action
+can and should be done at the beginning of a Landlock rule.
+
+
+Why a rule does not return an errno or a kill code?
+---------------------------------------------------
+
+seccomp filters can return multiple kind of code, including an errno value or a
+kill signal, which may be convenient for access control. Those return codes
+are hardwired in the userland ABI. Instead, Landlock's approach is to return a
+boolean to allow or deny an action, which is much simpler and more generic.
+Moreover, we do not really have a choice because, unlike to seccomp, Landlock
+rules are not enforced at the syscall entry point but may be executed at any
+point in the kernel (through LSM hooks) where an errno return code may not make
+sense. However, with this simple ABI and with the ability to call helpers,
+Landlock may gain features similar to seccomp-bpf in the future while being
+compatible with previous rules.
+
diff --git a/Documentation/security/landlock/user.rst b/Documentation/security/landlock/user.rst
new file mode 100644
index 000000000000..00a792d6b1cc
--- /dev/null
+++ b/Documentation/security/landlock/user.rst
@@ -0,0 +1,313 @@
+================================
+Landlock: userland documentation
+================================
+
+Landlock rules
+==============
+
+eBPF programs are used to create security rules. They are contained and can
+call only a whitelist of dedicated functions. Moreover, they cannot loop, which
+protects from denial of service. More information on BPF can be found in
+*Documentation/networking/filter.txt*.
+
+
+Writing a rule
+--------------
+
+To enforce a security policy, a thread first needs to create a Landlock rule.
+The easiest way to write an eBPF program depicting a security rule is to write
+it in the C language. As described in *samples/bpf/README.rst*, LLVM can
+compile such programs. Files *samples/bpf/landlock1_kern.c* and those in
+*tools/testing/selftests/landlock/rules/* can be used as examples. The
+following example is a simple rule to forbid file creation, whatever syscall
+may be used (e.g. open, mkdir, link...). The *ctx->event* contains the event
+ID for which the rule is actually ran. The *ctx->arg2* contains the action
+type performed on the file (cf. :ref:`fs_actions`).
+
+.. code-block:: c
+
+ static int deny_file_creation(struct landlock_context *ctx)
+ {
+ if (ctx->event != LANDLOCK_SUBTYPE_EVENT_FS)
+ return 1;
+ if (ctx->arg2 & LANDLOCK_ACTION_FS_NEW)
+ return 1;
+ return 0;
+ }
+
+Once the eBPF program is created, the next step is to create the metadata
+describing the Landlock rule. This metadata includes a subtype which contains
+the minimal ABI version that must be supported by the kernel, the event to
+which the rule is tied, and optional Landlock rule abilities.
+
+.. code-block:: c
+
+ static union bpf_prog_subtype subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+ };
+
+The Landlock ABI is important to inform the kernel which features or behavior
+the rule can handle. The user-space thread should set the lowest possible ABI
+to be as compatible as possible with older kernels. For the list of features
+provided by ABI, see :ref:`features`.
+
+A Landlock event describes the kind of kernel object for which a rule will be
+triggered to allow or deny an action. For example, the event
+LANDLOCK_SUBTYPE_EVENT_FS is triggered every time a landlocked thread performs
+an action related to the filesystem (e.g. open, read, write, mount...).
+
+The Landlock rule abilities should only be used if the rule needs a specific
+feature such as debugging. This should be avoided if not strictly necessary.
+
+The next step is to fill a :c:type:`union bpf_attr <bpf_attr>` with
+BPF_PROG_TYPE_LANDLOCK_RULE, the previously created subtype and other BPF
+program metadata. This bpf_attr must then be passed to the :manpage:`bpf(2)`
+syscall alongside the BPF_PROG_LOAD command. If everything is deemed correct
+by the kernel, the thread gets a file descriptor referring to this rule.
+
+In the following code, the *insn* variable is an array of BPF instructions
+which can be extracted from an ELF file as is done in bpf_load_file() from
+*samples/bpf/bpf_load.c*.
+
+.. code-block:: c
+
+ union bpf_attr attr = {
+ .prog_type = BPF_PROG_TYPE_LANDLOCK_RULE,
+ .insn_cnt = sizeof(insn) / sizeof(struct bpf_insn),
+ .insns = (__u64) (unsigned long) insn,
+ .license = (__u64) (unsigned long) "GPL",
+ .prog_subtype = &subtype,
+ .prog_subtype_size = sizeof(subtype),
+ };
+ int rule = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (rule == -1)
+ exit(1);
+
+
+Enforcing a rule
+----------------
+
+Once the Landlock rule has been created or received (e.g. through a UNIX
+socket), the thread willing to sandbox itself (and its future children) should
+perform the following two steps to properly sandbox itself with a rule.
+
+The thread should first request to never be allowed to get new privileges with a
+call to :manpage:`prctl(2)` and the PR_SET_NO_NEW_PRIVS option. More
+information can be found in *Documentation/prctl/no_new_privs.txt*.
+
+.. code-block:: c
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0))
+ exit(1);
+
+A thread can apply a rule to itself by using the :manpage:`seccomp(2)` syscall.
+The operation is SECCOMP_PREPEND_LANDLOCK_RULE, the flags must be empty and the
+*args* argument must point to a valid Landlock rule file descriptor.
+
+.. code-block:: c
+
+ if (seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &rule))
+ exit(1);
+
+If the syscall succeeds, the rule is now enforced on the calling thread and
+will be enforced on all its subsequently created children of the thread as
+well. Once a thread is landlocked, there is no way to remove this security
+policy, only stacking more restrictions is allowed. The rule evaluation is
+performed from the newest to the oldest.
+
+When a syscall ask for an action on a kernel object, if this action is denied,
+then an EPERM errno code is returned through the syscall.
+
+
+.. _inherited_rules:
+
+Inherited rules
+---------------
+
+Every new thread resulting from a :manpage:`clone(2)` inherits Landlock rule
+restrictions from its parent. This is similar to the seccomp inheritance as
+described in *Documentation/prctl/seccomp_filter.txt*.
+
+
+Ptrace restrictions
+-------------------
+
+A landlocked process has less privileges than a non-landlocked process and must
+then be subject to additional restrictions when manipulating another process.
+To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target
+process, a landlocked process must have a subset of the target process rules.
+
+
+.. _features:
+
+Landlock features
+=================
+
+In order to support new features over time without changing a rule behavior,
+every context field, flag or helpers has a minimal Landlock ABI in which they
+are available. A thread needs to specify this minimal ABI number in the
+subtype :c:type:`struct landlock_rule <landlock_rule>` defined in
+*include/uapi/linux/bpf.h*.
+
+
+Context
+-------
+
+The arch and syscall_nr fields may be useful to tighten an access control, but
+care must be taken to avoid pitfalls as explain in
+*Documentation/prctl/seccomp_filter.txt*.
+
+.. kernel-doc:: include/uapi/linux/bpf.h
+ :functions: landlock_context
+
+
+Landlock event types
+--------------------
+
+.. kernel-doc:: include/uapi/linux/bpf.h
+ :functions: landlock_subtype_event
+
+.. flat-table:: Event types availability and optional arguments from :c:type:`struct landlock_context <landlock_context>`
+
+ * - Event type
+ - Minimal ABI
+ - Optional *arg1*
+ - Optional *arg2*
+
+ * - LANDLOCK_SUBTYPE_EVENT_FS
+ - 1
+ - filesystem handle
+ - :ref:`fs_actions`
+
+ * - LANDLOCK_SUBTYPE_EVENT_FS_IOCTL
+ - 1
+ - filesystem handle
+ - :manpage:`ioctl(2)` request
+
+ * - LANDLOCK_SUBTYPE_EVENT_FS_LOCK
+ - 1
+ - filesystem handle
+ - :manpage:`flock(2)` operation
+
+ * - LANDLOCK_SUBTYPE_EVENT_FS_FCNTL
+ - 1
+ - filesystem handle
+ - :manpage:`fcntl(2)` command
+
+
+.. _fs_actions:
+
+File system action types
+------------------------
+
+Flags are used to express actions. This makes it possible to compose actions
+and leaves room for future improvements to add more fine-grained action types.
+
+.. kernel-doc:: include/uapi/linux/bpf.h
+ :doc: landlock_action_fs
+
+.. flat-table:: FS action types availability
+
+ * - Action flag
+ - Minimal ABI
+
+ * - LANDLOCK_ACTION_FS_EXEC
+ - 1
+
+ * - LANDLOCK_ACTION_FS_WRITE
+ - 1
+
+ * - LANDLOCK_ACTION_FS_READ
+ - 1
+
+ * - LANDLOCK_ACTION_FS_NEW
+ - 1
+
+ * - LANDLOCK_ACTION_FS_GET
+ - 1
+
+ * - LANDLOCK_ACTION_FS_REMOVE
+ - 1
+
+ * - LANDLOCK_ACTION_FS_IOCTL
+ - 1
+
+ * - LANDLOCK_ACTION_FS_LOCK
+ - 1
+
+ * - LANDLOCK_ACTION_FS_FCNTL
+ - 1
+
+
+Ability types
+-------------
+
+The ability of a Landlock rule describes the available features (i.e. context
+fields and available helpers). This is useful to abstract user-space
+privileges for Landlock rules, which may not need all abilities (e.g. debug).
+Only the minimal set of abilities should be used (e.g. disable debug once in
+production).
+
+
+.. kernel-doc:: include/uapi/linux/bpf.h
+ :doc: landlock_subtype_ability
+
+.. flat-table:: Ability types availability
+
+ * - Ability type
+ - Minimal ABI
+ - Capability
+
+ * - LANDLOCK_SUBTYPE_ABILITY_DEBUG
+ - 1
+ - CAP_SYS_ADMIN
+
+
+Helper functions
+----------------
+
+See *include/uapi/linux/bpf.h* for functions documentation.
+
+.. flat-table:: Generic functions availability
+
+ * - Helper
+ - Minimal ABI
+ - Ability
+
+ * - bpf_get_current_comm
+ - 1
+ - LANDLOCK_SUBTYPE_ABILITY_DEBUG
+
+ * - bpf_get_current_pid_tgid
+ - 1
+ - LANDLOCK_SUBTYPE_ABILITY_DEBUG
+
+ * - bpf_get_current_uid_gid
+ - 1
+ - LANDLOCK_SUBTYPE_ABILITY_DEBUG
+
+ * - bpf_get_trace_printk
+ - 1
+ - LANDLOCK_SUBTYPE_ABILITY_DEBUG
+
+.. flat-table:: File system functions availability
+
+ * - Helper
+ - Minimal ABI
+ - Ability
+
+ * - bpf_handle_fs_get_mode
+ - 1
+ - (none)
+
+
+Landlock ABI changelog
+======================
+
+ABI 1
+-----
+
+Initial version.
--
2.14.1

2017-08-21 00:11:45

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 03/10] bpf,landlock: Define an eBPF program type for a Landlock rule

Add a new type of eBPF program used by Landlock rules.

This new BPF program type will be registered with the Landlock LSM
initialization.

Add an initial Landlock Kconfig.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
---

Changes since v6:
* add 3 more sub-events: IOCTL, LOCK, FCNTL
https://lkml.kernel.org/r/[email protected]
* rename LANDLOCK_VERSION to LANDLOCK_ABI to better reflect its purpose,
and move it from landlock.h to common.h
* rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE: an eBPF
program could be used for something else than a rule
* simplify struct landlock_context by removing the arch and syscall_nr fields
* remove all eBPF map functions call, remove ABILITY_WRITE
* refactor bpf_landlock_func_proto() (suggested by Kees Cook)
* constify pointers
* fix doc inclusion

Changes since v5:
* rename file hooks.c to init.c
* fix spelling

Changes since v4:
* merge a minimal (not enabled) LSM code and Kconfig in this commit

Changes since v3:
* split commit
* revamp the landlock_context:
* add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
cross-check action with the event type
* replace args array with dedicated fields to ease the addition of new
fields
---
include/linux/bpf_types.h | 3 ++
include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
security/Kconfig | 1 +
security/Makefile | 2 +
security/landlock/Kconfig | 18 ++++++++
security/landlock/Makefile | 3 ++
security/landlock/common.h | 21 +++++++++
security/landlock/init.c | 98 ++++++++++++++++++++++++++++++++++++++++++
tools/include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
9 files changed, 340 insertions(+)
create mode 100644 security/landlock/Kconfig
create mode 100644 security/landlock/Makefile
create mode 100644 security/landlock/common.h
create mode 100644 security/landlock/init.c

diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 6f1a567667b8..8bac93970a47 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -18,6 +18,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops)
BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops)
BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops)
#endif
+#ifdef CONFIG_SECURITY_LANDLOCK
+BPF_PROG_TYPE(BPF_PROG_TYPE_LANDLOCK_RULE, bpf_landlock_ops)
+#endif

BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 8541ab85e432..20da634da941 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -129,6 +129,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_LWT_XMIT,
BPF_PROG_TYPE_SOCK_OPS,
BPF_PROG_TYPE_SK_SKB,
+ BPF_PROG_TYPE_LANDLOCK_RULE,
};

enum bpf_attach_type {
@@ -879,4 +880,100 @@ enum {
#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */

+/**
+ * enum landlock_subtype_event - event occurring when an action is performed on
+ * a particular kernel object
+ *
+ * An event is a policy decision point which exposes the same context type
+ * (especially the same arg[0-9] field types) for each rule execution.
+ *
+ * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
+ * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_IOCTL: custom IOCTL sub-event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_LOCK: custom LOCK sub-event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_FCNTL: custom FCNTL sub-event
+ */
+enum landlock_subtype_event {
+ LANDLOCK_SUBTYPE_EVENT_UNSPEC,
+ LANDLOCK_SUBTYPE_EVENT_FS,
+ LANDLOCK_SUBTYPE_EVENT_FS_IOCTL,
+ LANDLOCK_SUBTYPE_EVENT_FS_LOCK,
+ LANDLOCK_SUBTYPE_EVENT_FS_FCNTL,
+};
+#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS_FCNTL
+
+/**
+ * DOC: landlock_subtype_ability
+ *
+ * eBPF context and functions allowed for a rule
+ *
+ * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
+ * logs), which may be dangerous and should only be used for rule testing
+ */
+#define LANDLOCK_SUBTYPE_ABILITY_DEBUG (1ULL << 0)
+#define _LANDLOCK_SUBTYPE_ABILITY_NB 1
+#define _LANDLOCK_SUBTYPE_ABILITY_MASK ((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)
+
+/*
+ * Future options for a Landlock rule (e.g. run even if a previous rule denied
+ * an action).
+ */
+#define _LANDLOCK_SUBTYPE_OPTION_NB 0
+#define _LANDLOCK_SUBTYPE_OPTION_MASK ((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)
+
+/*
+ * Status visible in the @status field of a context (e.g. already called in
+ * this syscall session, with same args...).
+ *
+ * The @status field exposed to a rule shall depend on the rule version.
+ */
+#define _LANDLOCK_SUBTYPE_STATUS_NB 0
+#define _LANDLOCK_SUBTYPE_STATUS_MASK ((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)
+
+/**
+ * DOC: landlock_action_fs
+ *
+ * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
+ * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
+ * include mount actions)
+ * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
+ * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
+ * - %LANDLOCK_ACTION_FS_GET: open or receive a file
+ * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
+ *
+ * Each of the following actions are specific to syscall multiplexers. Each of
+ * them trigger a dedicated Landlock event where their command can be read.
+ *
+ * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
+ * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
+ * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
+ */
+#define LANDLOCK_ACTION_FS_EXEC (1ULL << 0)
+#define LANDLOCK_ACTION_FS_WRITE (1ULL << 1)
+#define LANDLOCK_ACTION_FS_READ (1ULL << 2)
+#define LANDLOCK_ACTION_FS_NEW (1ULL << 3)
+#define LANDLOCK_ACTION_FS_GET (1ULL << 4)
+#define LANDLOCK_ACTION_FS_REMOVE (1ULL << 5)
+#define LANDLOCK_ACTION_FS_IOCTL (1ULL << 6)
+#define LANDLOCK_ACTION_FS_LOCK (1ULL << 7)
+#define LANDLOCK_ACTION_FS_FCNTL (1ULL << 8)
+#define _LANDLOCK_ACTION_FS_NB 9
+#define _LANDLOCK_ACTION_FS_MASK ((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
+
+
+/**
+ * struct landlock_context - context accessible to a Landlock rule
+ *
+ * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
+ * @event: event type (&enum landlock_subtype_event)
+ * @arg1: event's first optional argument
+ * @arg2: event's second optional argument
+ */
+struct landlock_context {
+ __u64 status;
+ __u64 event;
+ __u64 arg1;
+ __u64 arg2;
+};
+
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/security/Kconfig b/security/Kconfig
index e8e449444e65..2dd023c90bcd 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -211,6 +211,7 @@ source security/tomoyo/Kconfig
source security/apparmor/Kconfig
source security/loadpin/Kconfig
source security/yama/Kconfig
+source security/landlock/Kconfig

source security/integrity/Kconfig

diff --git a/security/Makefile b/security/Makefile
index f2d71cdb8e19..3fdc2f19dc48 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
+subdir-$(CONFIG_SECURITY_LANDLOCK) += landlock

# always enable default capabilities
obj-y += commoncap.o
@@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
+obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o

# Object integrity file lists
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
new file mode 100644
index 000000000000..aa5808e116f1
--- /dev/null
+++ b/security/landlock/Kconfig
@@ -0,0 +1,18 @@
+config SECURITY_LANDLOCK
+ bool "Landlock sandbox support"
+ depends on SECURITY
+ depends on BPF_SYSCALL
+ depends on SECCOMP_FILTER
+ default y
+ help
+ Landlock is a stackable LSM which allows to load a security policy to
+ restrict processes (i.e. create a sandbox). The policy is a list of
+ stacked eBPF programs, called rules, dedicated to restrict access to
+ a type of kernel object (e.g. file).
+
+ You need to enable seccomp filter to apply a security policy to a
+ process hierarchy (e.g. application with built-in sandboxing).
+
+ See Documentation/security/landlock/ for further information.
+
+ If you are unsure how to answer this question, answer Y.
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
new file mode 100644
index 000000000000..7205f9a7a2ee
--- /dev/null
+++ b/security/landlock/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
+
+landlock-y := init.o
diff --git a/security/landlock/common.h b/security/landlock/common.h
new file mode 100644
index 000000000000..c82cbd3fb640
--- /dev/null
+++ b/security/landlock/common.h
@@ -0,0 +1,21 @@
+/*
+ * Landlock LSM - private headers
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _SECURITY_LANDLOCK_COMMON_H
+#define _SECURITY_LANDLOCK_COMMON_H
+
+/*
+ * This is not intended for the UAPI headers. Each userland software should use
+ * a static minimal ABI for the required features as explained in the
+ * documentation.
+ */
+#define LANDLOCK_ABI 1
+
+#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/init.c b/security/landlock/init.c
new file mode 100644
index 000000000000..c7922a91aa57
--- /dev/null
+++ b/security/landlock/init.c
@@ -0,0 +1,98 @@
+/*
+ * Landlock LSM - init
+ *
+ * Copyright © 2016-2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 */
+#include <linux/capability.h> /* capable */
+
+#include "common.h" /* LANDLOCK_* */
+
+
+static inline bool bpf_landlock_is_valid_access(int off, int size,
+ enum bpf_access_type type, struct bpf_insn_access_aux *info,
+ const union bpf_prog_subtype *prog_subtype)
+{
+ if (WARN_ON(!prog_subtype))
+ return false;
+
+ switch (prog_subtype->landlock_rule.event) {
+ case LANDLOCK_SUBTYPE_EVENT_FS:
+ case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
+ default:
+ return false;
+ }
+}
+
+static inline bool bpf_landlock_is_valid_subtype(
+ const union bpf_prog_subtype *prog_subtype)
+{
+ if (WARN_ON(!prog_subtype))
+ return false;
+
+ switch (prog_subtype->landlock_rule.event) {
+ case LANDLOCK_SUBTYPE_EVENT_FS:
+ break;
+ case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
+ default:
+ return false;
+ }
+
+ /* check Landlock ABI compatibility */
+ if (!prog_subtype->landlock_rule.abi ||
+ prog_subtype->landlock_rule.abi > LANDLOCK_ABI)
+ return false;
+ /* check if the rule's event, ability and option make sense */
+ if (!prog_subtype->landlock_rule.event ||
+ prog_subtype->landlock_rule.event >
+ _LANDLOCK_SUBTYPE_EVENT_LAST)
+ return false;
+ if (prog_subtype->landlock_rule.ability &
+ ~_LANDLOCK_SUBTYPE_ABILITY_MASK)
+ return false;
+ if (prog_subtype->landlock_rule.option &
+ ~_LANDLOCK_SUBTYPE_OPTION_MASK)
+ return false;
+
+ /* the ability to debug requires global CAP_SYS_ADMIN */
+ if (prog_subtype->landlock_rule.ability &
+ LANDLOCK_SUBTYPE_ABILITY_DEBUG &&
+ !capable(CAP_SYS_ADMIN))
+ return false;
+
+ return true;
+}
+
+static inline const struct bpf_func_proto *bpf_landlock_func_proto(
+ enum bpf_func_id func_id,
+ const union bpf_prog_subtype *prog_subtype)
+{
+ /* generic functions */
+ if (prog_subtype->landlock_rule.ability &
+ LANDLOCK_SUBTYPE_ABILITY_DEBUG) {
+ switch (func_id) {
+ case BPF_FUNC_get_current_comm:
+ return &bpf_get_current_comm_proto;
+ case BPF_FUNC_get_current_pid_tgid:
+ return &bpf_get_current_pid_tgid_proto;
+ case BPF_FUNC_get_current_uid_gid:
+ return &bpf_get_current_uid_gid_proto;
+ case BPF_FUNC_trace_printk:
+ return bpf_get_trace_printk_proto();
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+const struct bpf_verifier_ops bpf_landlock_ops = {
+ .get_func_proto = bpf_landlock_func_proto,
+ .is_valid_access = bpf_landlock_is_valid_access,
+ .is_valid_subtype = bpf_landlock_is_valid_subtype,
+};
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 2c0dc9d58ea0..e83bdecf9d27 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -129,6 +129,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_LWT_XMIT,
BPF_PROG_TYPE_SOCK_OPS,
BPF_PROG_TYPE_SK_SKB,
+ BPF_PROG_TYPE_LANDLOCK_RULE,
};

enum bpf_attach_type {
@@ -868,4 +869,100 @@ enum {
#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */

+/**
+ * enum landlock_subtype_event - event occurring when an action is performed on
+ * a particular kernel object
+ *
+ * An event is a policy decision point which exposes the same context type
+ * (especially the same arg[0-9] field types) for each rule execution.
+ *
+ * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
+ * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_IOCTL: custom IOCTL sub-event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_LOCK: custom LOCK sub-event
+ * @LANDLOCK_SUBTYPE_EVENT_FS_FCNTL: custom FCNTL sub-event
+ */
+enum landlock_subtype_event {
+ LANDLOCK_SUBTYPE_EVENT_UNSPEC,
+ LANDLOCK_SUBTYPE_EVENT_FS,
+ LANDLOCK_SUBTYPE_EVENT_FS_IOCTL,
+ LANDLOCK_SUBTYPE_EVENT_FS_LOCK,
+ LANDLOCK_SUBTYPE_EVENT_FS_FCNTL,
+};
+#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS_FCNTL
+
+/**
+ * DOC: landlock_subtype_ability
+ *
+ * eBPF context and functions allowed for a rule
+ *
+ * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
+ * logs), which may be dangerous and should only be used for rule testing
+ */
+#define LANDLOCK_SUBTYPE_ABILITY_DEBUG (1ULL << 0)
+#define _LANDLOCK_SUBTYPE_ABILITY_NB 1
+#define _LANDLOCK_SUBTYPE_ABILITY_MASK ((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)
+
+/*
+ * Future options for a Landlock rule (e.g. run even if a previous rule denied
+ * an action).
+ */
+#define _LANDLOCK_SUBTYPE_OPTION_NB 0
+#define _LANDLOCK_SUBTYPE_OPTION_MASK ((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)
+
+/*
+ * Status visible in the @status field of a context (e.g. already called in
+ * this syscall session, with same args...).
+ *
+ * The @status field exposed to a rule shall depend on the rule version.
+ */
+#define _LANDLOCK_SUBTYPE_STATUS_NB 0
+#define _LANDLOCK_SUBTYPE_STATUS_MASK ((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)
+
+/**
+ * DOC: landlock_action_fs
+ *
+ * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
+ * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
+ * include mount actions)
+ * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
+ * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
+ * - %LANDLOCK_ACTION_FS_GET: open or receive a file
+ * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
+ *
+ * Each of the following actions are specific to syscall multiplexers. Each of
+ * them trigger a dedicated Landlock event where their command can be read.
+ *
+ * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
+ * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
+ * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
+ */
+#define LANDLOCK_ACTION_FS_EXEC (1ULL << 0)
+#define LANDLOCK_ACTION_FS_WRITE (1ULL << 1)
+#define LANDLOCK_ACTION_FS_READ (1ULL << 2)
+#define LANDLOCK_ACTION_FS_NEW (1ULL << 3)
+#define LANDLOCK_ACTION_FS_GET (1ULL << 4)
+#define LANDLOCK_ACTION_FS_REMOVE (1ULL << 5)
+#define LANDLOCK_ACTION_FS_IOCTL (1ULL << 6)
+#define LANDLOCK_ACTION_FS_LOCK (1ULL << 7)
+#define LANDLOCK_ACTION_FS_FCNTL (1ULL << 8)
+#define _LANDLOCK_ACTION_FS_NB 9
+#define _LANDLOCK_ACTION_FS_MASK ((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
+
+
+/**
+ * struct landlock_context - context accessible to a Landlock rule
+ *
+ * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
+ * @event: event type (&enum landlock_subtype_event)
+ * @arg1: event's first optional argument
+ * @arg2: event's second optional argument
+ */
+struct landlock_context {
+ __u64 status;
+ __u64 event;
+ __u64 arg1;
+ __u64 arg2;
+};
+
#endif /* _UAPI__LINUX_BPF_H__ */
--
2.14.1

2017-08-21 00:11:00

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 01/10] selftest: Enhance kselftest_harness.h with a step mechanism

This step mechanism may be useful to return an information about the
error without being able to write to TH_LOG_STREAM.

Set _metadata->no_print to true to print this counter.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: Will Drewry <[email protected]>
Link: https://lkml.kernel.org/r/CAGXu5j+D-FP8Kt9unNOqKrQJP4DYTpmgkJxWykZyrYiVPz3Y3Q@mail.gmail.com
---

This patch is intended to the kselftest tree:
https://lkml.kernel.org/r/[email protected]

Changes since v6:
* add the step counter in assert/expect macros and use _metadata to
enable the counter (suggested by Kees Cook)
---
tools/testing/selftests/kselftest_harness.h | 31 ++++++++++++++++++++++-----
tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
2 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h
index c56f72e07cd7..850ff6946027 100644
--- a/tools/testing/selftests/kselftest_harness.h
+++ b/tools/testing/selftests/kselftest_harness.h
@@ -51,6 +51,9 @@
#define __KSELFTEST_HARNESS_H

#define _GNU_SOURCE
+#include <asm/types.h>
+#include <errno.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -555,12 +558,18 @@
* return while still providing an optional block to the API consumer.
*/
#define OPTIONAL_HANDLER(_assert) \
- for (; _metadata->trigger; _metadata->trigger = __bail(_assert))
+ for (; _metadata->trigger; _metadata->trigger = \
+ __bail(_assert, _metadata->no_print, _metadata->step))
+
+#define __INC_STEP(_metadata) \
+ if (_metadata->passed && _metadata->step < 255) \
+ _metadata->step++;

#define __EXPECT(_expected, _seen, _t, _assert) do { \
/* Avoid multiple evaluation of the cases */ \
__typeof__(_expected) __exp = (_expected); \
__typeof__(_seen) __seen = (_seen); \
+ __INC_STEP(_metadata); \
if (!(__exp _t __seen)) { \
unsigned long long __exp_print = (uintptr_t)__exp; \
unsigned long long __seen_print = (uintptr_t)__seen; \
@@ -576,6 +585,7 @@
#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
const char *__exp = (_expected); \
const char *__seen = (_seen); \
+ __INC_STEP(_metadata); \
if (!(strcmp(__exp, __seen) _t 0)) { \
__TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
_metadata->passed = 0; \
@@ -590,6 +600,8 @@ struct __test_metadata {
int termsig;
int passed;
int trigger; /* extra handler after the evaluation */
+ __u8 step;
+ bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
struct __test_metadata *prev, *next;
};

@@ -634,10 +646,13 @@ static inline void __register_test(struct __test_metadata *t)
}
}

-static inline int __bail(int for_realz)
+static inline int __bail(int for_realz, bool no_print, __u8 step)
{
- if (for_realz)
+ if (for_realz) {
+ if (no_print)
+ _exit(step);
abort();
+ }
return 0;
}

@@ -655,18 +670,24 @@ void __run_test(struct __test_metadata *t)
t->passed = 0;
} else if (child_pid == 0) {
t->fn(t);
- _exit(t->passed);
+ /* return the step that failed or 0 */
+ _exit(t->passed ? 0 : t->step);
} else {
/* TODO(wad) add timeout support. */
waitpid(child_pid, &status, 0);
if (WIFEXITED(status)) {
- t->passed = t->termsig == -1 ? WEXITSTATUS(status) : 0;
+ t->passed = t->termsig == -1 ? !WEXITSTATUS(status) : 0;
if (t->termsig != -1) {
fprintf(TH_LOG_STREAM,
"%s: Test exited normally "
"instead of by signal (code: %d)\n",
t->name,
WEXITSTATUS(status));
+ } else if (!t->passed) {
+ fprintf(TH_LOG_STREAM,
+ "%s: Test failed at step #%d\n",
+ t->name,
+ WEXITSTATUS(status));
}
} else if (WIFSIGNALED(status)) {
t->passed = 0;
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 73f5ea6778ce..4d6f92a9df6b 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -107,7 +107,7 @@ TEST(mode_strict_support)
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support CONFIG_SECCOMP");
}
- syscall(__NR_exit, 1);
+ syscall(__NR_exit, 0);
}

TEST_SIGNAL(mode_strict_cannot_call_prctl, SIGKILL)
--
2.14.1

2017-08-21 00:13:40

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 09/10] bpf,landlock: Add tests for Landlock

Test basic context access, ptrace protection and filesystem event with
multiple cases.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: Will Drewry <[email protected]>
---

Changes since v6:
* use the new kselftest_harness.h
* use const variables
* replace ASSERT_STEP with ASSERT_*
* rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE
* force sample library rebuild
* fix install target

Changes since v5:
* add subtype test
* add ptrace tests
* split and rename files
* cleanup and rebase
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/bpf/test_verifier.c | 55 ++++
tools/testing/selftests/landlock/.gitignore | 5 +
tools/testing/selftests/landlock/Makefile | 48 ++++
tools/testing/selftests/landlock/bpf/Makefile | 55 ++++
tools/testing/selftests/landlock/bpf/README.rst | 1 +
.../selftests/landlock/bpf/rule_fs_no_open.c | 32 +++
.../selftests/landlock/bpf/rule_fs_read_only.c | 32 +++
tools/testing/selftests/landlock/test.h | 28 ++
tools/testing/selftests/landlock/test_base.c | 27 ++
tools/testing/selftests/landlock/test_fs.c | 296 +++++++++++++++++++++
tools/testing/selftests/landlock/test_ptrace.c | 158 +++++++++++
12 files changed, 738 insertions(+)
create mode 100644 tools/testing/selftests/landlock/.gitignore
create mode 100644 tools/testing/selftests/landlock/Makefile
create mode 100644 tools/testing/selftests/landlock/bpf/Makefile
create mode 120000 tools/testing/selftests/landlock/bpf/README.rst
create mode 100644 tools/testing/selftests/landlock/bpf/rule_fs_no_open.c
create mode 100644 tools/testing/selftests/landlock/bpf/rule_fs_read_only.c
create mode 100644 tools/testing/selftests/landlock/test.h
create mode 100644 tools/testing/selftests/landlock/test_base.c
create mode 100644 tools/testing/selftests/landlock/test_fs.c
create mode 100644 tools/testing/selftests/landlock/test_ptrace.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 26ce4f7168be..099d19950739 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -12,6 +12,7 @@ TARGETS += gpio
TARGETS += intel_pstate
TARGETS += ipc
TARGETS += kcmp
+TARGETS += landlock
TARGETS += lib
TARGETS += membarrier
TARGETS += memfd
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 3146839a51bf..9fb19c975c1b 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -6499,6 +6499,61 @@ static struct bpf_test tests[] = {
.result = REJECT,
.has_prog_subtype = true,
},
+ {
+ "missing subtype",
+ .insns = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .errstr = "",
+ .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_LANDLOCK_RULE,
+ },
+ {
+ "landlock/fs: always accept",
+ .insns = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_LANDLOCK_RULE,
+ .has_prog_subtype = true,
+ .prog_subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+ },
+ },
+ {
+ "landlock/fs: read context",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+ offsetof(struct landlock_context, status)),
+ /* test operations on raw values */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+ offsetof(struct landlock_context, event)),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+ offsetof(struct landlock_context, arg1)),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+ offsetof(struct landlock_context, arg2)),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_LANDLOCK_RULE,
+ .has_prog_subtype = true,
+ .prog_subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+ },
+ },
};

static int probe_filter_length(const struct bpf_insn *fp)
diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
new file mode 100644
index 000000000000..04d01e3646da
--- /dev/null
+++ b/tools/testing/selftests/landlock/.gitignore
@@ -0,0 +1,5 @@
+/rule_*
+/test_base
+/test_fs
+/test_ptrace
+/tmp_*
diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
new file mode 100644
index 000000000000..77bbadfe373c
--- /dev/null
+++ b/tools/testing/selftests/landlock/Makefile
@@ -0,0 +1,48 @@
+LIBDIR := ../../../lib
+BPFOBJ := $(LIBDIR)/bpf/bpf.o
+LOADOBJ := ../../../../samples/bpf/bpf_load.o
+
+CFLAGS += -Wl,-no-as-needed -Wall -O2 -I../../../include/uapi -I$(LIBDIR)
+LDFLAGS += -lelf
+
+test_src = $(wildcard test_*.c)
+rule_src = $(wildcard bpf/rule_*.c)
+
+test_objs := $(test_src:.c=)
+rule_objs := $(notdir $(rule_src:.c=.o))
+
+TEST_PROGS := $(test_objs)
+TEST_PROGS_EXTENDED := $(rule_objs)
+
+.PHONY: all clean clean_tmp force
+
+all: $(test_objs) $(rule_objs)
+
+# force a rebuild of BPFOBJ when its dependencies are updated
+force:
+
+$(BPFOBJ): force
+ $(MAKE) -C $(dir $(BPFOBJ))
+
+$(LOADOBJ): force
+ $(MAKE) -C $(dir $(LOADOBJ))
+
+# minimize builds
+bpf/modules.order: $(rule_src)
+ $(MAKE) -C bpf
+ @touch $@
+
+$(rule_objs): bpf/modules.order
+ @
+
+$(test_objs): $(BPFOBJ) $(LOADOBJ) ../kselftest_harness.h
+
+include ../lib.mk
+
+clean_tmp:
+ $(RM) -r tmp_*
+
+clean: clean_tmp
+ $(MAKE) -C bpf clean
+ $(RM) $(test_objs)
+
diff --git a/tools/testing/selftests/landlock/bpf/Makefile b/tools/testing/selftests/landlock/bpf/Makefile
new file mode 100644
index 000000000000..857ad2483289
--- /dev/null
+++ b/tools/testing/selftests/landlock/bpf/Makefile
@@ -0,0 +1,55 @@
+# copied from samples/bpf/Makefile
+#
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+# Tell kbuild to always build the programs
+always := ../rule_fs_read_only.o
+always += ../rule_fs_no_open.o
+
+EXTRA_CFLAGS = -Wall -Wextra
+
+# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
+# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
+LLC ?= llc
+CLANG ?= clang
+
+# Verify LLVM compiler tools are available and bpf target is supported by llc
+.PHONY: all clean verify_cmds verify_target_bpf $(CLANG) $(LLC)
+
+# Trick to allow make to be run from this directory
+all:
+ $(MAKE) -C ../../../../../ $(CURDIR)/
+
+clean:
+ $(MAKE) -C ../../../../../ M=$(CURDIR) clean
+
+verify_cmds: $(CLANG) $(LLC)
+ @for TOOL in $^ ; do \
+ if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
+ echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
+ exit 1; \
+ else true; fi; \
+ done
+
+verify_target_bpf: verify_cmds
+ @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
+ echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
+ echo " NOTICE: LLVM version >= 3.7.1 required" ;\
+ exit 2; \
+ else true; fi
+
+$(src)/*.c: verify_target_bpf
+
+# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
+# But, there is no easy way to fix it, so just exclude it since it is
+# useless for BPF samples.
+$(obj)/../rule_%.o: $(src)/rule_%.c
+ $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
+ -I$(srctree)/tools/testing/selftests/bpf/ \
+ -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
+ -Wno-compare-distinct-pointer-types \
+ -Wno-gnu-variable-sized-type-not-at-end \
+ -Wno-address-of-packed-member -Wno-tautological-compare \
+ -Wno-unknown-warning-option \
+ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
diff --git a/tools/testing/selftests/landlock/bpf/README.rst b/tools/testing/selftests/landlock/bpf/README.rst
new file mode 120000
index 000000000000..605f48aa6f72
--- /dev/null
+++ b/tools/testing/selftests/landlock/bpf/README.rst
@@ -0,0 +1 @@
+../../../../../samples/bpf/README.rst
\ No newline at end of file
diff --git a/tools/testing/selftests/landlock/bpf/rule_fs_no_open.c b/tools/testing/selftests/landlock/bpf/rule_fs_no_open.c
new file mode 100644
index 000000000000..acde56b636a3
--- /dev/null
+++ b/tools/testing/selftests/landlock/bpf/rule_fs_no_open.c
@@ -0,0 +1,32 @@
+/*
+ * Landlock rule - no-open filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 <uapi/linux/bpf.h>
+
+#include <bpf_helpers.h>
+
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+ if (!(ctx->arg2 & LANDLOCK_ACTION_FS_GET))
+ return 0;
+ return 1;
+}
+
+SEC("subtype")
+static const union bpf_prog_subtype _subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/tools/testing/selftests/landlock/bpf/rule_fs_read_only.c b/tools/testing/selftests/landlock/bpf/rule_fs_read_only.c
new file mode 100644
index 000000000000..17680f2e90b0
--- /dev/null
+++ b/tools/testing/selftests/landlock/bpf/rule_fs_read_only.c
@@ -0,0 +1,32 @@
+/*
+ * Landlock rule - read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 <uapi/linux/bpf.h>
+
+#include <bpf_helpers.h>
+
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+ if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
+ return 0;
+ return 1;
+}
+
+SEC("subtype")
+static const union bpf_prog_subtype _subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/tools/testing/selftests/landlock/test.h b/tools/testing/selftests/landlock/test.h
new file mode 100644
index 000000000000..e15392bb84d4
--- /dev/null
+++ b/tools/testing/selftests/landlock/test.h
@@ -0,0 +1,28 @@
+/*
+ * Landlock helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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 <errno.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include "../kselftest_harness.h"
+#include "../../../../samples/bpf/bpf_load.h"
+
+#ifndef SECCOMP_PREPEND_LANDLOCK_RULE
+#define SECCOMP_PREPEND_LANDLOCK_RULE 2
+#endif
+
+#ifndef seccomp
+static int seccomp(unsigned int op, unsigned int flags, void *args)
+{
+ errno = 0;
+ return syscall(__NR_seccomp, op, flags, args);
+}
+#endif
diff --git a/tools/testing/selftests/landlock/test_base.c b/tools/testing/selftests/landlock/test_base.c
new file mode 100644
index 000000000000..dbf4f57f9690
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_base.c
@@ -0,0 +1,27 @@
+/*
+ * Landlock tests - base
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+
+#include "test.h"
+
+TEST(seccomp_landlock)
+{
+ int ret;
+
+ ret = seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, NULL);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EFAULT, errno) {
+ TH_LOG("Kernel does not support CONFIG_SECURITY_LANDLOCK");
+ }
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/test_fs.c b/tools/testing/selftests/landlock/test_fs.c
new file mode 100644
index 000000000000..7404734d2c58
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_fs.c
@@ -0,0 +1,296 @@
+/*
+ * Landlock tests - filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include <fcntl.h> /* open() */
+#include <sys/mount.h>
+#include <sys/stat.h> /* mkdir() */
+#include <sys/mman.h> /* mmap() */
+
+#include "test.h"
+
+#define TMP_PREFIX "tmp_"
+
+struct layout1 {
+ int file_ro;
+ int file_rw;
+ int file_wo;
+};
+
+static void setup_layout1(struct __test_metadata *_metadata,
+ struct layout1 *l1)
+{
+ int fd;
+ char buf[] = "fs_read_only";
+
+ l1->file_ro = -1;
+ l1->file_rw = -1;
+ l1->file_wo = -1;
+
+ fd = open(TMP_PREFIX "file_created",
+ O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(sizeof(buf), write(fd, buf, sizeof(buf)));
+ ASSERT_EQ(0, close(fd));
+
+ fd = mkdir(TMP_PREFIX "dir_created", 0600);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(0, close(fd));
+
+ l1->file_ro = open(TMP_PREFIX "file_ro",
+ O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+ ASSERT_LE(0, l1->file_ro);
+ ASSERT_EQ(sizeof(buf), write(l1->file_ro, buf, sizeof(buf)));
+ ASSERT_EQ(0, close(l1->file_ro));
+ l1->file_ro = open(TMP_PREFIX "file_ro",
+ O_RDONLY | O_CLOEXEC, 0600);
+ ASSERT_LE(0, l1->file_ro);
+
+ l1->file_rw = open(TMP_PREFIX "file_rw",
+ O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
+ ASSERT_LE(0, l1->file_rw);
+ ASSERT_EQ(sizeof(buf), write(l1->file_rw, buf, sizeof(buf)));
+ ASSERT_EQ(0, lseek(l1->file_rw, 0, SEEK_SET));
+
+ l1->file_wo = open(TMP_PREFIX "file_wo",
+ O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+ ASSERT_LE(0, l1->file_wo);
+ ASSERT_EQ(sizeof(buf), write(l1->file_wo, buf, sizeof(buf)));
+ ASSERT_EQ(0, lseek(l1->file_wo, 0, SEEK_SET));
+}
+
+static void cleanup_layout1(void)
+{
+ unlink(TMP_PREFIX "file_created");
+ unlink(TMP_PREFIX "file_ro");
+ unlink(TMP_PREFIX "file_rw");
+ unlink(TMP_PREFIX "file_wo");
+ unlink(TMP_PREFIX "should_not_exist");
+ rmdir(TMP_PREFIX "dir_created");
+}
+
+FIXTURE(fs_read_only) {
+ struct layout1 l1;
+ int prog;
+};
+
+FIXTURE_SETUP(fs_read_only)
+{
+ cleanup_layout1();
+ setup_layout1(_metadata, &self->l1);
+
+ ASSERT_EQ(0, load_bpf_file("rule_fs_read_only.o")) {
+ TH_LOG("%s", bpf_log_buf);
+ }
+ self->prog = prog_fd[0];
+}
+
+FIXTURE_TEARDOWN(fs_read_only)
+{
+ EXPECT_EQ(0, close(self->prog));
+ /* cleanup_layout1() would be denied here */
+}
+
+TEST_F(fs_read_only, load_prog) {}
+
+TEST_F(fs_read_only, read_only_file)
+{
+ int fd;
+ char buf_write[] = "should not be written";
+ char buf_read[2];
+
+ ASSERT_EQ(-1, write(self->l1.file_ro, buf_write, sizeof(buf_write)));
+ ASSERT_EQ(EBADF, errno);
+
+ ASSERT_EQ(-1, read(self->l1.file_wo, buf_read, sizeof(buf_read)));
+ ASSERT_EQ(EBADF, errno);
+
+ ASSERT_EQ(0, seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &self->prog)) {
+ TH_LOG("Failed to apply rule fs_read_only: %s",
+ strerror(errno));
+ }
+ _metadata->no_print = true;
+
+ fd = open(".", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
+ ASSERT_EQ(fd, -1);
+ ASSERT_NE(errno, EOPNOTSUPP)
+ ASSERT_EQ(errno, EPERM);
+
+ fd = open(TMP_PREFIX "file_created",
+ O_RDONLY | O_CLOEXEC);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(close(fd), 0);
+
+ fd = open(TMP_PREFIX "file_created",
+ O_RDWR | O_CLOEXEC);
+ ASSERT_EQ(fd, -1);
+ ASSERT_EQ(errno, EPERM);
+
+ fd = open(TMP_PREFIX "file_created",
+ O_WRONLY | O_CLOEXEC);
+ ASSERT_EQ(fd, -1);
+ ASSERT_EQ(errno, EPERM);
+
+ fd = open(TMP_PREFIX "should_not_exist",
+ O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+ ASSERT_EQ(fd, -1);
+ ASSERT_EQ(errno, EPERM);
+
+ ASSERT_EQ(-1,
+ write(self->l1.file_ro, buf_write, sizeof(buf_write)));
+ ASSERT_EQ(errno, EBADF);
+ ASSERT_EQ(sizeof(buf_read),
+ read(self->l1.file_ro, buf_read, sizeof(buf_read)));
+
+ ASSERT_EQ(-1,
+ write(self->l1.file_rw, buf_write, sizeof(buf_write)));
+ ASSERT_EQ(errno, EPERM);
+ ASSERT_EQ(sizeof(buf_read),
+ read(self->l1.file_rw, buf_read, sizeof(buf_read)));
+
+ ASSERT_EQ(-1, write(self->l1.file_wo, buf_write, sizeof(buf_write)));
+ ASSERT_EQ(errno, EPERM);
+ ASSERT_EQ(-1, read(self->l1.file_wo, buf_read, sizeof(buf_read)));
+ ASSERT_EQ(errno, EBADF);
+
+ ASSERT_EQ(-1, unlink(TMP_PREFIX "file_created"));
+ ASSERT_EQ(errno, EPERM);
+ ASSERT_EQ(-1, rmdir(TMP_PREFIX "dir_created"));
+ ASSERT_EQ(errno, EPERM);
+
+ ASSERT_EQ(0, close(self->l1.file_ro));
+ ASSERT_EQ(0, close(self->l1.file_rw));
+ ASSERT_EQ(0, close(self->l1.file_wo));
+}
+
+TEST_F(fs_read_only, read_only_mount)
+{
+ ASSERT_EQ(0, mount(".", TMP_PREFIX "dir_created",
+ NULL, MS_BIND, NULL));
+ ASSERT_EQ(0, umount2(TMP_PREFIX "dir_created", MNT_FORCE));
+
+ ASSERT_EQ(0, seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &self->prog)) {
+ TH_LOG("Failed to apply rule fs_read_only: %s",
+ strerror(errno));
+ }
+
+ ASSERT_EQ(-1, mount(".", TMP_PREFIX "dir_created",
+ NULL, MS_BIND, NULL));
+ ASSERT_EQ(errno, EPERM);
+ ASSERT_EQ(-1, umount("/"));
+ ASSERT_EQ(errno, EPERM);
+}
+
+TEST_F(fs_read_only, read_only_mem)
+{
+ void *addr;
+
+ addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
+ MAP_SHARED, self->l1.file_rw, 0);
+ ASSERT_NE(NULL, addr);
+ ASSERT_EQ(0, munmap(addr, 1));
+
+ ASSERT_EQ(0, seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &self->prog)) {
+ TH_LOG("Failed to apply rule fs_read_only: %s",
+ strerror(errno));
+ }
+
+ addr = mmap(NULL, 1, PROT_READ, MAP_SHARED,
+ self->l1.file_rw, 0);
+ ASSERT_NE(addr, NULL);
+ ASSERT_EQ(-1, mprotect(addr, 1, PROT_WRITE));
+ ASSERT_EQ(errno, EPERM);
+ ASSERT_EQ(0, munmap(addr, 1));
+
+ addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED,
+ self->l1.file_rw, 0);
+ ASSERT_NE(addr, NULL);
+ ASSERT_EQ(errno, EPERM);
+
+ addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ self->l1.file_rw, 0);
+ ASSERT_NE(addr, NULL);
+ ASSERT_EQ(0, munmap(addr, 1));
+}
+
+FIXTURE(fs_no_open) {
+ struct layout1 l1;
+ int prog;
+};
+
+FIXTURE_SETUP(fs_no_open)
+{
+ cleanup_layout1();
+ setup_layout1(_metadata, &self->l1);
+
+ ASSERT_EQ(0, load_bpf_file("rule_fs_no_open.o")) {
+ TH_LOG("%s", bpf_log_buf);
+ }
+ self->prog = prog_fd[0];
+}
+
+FIXTURE_TEARDOWN(fs_no_open)
+{
+ EXPECT_EQ(0, close(self->prog));
+ cleanup_layout1();
+}
+
+static void landlocked_deny_open(struct __test_metadata *_metadata,
+ struct layout1 *l1)
+{
+ int fd;
+ void *addr;
+
+ fd = open(".", O_DIRECTORY | O_CLOEXEC);
+ ASSERT_EQ(-1, fd);
+ ASSERT_EQ(EPERM, errno);
+
+ addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
+ MAP_SHARED, l1->file_rw, 0);
+ ASSERT_NE(NULL, addr);
+ ASSERT_EQ(0, munmap(addr, 1));
+}
+
+TEST_F(fs_no_open, deny_open_for_hierarchy) {
+ int fd;
+ int status;
+ pid_t child;
+
+ fd = open(".", O_DIRECTORY | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, close(fd));
+
+ ASSERT_EQ(0, seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &self->prog)) {
+ TH_LOG("Failed to apply rule fs_no_open: %s", strerror(errno));
+ }
+
+ landlocked_deny_open(_metadata, &self->l1);
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (!child) {
+ landlocked_deny_open(_metadata, &self->l1);
+ _exit(0);
+ }
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ _exit(WEXITSTATUS(status));
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/test_ptrace.c b/tools/testing/selftests/landlock/test_ptrace.c
new file mode 100644
index 000000000000..8d4d243cae1f
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_ptrace.c
@@ -0,0 +1,158 @@
+/*
+ * Landlock tests - ptrace
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <signal.h> /* raise */
+#include <sys/ptrace.h>
+#include <sys/types.h> /* waitpid */
+#include <sys/wait.h> /* waitpid */
+#include <unistd.h> /* fork, pipe */
+
+#include "test.h"
+
+static void apply_null_sandbox(struct __test_metadata *_metadata)
+{
+ const struct bpf_insn prog_accept[] = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ const union bpf_prog_subtype subtype = {
+ .landlock_rule = {
+ .abi = 1,
+ .event = LANDLOCK_SUBTYPE_EVENT_FS,
+ }
+ };
+ int prog;
+ char log[256] = "";
+
+ prog = bpf_load_program(BPF_PROG_TYPE_LANDLOCK_RULE,
+ (const struct bpf_insn *)&prog_accept,
+ sizeof(prog_accept) / sizeof(struct bpf_insn), "GPL",
+ 0, log, sizeof(log), &subtype);
+ ASSERT_NE(-1, prog) {
+ TH_LOG("Failed to load minimal rule: %s\n%s",
+ strerror(errno), log);
+ }
+ ASSERT_EQ(0, seccomp(SECCOMP_PREPEND_LANDLOCK_RULE, 0, &prog)) {
+ TH_LOG("Failed to apply minimal rule: %s", strerror(errno));
+ }
+ EXPECT_EQ(0, close(prog));
+}
+
+/* PTRACE_TRACEME and PTRACE_ATTACH without Landlock rules effect */
+static void check_ptrace(struct __test_metadata *_metadata,
+ int sandbox_both, int sandbox_parent, int sandbox_child,
+ int expect_ptrace)
+{
+ pid_t child;
+ int status;
+ int pipefd[2];
+
+ ASSERT_EQ(0, pipe(pipefd));
+ if (sandbox_both)
+ apply_null_sandbox(_metadata);
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ char buf;
+
+ EXPECT_EQ(0, close(pipefd[1]));
+ if (sandbox_child)
+ apply_null_sandbox(_metadata);
+
+ /* test traceme */
+ ASSERT_EQ(expect_ptrace, ptrace(PTRACE_TRACEME));
+ if (expect_ptrace) {
+ ASSERT_EQ(EPERM, errno);
+ } else {
+ ASSERT_EQ(0, raise(SIGSTOP));
+ }
+
+ /* sync */
+ ASSERT_EQ(1, read(pipefd[0], &buf, 1)) {
+ TH_LOG("Failed to read() sync from parent");
+ }
+ ASSERT_EQ('.', buf);
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
+
+ EXPECT_EQ(0, close(pipefd[0]));
+ if (sandbox_parent)
+ apply_null_sandbox(_metadata);
+
+ /* test traceme */
+ if (!expect_ptrace) {
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFSTOPPED(status));
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
+ }
+ /* test attach */
+ ASSERT_EQ(expect_ptrace, ptrace(PTRACE_ATTACH, child, NULL, 0));
+ if (expect_ptrace) {
+ ASSERT_EQ(EPERM, errno);
+ } else {
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFSTOPPED(status));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child, NULL, 0));
+ }
+
+ /* sync */
+ ASSERT_EQ(1, write(pipefd[1], ".", 1)) {
+ TH_LOG("Failed to write() sync to child");
+ }
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ _metadata->passed = 0;
+}
+
+TEST(ptrace_allow_without_sandbox)
+{
+ /* no sandbox */
+ check_ptrace(_metadata, 0, 0, 0, 0);
+}
+
+TEST(ptrace_allow_with_one_sandbox)
+{
+ /* child sandbox */
+ check_ptrace(_metadata, 0, 0, 1, 0);
+}
+
+TEST(ptrace_allow_with_nested_sandbox)
+{
+ /* inherited and child sandbox */
+ check_ptrace(_metadata, 1, 0, 1, 0);
+}
+
+TEST(ptrace_deny_with_parent_sandbox)
+{
+ /* parent sandbox */
+ check_ptrace(_metadata, 0, 1, 0, -1);
+}
+
+TEST(ptrace_deny_with_nested_and_parent_sandbox)
+{
+ /* inherited and parent sandbox */
+ check_ptrace(_metadata, 1, 1, 0, -1);
+}
+
+TEST(ptrace_deny_with_forked_sandbox)
+{
+ /* inherited, parent and child sandbox */
+ check_ptrace(_metadata, 1, 1, 1, -1);
+}
+
+TEST(ptrace_deny_with_sibling_sandbox)
+{
+ /* parent and child sandbox */
+ check_ptrace(_metadata, 0, 1, 1, -1);
+}
+
+TEST_HARNESS_MAIN
--
2.14.1

2017-08-21 00:13:41

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH net-next v7 07/10] landlock: Add ptrace restrictions

A landlocked process has less privileges than a non-landlocked process
and must then be subject to additional restrictions when manipulating
processes. To be allowed to use ptrace(2) and related syscalls on a
target process, a landlocked process must have a subset of the target
process' rules.

Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: James Morris <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
---

Changes since v6:
* factor out ptrace check
* constify pointers
* cleanup headers
* use the new security_add_hooks()
---
security/landlock/Makefile | 2 +-
security/landlock/hooks_ptrace.c | 123 +++++++++++++++++++++++++++++++++++++++
security/landlock/hooks_ptrace.h | 11 ++++
security/landlock/init.c | 2 +
4 files changed, 137 insertions(+), 1 deletion(-)
create mode 100644 security/landlock/hooks_ptrace.c
create mode 100644 security/landlock/hooks_ptrace.h

diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 8153b024ffd7..7ff911328e74 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -5,4 +5,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function

obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

-landlock-y := init.o providers.o hooks.o hooks_fs.o
+landlock-y := init.o providers.o hooks.o hooks_ptrace.o hooks_fs.o
diff --git a/security/landlock/hooks_ptrace.c b/security/landlock/hooks_ptrace.c
new file mode 100644
index 000000000000..0f1c13172f54
--- /dev/null
+++ b/security/landlock/hooks_ptrace.c
@@ -0,0 +1,123 @@
+/*
+ * Landlock LSM - ptrace hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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/errno.h>
+#include <linux/kernel.h> /* ARRAY_SIZE */
+#include <linux/lsm_hooks.h>
+#include <linux/sched.h> /* struct task_struct */
+#include <linux/seccomp.h>
+
+#include "common.h" /* struct landlock_events */
+#include "hooks.h" /* landlocked() */
+#include "hooks_ptrace.h"
+
+
+static bool landlock_events_are_subset(const struct landlock_events *parent,
+ const struct landlock_events *child)
+{
+ size_t i;
+
+ if (!parent || !child)
+ return false;
+ if (parent == child)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(child->rules); i++) {
+ struct landlock_rule *walker;
+ bool found_parent = false;
+
+ if (!parent->rules[i])
+ continue;
+ for (walker = child->rules[i]; walker; walker = walker->prev) {
+ if (walker == parent->rules[i]) {
+ found_parent = true;
+ break;
+ }
+ }
+ if (!found_parent)
+ return false;
+ }
+ return true;
+}
+
+static bool landlock_task_has_subset_events(const struct task_struct *parent,
+ const struct task_struct *child)
+{
+#ifdef CONFIG_SECCOMP_FILTER
+ if (landlock_events_are_subset(parent->seccomp.landlock_events,
+ child->seccomp.landlock_events))
+ /* must be ANDed with other providers (i.e. cgroup) */
+ return true;
+#endif /* CONFIG_SECCOMP_FILTER */
+ return false;
+}
+
+static int landlock_task_ptrace(const struct task_struct *parent,
+ const struct task_struct *child)
+{
+ if (!landlocked(parent))
+ return 0;
+
+ if (!landlocked(child))
+ return -EPERM;
+
+ if (landlock_task_has_subset_events(parent, child))
+ return 0;
+
+ return -EPERM;
+}
+
+/**
+ * landlock_ptrace_access_check - determine whether the current process may
+ * access another
+ *
+ * @child: the process to be accessed
+ * @mode: the mode of attachment
+ *
+ * If the current task has Landlock rules, then the child must have at least
+ * the same rules. Else denied.
+ *
+ * Determine whether a process may access another, returning 0 if permission
+ * granted, -errno if denied.
+ */
+static int landlock_ptrace_access_check(struct task_struct *child,
+ unsigned int mode)
+{
+ return landlock_task_ptrace(current, child);
+}
+
+/**
+ * landlock_ptrace_traceme - determine whether another process may trace the
+ * current one
+ *
+ * @parent: the task proposed to be the tracer
+ *
+ * If the parent has Landlock rules, then the current task must have the same
+ * or more rules.
+ * Else denied.
+ *
+ * Determine whether the nominated task is permitted to trace the current
+ * process, returning 0 if permission is granted, -errno if denied.
+ */
+static int landlock_ptrace_traceme(struct task_struct *parent)
+{
+ return landlock_task_ptrace(parent, current);
+}
+
+static struct security_hook_list landlock_hooks[] = {
+ LSM_HOOK_INIT(ptrace_access_check, landlock_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, landlock_ptrace_traceme),
+};
+
+__init void landlock_add_hooks_ptrace(void)
+{
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), LANDLOCK_NAME);
+}
diff --git a/security/landlock/hooks_ptrace.h b/security/landlock/hooks_ptrace.h
new file mode 100644
index 000000000000..15b1f3479e0e
--- /dev/null
+++ b/security/landlock/hooks_ptrace.h
@@ -0,0 +1,11 @@
+/*
+ * Landlock LSM - ptrace hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <[email protected]>
+ *
+ * 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.
+ */
+
+__init void landlock_add_hooks_ptrace(void);
diff --git a/security/landlock/init.c b/security/landlock/init.c
index 81f373f7cc52..424f457b1832 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -14,6 +14,7 @@

#include "common.h" /* LANDLOCK_* */
#include "hooks_fs.h"
+#include "hooks_ptrace.h"


static inline bool bpf_landlock_is_valid_access(int off, int size,
@@ -122,5 +123,6 @@ void __init landlock_add_hooks(void)
{
pr_info("%s: ABI %u, ready to sandbox with %s\n",
LANDLOCK_NAME, LANDLOCK_ABI, "seccomp");
+ landlock_add_hooks_ptrace();
landlock_add_hooks_fs();
}
--
2.14.1

2017-08-22 22:01:03

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem


On 21/08/2017 02:09, Mickaël Salaün wrote:
> 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.
>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> Cc: Daniel Borkmann <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Kees Cook <[email protected]>
> Cc: Serge E. Hallyn <[email protected]>
> ---
>
> Changes since v6:
> * add 3 more sub-events: IOCTL, LOCK, FCNTL
> https://lkml.kernel.org/r/[email protected]
> * use the new security_add_hooks()
> * explain the -Werror=unused-function
> * constify pointers
> * cleanup headers
>
> Changes since v5:
> * split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
> * add more documentation
> * cosmetic fixes
> * rebase (SCALAR_VALUE)
>
> 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
> ---
> include/linux/lsm_hooks.h | 5 +
> security/landlock/Makefile | 7 +-
> security/landlock/common.h | 2 +
> security/landlock/hooks.c | 83 ++++++
> security/landlock/hooks.h | 177 +++++++++++++
> security/landlock/hooks_fs.c | 586 +++++++++++++++++++++++++++++++++++++++++++
> security/landlock/hooks_fs.h | 19 ++
> security/landlock/init.c | 10 +
> security/security.c | 12 +-
> 9 files changed, 899 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/security/landlock/init.c b/security/landlock/init.c
> index 09acbc74abd6..1e6660fed697 100644
> --- a/security/landlock/init.c
> +++ b/security/landlock/init.c
> @@ -10,8 +10,10 @@
>
> #include <linux/bpf.h> /* enum bpf_access_type */
> #include <linux/capability.h> /* capable */
> +#include <linux/lsm_hooks.h>
>
> #include "common.h" /* LANDLOCK_* */
> +#include "hooks_fs.h"
>
>
> static inline bool bpf_landlock_is_valid_access(int off, int size,
> @@ -23,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,
> + &info->reg_type, prog_subtype);

I forgot to handle LANDLOCK_SUBTYPE_EVENT_FS_{IOCTL,LOCK_FCNTL} here and
I included some hunks in the wrong patches. I will fix this in the next
series and add tests for those anyway. :)

Regards,
Mickaël


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-23 02:45:00

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier

On Mon, Aug 21, 2017 at 02:09:25AM +0200, Micka?l Sala?n wrote:
> The goal of the program subtype is to be able to have different static
> fine-grained verifications for a unique program type.
>
> The struct bpf_verifier_ops gets a new optional function:
> is_valid_subtype(). This new verifier is called at the beginning of the
> eBPF program verification to check if the (optional) program subtype is
> valid.
>
> For now, only Landlock eBPF programs are using a program subtype (see
> next commit) but this could be used by other program types in the future.
>
> Signed-off-by: Micka?l Sala?n <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Daniel Borkmann <[email protected]>
> Cc: David S. Miller <[email protected]>
> Link: https://lkml.kernel.org/r/[email protected]
> ---
>
> Changes since v6:
> * rename Landlock version to ABI to better reflect its purpose
> * fix unsigned integer checks
> * fix pointer cast
> * constify pointers
> * rebase
>
> Changes since v5:
> * use a prog_subtype pointer and make it future-proof
> * add subtype test
> * constify bpf_load_program()'s subtype argument
> * cleanup subtype initialization
> * rebase
>
> Changes since v4:
> * replace the "status" field with "version" (more generic)
> * replace the "access" field with "ability" (less confusing)
>
> Changes since v3:
> * remove the "origin" field
> * add an "option" field
> * cleanup comments
> ---
> include/linux/bpf.h | 7 ++-
> include/linux/filter.h | 2 +
> include/uapi/linux/bpf.h | 11 +++++
> kernel/bpf/syscall.c | 22 ++++++++-
> kernel/bpf/verifier.c | 17 +++++--
> kernel/trace/bpf_trace.c | 15 ++++--
> net/core/filter.c | 71 ++++++++++++++++++-----------
> samples/bpf/bpf_load.c | 3 +-
> samples/bpf/cookie_uid_helper_example.c | 2 +-
> samples/bpf/fds_example.c | 2 +-
> samples/bpf/sock_example.c | 3 +-
> samples/bpf/test_cgrp2_attach.c | 2 +-
> samples/bpf/test_cgrp2_attach2.c | 2 +-
> samples/bpf/test_cgrp2_sock.c | 2 +-
> tools/include/uapi/linux/bpf.h | 11 +++++
> tools/lib/bpf/bpf.c | 10 +++-
> tools/lib/bpf/bpf.h | 5 +-
> tools/lib/bpf/libbpf.c | 4 +-
> tools/perf/tests/bpf.c | 2 +-
> tools/testing/selftests/bpf/test_align.c | 2 +-
> tools/testing/selftests/bpf/test_tag.c | 2 +-
> tools/testing/selftests/bpf/test_verifier.c | 17 ++++++-
> 22 files changed, 158 insertions(+), 56 deletions(-)

...

> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index 7015116331af..0c3fadbb5a58 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -464,6 +464,8 @@ struct bpf_prog {
> u32 len; /* Number of filter blocks */
> u32 jited_len; /* Size of jited insns in bytes */
> u8 tag[BPF_TAG_SIZE];
> + u8 has_subtype;
> + union bpf_prog_subtype subtype; /* Fine-grained verifications */

these burn a hole in very performance sensitive structure.
Also there are bits rigth above. use them instead of u8 has_subtype?
or can these two fields be part of bpf_prog_aux ?

> struct bpf_prog_aux *aux; /* Auxiliary fields */
> struct sock_fprog_kern *orig_prog; /* Original BPF program */
> unsigned int (*bpf_func)(const void *ctx,
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 843818dff96d..8541ab85e432 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -177,6 +177,15 @@ enum bpf_attach_type {
> /* Specify numa node during map creation */
> #define BPF_F_NUMA_NODE (1U << 2)
>
> +union bpf_prog_subtype {
> + struct {
> + __u32 abi; /* minimal ABI version, cf. user doc */

the concept of abi (version) sounds a bit weird to me.
Why bother with it at all?
Once the first set of patches lands the kernel as whole will have landlock feature
with a set of helpers, actions, event types.
Some future patches will extend the landlock feature step by step.
This abi concept assumes that anyone who adds new helper would need
to keep incrementing this 'abi'. What value does it give to user or to kernel?
The users will already know that landlock is present in kernel 4.14 or whatever
and the kernel 4.18 has more landlock features. Why bother with extra abi number?

> + __u32 event; /* enum landlock_subtype_event */
> + __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
> + __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
> + } landlock_rule;
> +} __attribute__((aligned(8)));
> +
> union bpf_attr {
> struct { /* anonymous struct used by BPF_MAP_CREATE command */
> __u32 map_type; /* one of enum bpf_map_type */
> @@ -212,6 +221,8 @@ union bpf_attr {
> __aligned_u64 log_buf; /* user supplied buffer */
> __u32 kern_version; /* checked when prog_type=kprobe */
> __u32 prog_flags;
> + __aligned_u64 prog_subtype; /* bpf_prog_subtype address */
> + __u32 prog_subtype_size;
> };

more general question: what is the status of security/ bits?
I'm assuming they still need to be reviewed and explicitly acked by James, right?

2017-08-23 07:46:50

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier


On 23/08/2017 04:44, Alexei Starovoitov wrote:
> On Mon, Aug 21, 2017 at 02:09:25AM +0200, Micka?l Sala?n wrote:
>> The goal of the program subtype is to be able to have different static
>> fine-grained verifications for a unique program type.
>>
>> The struct bpf_verifier_ops gets a new optional function:
>> is_valid_subtype(). This new verifier is called at the beginning of the
>> eBPF program verification to check if the (optional) program subtype is
>> valid.
>>
>> For now, only Landlock eBPF programs are using a program subtype (see
>> next commit) but this could be used by other program types in the future.
>>
>> Signed-off-by: Micka?l Sala?n <[email protected]>
>> Cc: Alexei Starovoitov <[email protected]>
>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>> Cc: Daniel Borkmann <[email protected]>
>> Cc: David S. Miller <[email protected]>
>> Link: https://lkml.kernel.org/r/[email protected]
>> ---
>>
>> Changes since v6:
>> * rename Landlock version to ABI to better reflect its purpose
>> * fix unsigned integer checks
>> * fix pointer cast
>> * constify pointers
>> * rebase
>>
>> Changes since v5:
>> * use a prog_subtype pointer and make it future-proof
>> * add subtype test
>> * constify bpf_load_program()'s subtype argument
>> * cleanup subtype initialization
>> * rebase
>>
>> Changes since v4:
>> * replace the "status" field with "version" (more generic)
>> * replace the "access" field with "ability" (less confusing)
>>
>> Changes since v3:
>> * remove the "origin" field
>> * add an "option" field
>> * cleanup comments
>> ---
>> include/linux/bpf.h | 7 ++-
>> include/linux/filter.h | 2 +
>> include/uapi/linux/bpf.h | 11 +++++
>> kernel/bpf/syscall.c | 22 ++++++++-
>> kernel/bpf/verifier.c | 17 +++++--
>> kernel/trace/bpf_trace.c | 15 ++++--
>> net/core/filter.c | 71 ++++++++++++++++++-----------
>> samples/bpf/bpf_load.c | 3 +-
>> samples/bpf/cookie_uid_helper_example.c | 2 +-
>> samples/bpf/fds_example.c | 2 +-
>> samples/bpf/sock_example.c | 3 +-
>> samples/bpf/test_cgrp2_attach.c | 2 +-
>> samples/bpf/test_cgrp2_attach2.c | 2 +-
>> samples/bpf/test_cgrp2_sock.c | 2 +-
>> tools/include/uapi/linux/bpf.h | 11 +++++
>> tools/lib/bpf/bpf.c | 10 +++-
>> tools/lib/bpf/bpf.h | 5 +-
>> tools/lib/bpf/libbpf.c | 4 +-
>> tools/perf/tests/bpf.c | 2 +-
>> tools/testing/selftests/bpf/test_align.c | 2 +-
>> tools/testing/selftests/bpf/test_tag.c | 2 +-
>> tools/testing/selftests/bpf/test_verifier.c | 17 ++++++-
>> 22 files changed, 158 insertions(+), 56 deletions(-)
>
> ...
>
>> diff --git a/include/linux/filter.h b/include/linux/filter.h
>> index 7015116331af..0c3fadbb5a58 100644
>> --- a/include/linux/filter.h
>> +++ b/include/linux/filter.h
>> @@ -464,6 +464,8 @@ struct bpf_prog {
>> u32 len; /* Number of filter blocks */
>> u32 jited_len; /* Size of jited insns in bytes */
>> u8 tag[BPF_TAG_SIZE];
>> + u8 has_subtype;
>> + union bpf_prog_subtype subtype; /* Fine-grained verifications */
>
> these burn a hole in very performance sensitive structure.
> Also there are bits rigth above. use them instead of u8 has_subtype?
> or can these two fields be part of bpf_prog_aux ?

OK, I'll create one bit variable and a bpf_prog_subtype field in the
bpf_prog_aux struct then.


>
>> struct bpf_prog_aux *aux; /* Auxiliary fields */
>> struct sock_fprog_kern *orig_prog; /* Original BPF program */
>> unsigned int (*bpf_func)(const void *ctx,
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index 843818dff96d..8541ab85e432 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -177,6 +177,15 @@ enum bpf_attach_type {
>> /* Specify numa node during map creation */
>> #define BPF_F_NUMA_NODE (1U << 2)
>>
>> +union bpf_prog_subtype {
>> + struct {
>> + __u32 abi; /* minimal ABI version, cf. user doc */
>
> the concept of abi (version) sounds a bit weird to me.
> Why bother with it at all?
> Once the first set of patches lands the kernel as whole will have landlock feature
> with a set of helpers, actions, event types.
> Some future patches will extend the landlock feature step by step.
> This abi concept assumes that anyone who adds new helper would need
> to keep incrementing this 'abi'. What value does it give to user or to kernel?
> The users will already know that landlock is present in kernel 4.14 or whatever
> and the kernel 4.18 has more landlock features. Why bother with extra abi number?

That's right for helpers and context fields, but we can't check the use
of one field's content. The status field is intended to be a bitfield
extendable in the future. For example, one use case is to set a flag to
inform the eBPF program that it was already called with the same context
and can skip most of its check (if not related to maps). Same goes for
the FS action bitfield, one may want to add more of them. Another
example may be the check for abilities. We may want to relax/remove the
capability require to set one of them. With an ABI version, the user can
easily check if the current kernel support that.

>
>> + __u32 event; /* enum landlock_subtype_event */
>> + __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
>> + __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
>> + } landlock_rule;
>> +} __attribute__((aligned(8)));
>> +
>> union bpf_attr {
>> struct { /* anonymous struct used by BPF_MAP_CREATE command */
>> __u32 map_type; /* one of enum bpf_map_type */
>> @@ -212,6 +221,8 @@ union bpf_attr {
>> __aligned_u64 log_buf; /* user supplied buffer */
>> __u32 kern_version; /* checked when prog_type=kprobe */
>> __u32 prog_flags;
>> + __aligned_u64 prog_subtype; /* bpf_prog_subtype address */
>> + __u32 prog_subtype_size;
>> };
>
> more general question: what is the status of security/ bits?
> I'm assuming they still need to be reviewed and explicitly acked by James, right?

Right, the review process is ongoing. :)
BTW, I'll be at Linux Security Summit (co-located with Plumbers) next
month. We'll be able to clarify some points there too.

Regards,
Micka?l


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-24 01:22:15

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier

On Wed, Aug 23, 2017 at 09:45:24AM +0200, Micka?l Sala?n wrote:
> >>
> >> +union bpf_prog_subtype {
> >> + struct {
> >> + __u32 abi; /* minimal ABI version, cf. user doc */
> >
> > the concept of abi (version) sounds a bit weird to me.
> > Why bother with it at all?
> > Once the first set of patches lands the kernel as whole will have landlock feature
> > with a set of helpers, actions, event types.
> > Some future patches will extend the landlock feature step by step.
> > This abi concept assumes that anyone who adds new helper would need
> > to keep incrementing this 'abi'. What value does it give to user or to kernel?
> > The users will already know that landlock is present in kernel 4.14 or whatever
> > and the kernel 4.18 has more landlock features. Why bother with extra abi number?
>
> That's right for helpers and context fields, but we can't check the use
> of one field's content. The status field is intended to be a bitfield
> extendable in the future. For example, one use case is to set a flag to
> inform the eBPF program that it was already called with the same context
> and can skip most of its check (if not related to maps). Same goes for

'status' field ? I don't see it in the current patch set.
You mean something like scratch space in landlock_ctx that
program can write to? Sure, that's a good extension.

> the FS action bitfield, one may want to add more of them. Another
> example may be the check for abilities. We may want to relax/remove the
> capability require to set one of them. With an ABI version, the user can
> easily check if the current kernel support that.

sure. there will be future extensions. I still fail to see
why 'abi' field is needed.
Also consider that bpf core itself is being extended all the time as well.
The verifier gets smarter and smarter, so the programs deemed unsafe
a year ago now recognized properly by the verifier.
New instructions being added to the core and so on.
That means that newer landlock programs will not be acceptable by
older kernels. We cannot increment abi/version with every such change.
It's also possible that in the future we may catch a security bug
in the verifier that will start rejecting some corner case of the programs.
The only way to use landlock is to develop a set of programs/rules
for kernel version X and we together will guarantee that these programs
will work fine in the future kernels.
There is a good chance that the rules developed for kernel X+1 will _not_
be loadable on older kernel X even if you don't change anything on
landlock side (helpers, actions, events), so landlock abi/version will
stay the same but you won't get the effect you're looking to get from
this abi concept. Since landlock abi=1 in kernel X and abi=1 in kernel X+1
doesn't mean that landlock rules developed for X+1 will work on X.
Beyond bpf core there are other moving pieces. LSM may get new hooks,
seccomp side will be changed, etc. Incrementing landlock abi is not pracitcal.

2017-08-24 02:30:04

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 03/10] bpf,landlock: Define an eBPF program type for a Landlock rule

On Mon, Aug 21, 2017 at 02:09:26AM +0200, Mickaël Salaün wrote:
> Add a new type of eBPF program used by Landlock rules.
>
> This new BPF program type will be registered with the Landlock LSM
> initialization.
>
> Add an initial Landlock Kconfig.
>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> Cc: Daniel Borkmann <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: James Morris <[email protected]>
> Cc: Kees Cook <[email protected]>
> Cc: Serge E. Hallyn <[email protected]>
> ---
>
> Changes since v6:
> * add 3 more sub-events: IOCTL, LOCK, FCNTL
> https://lkml.kernel.org/r/[email protected]
> * rename LANDLOCK_VERSION to LANDLOCK_ABI to better reflect its purpose,
> and move it from landlock.h to common.h
> * rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE: an eBPF
> program could be used for something else than a rule
> * simplify struct landlock_context by removing the arch and syscall_nr fields
> * remove all eBPF map functions call, remove ABILITY_WRITE
> * refactor bpf_landlock_func_proto() (suggested by Kees Cook)
> * constify pointers
> * fix doc inclusion
>
> Changes since v5:
> * rename file hooks.c to init.c
> * fix spelling
>
> Changes since v4:
> * merge a minimal (not enabled) LSM code and Kconfig in this commit
>
> Changes since v3:
> * split commit
> * revamp the landlock_context:
> * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
> cross-check action with the event type
> * replace args array with dedicated fields to ease the addition of new
> fields
> ---
> include/linux/bpf_types.h | 3 ++
> include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
> security/Kconfig | 1 +
> security/Makefile | 2 +
> security/landlock/Kconfig | 18 ++++++++
> security/landlock/Makefile | 3 ++
> security/landlock/common.h | 21 +++++++++
> security/landlock/init.c | 98 ++++++++++++++++++++++++++++++++++++++++++
> tools/include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 340 insertions(+)
> create mode 100644 security/landlock/Kconfig
> create mode 100644 security/landlock/Makefile
> create mode 100644 security/landlock/common.h
> create mode 100644 security/landlock/init.c
>
> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> index 6f1a567667b8..8bac93970a47 100644
> --- a/include/linux/bpf_types.h
> +++ b/include/linux/bpf_types.h
> @@ -18,6 +18,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops)
> BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops)
> BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops)
> #endif
> +#ifdef CONFIG_SECURITY_LANDLOCK
> +BPF_PROG_TYPE(BPF_PROG_TYPE_LANDLOCK_RULE, bpf_landlock_ops)
> +#endif
>
> BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
> BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 8541ab85e432..20da634da941 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -129,6 +129,7 @@ enum bpf_prog_type {
> BPF_PROG_TYPE_LWT_XMIT,
> BPF_PROG_TYPE_SOCK_OPS,
> BPF_PROG_TYPE_SK_SKB,
> + BPF_PROG_TYPE_LANDLOCK_RULE,
> };
>
> enum bpf_attach_type {
> @@ -879,4 +880,100 @@ enum {
> #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
> #define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */
>
> +/**
> + * enum landlock_subtype_event - event occurring when an action is performed on
> + * a particular kernel object
> + *
> + * An event is a policy decision point which exposes the same context type
> + * (especially the same arg[0-9] field types) for each rule execution.
> + *
> + * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
> + * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
> + * @LANDLOCK_SUBTYPE_EVENT_FS_IOCTL: custom IOCTL sub-event
> + * @LANDLOCK_SUBTYPE_EVENT_FS_LOCK: custom LOCK sub-event
> + * @LANDLOCK_SUBTYPE_EVENT_FS_FCNTL: custom FCNTL sub-event
> + */
> +enum landlock_subtype_event {
> + LANDLOCK_SUBTYPE_EVENT_UNSPEC,
> + LANDLOCK_SUBTYPE_EVENT_FS,
> + LANDLOCK_SUBTYPE_EVENT_FS_IOCTL,
> + LANDLOCK_SUBTYPE_EVENT_FS_LOCK,
> + LANDLOCK_SUBTYPE_EVENT_FS_FCNTL,
> +};
> +#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS_FCNTL
> +
> +/**
> + * DOC: landlock_subtype_ability
> + *
> + * eBPF context and functions allowed for a rule
> + *
> + * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
> + * logs), which may be dangerous and should only be used for rule testing
> + */
> +#define LANDLOCK_SUBTYPE_ABILITY_DEBUG (1ULL << 0)
> +#define _LANDLOCK_SUBTYPE_ABILITY_NB 1
> +#define _LANDLOCK_SUBTYPE_ABILITY_MASK ((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)

can you move the last two macros out of uapi?
There is really no need for the implementation details to
leak into uapi.

> +
> +/*
> + * Future options for a Landlock rule (e.g. run even if a previous rule denied
> + * an action).
> + */
> +#define _LANDLOCK_SUBTYPE_OPTION_NB 0
> +#define _LANDLOCK_SUBTYPE_OPTION_MASK ((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)

same here

> +
> +/*
> + * Status visible in the @status field of a context (e.g. already called in
> + * this syscall session, with same args...).
> + *
> + * The @status field exposed to a rule shall depend on the rule version.
> + */
> +#define _LANDLOCK_SUBTYPE_STATUS_NB 0
> +#define _LANDLOCK_SUBTYPE_STATUS_MASK ((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)

and here

> +
> +/**
> + * DOC: landlock_action_fs
> + *
> + * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
> + * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
> + * include mount actions)
> + * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
> + * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
> + * - %LANDLOCK_ACTION_FS_GET: open or receive a file
> + * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
> + *
> + * Each of the following actions are specific to syscall multiplexers. Each of
> + * them trigger a dedicated Landlock event where their command can be read.
> + *
> + * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
> + * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
> + * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
> + */
> +#define LANDLOCK_ACTION_FS_EXEC (1ULL << 0)
> +#define LANDLOCK_ACTION_FS_WRITE (1ULL << 1)
> +#define LANDLOCK_ACTION_FS_READ (1ULL << 2)
> +#define LANDLOCK_ACTION_FS_NEW (1ULL << 3)
> +#define LANDLOCK_ACTION_FS_GET (1ULL << 4)
> +#define LANDLOCK_ACTION_FS_REMOVE (1ULL << 5)
> +#define LANDLOCK_ACTION_FS_IOCTL (1ULL << 6)
> +#define LANDLOCK_ACTION_FS_LOCK (1ULL << 7)
> +#define LANDLOCK_ACTION_FS_FCNTL (1ULL << 8)
> +#define _LANDLOCK_ACTION_FS_NB 9
> +#define _LANDLOCK_ACTION_FS_MASK ((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)

and here

> +
> +
> +/**
> + * struct landlock_context - context accessible to a Landlock rule
> + *
> + * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
> + * @event: event type (&enum landlock_subtype_event)
> + * @arg1: event's first optional argument
> + * @arg2: event's second optional argument
> + */
> +struct landlock_context {
> + __u64 status;
> + __u64 event;
> + __u64 arg1;
> + __u64 arg2;
> +};

looking at all the uapi additions.. probably worth moving them
into separate .h like we did with bpf_perf_event.h
How about include/uapi/linux/landlock.h ?
It can include bpf.h first thing and then the rest of
landlock related bits.
so all, but BPF_PROG_TYPE_LANDLOCK_RULE will go there.

> +++ b/security/landlock/Kconfig
> @@ -0,0 +1,18 @@
> +config SECURITY_LANDLOCK
> + bool "Landlock sandbox support"
> + depends on SECURITY
> + depends on BPF_SYSCALL
> + depends on SECCOMP_FILTER
> + default y

all new features need to start with default n

> +static inline const struct bpf_func_proto *bpf_landlock_func_proto(
> + enum bpf_func_id func_id,
> + const union bpf_prog_subtype *prog_subtype)
> +{
> + /* generic functions */
> + if (prog_subtype->landlock_rule.ability &
> + LANDLOCK_SUBTYPE_ABILITY_DEBUG) {
> + switch (func_id) {
> + case BPF_FUNC_get_current_comm:
> + return &bpf_get_current_comm_proto;
> + case BPF_FUNC_get_current_pid_tgid:
> + return &bpf_get_current_pid_tgid_proto;
> + case BPF_FUNC_get_current_uid_gid:
> + return &bpf_get_current_uid_gid_proto;

why current_*() helpers are 'debug' only?

2017-08-24 02:31:46

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 01/10] selftest: Enhance kselftest_harness.h with a step mechanism

On Mon, Aug 21, 2017 at 02:09:24AM +0200, Micka?l Sala?n wrote:
> This step mechanism may be useful to return an information about the
> error without being able to write to TH_LOG_STREAM.
>
> Set _metadata->no_print to true to print this counter.
>
> Signed-off-by: Micka?l Sala?n <[email protected]>
> Cc: Andy Lutomirski <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Kees Cook <[email protected]>
> Cc: Shuah Khan <[email protected]>
> Cc: Will Drewry <[email protected]>
> Link: https://lkml.kernel.org/r/CAGXu5j+D-FP8Kt9unNOqKrQJP4DYTpmgkJxWykZyrYiVPz3Y3Q@mail.gmail.com
> ---
>
> This patch is intended to the kselftest tree:
> https://lkml.kernel.org/r/[email protected]
>
> Changes since v6:
> * add the step counter in assert/expect macros and use _metadata to
> enable the counter (suggested by Kees Cook)
> ---
> tools/testing/selftests/kselftest_harness.h | 31 ++++++++++++++++++++++-----
> tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
> 2 files changed, 27 insertions(+), 6 deletions(-)

is there a dependency on this in patches 2+ ?
if not, I would send this patch to selftests right away.

2017-08-24 02:50:39

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem

On Mon, Aug 21, 2017 at 02:09:28AM +0200, Micka?l Sala?n wrote:
> 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.
>
> Signed-off-by: Micka?l Sala?n <[email protected]>

...

> +/* 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)
...

> +HOOK_NEW_FS(sb_remount, 2,
> + struct super_block *, sb,
> + void *, data,
> + WRAP_ARG_SB, sb,
> + WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
> +);

this looks wrong. casting super_block to dentry?

> +/* 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
> +);

more general question: why you're not wrapping all useful
arguments? Like in the above dentry can be acted upon
by the landlock rule and it's readily available...

The limitation of only 2 args looks odd.
Is it a hard limitation ? how hard to extend?

2017-08-24 02:59:09

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 08/10] bpf: Add a Landlock sandbox example

On Mon, Aug 21, 2017 at 02:09:31AM +0200, Micka?l Sala?n wrote:
> Add a basic sandbox tool to create a process isolated from some part of
> the system. This sandbox create a read-only environment. It is only
> allowed to write to a character device such as a TTY:
>
> # :> X
> # echo $?
> 0
> # ./samples/bpf/landlock1 /bin/sh -i
> Launching a new sandboxed process.
> # :> Y
> cannot create Y: Operation not permitted
>
> Signed-off-by: Micka?l Sala?n <[email protected]>

...

> +SEC("landlock1")
> +static int landlock_fs_prog1(struct landlock_context *ctx)
> +{
> + char fmt_error_mode[] = "landlock1: error: get_mode:%lld\n";
> + char fmt_error_access[] = "landlock1: error: access denied\n";
> + long long ret;
> +
> + /*
> + * The argument ctx->arg2 contains bitflags of actions for which the
> + * rule is run. The flag LANDLOCK_ACTION_FS_WRITE means that a write
> + * is requested by one of the userspace processes restricted by this
> + * rule. The following test allows any actions which does not include a
> + * write.
> + */
> + if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
> + return 0;
> +
> + /*
> + * The argument ctx->arg1 is a file handle for which the process want
> + * to access. The function bpf_handle_fs_get_mode() return the mode of
> + * a file (e.g. S_IFBLK, S_IFDIR, S_IFREG...). If there is an error,
> + * for example if the argument is not a file handle, then an
> + * -errno value is returned. Otherwise the caller get the file mode as
> + * with stat(2).
> + */
> + ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
> + if (ret < 0) {
> +
> + /*
> + * The bpf_trace_printk() function enable to write in the
> + * kernel eBPF debug log, accessible through
> + * /sys/kernel/debug/tracing/trace_pipe . To be allowed to call
> + * this function, a Landlock rule must have the
> + * LANDLOCK_SUBTYPE_ABILITY_DEBUG ability, which is only
> + * allowed for CAP_SYS_ADMIN.
> + */
> + bpf_trace_printk(fmt_error_mode, sizeof(fmt_error_mode), ret);
> + return 1;
> + }
> +
> + /*
> + * This check allows the action on the file if it is a directory or a
> + * pipe. Otherwise, a message is printed to the eBPF log.
> + */
> + if (S_ISCHR(ret) || S_ISFIFO(ret))
> + return 0;
> + bpf_trace_printk(fmt_error_access, sizeof(fmt_error_access));
> + return 1;
> +}
> +
> +/*
> + * This subtype enable to set the ABI, which ensure that the eBPF context and
> + * program behavior will be compatible with this Landlock rule.
> + */
> +SEC("subtype")
> +static const union bpf_prog_subtype _subtype = {
> + .landlock_rule = {
> + .abi = 1,
> + .event = LANDLOCK_SUBTYPE_EVENT_FS,
> + .ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
> + }
> +};

from rule writer perspective can you somehow merge subtype definition
with the program? It seems they go hand in hand.
Like section name of the program can be:
SEC("landlock_rule1/event=fs/ability=debug")
static int landlock_fs_prog1(struct landlock_context *ctx)...
and the loader can parse this string and prepare appropriate
data structures for the kernel.

2017-08-25 08:00:01

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 01/10] selftest: Enhance kselftest_harness.h with a step mechanism



On 24/08/2017 04:31, Alexei Starovoitov wrote:
> On Mon, Aug 21, 2017 at 02:09:24AM +0200, Micka?l Sala?n wrote:
>> This step mechanism may be useful to return an information about the
>> error without being able to write to TH_LOG_STREAM.
>>
>> Set _metadata->no_print to true to print this counter.
>>
>> Signed-off-by: Micka?l Sala?n <[email protected]>
>> Cc: Andy Lutomirski <[email protected]>
>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>> Cc: Kees Cook <[email protected]>
>> Cc: Shuah Khan <[email protected]>
>> Cc: Will Drewry <[email protected]>
>> Link: https://lkml.kernel.org/r/CAGXu5j+D-FP8Kt9unNOqKrQJP4DYTpmgkJxWykZyrYiVPz3Y3Q@mail.gmail.com
>> ---
>>
>> This patch is intended to the kselftest tree:
>> https://lkml.kernel.org/r/[email protected]
>>
>> Changes since v6:
>> * add the step counter in assert/expect macros and use _metadata to
>> enable the counter (suggested by Kees Cook)
>> ---
>> tools/testing/selftests/kselftest_harness.h | 31 ++++++++++++++++++++++-----
>> tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
>> 2 files changed, 27 insertions(+), 6 deletions(-)
>
> is there a dependency on this in patches 2+ ?
> if not, I would send this patch to selftests right away.
>
>

The Landlock tests [patch 9/10] rely on it for now.

I sent it three weeks ago:
https://lkml.kernel.org/r/[email protected]

Anyway, until this patch is merged in the kselftest tree and then
available to net-next, I'll have to keep it here.


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-25 08:03:32

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 03/10] bpf,landlock: Define an eBPF program type for a Landlock rule



On 24/08/2017 04:28, Alexei Starovoitov wrote:
> On Mon, Aug 21, 2017 at 02:09:26AM +0200, Mickaël Salaün wrote:
>> Add a new type of eBPF program used by Landlock rules.
>>
>> This new BPF program type will be registered with the Landlock LSM
>> initialization.
>>
>> Add an initial Landlock Kconfig.
>>
>> Signed-off-by: Mickaël Salaün <[email protected]>
>> Cc: Alexei Starovoitov <[email protected]>
>> Cc: Andy Lutomirski <[email protected]>
>> Cc: Daniel Borkmann <[email protected]>
>> Cc: David S. Miller <[email protected]>
>> Cc: James Morris <[email protected]>
>> Cc: Kees Cook <[email protected]>
>> Cc: Serge E. Hallyn <[email protected]>
>> ---
>>
>> Changes since v6:
>> * add 3 more sub-events: IOCTL, LOCK, FCNTL
>> https://lkml.kernel.org/r/[email protected]
>> * rename LANDLOCK_VERSION to LANDLOCK_ABI to better reflect its purpose,
>> and move it from landlock.h to common.h
>> * rename BPF_PROG_TYPE_LANDLOCK to BPF_PROG_TYPE_LANDLOCK_RULE: an eBPF
>> program could be used for something else than a rule
>> * simplify struct landlock_context by removing the arch and syscall_nr fields
>> * remove all eBPF map functions call, remove ABILITY_WRITE
>> * refactor bpf_landlock_func_proto() (suggested by Kees Cook)
>> * constify pointers
>> * fix doc inclusion
>>
>> Changes since v5:
>> * rename file hooks.c to init.c
>> * fix spelling
>>
>> Changes since v4:
>> * merge a minimal (not enabled) LSM code and Kconfig in this commit
>>
>> Changes since v3:
>> * split commit
>> * revamp the landlock_context:
>> * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
>> cross-check action with the event type
>> * replace args array with dedicated fields to ease the addition of new
>> fields
>> ---
>> include/linux/bpf_types.h | 3 ++
>> include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
>> security/Kconfig | 1 +
>> security/Makefile | 2 +
>> security/landlock/Kconfig | 18 ++++++++
>> security/landlock/Makefile | 3 ++
>> security/landlock/common.h | 21 +++++++++
>> security/landlock/init.c | 98 ++++++++++++++++++++++++++++++++++++++++++
>> tools/include/uapi/linux/bpf.h | 97 +++++++++++++++++++++++++++++++++++++++++
>> 9 files changed, 340 insertions(+)
>> create mode 100644 security/landlock/Kconfig
>> create mode 100644 security/landlock/Makefile
>> create mode 100644 security/landlock/common.h
>> create mode 100644 security/landlock/init.c
>>
>> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
>> index 6f1a567667b8..8bac93970a47 100644
>> --- a/include/linux/bpf_types.h
>> +++ b/include/linux/bpf_types.h
>> @@ -18,6 +18,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops)
>> BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops)
>> BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops)
>> #endif
>> +#ifdef CONFIG_SECURITY_LANDLOCK
>> +BPF_PROG_TYPE(BPF_PROG_TYPE_LANDLOCK_RULE, bpf_landlock_ops)
>> +#endif
>>
>> BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
>> BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index 8541ab85e432..20da634da941 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -129,6 +129,7 @@ enum bpf_prog_type {
>> BPF_PROG_TYPE_LWT_XMIT,
>> BPF_PROG_TYPE_SOCK_OPS,
>> BPF_PROG_TYPE_SK_SKB,
>> + BPF_PROG_TYPE_LANDLOCK_RULE,
>> };
>>
>> enum bpf_attach_type {
>> @@ -879,4 +880,100 @@ enum {
>> #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
>> #define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */
>>
>> +/**
>> + * enum landlock_subtype_event - event occurring when an action is performed on
>> + * a particular kernel object
>> + *
>> + * An event is a policy decision point which exposes the same context type
>> + * (especially the same arg[0-9] field types) for each rule execution.
>> + *
>> + * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
>> + * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
>> + * @LANDLOCK_SUBTYPE_EVENT_FS_IOCTL: custom IOCTL sub-event
>> + * @LANDLOCK_SUBTYPE_EVENT_FS_LOCK: custom LOCK sub-event
>> + * @LANDLOCK_SUBTYPE_EVENT_FS_FCNTL: custom FCNTL sub-event
>> + */
>> +enum landlock_subtype_event {
>> + LANDLOCK_SUBTYPE_EVENT_UNSPEC,
>> + LANDLOCK_SUBTYPE_EVENT_FS,
>> + LANDLOCK_SUBTYPE_EVENT_FS_IOCTL,
>> + LANDLOCK_SUBTYPE_EVENT_FS_LOCK,
>> + LANDLOCK_SUBTYPE_EVENT_FS_FCNTL,
>> +};
>> +#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS_FCNTL
>> +
>> +/**
>> + * DOC: landlock_subtype_ability
>> + *
>> + * eBPF context and functions allowed for a rule
>> + *
>> + * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
>> + * logs), which may be dangerous and should only be used for rule testing
>> + */
>> +#define LANDLOCK_SUBTYPE_ABILITY_DEBUG (1ULL << 0)
>> +#define _LANDLOCK_SUBTYPE_ABILITY_NB 1
>> +#define _LANDLOCK_SUBTYPE_ABILITY_MASK ((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)
>
> can you move the last two macros out of uapi?
> There is really no need for the implementation details to
> leak into uapi.

Right, I'll move them.

>
>> +
>> +/*
>> + * Future options for a Landlock rule (e.g. run even if a previous rule denied
>> + * an action).
>> + */
>> +#define _LANDLOCK_SUBTYPE_OPTION_NB 0
>> +#define _LANDLOCK_SUBTYPE_OPTION_MASK ((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)
>
> same here
>
>> +
>> +/*
>> + * Status visible in the @status field of a context (e.g. already called in
>> + * this syscall session, with same args...).
>> + *
>> + * The @status field exposed to a rule shall depend on the rule version.
>> + */
>> +#define _LANDLOCK_SUBTYPE_STATUS_NB 0
>> +#define _LANDLOCK_SUBTYPE_STATUS_MASK ((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)
>
> and here
>
>> +
>> +/**
>> + * DOC: landlock_action_fs
>> + *
>> + * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
>> + * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
>> + * include mount actions)
>> + * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
>> + * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
>> + * - %LANDLOCK_ACTION_FS_GET: open or receive a file
>> + * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
>> + *
>> + * Each of the following actions are specific to syscall multiplexers. Each of
>> + * them trigger a dedicated Landlock event where their command can be read.
>> + *
>> + * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
>> + * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
>> + * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
>> + */
>> +#define LANDLOCK_ACTION_FS_EXEC (1ULL << 0)
>> +#define LANDLOCK_ACTION_FS_WRITE (1ULL << 1)
>> +#define LANDLOCK_ACTION_FS_READ (1ULL << 2)
>> +#define LANDLOCK_ACTION_FS_NEW (1ULL << 3)
>> +#define LANDLOCK_ACTION_FS_GET (1ULL << 4)
>> +#define LANDLOCK_ACTION_FS_REMOVE (1ULL << 5)
>> +#define LANDLOCK_ACTION_FS_IOCTL (1ULL << 6)
>> +#define LANDLOCK_ACTION_FS_LOCK (1ULL << 7)
>> +#define LANDLOCK_ACTION_FS_FCNTL (1ULL << 8)
>> +#define _LANDLOCK_ACTION_FS_NB 9
>> +#define _LANDLOCK_ACTION_FS_MASK ((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
>
> and here
>
>> +
>> +
>> +/**
>> + * struct landlock_context - context accessible to a Landlock rule
>> + *
>> + * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
>> + * @event: event type (&enum landlock_subtype_event)
>> + * @arg1: event's first optional argument
>> + * @arg2: event's second optional argument
>> + */
>> +struct landlock_context {
>> + __u64 status;
>> + __u64 event;
>> + __u64 arg1;
>> + __u64 arg2;
>> +};
>
> looking at all the uapi additions.. probably worth moving them
> into separate .h like we did with bpf_perf_event.h
> How about include/uapi/linux/landlock.h ?
> It can include bpf.h first thing and then the rest of
> landlock related bits.
> so all, but BPF_PROG_TYPE_LANDLOCK_RULE will go there.

Sounds good.


>
>> +++ b/security/landlock/Kconfig
>> @@ -0,0 +1,18 @@
>> +config SECURITY_LANDLOCK
>> + bool "Landlock sandbox support"
>> + depends on SECURITY
>> + depends on BPF_SYSCALL
>> + depends on SECCOMP_FILTER
>> + default y
>
> all new features need to start with default n

OK

>
>> +static inline const struct bpf_func_proto *bpf_landlock_func_proto(
>> + enum bpf_func_id func_id,
>> + const union bpf_prog_subtype *prog_subtype)
>> +{
>> + /* generic functions */
>> + if (prog_subtype->landlock_rule.ability &
>> + LANDLOCK_SUBTYPE_ABILITY_DEBUG) {
>> + switch (func_id) {
>> + case BPF_FUNC_get_current_comm:
>> + return &bpf_get_current_comm_proto;
>> + case BPF_FUNC_get_current_pid_tgid:
>> + return &bpf_get_current_pid_tgid_proto;
>> + case BPF_FUNC_get_current_uid_gid:
>> + return &bpf_get_current_uid_gid_proto;
>
> why current_*() helpers are 'debug' only?

This helpers should not be needed for access control. It sounds like a
really bad idea to rely on a command name, a PID or even the UID,
especially for a kind of hierarchy-based access control like Landlock. I
want to avoid people from using these features except for debug
purposes. This could be changed in the future if there is a legitimate
use case of some of these features, though.


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-25 08:17:56

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem


On 24/08/2017 04:50, Alexei Starovoitov wrote:
> On Mon, Aug 21, 2017 at 02:09:28AM +0200, Micka?l Sala?n wrote:
>> 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.
>>
>> Signed-off-by: Micka?l Sala?n <[email protected]>
>
> ...
>
>> +/* 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)
> ...
>
>> +HOOK_NEW_FS(sb_remount, 2,
>> + struct super_block *, sb,
>> + void *, data,
>> + WRAP_ARG_SB, sb,
>> + WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
>> +);
>
> this looks wrong. casting super_block to dentry?

This is called when remounting a block device. The WRAP_ARG_SB take the
sb->s_root as a dentry, it is not a cast. What do you expect from this hook?

>
>> +/* 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
>> +);
>
> more general question: why you're not wrapping all useful
> arguments? Like in the above dentry can be acted upon
> by the landlock rule and it's readily available...

The context used for the FS event must have the exact same types for all
calls. This event is meant to be generic but we can add more specific
ones if needed, like I do with FS_IOCTL.

The idea is to enable people to write simple rules, while being able to
write fine grain rules for special cases (e.g. IOCTL) if needed.

>
> The limitation of only 2 args looks odd.
> Is it a hard limitation ? how hard to extend?

It's not a hard limit at all. Actually, the FS_FNCTL event should have
three arguments (I'll add them in the next series): FS handle, FCNTL
command and FCNTL argument. I made sure that it's really easy to add
more arguments to the context of an event.


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-25 08:19:09

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 08/10] bpf: Add a Landlock sandbox example



On 24/08/2017 04:59, Alexei Starovoitov wrote:
> On Mon, Aug 21, 2017 at 02:09:31AM +0200, Micka?l Sala?n wrote:
>> Add a basic sandbox tool to create a process isolated from some part of
>> the system. This sandbox create a read-only environment. It is only
>> allowed to write to a character device such as a TTY:
>>
>> # :> X
>> # echo $?
>> 0
>> # ./samples/bpf/landlock1 /bin/sh -i
>> Launching a new sandboxed process.
>> # :> Y
>> cannot create Y: Operation not permitted
>>
>> Signed-off-by: Micka?l Sala?n <[email protected]>
>
> ...
>
>> +SEC("landlock1")
>> +static int landlock_fs_prog1(struct landlock_context *ctx)
>> +{
>> + char fmt_error_mode[] = "landlock1: error: get_mode:%lld\n";
>> + char fmt_error_access[] = "landlock1: error: access denied\n";
>> + long long ret;
>> +
>> + /*
>> + * The argument ctx->arg2 contains bitflags of actions for which the
>> + * rule is run. The flag LANDLOCK_ACTION_FS_WRITE means that a write
>> + * is requested by one of the userspace processes restricted by this
>> + * rule. The following test allows any actions which does not include a
>> + * write.
>> + */
>> + if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
>> + return 0;
>> +
>> + /*
>> + * The argument ctx->arg1 is a file handle for which the process want
>> + * to access. The function bpf_handle_fs_get_mode() return the mode of
>> + * a file (e.g. S_IFBLK, S_IFDIR, S_IFREG...). If there is an error,
>> + * for example if the argument is not a file handle, then an
>> + * -errno value is returned. Otherwise the caller get the file mode as
>> + * with stat(2).
>> + */
>> + ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
>> + if (ret < 0) {
>> +
>> + /*
>> + * The bpf_trace_printk() function enable to write in the
>> + * kernel eBPF debug log, accessible through
>> + * /sys/kernel/debug/tracing/trace_pipe . To be allowed to call
>> + * this function, a Landlock rule must have the
>> + * LANDLOCK_SUBTYPE_ABILITY_DEBUG ability, which is only
>> + * allowed for CAP_SYS_ADMIN.
>> + */
>> + bpf_trace_printk(fmt_error_mode, sizeof(fmt_error_mode), ret);
>> + return 1;
>> + }
>> +
>> + /*
>> + * This check allows the action on the file if it is a directory or a
>> + * pipe. Otherwise, a message is printed to the eBPF log.
>> + */
>> + if (S_ISCHR(ret) || S_ISFIFO(ret))
>> + return 0;
>> + bpf_trace_printk(fmt_error_access, sizeof(fmt_error_access));
>> + return 1;
>> +}
>> +
>> +/*
>> + * This subtype enable to set the ABI, which ensure that the eBPF context and
>> + * program behavior will be compatible with this Landlock rule.
>> + */
>> +SEC("subtype")
>> +static const union bpf_prog_subtype _subtype = {
>> + .landlock_rule = {
>> + .abi = 1,
>> + .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> + .ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
>> + }
>> +};
>
> from rule writer perspective can you somehow merge subtype definition
> with the program? It seems they go hand in hand.
> Like section name of the program can be:
> SEC("landlock_rule1/event=fs/ability=debug")
> static int landlock_fs_prog1(struct landlock_context *ctx)...
> and the loader can parse this string and prepare appropriate
> data structures for the kernel.

Right, I'll try that.


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-26 01:07:20

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 01/10] selftest: Enhance kselftest_harness.h with a step mechanism

On Fri, Aug 25, 2017 at 09:58:33AM +0200, Micka?l Sala?n wrote:
>
>
> On 24/08/2017 04:31, Alexei Starovoitov wrote:
> > On Mon, Aug 21, 2017 at 02:09:24AM +0200, Micka?l Sala?n wrote:
> >> This step mechanism may be useful to return an information about the
> >> error without being able to write to TH_LOG_STREAM.
> >>
> >> Set _metadata->no_print to true to print this counter.
> >>
> >> Signed-off-by: Micka?l Sala?n <[email protected]>
> >> Cc: Andy Lutomirski <[email protected]>
> >> Cc: Arnaldo Carvalho de Melo <[email protected]>
> >> Cc: Kees Cook <[email protected]>
> >> Cc: Shuah Khan <[email protected]>
> >> Cc: Will Drewry <[email protected]>
> >> Link: https://lkml.kernel.org/r/CAGXu5j+D-FP8Kt9unNOqKrQJP4DYTpmgkJxWykZyrYiVPz3Y3Q@mail.gmail.com
> >> ---
> >>
> >> This patch is intended to the kselftest tree:
> >> https://lkml.kernel.org/r/[email protected]
> >>
> >> Changes since v6:
> >> * add the step counter in assert/expect macros and use _metadata to
> >> enable the counter (suggested by Kees Cook)
> >> ---
> >> tools/testing/selftests/kselftest_harness.h | 31 ++++++++++++++++++++++-----
> >> tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
> >> 2 files changed, 27 insertions(+), 6 deletions(-)
> >
> > is there a dependency on this in patches 2+ ?
> > if not, I would send this patch to selftests right away.
> >
> >
>
> The Landlock tests [patch 9/10] rely on it for now.
>
> I sent it three weeks ago:
> https://lkml.kernel.org/r/[email protected]
>
> Anyway, until this patch is merged in the kselftest tree and then
> available to net-next, I'll have to keep it here.

Shuah,
could you please pick up this patch into your tree?

2017-08-26 01:16:23

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem

On Fri, Aug 25, 2017 at 10:16:39AM +0200, Micka?l Sala?n wrote:
> >
> >> +/* 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)
> > ...
> >
> >> +HOOK_NEW_FS(sb_remount, 2,
> >> + struct super_block *, sb,
> >> + void *, data,
> >> + WRAP_ARG_SB, sb,
> >> + WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
> >> +);
> >
> > this looks wrong. casting super_block to dentry?
>
> This is called when remounting a block device. The WRAP_ARG_SB take the
> sb->s_root as a dentry, it is not a cast. What do you expect from this hook?

got it. I missed -> part. Now it makes sense.

> >
> >> +/* 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
> >> +);
> >
> > more general question: why you're not wrapping all useful
> > arguments? Like in the above dentry can be acted upon
> > by the landlock rule and it's readily available...
>
> The context used for the FS event must have the exact same types for all
> calls. This event is meant to be generic but we can add more specific
> ones if needed, like I do with FS_IOCTL.

I see. So all FS events will have dentry as first argument
regardless of how it is in LSM hook ?
I guess that will simplify the rules indeed.
I suspect you're doing it to simplify the LSM->landlock shim layer as well, right?

> The idea is to enable people to write simple rules, while being able to
> write fine grain rules for special cases (e.g. IOCTL) if needed.
>
> >
> > The limitation of only 2 args looks odd.
> > Is it a hard limitation ? how hard to extend?
>
> It's not a hard limit at all. Actually, the FS_FNCTL event should have
> three arguments (I'll add them in the next series): FS handle, FCNTL
> command and FCNTL argument. I made sure that it's really easy to add
> more arguments to the context of an event.

The reason I'm asking, because I'm not completely convinced that
adding another argument to existing event will be backwards compatible.
It looks like you're expecting only two args for all FS events, right?
How can you add 3rd argument? All FS events would have to get it,
but in some LSM hooks such argument will be meaningless, whereas
in other places it will carry useful info that rule can operate on.
Would that mean that we'll have FS_3 event type and only few LSM
hooks will be converted to it. That works, but then we'll lose
compatiblity with old rules written for FS event and that given hook.
Otherwise we'd need to have fancy logic to accept old FS event
into FS_3 LSM hook.


2017-08-27 13:33:05

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem


On 26/08/2017 03:16, Alexei Starovoitov wrote:
> On Fri, Aug 25, 2017 at 10:16:39AM +0200, Micka?l Sala?n wrote:

>>>
>>>> +/* 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
>>>> +);
>>>
>>> more general question: why you're not wrapping all useful
>>> arguments? Like in the above dentry can be acted upon
>>> by the landlock rule and it's readily available...
>>
>> The context used for the FS event must have the exact same types for all
>> calls. This event is meant to be generic but we can add more specific
>> ones if needed, like I do with FS_IOCTL.
>
> I see. So all FS events will have dentry as first argument
> regardless of how it is in LSM hook ?

All FS events will have a const struct bpf_handle_fs pointer as first
argument, which wrap either a struct file, a struct dentry, a struct
path or a struct inode. Having only one type (struct bpf_handle_fs) is
needed for the eBPF type checker to verify if a Landlock rule (tied to
an event) can access a context field and which operation is allowed
(with this pointer).

> I guess that will simplify the rules indeed.
> I suspect you're doing it to simplify the LSM->landlock shim layer as well, right?

That's right. This ABI is independent from the LSM API and much more
simpler to use.

>
>> The idea is to enable people to write simple rules, while being able to
>> write fine grain rules for special cases (e.g. IOCTL) if needed.
>>
>>>
>>> The limitation of only 2 args looks odd.
>>> Is it a hard limitation ? how hard to extend?
>>
>> It's not a hard limit at all. Actually, the FS_FNCTL event should have
>> three arguments (I'll add them in the next series): FS handle, FCNTL
>> command and FCNTL argument. I made sure that it's really easy to add
>> more arguments to the context of an event.
>
> The reason I'm asking, because I'm not completely convinced that
> adding another argument to existing event will be backwards compatible.
> It looks like you're expecting only two args for all FS events, right?

There is four events right now: FS, FS_IOCTL, FS_LOCK and FS_FCNTL. Each
of them are independent. Their context fields can be of the same or
different eBPF type (e.g. scalar, file handle) and numbers. Actually,
these four events have the same arg1 field (file handle) and the same
arg2 eBPF type (scalar), even if arg2 does not have the same semantic
(i.e. abstract FS action, IOCTL command?).

For example, if we want to extend the FS_FCNTL's context in the future,
we will just have to add an arg3. The check is performed in
landlock_is_valid_access() and landlock_decide(). If a field is not used
by an event, then this field will have a NOT_INIT type and accessing it
will be denied.

> How can you add 3rd argument? All FS events would have to get it,
> but in some LSM hooks such argument will be meaningless, whereas
> in other places it will carry useful info that rule can operate on.
> Would that mean that we'll have FS_3 event type and only few LSM
> hooks will be converted to it. That works, but then we'll lose
> compatiblity with old rules written for FS event and that given hook.
> Otherwise we'd need to have fancy logic to accept old FS event
> into FS_3 LSM hook.

If we want to add a third argument to the FS event, then it will become
accessible because its type will be different than NOT_INIT. This keep
the compatibility with old rules because this new field was then denied.

If we want to add a new argument but only for a subset of the hooks used
by the FS event, then we need to create a new event, like FS_FCNTL. For
example, we may want to add a FS_RENAME event to be able to tie the
source file and the destination file of a rename call.

Anyway, I added the subtype/ABI version as a safeguard in case of
unexpected future evolution.


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-08-28 03:39:42

by James Morris

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH net-next v7 00/10] Landlock LSM: Toward unprivileged sandboxing

On Mon, 21 Aug 2017, Mickaël Salaün wrote:

> ## Why a new LSM? Are SELinux, AppArmor, Smack and Tomoyo not good enough?
>
> The current access control LSMs are fine for their purpose which is to give the
> *root* the ability to enforce a security policy for the *system*. What is
> missing is a way to enforce a security policy for any application by its
> developer and *unprivileged user* as seccomp can do for raw syscall filtering.
>

You could mention here that the first case is Mandatory Access Control,
in general terms.



--
James Morris
<[email protected]>

2017-08-28 03:47:41

by James Morris

[permalink] [raw]
Subject: Re: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier

On Tue, 22 Aug 2017, Alexei Starovoitov wrote:

> more general question: what is the status of security/ bits?
> I'm assuming they still need to be reviewed and explicitly acked by James, right?

Yep, along with other core security developers where possible.


--
James Morris
<[email protected]>

2017-08-28 03:49:47

by James Morris

[permalink] [raw]
Subject: Re: [kernel-hardening] Re: [PATCH net-next v7 02/10] bpf: Add eBPF program subtype and is_valid_subtype() verifier

On Wed, 23 Aug 2017, Micka?l Sala?n wrote:

> >> + struct {
> >> + __u32 abi; /* minimal ABI version, cf. user doc */
> >
> > the concept of abi (version) sounds a bit weird to me.
> > Why bother with it at all?
> > Once the first set of patches lands the kernel as whole will have landlock feature
> > with a set of helpers, actions, event types.
> > Some future patches will extend the landlock feature step by step.
> > This abi concept assumes that anyone who adds new helper would need
> > to keep incrementing this 'abi'. What value does it give to user or to kernel?
> > The users will already know that landlock is present in kernel 4.14 or whatever
> > and the kernel 4.18 has more landlock features. Why bother with extra abi number?
>
> That's right for helpers and context fields, but we can't check the use
> of one field's content. The status field is intended to be a bitfield
> extendable in the future. For example, one use case is to set a flag to
> inform the eBPF program that it was already called with the same context
> and can skip most of its check (if not related to maps). Same goes for
> the FS action bitfield, one may want to add more of them. Another
> example may be the check for abilities. We may want to relax/remove the
> capability require to set one of them. With an ABI version, the user can
> easily check if the current kernel support that.

Don't call it an ABI, perhaps minimum policy version (similar to
what SELinux does). Changes need to be made so that any existing
userspace still works.



--
James Morris
<[email protected]>

2017-08-28 04:10:42

by James Morris

[permalink] [raw]
Subject: Re: [PATCH net-next v7 04/10] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode()

On Mon, 21 Aug 2017, Mickaël Salaün wrote:

> @@ -85,6 +90,8 @@ enum bpf_arg_type {
>
> ARG_PTR_TO_CTX, /* pointer to context */
> ARG_ANYTHING, /* any (initialized) argument is ok */
> +
> + ARG_CONST_PTR_TO_HANDLE_FS, /* pointer to an abstract FS struct */
> };

Looks like a spurious empty line.

--
James Morris
<[email protected]>

2017-08-28 05:26:29

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH net-next v7 05/10] landlock: Add LSM hooks related to filesystem

On Sun, Aug 27, 2017 at 03:31:35PM +0200, Micka?l Sala?n wrote:
>
> > How can you add 3rd argument? All FS events would have to get it,
> > but in some LSM hooks such argument will be meaningless, whereas
> > in other places it will carry useful info that rule can operate on.
> > Would that mean that we'll have FS_3 event type and only few LSM
> > hooks will be converted to it. That works, but then we'll lose
> > compatiblity with old rules written for FS event and that given hook.
> > Otherwise we'd need to have fancy logic to accept old FS event
> > into FS_3 LSM hook.
>
> If we want to add a third argument to the FS event, then it will become
> accessible because its type will be different than NOT_INIT. This keep
> the compatibility with old rules because this new field was then denied.
>
> If we want to add a new argument but only for a subset of the hooks used
> by the FS event, then we need to create a new event, like FS_FCNTL. For
> example, we may want to add a FS_RENAME event to be able to tie the
> source file and the destination file of a rename call.

that's exactly my point. To add another argument FS event
to a subset of hooks will require either new FS_FOO and
to be backwards compatible these hooks will call _both_ FS and FS_FOO
or some magic logic on kernel side that will allow old FS rules
to be attached to FS_FOO hooks?
Two calls doesn't scale and if we do 'magic logic' can we do it now
and avoid introducing events altogether?
Like all landlock programs can be landlock type and they would need
to declare what arg1, arg2, argN they expect. Then at attach
time the kernel only needs to verify that hook arg types match
what program requested.

> Anyway, I added the subtype/ABI version as a safeguard in case of
> unexpected future evolution.

I don't think that abi/version field adds anything in this context.
I still think it should simply be removed.

2017-08-28 18:01:55

by Shuah Khan

[permalink] [raw]
Subject: Re: [PATCH net-next v7 01/10] selftest: Enhance kselftest_harness.h with a step mechanism

On 08/25/2017 07:07 PM, Alexei Starovoitov wrote:
> On Fri, Aug 25, 2017 at 09:58:33AM +0200, Mickaël Salaün wrote:
>>
>>
>> On 24/08/2017 04:31, Alexei Starovoitov wrote:
>>> On Mon, Aug 21, 2017 at 02:09:24AM +0200, Mickaël Salaün wrote:
>>>> This step mechanism may be useful to return an information about the
>>>> error without being able to write to TH_LOG_STREAM.
>>>>
>>>> Set _metadata->no_print to true to print this counter.
>>>>
>>>> Signed-off-by: Mickaël Salaün <[email protected]>
>>>> Cc: Andy Lutomirski <[email protected]>
>>>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>>>> Cc: Kees Cook <[email protected]>
>>>> Cc: Shuah Khan <[email protected]>
>>>> Cc: Will Drewry <[email protected]>
>>>> Link: https://lkml.kernel.org/r/CAGXu5j+D-FP8Kt9unNOqKrQJP4DYTpmgkJxWykZyrYiVPz3Y3Q@mail.gmail.com
>>>> ---
>>>>
>>>> This patch is intended to the kselftest tree:
>>>> https://lkml.kernel.org/r/[email protected]
>>>>
>>>> Changes since v6:
>>>> * add the step counter in assert/expect macros and use _metadata to
>>>> enable the counter (suggested by Kees Cook)
>>>> ---
>>>> tools/testing/selftests/kselftest_harness.h | 31 ++++++++++++++++++++++-----
>>>> tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +-
>>>> 2 files changed, 27 insertions(+), 6 deletions(-)
>>>
>>> is there a dependency on this in patches 2+ ?
>>> if not, I would send this patch to selftests right away.
>>>
>>>
>>
>> The Landlock tests [patch 9/10] rely on it for now.
>>
>> I sent it three weeks ago:
>> https://lkml.kernel.org/r/[email protected]
>>
>> Anyway, until this patch is merged in the kselftest tree and then
>> available to net-next, I'll have to keep it here.
>
> Shuah,
> could you please pick up this patch into your tree?
>

Thanks. Applied to linux-kselftest next for 4.14-rc1.

-- Shuah

2017-09-01 10:25:51

by Alban Crequy

[permalink] [raw]
Subject: Re: [PATCH net-next v7 08/10] bpf: Add a Landlock sandbox example

Hi Mickaël,

On 21 August 2017 at 02:09, Mickaël Salaün <[email protected]> wrote:
> Add a basic sandbox tool to create a process isolated from some part of
> the system. This sandbox create a read-only environment. It is only
> allowed to write to a character device such as a TTY:
...
> + /*
> + * This check allows the action on the file if it is a directory or a
> + * pipe. Otherwise, a message is printed to the eBPF log.
> + */
> + if (S_ISCHR(ret) || S_ISFIFO(ret))
> + return 0;


The comment says "directory", but the code checks for "character device".

Thanks!
Alban

2017-09-02 13:21:12

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH net-next v7 08/10] bpf: Add a Landlock sandbox example


On 01/09/2017 12:25, Alban Crequy wrote:
> Hi Mickaël,
>
> On 21 August 2017 at 02:09, Mickaël Salaün <[email protected]> wrote:
>> Add a basic sandbox tool to create a process isolated from some part of
>> the system. This sandbox create a read-only environment. It is only
>> allowed to write to a character device such as a TTY:
> ...
>> + /*
>> + * This check allows the action on the file if it is a directory or a
>> + * pipe. Otherwise, a message is printed to the eBPF log.
>> + */
>> + if (S_ISCHR(ret) || S_ISFIFO(ret))
>> + return 0;
>
>
> The comment says "directory", but the code checks for "character device".
>
> Thanks!
> Alban
>

Fixed, thanks!


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature