Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1470049 > unrolled thread
| Started by | Mickaël Salaün <mic@digikod.net> |
|---|---|
| First post | 2016-08-25 12:50 +0200 |
| Last post | 2016-08-30 22:00 +0200 |
| Articles | 20 on this page of 25 — 2 participants |
Back to article view | Back to linux.kernel
[RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
[RFC v2 10/10] samples/landlock: Add sandbox example Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
[RFC v2 05/10] seccomp: Handle Landlock Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
[RFC v2 02/10] bpf: Move u64_to_ptr() to BPF headers and inline it Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
[RFC v2 08/10] landlock: Handle file system comparisons Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
Re: [RFC v2 08/10] landlock: Handle file system comparisons Andy Lutomirski <luto@amacapital.net> - 2016-08-25 13:20 +0200
Re: [RFC v2 08/10] landlock: Handle file system comparisons Mickaël Salaün <mic@digikod.net> - 2016-08-25 16:20 +0200
Re: [RFC v2 08/10] landlock: Handle file system comparisons Andy Lutomirski <luto@amacapital.net> - 2016-08-26 17:00 +0200
Re: [RFC v2 08/10] landlock: Handle file system comparisons Mickaël Salaün <mic@digikod.net> - 2016-08-27 15:50 +0200
[RFC v2 01/10] landlock: Add Kconfig Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
[RFC v2 07/10] landlock: Add errno check Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
Re: [RFC v2 07/10] landlock: Add errno check Andy Lutomirski <luto@amacapital.net> - 2016-08-25 13:20 +0200
[RFC v2 06/10] landlock: Add LSM hooks Mickaël Salaün <mic@digikod.net> - 2016-08-25 12:50 +0200
Re: [RFC v2 06/10] landlock: Add LSM hooks Andy Lutomirski <luto@amacapital.net> - 2016-08-30 21:00 +0200
Re: [RFC v2 06/10] landlock: Add LSM hooks Mickaël Salaün <mic@digikod.net> - 2016-08-30 22:20 +0200
Re: [RFC v2 06/10] landlock: Add LSM hooks Andy Lutomirski <luto@amacapital.net> - 2016-08-30 22:20 +0200
Re: [RFC v2 06/10] landlock: Add LSM hooks Mickaël Salaün <mic@digikod.net> - 2016-08-30 22:30 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Andy Lutomirski <luto@amacapital.net> - 2016-08-25 13:30 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Mickaël Salaün <mic@digikod.net> - 2016-08-25 16:00 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Andy Lutomirski <luto@amacapital.net> - 2016-08-27 09:50 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Mickaël Salaün <mic@digikod.net> - 2016-08-27 17:20 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing (cgroup delegation) Mickaël Salaün <mic@digikod.net> - 2016-08-27 17:30 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Andy Lutomirski <luto@amacapital.net> - 2016-08-30 18:10 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Andy Lutomirski <luto@amacapital.net> - 2016-08-30 22:00 +0200
Re: [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing Mickaël Salaün <mic@digikod.net> - 2016-08-30 22:00 +0200
Page 1 of 2 [1] 2 Next page →
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 00/10] Landlock LSM: Unprivileged sandboxing |
| Message-ID | <sa0op-8hT-3@gated-at.bofh.it> |
Hi,
This series is a proof of concept to fill some missing part of seccomp as the
ability to check syscall argument pointers or creating more dynamic security
policies. 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 the Seatbelt/XNU Sandbox or the
OpenBSD Pledge. This kind of sandbox help to mitigate the security impact of
bugs or unexpected/malicious behaviors in userland applications.
The first RFC [1] was focused on extending seccomp while staying at the syscall
level. This brought a working PoC but with some (mitigated) ToCToU race
conditions due to the seccomp ptrace hole (now fixed) and the non-atomic
syscall argument evaluation (hence the LSM hooks).
# Landlock LSM
This second RFC is a fresh revamp of the code while keeping some working ideas.
This series is mainly focused on LSM hooks, while keeping the possibility to
tied them to syscalls. This new code removes all race conditions by design. It
now use eBPF instead of a subset of cBPF (as used by seccomp-bpf). This allow
to remove the previous stacked cBPF hack to do complex access checks thanks to
dedicated eBPF functions. An eBPF program is still very limited (i.e. can only
call a whitelist of functions) and can not do a denial of service (i.e. no
loop). The other major improvement is the replacement of the previous custom
checker groups of syscall arguments with a new dedicated eBPF map to collect
and compare Landlock handles with system resources (e.g. files or network
connections).
The approach taken is to add the minimum amount of code while still allowing
the userland to create quite complex access rules. A dedicated security policy
language such as used by SELinux, AppArmor and other major LSMs is a lot of
code and dedicated to a trusted process (i.e. root/administrator).
# eBPF
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 then
expose a minimal attack surface. The eBPF bytecode is minimal while powerful,
widely used and thought to be used by not so trusted application. Reusing this
code allows to not reproduce the same mistakes and minimize new code while
still taking a generic approach. There is only some new features like a new
kind of arraymap and few dedicated eBPF functions.
An eBPF program have access to an eBPF context which contains the LSM hook
arguments (as does seccomp-bpf with syscall arguments). They 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 nor inconsistent
evaluation (i.e. incorrect mirroring of the OS code and state [2]).
There is one new eBPF program type per LSM hook. This allow to statically check
which context access is performed by an eBPF program. This is needed to deny
kernel address leak and ensure the right use of LSM hook arguments with eBPF
functions. Moreover, this safe pointer handling remove the need for runtime
check or abstract data, which improve performances. Any user can add multiple
Landlock eBPF programs per LSM hook. They are stacked and evaluated one after
the other (cf. seccomp-bpf).
# LSM hooks
Contrary to syscalls, LSM hooks are security checkpoints and are not
architecture dependant. They are designed to match a security need reflected by
a security policy (e.g. access to a file). Exposing parts of some LSM hooks
instead of using the syscall API for sandboxing should help to avoid bugs and
hacks as encountered by the first RFC. Instead of redoing the work of the LSM
hooks through syscalls, we should use and expose them as does policies of
access control LSM.
Only a subset of the hooks are meaningful for an unprivileged sandbox mechanism
(e.g. file system or network access control). Landlock use an abstraction of
raw LSM hooks, which allow to deal with possible future API changes of the LSM
hook API. Moreover, thanks to the ePBF program typing (per LSM hook) used by
Landlock, it should not be hard to make such evolutions backward compatible.
# Use case scenario
First, a process need to create a new dedicated eBPF map containing handles.
This handles are references to system resources (e.g. file or directory) and
grouped in one or multiple maps to be efficiently managed and checked in
batches. This kind of map can be passed to Landlock eBPF functions to compare,
for example, with a file access request. The handles are only accessible from
the eBPF programs created by the same thread.
The loaded Landlock eBPF programs can be triggered by a seccomp filter
returning RET_LANDLOCK. In addition, a cookie (16-bit value) can be passed from
a seccomp filter to eBPF programs. This allow flexible security policies
between seccomp and Landlock.
A triggered Landlock eBPF program can then allow or deny an access, according
to its type (i.e. LSM hook), thanks to errno return values.
# Sandbox example with conditional access control depending on cgroup
$ mkdir /sys/fs/cgroup/sandboxed
$ ls /home
user1
$ LANDLOCK_CGROUPS='/sys/fs/cgroup/sandboxed' \
LANDLOCK_ALLOWED='/bin:/lib:/usr:/tmp:/proc/self/fd/0' \
./sandbox /bin/sh -i
$ ls /home
user1
$ echo $$ > /sys/fs/cgroup/sandboxed/cgroup.procs
$ ls /home
ls: cannot open directory '/home': Permission denied
# Current limitations and possible improvements
For now, eBPF programs can only return an errno code. It may be interesting to
be able to do other actions like seccomp-filter does (e.g. kill process). Such
features can easily be implemented but the main advantage of the current
approach is to be able to only execute eBPF programs until one return an errno
code instead of executing all programs like seccomp-filter does.
It is quite easy to add new eBPF functions to extend Landlock. The main concern
should be about the ability to leak information from the current process to
another one (e.g. through maps) to not reproduce the same security sensitive
behavior as ptrace.
This design does not seems 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 userland 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 LLVM, it is
possible to express an eBPF program with a subset of C.
# FAQ
## Why not use a language like used by SElinux or AppArmor?
This kind of LSMs are dedicated to administrators. They already manage the
system and are not a threat to the system security. However, seccomp, and
Landlock too, should be available to anyone, which potentially include
untrusted users and processes. To reduce the attack surface, Landlock should
expose the minimum amount of code, hence minimal complexity. Moreover, another
threat is to make accessible to a malicious code a new way to gain more
information. For example, Landlock features should not allow a program to get
the file owner if the directory containing this file is not readable. This data
could then be exfiltrated thanks to the access result. Thus, we should limit
the expressiveness of the available checks. The current approach is to do the
checks in such a way that only a comparison with an already accessed resource
(e.g. file descriptor) is possible. This allow to have a reference to compare
with, without exposing much information.
## Why a new LSM? Does SELinux, AppArmor, Smack or Tomoyo are 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 applications by its
developer and *unprivileged user* as seccomp can do for raw syscall filtering.
Moreover, Landlock handles stacked hook programs from different users. It must
then ensure there is no possible malicious interactions between this programs.
Difference with other (access control) LSMs:
* not only dedicated to administrators (i.e. no_new_priv);
* limited kernel attack surface (e.g. policy parsing);
* helpers to compare complex objects (path/FD), no access to internal kernel
data (do not leak addresses);
* constraint policy rules/programs (no DoS: deterministic execution time);
* do not leak more information than the loader process can legitimately have
access to (minimize metadata inference): must compare from an already allowed
file (through a handle).
## Why does seccomp-filter is not enough?
A seccomp filter can access to raw syscall arguments which means that it is not
possible to filter according to pointed data as a file path. As demonstrated
the first version of this patch series, 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 LSM hooks level. The LSM hooks are designed to handle this
kind of checks. This series use this approach to leverage the ability of
unprivileged users to limit themselves.
Cf. "What it isn't?" in Documentation/prctl/seccomp_filter.txt
## As a developer, why do I need this feature?
Landlock's goal is to help userland to limit its attack surface.
Security-conscious developers would like to protect users from a security bug
in their applications and the third-party dependencies they are using. Such a
bug can compromise all the user data and help an attacker to perform a
privilege escalation. Using an *unprivileged sandbox* feature such as Landlock
empower the developer with the ability to properly compartmentalize its
software and limit the impact of being compromised.
## As a user, why do I need a this feature?
Any user can already use seccomp-filter to whitelist a set of syscalls to
reduce the kernel attack surface for a set of processes. However an
unprivileged user can't create a security policy as the root user can thanks to
SELinux and other access control LSMs. Landlock allows any unprivileged user to
protect their data from being accessed by any process they run but only an
identified subset. User tools can be created to help create such a high-level
access control policy. This policy may not be powerful enough to express the
same policies as the current access control LSMs, because of the threat an
unprivileged user can be to the system, but it should be enough for most
use-cases (e.g. blacklist or whitelist a set of file hierarchies).
## Does Landlock can limit network access or other resources?
Limiting network access is obviously in the scope of Landlock but it is not yet
implemented. The main goal now is to get feedback about the whole concept, the
API and the file access control part. More access control types could be
implemented in the future.
## Why using 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
childs. It make sense to use an unique access-restricting syscall (that should
be allowed by seccomp-filter rules) which can only drop privileges. Moreover, a
Landlock eBPF program 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 enforcing via seccomp(2).
# Differences from the RFC 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/1458784008-16277-1-git-send-email-mic@digikod.net
[2] https://crypto.stanford.edu/cs155/papers/traps.pdf
This series can be applied on Linux 4.7 and be tested with
CONFIG_SECURITY_LANDLOCK and CONFIG_CGROUPS. I would really appreciate
constructive comments on the usability, architecture, code and userland API of
Landlock LSM.
Regards,
Mickaël Salaün (10):
landlock: Add Kconfig
bpf: Move u64_to_ptr() to BPF headers and inline it
bpf,landlock: Add a new arraymap type to deal with (Landlock) handles
seccomp: Split put_seccomp_filter() with put_seccomp()
seccomp: Handle Landlock
landlock: Add LSM hooks
landlock: Add errno check
landlock: Handle file system comparisons
landlock: Handle cgroups
samples/landlock: Add sandbox example
include/linux/bpf.h | 41 +++++
include/linux/lsm_hooks.h | 5 +
include/linux/seccomp.h | 54 ++++++-
include/uapi/asm-generic/errno-base.h | 1 +
include/uapi/linux/bpf.h | 103 ++++++++++++
include/uapi/linux/seccomp.h | 2 +
kernel/bpf/arraymap.c | 222 +++++++++++++++++++++++++
kernel/bpf/syscall.c | 18 ++-
kernel/bpf/verifier.c | 32 +++-
kernel/fork.c | 41 ++++-
kernel/seccomp.c | 211 +++++++++++++++++++++++-
samples/Makefile | 2 +-
samples/landlock/.gitignore | 1 +
samples/landlock/Makefile | 16 ++
samples/landlock/sandbox.c | 295 ++++++++++++++++++++++++++++++++++
security/Kconfig | 1 +
security/Makefile | 2 +
security/landlock/Kconfig | 19 +++
security/landlock/Makefile | 3 +
security/landlock/checker_cgroup.c | 96 +++++++++++
security/landlock/checker_cgroup.h | 18 +++
security/landlock/checker_fs.c | 183 +++++++++++++++++++++
security/landlock/checker_fs.h | 20 +++
security/landlock/lsm.c | 228 ++++++++++++++++++++++++++
security/security.c | 1 +
25 files changed, 1592 insertions(+), 23 deletions(-)
create mode 100644 samples/landlock/.gitignore
create mode 100644 samples/landlock/Makefile
create mode 100644 samples/landlock/sandbox.c
create mode 100644 security/landlock/Kconfig
create mode 100644 security/landlock/Makefile
create mode 100644 security/landlock/checker_cgroup.c
create mode 100644 security/landlock/checker_cgroup.h
create mode 100644 security/landlock/checker_fs.c
create mode 100644 security/landlock/checker_fs.h
create mode 100644 security/landlock/lsm.c
--
2.8.1
[toc] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 10/10] samples/landlock: Add sandbox example |
| Message-ID | <sa0op-8hT-7@gated-at.bofh.it> |
| In reply to | #1470049 |
Add a basic sandbox tool to create a process isolated from some part of
the system. This can depend of the current cgroup.
Example:
$ mkdir /sys/fs/cgroup/sandboxed
$ ls /home
user1
$ LANDLOCK_CGROUPS='/sys/fs/cgroup/sandboxed' \
LANDLOCK_ALLOWED='/bin:/lib:/usr:/tmp:/proc/self/fd/0' \
./sandbox /bin/sh -i
$ ls /home
user1
$ echo $$ > /sys/fs/cgroup/sandboxed/cgroup.procs
$ ls /home
ls: cannot open directory '/home': Permission denied
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
samples/Makefile | 2 +-
samples/landlock/.gitignore | 1 +
samples/landlock/Makefile | 16 +++
samples/landlock/sandbox.c | 295 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 313 insertions(+), 1 deletion(-)
create mode 100644 samples/landlock/.gitignore
create mode 100644 samples/landlock/Makefile
create mode 100644 samples/landlock/sandbox.c
diff --git a/samples/Makefile b/samples/Makefile
index 2e3b523d7097..42e6a613f728 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -2,4 +2,4 @@
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \
hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
- configfs/ connector/ v4l/
+ configfs/ connector/ v4l/ landlock/
diff --git a/samples/landlock/.gitignore b/samples/landlock/.gitignore
new file mode 100644
index 000000000000..f6c6da930a30
--- /dev/null
+++ b/samples/landlock/.gitignore
@@ -0,0 +1 @@
+/sandbox
diff --git a/samples/landlock/Makefile b/samples/landlock/Makefile
new file mode 100644
index 000000000000..d1044b2afd27
--- /dev/null
+++ b/samples/landlock/Makefile
@@ -0,0 +1,16 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SECURITY_LANDLOCK) := sandbox
+sandbox-objs := sandbox.o
+
+always := $(hostprogs-y)
+
+HOSTCFLAGS += -I$(objtree)/usr/include
+
+# Trick to allow make to be run from this directory
+all:
+ $(MAKE) -C ../../ $$PWD/
+
+clean:
+ $(MAKE) -C ../../ M=$$PWD clean
diff --git a/samples/landlock/sandbox.c b/samples/landlock/sandbox.c
new file mode 100644
index 000000000000..86604963c30c
--- /dev/null
+++ b/samples/landlock/sandbox.c
@@ -0,0 +1,295 @@
+/*
+ * Landlock LSM - Sandbox Example
+ *
+ * Copyright (C) 2016 Mickaël Salaün <mic@digikod.net>
+ *
+ * The code may be used by anyone for any purpose, and can serve as a starting
+ * point for developing a sandbox.
+ */
+
+#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>
+
+#include "../../tools/include/linux/filter.h"
+
+#include "../bpf/libbpf.c"
+
+#ifndef seccomp
+static int seccomp(unsigned int op, unsigned int flags, void *args)
+{
+ errno = 0;
+ return syscall(__NR_seccomp, op, flags, args);
+}
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+static int apply_sandbox(const char **allowed_paths, int path_nb, const char **cgroup_paths, int cgroup_nb)
+{
+ __u32 key;
+ int i, ret = 0, map_fs = -1, map_cg = -1, offset;
+
+ /* set up the test sandbox */
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ perror("prctl(no_new_priv)");
+ return 1;
+ }
+
+ /* register a new syscall filter */
+ struct sock_filter filter0[] = {
+ /* pass a cookie containing 5 to the LSM hook filter */
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LANDLOCK | 5),
+ };
+ struct sock_fprog prog0 = {
+ .len = (unsigned short)ARRAY_SIZE(filter0),
+ .filter = filter0,
+ };
+ if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog0)) {
+ perror("seccomp(set_filter)");
+ return 1;
+ }
+
+ if (path_nb) {
+ map_fs = bpf_create_map(BPF_MAP_TYPE_LANDLOCK_ARRAY, sizeof(key), sizeof(struct landlock_handle), 10, 0);
+ if (map_fs < 0) {
+ fprintf(stderr, "bpf_create_map(fs");
+ perror(")");
+ return 1;
+ }
+ for (key = 0; key < path_nb; key++) {
+ int fd = open(allowed_paths[key], O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ fprintf(stderr, "open(fs: \"%s\"", allowed_paths[key]);
+ perror(")");
+ return 1;
+ }
+ struct landlock_handle handle = {
+ .type = BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD,
+ .fd = (__u64)fd,
+ };
+
+ /* register a new LSM handle */
+ if (bpf_update_elem(map_fs, &key, &handle, BPF_ANY)) {
+ fprintf(stderr, "bpf_update_elem(fs: \"%s\"", allowed_paths[key]);
+ perror(")");
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ }
+ }
+ if (cgroup_nb) {
+ map_cg = bpf_create_map(BPF_MAP_TYPE_LANDLOCK_ARRAY, sizeof(key), sizeof(struct landlock_handle), 10, 0);
+ if (map_cg < 0) {
+ fprintf(stderr, "bpf_create_map(cgroup");
+ perror(")");
+ ret = 1;
+ goto err_map_cgroup;
+ }
+ for (key = 0; key < cgroup_nb; key++) {
+ int fd = open(cgroup_paths[key], O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ fprintf(stderr, "open(cgroup: \"%s\"", cgroup_paths[key]);
+ perror(")");
+ return 1;
+ }
+ struct landlock_handle handle = {
+ .type = BPF_MAP_HANDLE_TYPE_LANDLOCK_CGROUP_FD,
+ .fd = (__u64)fd,
+ };
+
+ /* register a new LSM handle */
+ if (bpf_update_elem(map_cg, &key, &handle, BPF_ANY)) {
+ fprintf(stderr, "bpf_update_elem(cgroup: \"%s\"", cgroup_paths[key]);
+ perror(")");
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ }
+ }
+
+ /* load a LSM filter hook (eBPF) */
+ struct bpf_insn hook_pre[] = {
+ /* save context */
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+
+ /* check our cookie (not used in this example) */
+ BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_6, offsetof(struct landlock_data, cookie)),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 5, 2),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ struct bpf_insn hook_path[] = {
+ /* specify an option, if any */
+ BPF_MOV32_IMM(BPF_REG_1, 0),
+ /* handles to compare with */
+ BPF_LD_MAP_FD(BPF_REG_2, map_fs),
+ BPF_MOV64_IMM(BPF_REG_3, BPF_MAP_ARRAY_OP_OR),
+ /* hook argument (struct file) */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_6, offsetof(struct landlock_data, args[0])),
+ /* checker function */
+ BPF_EMIT_CALL(BPF_FUNC_landlock_cmp_fs_beneath_with_struct_file),
+
+ /* if the checked path is beneath the handle */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ /* allow anonymous mapping */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -ENOENT, 2),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ /* deny by default, if any error */
+ BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 2),
+ BPF_MOV32_IMM(BPF_REG_0, EACCES),
+ BPF_EXIT_INSN(),
+ };
+ struct bpf_insn hook_cgroup[] = {
+ /* specify an option, if any */
+ BPF_MOV32_IMM(BPF_REG_1, 0),
+ /* handles to compare with */
+ BPF_LD_MAP_FD(BPF_REG_2, map_cg),
+ BPF_MOV64_IMM(BPF_REG_3, BPF_MAP_ARRAY_OP_OR),
+ /* checker function */
+ BPF_EMIT_CALL(BPF_FUNC_landlock_cmp_cgroup_beneath),
+
+ /* if the current process is in a blacklisted cgroup */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 1, 2),
+ BPF_MOV32_IMM(BPF_REG_0, EACCES),
+ BPF_EXIT_INSN(),
+ };
+ struct bpf_insn hook_post[] = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ /* deny all processes if no cgroup is specified */
+ if (cgroup_nb == 0) {
+ hook_post[0] = BPF_MOV32_IMM(BPF_REG_0, EACCES);
+ }
+
+ unsigned long hook_size = sizeof(hook_pre) + sizeof(hook_path) * (path_nb ? 1 : 0) +
+ sizeof(hook_cgroup) * (cgroup_nb ? 1 : 0) + sizeof(hook_post);
+
+ struct bpf_insn *hook0 = malloc(hook_size);
+ if (!hook0) {
+ perror("malloc");
+ ret = 1;
+ goto err_alloc;
+ }
+ memcpy(hook0, hook_pre, sizeof(hook_pre));
+ offset = sizeof(hook_pre) / sizeof(hook0[0]);
+ if (path_nb) {
+ memcpy(hook0 + offset, hook_path, sizeof(hook_path));
+ offset += sizeof(hook_path) / sizeof(hook0[0]);
+ }
+ if (cgroup_nb) {
+ memcpy(hook0 + offset, hook_cgroup, sizeof(hook_cgroup));
+ offset += sizeof(hook_cgroup) / sizeof(hook0[0]);
+ }
+ memcpy(hook0 + offset, hook_post, sizeof(hook_post));
+
+ /* TODO: handle inode_permission hook (e.g. chdir) */
+ enum bpf_prog_type hook_types[] = {
+ BPF_PROG_TYPE_LANDLOCK_FILE_OPEN,
+ BPF_PROG_TYPE_LANDLOCK_FILE_PERMISSION,
+ BPF_PROG_TYPE_LANDLOCK_MMAP_FILE,
+ };
+ for (i = 0; i < ARRAY_SIZE(hook_types); i++) {
+ int bpf0 = bpf_prog_load(hook_types[i],
+ hook0, hook_size, "GPL", 0);
+ if (bpf0 == -1) {
+ perror("bpf");
+ fprintf(stderr, "%s", bpf_log_buf);
+ ret = 1;
+ break;
+ }
+ if (seccomp(SECCOMP_SET_LANDLOCK_HOOK, 0, &bpf0)) {
+ perror("seccomp(set_hook)");
+ ret = 1;
+ close(bpf0);
+ break;
+ }
+ close(bpf0);
+ }
+
+ free(hook0);
+err_alloc:
+ if (cgroup_nb) {
+ close(map_cg);
+ }
+err_map_cgroup:
+ if (path_nb) {
+ close(map_fs);
+ }
+ return ret;
+}
+
+#define ENV_FS_PATH_NAME "LANDLOCK_ALLOWED"
+#define ENV_CGROUP_PATH_NAME "LANDLOCK_CGROUPS"
+#define ENV_PATH_TOKEN ":"
+
+static int parse_path(char *env_path, const char ***path_list) {
+ int i, path_nb = 0;
+
+ if (env_path) {
+ path_nb++;
+ for (i = 0; env_path[i]; i++) {
+ if (env_path[i] == ENV_PATH_TOKEN[0]) {
+ path_nb++;
+ }
+ }
+ }
+ *path_list = malloc(path_nb * sizeof(**path_list));
+ for (i = 0; i < path_nb; i++) {
+ (*path_list)[i] = strsep(&env_path, ENV_PATH_TOKEN);
+ }
+
+ return path_nb;
+}
+
+int main(int argc, char * const argv[], char * const *envp)
+{
+ char *cmd_path;
+ char *env_path_allowed, *env_path_cgroup;
+ int path_nb, cgroup_nb;
+ const char **sb_paths = NULL;
+ const char **cg_paths = NULL;
+ char * const *cmd_argv;
+
+ env_path_allowed = getenv(ENV_FS_PATH_NAME);
+ if (env_path_allowed)
+ env_path_allowed = strdup(env_path_allowed);
+ env_path_cgroup = getenv(ENV_CGROUP_PATH_NAME);
+ if (env_path_cgroup)
+ env_path_cgroup = strdup(env_path_cgroup);
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]);
+ fprintf(stderr, "Environment variables containing paths, each separated by a colon:\n");
+ fprintf(stderr, "* %s (whitelist of allowed files and directories)\n", ENV_FS_PATH_NAME);
+ fprintf(stderr, "* %s (optional cgroups for which the sandbox is enabled)\n", ENV_CGROUP_PATH_NAME);
+ fprintf(stderr, "\nexample:\n%s='/sys/fs/cgroup/sandboxed' %s='/bin:/lib:/usr:/tmp:/proc/self/fd/0' %s /bin/sh -i\n", ENV_CGROUP_PATH_NAME, ENV_FS_PATH_NAME, argv[0]);
+ return 1;
+ }
+ path_nb = parse_path(env_path_allowed, &sb_paths);
+ cgroup_nb = parse_path(env_path_cgroup, &cg_paths);
+ cmd_path = argv[1];
+ cmd_argv = argv + 1;
+ if (apply_sandbox(sb_paths, path_nb, cg_paths, cgroup_nb))
+ return 1;
+ execve(cmd_path, cmd_argv, envp);
+ perror("execve");
+ return 1;
+}
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 05/10] seccomp: Handle Landlock |
| Message-ID | <sa0op-8hT-13@gated-at.bofh.it> |
| In reply to | #1470049 |
A Landlock program can be triggered when a seccomp filter return
RET_LANDLOCK. Moreover, it is possible to return a 16-bit cookie which
will be readable by the Landlock programs.
Only seccomp filters loaded from the same thread and before a Landlock
program can trigger it. Multiple Landlock programs can be triggered by
one or more seccomp filters. This way, each RET_LANDLOCK (with specific
cookie) will trigger all the allowed Landlock programs once.
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Will Drewry <wad@chromium.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
include/linux/seccomp.h | 49 +++++++++++
include/uapi/linux/seccomp.h | 2 +
kernel/fork.c | 39 ++++++++-
kernel/seccomp.c | 190 ++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 275 insertions(+), 5 deletions(-)
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index 29b20fe8fd4d..785ccbebf687 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -10,7 +10,33 @@
#include <linux/thread_info.h>
#include <asm/seccomp.h>
+#ifdef CONFIG_SECURITY_LANDLOCK
+#include <linux/bpf.h> /* struct bpf_prog */
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
struct seccomp_filter;
+
+#ifdef CONFIG_SECURITY_LANDLOCK
+struct seccomp_landlock_ret {
+ struct seccomp_landlock_ret *prev;
+ /* @filter points to a @landlock_filter list */
+ struct seccomp_filter *filter;
+ u16 cookie;
+ bool triggered;
+};
+
+struct seccomp_landlock_prog {
+ atomic_t usage;
+ struct seccomp_landlock_prog *prev;
+ /*
+ * List of filters (through filter->landlock_prev) allowed to trigger
+ * this Landlock program.
+ */
+ struct seccomp_filter *filter;
+ struct bpf_prog *prog;
+};
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
/**
* struct seccomp - the state of a seccomp'ed process
*
@@ -18,6 +44,10 @@ 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_filter: list of filters allowed to trigger an associated
+ * Landlock hook via a RET_LANDLOCK.
+ * @landlock_ret: stored values from a RET_LANDLOCK.
+ * @landlock_prog: list of Landlock programs.
*
* @filter must only be accessed from the context of current as there
* is no read locking.
@@ -25,6 +55,12 @@ struct seccomp_filter;
struct seccomp {
int mode;
struct seccomp_filter *filter;
+
+#ifdef CONFIG_SECURITY_LANDLOCK
+ struct seccomp_filter *landlock_filter;
+ struct seccomp_landlock_ret *landlock_ret;
+ struct seccomp_landlock_prog *landlock_prog;
+#endif /* CONFIG_SECURITY_LANDLOCK */
};
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
@@ -85,6 +121,12 @@ static inline int seccomp_mode(struct seccomp *s)
#ifdef CONFIG_SECCOMP_FILTER
extern void put_seccomp(struct task_struct *tsk);
extern void get_seccomp_filter(struct task_struct *tsk);
+#ifdef CONFIG_SECURITY_LANDLOCK
+extern void put_landlock_ret(struct seccomp_landlock_ret *landlock_ret);
+extern struct seccomp_landlock_ret *dup_landlock_ret(
+ struct seccomp_landlock_ret *ret_orig);
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
#else /* CONFIG_SECCOMP_FILTER */
static inline void put_seccomp(struct task_struct *tsk)
{
@@ -95,6 +137,13 @@ static inline void get_seccomp_filter(struct task_struct *tsk)
{
return;
}
+
+#ifdef CONFIG_SECURITY_LANDLOCK
+static inline void put_landlock_ret(struct seccomp_landlock_ret *landlock_ret) {}
+static inline struct seccomp_landlock_ret *dup_landlock_ret(
+ struct seccomp_landlock_ret *ret_orig) {}
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
#endif /* CONFIG_SECCOMP_FILTER */
#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index 0f238a43ff1e..b4aab1c19b8a 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_SET_LANDLOCK_HOOK 2
/* Valid flags for SECCOMP_SET_MODE_FILTER */
#define SECCOMP_FILTER_FLAG_TSYNC 1
@@ -28,6 +29,7 @@
#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
+#define SECCOMP_RET_LANDLOCK 0x00070000U /* trigger LSM evaluation */
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
diff --git a/kernel/fork.c b/kernel/fork.c
index b23a71ec8003..3658c1e95e03 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -369,7 +369,12 @@ 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_filter = NULL;
+ tsk->seccomp.landlock_ret = NULL;
+ tsk->seccomp.landlock_prog = NULL;
+#endif /* CONFIG_SECURITY_LANDLOCK */
+#endif /* CONFIG_SECCOMP */
setup_thread_stack(tsk, orig);
clear_user_return_notifier(tsk);
@@ -1200,9 +1205,12 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
return 0;
}
-static void copy_seccomp(struct task_struct *p)
+static int copy_seccomp(struct task_struct *p)
{
#ifdef CONFIG_SECCOMP
+#ifdef CONFIG_SECURITY_LANDLOCK
+ struct seccomp_landlock_ret *ret_walk;
+#endif /* CONFIG_SECURITY_LANDLOCK */
/*
* Must be called with sighand->lock held, which is common to
* all threads in the group. Holding cred_guard_mutex is not
@@ -1213,7 +1221,27 @@ static void copy_seccomp(struct task_struct *p)
/* Ref-count the new filter user, and assign it. */
get_seccomp_filter(current);
- p->seccomp = current->seccomp;
+ p->seccomp.mode = current->seccomp.mode;
+ p->seccomp.filter = current->seccomp.filter;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ /* No copy for: landlock_filter, landlock_handle */
+ p->seccomp.landlock_prog = current->seccomp.landlock_prog;
+ if (p->seccomp.landlock_prog)
+ atomic_inc(&p->seccomp.landlock_prog->usage);
+ /* Deep copy for landlock_ret to avoid allocating for each syscall */
+ for (ret_walk = current->seccomp.landlock_ret;
+ ret_walk; ret_walk = ret_walk->prev) {
+ struct seccomp_landlock_ret *ret_new;
+
+ ret_new = dup_landlock_ret(ret_walk);
+ if (IS_ERR(ret_new)) {
+ put_landlock_ret(p->seccomp.landlock_ret);
+ return PTR_ERR(ret_new);
+ }
+ ret_new->prev = p->seccomp.landlock_ret;
+ p->seccomp.landlock_ret = ret_new;
+ }
+#endif /* CONFIG_SECURITY_LANDLOCK */
/*
* Explicitly enable no_new_privs here in case it got set
@@ -1231,6 +1259,7 @@ static void copy_seccomp(struct task_struct *p)
if (p->seccomp.mode != SECCOMP_MODE_DISABLED)
set_tsk_thread_flag(p, TIF_SECCOMP);
#endif
+ return 0;
}
SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)
@@ -1589,7 +1618,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
* Copy seccomp details explicitly here, in case they were changed
* before holding sighand lock.
*/
- copy_seccomp(p);
+ retval = copy_seccomp(p);
+ if (retval)
+ goto bad_fork_cancel_cgroup;
/*
* Process group and session signals need to be delivered to just the
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index f1f475691c27..5df7274c7ec3 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -6,6 +6,8 @@
* Copyright (C) 2012 Google, Inc.
* Will Drewry <wad@chromium.org>
*
+ * Copyright (C) 2016 Mickaël Salaün <mic@digikod.net>
+ *
* This defines a simple but solid secure-computing facility.
*
* Mode 1 uses a fixed list of allowed system calls.
@@ -33,6 +35,10 @@
#include <linux/tracehook.h>
#include <linux/uaccess.h>
+#ifdef CONFIG_SECURITY_LANDLOCK
+#include <linux/bpf.h> /* bpf_prog_put() */
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
/**
* struct seccomp_filter - container for seccomp BPF programs
*
@@ -58,6 +64,9 @@ struct seccomp_filter {
atomic_t usage;
struct seccomp_filter *prev;
struct bpf_prog *prog;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ struct seccomp_filter *landlock_prev;
+#endif /* CONFIG_SECURITY_LANDLOCK */
};
static void put_seccomp_filter(struct seccomp_filter *filter);
@@ -179,6 +188,10 @@ static u32 seccomp_run_filters(struct seccomp_data *sd)
{
struct seccomp_data sd_local;
u32 ret = SECCOMP_RET_ALLOW;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ struct seccomp_landlock_ret *landlock_ret, *init_landlock_ret =
+ current->seccomp.landlock_ret;
+#endif /* CONFIG_SECURITY_LANDLOCK */
/* Make sure cross-thread synced filter points somewhere sane. */
struct seccomp_filter *f =
lockless_dereference(current->seccomp.filter);
@@ -191,6 +204,14 @@ static u32 seccomp_run_filters(struct seccomp_data *sd)
populate_seccomp_data(&sd_local);
sd = &sd_local;
}
+#ifdef CONFIG_SECURITY_LANDLOCK
+ for (landlock_ret = init_landlock_ret;
+ landlock_ret;
+ landlock_ret = landlock_ret->prev) {
+ /* No need to clean the cookie. */
+ landlock_ret->triggered = false;
+ }
+#endif /* CONFIG_SECURITY_LANDLOCK */
/*
* All filters in the list are evaluated and the lowest BPF return
@@ -198,8 +219,27 @@ static u32 seccomp_run_filters(struct seccomp_data *sd)
*/
for (; f; f = f->prev) {
u32 cur_ret = BPF_PROG_RUN(f->prog, (void *)sd);
+ u32 action = cur_ret & SECCOMP_RET_ACTION;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ u32 data = cur_ret & SECCOMP_RET_DATA;
+ if (action == SECCOMP_RET_LANDLOCK) {
+ /*
+ * Keep track of filters from the current task that
+ * trigger a RET_LANDLOCK.
+ */
+ for (landlock_ret = init_landlock_ret;
+ landlock_ret;
+ landlock_ret = landlock_ret->prev) {
+ if (landlock_ret->filter == f) {
+ landlock_ret->triggered = true;
+ landlock_ret->cookie = data;
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_SECURITY_LANDLOCK */
- if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
+ if (action < (ret & SECCOMP_RET_ACTION))
ret = cur_ret;
}
return ret;
@@ -426,6 +466,9 @@ static long seccomp_attach_filter(unsigned int flags,
{
unsigned long total_insns;
struct seccomp_filter *walker;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ struct seccomp_landlock_ret *landlock_ret;
+#endif /* CONFIG_SECURITY_LANDLOCK */
assert_spin_locked(¤t->sighand->siglock);
@@ -450,6 +493,21 @@ static long seccomp_attach_filter(unsigned int flags,
* task reference.
*/
filter->prev = current->seccomp.filter;
+#ifdef CONFIG_SECURITY_LANDLOCK
+ filter->landlock_prev = current->seccomp.landlock_filter;
+ current->seccomp.landlock_filter = filter;
+
+ /* Dedicated Landlock result */
+ landlock_ret = kmalloc(sizeof(*landlock_ret), GFP_KERNEL);
+ if (!landlock_ret)
+ return -ENOMEM;
+ landlock_ret->prev = current->seccomp.landlock_ret;
+ atomic_inc(&filter->usage);
+ landlock_ret->filter = filter;
+ landlock_ret->cookie = 0;
+ landlock_ret->triggered = false;
+ current->seccomp.landlock_ret = landlock_ret;
+#endif /* CONFIG_SECURITY_LANDLOCK */
current->seccomp.filter = filter;
/* Now that the new filter is in place, synchronize to all threads. */
@@ -459,6 +517,55 @@ static long seccomp_attach_filter(unsigned int flags,
return 0;
}
+#ifdef CONFIG_SECURITY_LANDLOCK
+struct seccomp_landlock_ret *dup_landlock_ret(
+ struct seccomp_landlock_ret *ret_orig)
+{
+ struct seccomp_landlock_ret *ret_new;
+
+ if (!ret_orig)
+ return NULL;
+ ret_new = kmalloc(sizeof(*ret_new), GFP_KERNEL);
+ if (!ret_new)
+ return ERR_PTR(-ENOMEM);
+ ret_new->filter = ret_orig->filter;
+ if (ret_new->filter)
+ atomic_inc(&ret_new->filter->usage);
+ ret_new->cookie = 0;
+ ret_new->triggered = false;
+ ret_new->prev = NULL;
+ return ret_new;
+}
+
+static void put_landlock_prog(struct seccomp_landlock_prog *landlock_prog)
+{
+ struct seccomp_landlock_prog *orig = landlock_prog;
+
+ /* Clean up single-reference branches iteratively. */
+ while (orig && atomic_dec_and_test(&orig->usage)) {
+ struct seccomp_landlock_prog *freeme = orig;
+
+ put_seccomp_filter(orig->filter);
+ bpf_prog_put(orig->prog);
+ orig = orig->prev;
+ kfree(freeme);
+ }
+}
+
+void put_landlock_ret(struct seccomp_landlock_ret *landlock_ret)
+{
+ struct seccomp_landlock_ret *orig = landlock_ret;
+
+ while (orig) {
+ struct seccomp_landlock_ret *freeme = orig;
+
+ put_seccomp_filter(orig->filter);
+ orig = orig->prev;
+ kfree(freeme);
+ }
+}
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
/* get_seccomp_filter - increments the reference count of the filter on @tsk */
void get_seccomp_filter(struct task_struct *tsk)
{
@@ -485,7 +592,9 @@ static void put_seccomp_filter(struct seccomp_filter *filter)
/* Clean up single-reference branches iteratively. */
while (orig && atomic_dec_and_test(&orig->usage)) {
struct seccomp_filter *freeme = orig;
+
orig = orig->prev;
+ /* must not put orig->landlock_prev */
seccomp_filter_free(freeme);
}
}
@@ -493,6 +602,10 @@ static void put_seccomp_filter(struct seccomp_filter *filter)
void put_seccomp(struct task_struct *tsk)
{
put_seccomp_filter(tsk->seccomp.filter);
+#ifdef CONFIG_SECURITY_LANDLOCK
+ put_landlock_prog(tsk->seccomp.landlock_prog);
+ put_landlock_ret(tsk->seccomp.landlock_ret);
+#endif /* CONFIG_SECURITY_LANDLOCK */
}
/**
@@ -609,6 +722,8 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)
case SECCOMP_RET_TRACE:
return filter_ret; /* Save the rest for phase 2. */
+ case SECCOMP_RET_LANDLOCK:
+ /* fall through */
case SECCOMP_RET_ALLOW:
return SECCOMP_PHASE1_OK;
@@ -814,6 +929,75 @@ static inline long seccomp_set_mode_filter(unsigned int flags,
}
#endif
+
+#ifdef CONFIG_SECURITY_LANDLOCK
+
+/* Limit Landlock programs to 256KB. */
+#define LANDLOCK_PROG_LIST_MAX_PAGES (1 << 6)
+
+static long landlock_set_hook(unsigned int flags, const char __user *user_bpf_fd)
+{
+ long result;
+ unsigned long prog_list_pages;
+ struct seccomp_landlock_prog *landlock_prog, *cp_walker;
+ int bpf_fd;
+ struct bpf_prog *prog;
+
+ if (!task_no_new_privs(current) &&
+ security_capable_noaudit(current_cred(),
+ current_user_ns(), CAP_SYS_ADMIN) != 0)
+ return -EACCES;
+ if (!user_bpf_fd)
+ return -EINVAL;
+
+ /* could be used for TSYNC */
+ if (flags)
+ return -EINVAL;
+
+ if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(user_bpf_fd)))
+ return -EFAULT;
+ prog = bpf_prog_get(bpf_fd);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+ switch (prog->type) {
+ /* TODO: add LSM hooks */
+ default:
+ result = -EINVAL;
+ goto put_prog;
+ }
+
+ /* validate allocated memory */
+ prog_list_pages = prog->pages;
+ for (cp_walker = current->seccomp.landlock_prog; cp_walker;
+ cp_walker = cp_walker->prev) {
+ /* TODO: add penalty for each prog? */
+ prog_list_pages += cp_walker->prog->pages;
+ }
+ if (prog_list_pages > LANDLOCK_PROG_LIST_MAX_PAGES) {
+ result = -ENOMEM;
+ goto put_prog;
+ }
+
+ landlock_prog = kmalloc(sizeof(*landlock_prog), GFP_KERNEL);
+ if (!landlock_prog) {
+ result = -ENOMEM;
+ goto put_prog;
+ }
+ landlock_prog->prog = prog;
+ landlock_prog->filter = current->seccomp.filter;
+ if (landlock_prog->filter)
+ atomic_inc(&landlock_prog->filter->usage);
+ atomic_set(&landlock_prog->usage, 1);
+ landlock_prog->prev = current->seccomp.landlock_prog;
+ current->seccomp.landlock_prog = landlock_prog;
+ return 0;
+
+put_prog:
+ bpf_prog_put(prog);
+ return result;
+}
+#endif /* CONFIG_SECURITY_LANDLOCK */
+
/* Common entry point for both prctl and syscall. */
static long do_seccomp(unsigned int op, unsigned int flags,
const char __user *uargs)
@@ -825,6 +1009,10 @@ 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);
+#ifdef CONFIG_SECURITY_LANDLOCK
+ case SECCOMP_SET_LANDLOCK_HOOK:
+ return landlock_set_hook(flags, uargs);
+#endif /* CONFIG_SECURITY_LANDLOCK */
default:
return -EINVAL;
}
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 02/10] bpf: Move u64_to_ptr() to BPF headers and inline it |
| Message-ID | <sa0op-8hT-17@gated-at.bofh.it> |
| In reply to | #1470049 |
This helper will be useful for arraymap (next commit).
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/bpf.h | 6 ++++++
kernel/bpf/syscall.c | 6 ------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0de4de6dd43e..ca3742729ae7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -251,6 +251,12 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
/* verify correctness of eBPF program */
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
+
+/* helper to convert user pointers passed inside __aligned_u64 fields */
+static inline void __user *u64_to_ptr(__u64 val)
+{
+ return (void __user *) (unsigned long) val;
+}
#else
static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl)
{
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 46ecce4b79ed..d305a3ce0fa7 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -247,12 +247,6 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd)
return map;
}
-/* helper to convert user pointers passed inside __aligned_u64 fields */
-static void __user *u64_to_ptr(__u64 val)
-{
- return (void __user *) (unsigned long) val;
-}
-
int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value)
{
return -ENOTSUPP;
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 08/10] landlock: Handle file system comparisons |
| Message-ID | <sa0oq-8hT-29@gated-at.bofh.it> |
| In reply to | #1470049 |
Add eBPF functions to compare file system access with a Landlock file
system handle:
* bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file)
This function allows to compare the dentry, inode, device or mount
point of the currently accessed file, with a reference handle.
* bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file)
This function allows an eBPF program to check if the current accessed
file is the same or in the hierarchy of a reference handle.
The goal of file system handle is to abstract kernel objects such as a
struct file or a struct inode. Userland can create this kind of handle
thanks to the BPF_MAP_UPDATE_ELEM command. The element is a struct
landlock_handle containing the handle type (e.g.
BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) and a file descriptor. This could
also be any descriptions able to match a struct file or a struct inode
(e.g. path or glob string).
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/bpf.h | 4 +-
include/uapi/linux/bpf.h | 52 +++++++++++-
kernel/bpf/arraymap.c | 17 +++-
kernel/bpf/verifier.c | 6 ++
security/landlock/Makefile | 2 +-
security/landlock/checker_fs.c | 183 +++++++++++++++++++++++++++++++++++++++++
security/landlock/checker_fs.h | 20 +++++
security/landlock/lsm.c | 11 ++-
8 files changed, 288 insertions(+), 7 deletions(-)
create mode 100644 security/landlock/checker_fs.c
create mode 100644 security/landlock/checker_fs.h
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 557e7efdf0cd..79014aedbea4 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -84,6 +84,7 @@ enum bpf_arg_type {
ARG_PTR_TO_STRUCT_FILE, /* pointer to struct file */
ARG_PTR_TO_STRUCT_CRED, /* pointer to struct cred */
+ ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS, /* pointer to Landlock FS handle */
};
/* type of values returned from helper functions */
@@ -146,6 +147,7 @@ enum bpf_reg_type {
/* Landlock */
PTR_TO_STRUCT_FILE,
PTR_TO_STRUCT_CRED,
+ CONST_PTR_TO_LANDLOCK_HANDLE_FS,
};
struct bpf_prog;
@@ -207,7 +209,7 @@ struct bpf_array {
#ifdef CONFIG_SECURITY_LANDLOCK
struct map_landlock_handle {
- u32 type;
+ u32 type; /* e.g. BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD */
union {
struct file *file;
};
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 983d14e910ff..88af79dd668c 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -89,10 +89,20 @@ enum bpf_map_type {
enum bpf_map_array_type {
BPF_MAP_ARRAY_TYPE_UNSPEC,
+ BPF_MAP_ARRAY_TYPE_LANDLOCK_FS,
};
enum bpf_map_handle_type {
BPF_MAP_HANDLE_TYPE_UNSPEC,
+ BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD,
+ BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_GLOB,
+};
+
+enum bpf_map_array_op {
+ BPF_MAP_ARRAY_OP_UNSPEC,
+ BPF_MAP_ARRAY_OP_OR,
+ BPF_MAP_ARRAY_OP_AND,
+ BPF_MAP_ARRAY_OP_XOR,
};
enum bpf_prog_type {
@@ -325,6 +335,35 @@ enum bpf_func_id {
*/
BPF_FUNC_skb_get_tunnel_opt,
BPF_FUNC_skb_set_tunnel_opt,
+
+ /**
+ * bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file)
+ * Compare file system handles with a struct file
+ *
+ * @prop: properties to check against (e.g. LANDLOCK_FLAG_FS_DENTRY)
+ * @map: handles to compare against
+ * @map_op: which elements of the map to use (e.g. BPF_MAP_ARRAY_OP_OR)
+ * @file: struct file address to compare with (taken from the context)
+ *
+ * Return: 0 if the file match the handles, 1 otherwise, or a negative
+ * value if an error occurred.
+ */
+ BPF_FUNC_landlock_cmp_fs_prop_with_struct_file,
+
+ /**
+ * bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file)
+ * Check if a struct file is a leaf of file system handles
+ *
+ * @opt: check options (e.g. LANDLOCK_FLAG_OPT_REVERSE)
+ * @map: handles to compare against
+ * @map_op: which elements of the map to use (e.g. BPF_MAP_ARRAY_OP_OR)
+ * @file: struct file address to compare with (taken from the context)
+ *
+ * Return: 0 if the file is the same or beneath the handles,
+ * 1 otherwise, or a negative value if an error occurred.
+ */
+ BPF_FUNC_landlock_cmp_fs_beneath_with_struct_file,
+
__BPF_FUNC_MAX_ID,
};
@@ -398,6 +437,17 @@ struct bpf_tunnel_key {
__u32 tunnel_label;
};
+/* Handle check flags */
+#define LANDLOCK_FLAG_FS_DENTRY (1 << 0)
+#define LANDLOCK_FLAG_FS_INODE (1 << 1)
+#define LANDLOCK_FLAG_FS_DEVICE (1 << 2)
+#define LANDLOCK_FLAG_FS_MOUNT (1 << 3)
+#define _LANDLOCK_FLAG_FS_MASK ((1 << 4) - 1)
+
+/* Handle option flags */
+#define LANDLOCK_FLAG_OPT_REVERSE (1<<0)
+#define _LANDLOCK_FLAG_OPT_MASK ((1 << 1) - 1)
+
/* Map handle entry */
struct landlock_handle {
__u32 type; /* enum bpf_map_handle_type */
@@ -410,7 +460,7 @@ struct landlock_handle {
/**
* struct landlock_data
*
- * @hook: LSM hook ID
+ * @hook: LSM hook ID (e.g. BPF_PROG_TYPE_LANDLOCK_FILE_OPEN)
* @cookie: value set by a seccomp-filter return value RET_LANDLOCK. This come
* from a trusted seccomp-bpf program: the same process that loaded
* this Landlock hook program.
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 5938b8ee475b..6804dafd8355 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -508,7 +508,12 @@ static struct bpf_map *landlock_array_map_alloc(union bpf_attr *attr)
static void landlock_put_handle(struct map_landlock_handle *handle)
{
switch (handle->type) {
- /* TODO: add handle types */
+ case BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD:
+ if (likely(handle->file))
+ fput(handle->file);
+ else
+ WARN_ON(1);
+ break;
default:
WARN_ON(1);
}
@@ -533,7 +538,9 @@ static enum bpf_map_array_type landlock_get_array_type(
enum bpf_map_handle_type handle_type)
{
switch (handle_type) {
- /* TODO: add handle types */
+ case BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD:
+ case BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_GLOB:
+ return BPF_MAP_ARRAY_TYPE_LANDLOCK_FS;
case BPF_MAP_HANDLE_TYPE_UNSPEC:
default:
return -EINVAL;
@@ -550,6 +557,7 @@ static inline long landlock_store_handle(struct map_landlock_handle *dst,
struct landlock_handle *khandle)
{
struct path kpath;
+ struct file *handle_file;
if (unlikely(!khandle))
return -EINVAL;
@@ -557,7 +565,10 @@ static inline long landlock_store_handle(struct map_landlock_handle *dst,
/* access control already done for the FD */
switch (khandle->type) {
- /* TODO: add handle types */
+ case BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD:
+ FGET_OR_RET(handle_file, khandle->fd);
+ dst->file = handle_file;
+ break;
default:
WARN_ON(1);
path_put(&kpath);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2931e2efcc10..b182c88d5c13 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -246,6 +246,7 @@ static const char * const reg_type_str[] = {
[PTR_TO_PACKET_END] = "pkt_end",
[PTR_TO_STRUCT_FILE] = "struct_file",
[PTR_TO_STRUCT_CRED] = "struct_cred",
+ [CONST_PTR_TO_LANDLOCK_HANDLE_FS] = "landlock_handle_fs",
};
static void print_verifier_state(struct verifier_state *state)
@@ -558,6 +559,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case CONST_PTR_TO_MAP:
case PTR_TO_STRUCT_FILE:
case PTR_TO_STRUCT_CRED:
+ case CONST_PTR_TO_LANDLOCK_HANDLE_FS:
return true;
default:
return false;
@@ -951,6 +953,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
expected_type = PTR_TO_STRUCT_FILE;
} else if (arg_type == ARG_PTR_TO_STRUCT_CRED) {
expected_type = PTR_TO_STRUCT_CRED;
+ } else if (arg_type == ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS) {
+ expected_type = CONST_PTR_TO_LANDLOCK_HANDLE_FS;
} else if (arg_type == ARG_PTR_TO_STACK ||
arg_type == ARG_PTR_TO_RAW_STACK) {
expected_type = PTR_TO_STACK;
@@ -1727,6 +1731,8 @@ static struct bpf_map *ld_imm64_to_map_ptr(struct bpf_insn *insn)
static inline enum bpf_reg_type bpf_reg_type_from_map(struct bpf_map *map)
{
switch (map->map_array_type) {
+ case BPF_MAP_ARRAY_TYPE_LANDLOCK_FS:
+ return CONST_PTR_TO_LANDLOCK_HANDLE_FS;
case BPF_MAP_ARRAY_TYPE_UNSPEC:
default:
return CONST_PTR_TO_MAP;
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 59669d70bc7e..27f359a8cfaa 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
-landlock-y := lsm.o
+landlock-y := lsm.o checker_fs.o
diff --git a/security/landlock/checker_fs.c b/security/landlock/checker_fs.c
new file mode 100644
index 000000000000..4d2f1e5d41b6
--- /dev/null
+++ b/security/landlock/checker_fs.c
@@ -0,0 +1,183 @@
+/*
+ * Landlock LSM - File System Checkers
+ *
+ * Copyright (C) 2016 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h> /* enum bpf_map_array_op */
+#include <linux/errno.h>
+#include <linux/fs.h> /* path_is_under() */
+#include <linux/path.h> /* struct path */
+
+#include "checker_fs.h"
+
+#define EQUAL_NOT_NULL(a, b) (a && a == b)
+
+/*
+ * bpf_landlock_cmp_fs_prop_with_struct_file
+ *
+ * Cf. include/uapi/linux/bpf.h
+ */
+static inline u64 bpf_landlock_cmp_fs_prop_with_struct_file(u64 r1_property,
+ u64 r2_map, u64 r3_map_op, u64 r4_file, u64 r5)
+{
+ u8 property = (u8) r1_property;
+ struct bpf_map *map = (struct bpf_map *) (unsigned long) r2_map;
+ enum bpf_map_array_op map_op = r3_map_op;
+ struct file *file = (struct file *) (unsigned long) r4_file;
+ struct bpf_array *array = container_of(map, struct bpf_array, map);
+ struct path *p1, *p2;
+ struct map_landlock_handle *handle;
+ int i;
+ bool result_dentry = !(property & LANDLOCK_FLAG_FS_DENTRY);
+ bool result_inode = !(property & LANDLOCK_FLAG_FS_INODE);
+ bool result_device = !(property & LANDLOCK_FLAG_FS_DEVICE);
+ bool result_mount = !(property & LANDLOCK_FLAG_FS_MOUNT);
+
+ /* ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS is a arraymap */
+ if (unlikely(!map)) {
+ WARN_ON(1);
+ return -EFAULT;
+ }
+ if (unlikely(!file))
+ return -ENOENT;
+ if (unlikely((property | _LANDLOCK_FLAG_FS_MASK) != _LANDLOCK_FLAG_FS_MASK))
+ return -EINVAL;
+
+ /* for now, only handle OP_OR */
+ switch (map_op) {
+ case BPF_MAP_ARRAY_OP_OR:
+ break;
+ case BPF_MAP_ARRAY_OP_UNSPEC:
+ case BPF_MAP_ARRAY_OP_AND:
+ case BPF_MAP_ARRAY_OP_XOR:
+ default:
+ return -EINVAL;
+ }
+
+ synchronize_rcu();
+
+ for (i = 0; i < array->n_entries; i++) {
+ handle = (struct map_landlock_handle *)
+ (array->value + array->elem_size * i);
+
+ if (handle->type != BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) {
+ WARN_ON(1);
+ return -EFAULT;
+ }
+
+ p1 = &handle->file->f_path;
+ p2 = &file->f_path;
+ if (unlikely(!p1 || !p2)) {
+ WARN_ON(1);
+ return -EFAULT;
+ }
+
+ if (!result_dentry && p1->dentry == p2->dentry)
+ result_dentry = true;
+ /* TODO: use d_inode_rcu() instead? */
+ if (!result_inode
+ && EQUAL_NOT_NULL(d_inode(p1->dentry)->i_ino,
+ d_inode(p2->dentry)->i_ino))
+ result_inode = true;
+ /* check superblock instead of device major/minor */
+ if (!result_device
+ && EQUAL_NOT_NULL(d_inode(p1->dentry)->i_sb,
+ d_inode(p2->dentry)->i_sb))
+ result_device = true;
+ if (!result_mount && EQUAL_NOT_NULL(p1->mnt, p2->mnt))
+ result_mount = true;
+ if (result_dentry && result_inode && result_device && result_mount)
+ return 0;
+ }
+ return 1;
+}
+
+const struct bpf_func_proto bpf_landlock_cmp_fs_prop_with_struct_file_proto = {
+ .func = bpf_landlock_cmp_fs_prop_with_struct_file,
+ .gpl_only = true,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_ANYTHING,
+ .arg2_type = ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_PTR_TO_STRUCT_FILE,
+};
+
+/*
+ * bpf_landlock_cmp_fs_beneath_with_struct_file
+ *
+ * Cf. include/uapi/linux/bpf.h
+ */
+static inline u64 bpf_landlock_cmp_fs_beneath_with_struct_file(u64 r1_option,
+ u64 r2_map, u64 r3_map_op, u64 r4_file, u64 r5)
+{
+ u8 option = (u8) r1_option;
+ struct bpf_map *map = (struct bpf_map *) (unsigned long) r2_map;
+ enum bpf_map_array_op map_op = r3_map_op;
+ struct file *file = (struct file *) (unsigned long) r4_file;
+ struct bpf_array *array = container_of(map, struct bpf_array, map);
+ struct path *p1, *p2;
+ struct map_landlock_handle *handle;
+ int i;
+
+ /* ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS is an arraymap */
+ if (unlikely(!map)) {
+ WARN_ON(1);
+ return -EFAULT;
+ }
+ /* @file can be null for anonymous mmap */
+ if (unlikely(!file))
+ return -ENOENT;
+ if (unlikely((option | _LANDLOCK_FLAG_OPT_MASK) != _LANDLOCK_FLAG_OPT_MASK))
+ return -EINVAL;
+
+ /* for now, only handle OP_OR */
+ switch (map_op) {
+ case BPF_MAP_ARRAY_OP_OR:
+ break;
+ case BPF_MAP_ARRAY_OP_UNSPEC:
+ case BPF_MAP_ARRAY_OP_AND:
+ case BPF_MAP_ARRAY_OP_XOR:
+ default:
+ return -EINVAL;
+ }
+
+ synchronize_rcu();
+
+ for (i = 0; i < array->n_entries; i++) {
+ handle = (struct map_landlock_handle *)
+ (array->value + array->elem_size * i);
+
+ /* protected by the proto types, should not happen */
+ if (unlikely(handle->type != BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD)) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (option & LANDLOCK_FLAG_OPT_REVERSE) {
+ p1 = &file->f_path;
+ p2 = &handle->file->f_path;
+ } else {
+ p1 = &handle->file->f_path;
+ p2 = &file->f_path;
+ }
+
+ if (path_is_under(p2, p1))
+ return 0;
+ }
+ return 1;
+}
+
+const struct bpf_func_proto bpf_landlock_cmp_fs_beneath_with_struct_file_proto = {
+ .func = bpf_landlock_cmp_fs_beneath_with_struct_file,
+ .gpl_only = true,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_ANYTHING,
+ .arg2_type = ARG_CONST_PTR_TO_LANDLOCK_HANDLE_FS,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_PTR_TO_STRUCT_FILE,
+};
diff --git a/security/landlock/checker_fs.h b/security/landlock/checker_fs.h
new file mode 100644
index 000000000000..a62f84e39efd
--- /dev/null
+++ b/security/landlock/checker_fs.h
@@ -0,0 +1,20 @@
+/*
+ * Landlock LSM - File System Checkers
+ *
+ * Copyright (C) 2016 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SECURITY_LANDLOCK_CHECKER_FS_H
+#define _SECURITY_LANDLOCK_CHECKER_FS_H
+
+#include <linux/fs.h>
+#include <linux/seccomp.h>
+
+extern const struct bpf_func_proto bpf_landlock_cmp_fs_prop_with_struct_file_proto;
+extern const struct bpf_func_proto bpf_landlock_cmp_fs_beneath_with_struct_file_proto;
+
+#endif /* _SECURITY_LANDLOCK_CHECKER_FS_H */
diff --git a/security/landlock/lsm.c b/security/landlock/lsm.c
index 322309068066..8645743243b6 100644
--- a/security/landlock/lsm.c
+++ b/security/landlock/lsm.c
@@ -16,6 +16,8 @@
#include <linux/lsm_hooks.h>
#include <linux/seccomp.h> /* struct seccomp_* */
+#include "checker_fs.h"
+
#define LANDLOCK_HOOK_INIT(NAME) LSM_HOOK_INIT(NAME, landlock_hook_##NAME)
#define LANDLOCK_HOOKx(X, NAME, CNAME, ...) \
@@ -117,7 +119,14 @@ static int landlock_run_prog(__u64 args[6])
static const struct bpf_func_proto *bpf_landlock_func_proto(
enum bpf_func_id func_id)
{
- return NULL;
+ switch (func_id) {
+ case BPF_FUNC_landlock_cmp_fs_prop_with_struct_file:
+ return &bpf_landlock_cmp_fs_prop_with_struct_file_proto;
+ case BPF_FUNC_landlock_cmp_fs_beneath_with_struct_file:
+ return &bpf_landlock_cmp_fs_beneath_with_struct_file_proto;
+ default:
+ return NULL;
+ }
}
static u32 landlock_convert_ctx_access(enum bpf_access_type type, int dst_reg,
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-25 13:20 +0200 |
| Subject | Re: [RFC v2 08/10] landlock: Handle file system comparisons |
| Message-ID | <sa0Rs-fC-35@gated-at.bofh.it> |
| In reply to | #1470054 |
On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: > Add eBPF functions to compare file system access with a Landlock file > system handle: > * bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file) > This function allows to compare the dentry, inode, device or mount > point of the currently accessed file, with a reference handle. > * bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file) > This function allows an eBPF program to check if the current accessed > file is the same or in the hierarchy of a reference handle. > > The goal of file system handle is to abstract kernel objects such as a > struct file or a struct inode. Userland can create this kind of handle > thanks to the BPF_MAP_UPDATE_ELEM command. The element is a struct > landlock_handle containing the handle type (e.g. > BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) and a file descriptor. This could > also be any descriptions able to match a struct file or a struct inode > (e.g. path or glob string). This needs Eric's opinion. Also, where do all the struct file *'s get stashed? Are they preserved in the arraymap? What prevents reference cycles or absurdly large numbers of struct files getting pinned? --Andy
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 16:20 +0200 |
| Subject | Re: [RFC v2 08/10] landlock: Handle file system comparisons |
| Message-ID | <sa3FD-204-9@gated-at.bofh.it> |
| In reply to | #1470072 |
[Multipart message — attachments visible in raw view] — view raw
On 25/08/2016 13:12, Andy Lutomirski wrote: > On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: >> Add eBPF functions to compare file system access with a Landlock file >> system handle: >> * bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file) >> This function allows to compare the dentry, inode, device or mount >> point of the currently accessed file, with a reference handle. >> * bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file) >> This function allows an eBPF program to check if the current accessed >> file is the same or in the hierarchy of a reference handle. >> >> The goal of file system handle is to abstract kernel objects such as a >> struct file or a struct inode. Userland can create this kind of handle >> thanks to the BPF_MAP_UPDATE_ELEM command. The element is a struct >> landlock_handle containing the handle type (e.g. >> BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) and a file descriptor. This could >> also be any descriptions able to match a struct file or a struct inode >> (e.g. path or glob string). > > This needs Eric's opinion. > > Also, where do all the struct file *'s get stashed? Are they > preserved in the arraymap? What prevents reference cycles or absurdly > large numbers of struct files getting pinned? Yes, the struct file are kept in the arraymap and dropped when there is no more reference on them. Currently, the limitations are the maximum number of open file descriptors referring to an arraymap and the maximum number of eBPF Landlock programs loaded in a process (LANDLOCK_PROG_LIST_MAX_PAGES in kernel/seccomp.c). What kind of reference cycles have you in mind? It probably needs another limit for kernel object references as well. What is the best option here? Add another static limitation or use an existing one? Mickaël
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-26 17:00 +0200 |
| Subject | Re: [RFC v2 08/10] landlock: Handle file system comparisons |
| Message-ID | <saqLU-8tm-33@gated-at.bofh.it> |
| In reply to | #1470178 |
On Thu, Aug 25, 2016 at 7:10 AM, Mickaël Salaün <mic@digikod.net> wrote: > > On 25/08/2016 13:12, Andy Lutomirski wrote: >> On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: >>> Add eBPF functions to compare file system access with a Landlock file >>> system handle: >>> * bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file) >>> This function allows to compare the dentry, inode, device or mount >>> point of the currently accessed file, with a reference handle. >>> * bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file) >>> This function allows an eBPF program to check if the current accessed >>> file is the same or in the hierarchy of a reference handle. >>> >>> The goal of file system handle is to abstract kernel objects such as a >>> struct file or a struct inode. Userland can create this kind of handle >>> thanks to the BPF_MAP_UPDATE_ELEM command. The element is a struct >>> landlock_handle containing the handle type (e.g. >>> BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) and a file descriptor. This could >>> also be any descriptions able to match a struct file or a struct inode >>> (e.g. path or glob string). >> >> This needs Eric's opinion. >> >> Also, where do all the struct file *'s get stashed? Are they >> preserved in the arraymap? What prevents reference cycles or absurdly >> large numbers of struct files getting pinned? > > Yes, the struct file are kept in the arraymap and dropped when there is > no more reference on them. Currently, the limitations are the maximum > number of open file descriptors referring to an arraymap and the maximum > number of eBPF Landlock programs loaded in a process > (LANDLOCK_PROG_LIST_MAX_PAGES in kernel/seccomp.c). > > What kind of reference cycles have you in mind? Shoving evil things into the arraymaps, e.g. unix sockets with SCM_RIGHTS messages pending, eBPF program references, the arraymap fd itself, another arraymap fd, etc. > > It probably needs another limit for kernel object references as well. > What is the best option here? Add another static limitation or use an > existing one? Dunno. If RLIMIT_FILE could be made to work, that would be nice. --Andy
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-27 15:50 +0200 |
| Subject | Re: [RFC v2 08/10] landlock: Handle file system comparisons |
| Message-ID | <saM9H-5a8-7@gated-at.bofh.it> |
| In reply to | #1470791 |
[Multipart message — attachments visible in raw view] — view raw
On 26/08/2016 16:57, Andy Lutomirski wrote: > On Thu, Aug 25, 2016 at 7:10 AM, Mickaël Salaün <mic@digikod.net> wrote: >> >> On 25/08/2016 13:12, Andy Lutomirski wrote: >>> On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: >>>> Add eBPF functions to compare file system access with a Landlock file >>>> system handle: >>>> * bpf_landlock_cmp_fs_prop_with_struct_file(prop, map, map_op, file) >>>> This function allows to compare the dentry, inode, device or mount >>>> point of the currently accessed file, with a reference handle. >>>> * bpf_landlock_cmp_fs_beneath_with_struct_file(opt, map, map_op, file) >>>> This function allows an eBPF program to check if the current accessed >>>> file is the same or in the hierarchy of a reference handle. >>>> >>>> The goal of file system handle is to abstract kernel objects such as a >>>> struct file or a struct inode. Userland can create this kind of handle >>>> thanks to the BPF_MAP_UPDATE_ELEM command. The element is a struct >>>> landlock_handle containing the handle type (e.g. >>>> BPF_MAP_HANDLE_TYPE_LANDLOCK_FS_FD) and a file descriptor. This could >>>> also be any descriptions able to match a struct file or a struct inode >>>> (e.g. path or glob string). >>> >>> This needs Eric's opinion. >>> >>> Also, where do all the struct file *'s get stashed? Are they >>> preserved in the arraymap? What prevents reference cycles or absurdly >>> large numbers of struct files getting pinned? >> >> Yes, the struct file are kept in the arraymap and dropped when there is >> no more reference on them. Currently, the limitations are the maximum >> number of open file descriptors referring to an arraymap and the maximum >> number of eBPF Landlock programs loaded in a process >> (LANDLOCK_PROG_LIST_MAX_PAGES in kernel/seccomp.c). >> >> What kind of reference cycles have you in mind? > > Shoving evil things into the arraymaps, e.g. unix sockets with > SCM_RIGHTS messages pending, eBPF program references, the arraymap fd > itself, another arraymap fd, etc. The arraymap of Landlock handles is strongly typed and can check the kind of FD it get when creating/updating an entry, which is done for the cgroup type. It may be wise to add another check for FS types as well, which should be a one-liner. I'll do it for the next round. >> >> It probably needs another limit for kernel object references as well. >> What is the best option here? Add another static limitation or use an >> existing one? > > Dunno. If RLIMIT_FILE could be made to work, that would be nice. The RLIMIT_NOFILE is used for the eBPF map creation, but only the memory limit is used to store the map entries (struct file pointers). I'll add a new static limit for the number of FD-based arraymap entries because it does not reflect the same semantic. The struct files are not usable as FD, their only purpose is to be able to compare with another file. Mickaël
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 01/10] landlock: Add Kconfig |
| Message-ID | <sa0oq-8hT-31@gated-at.bofh.it> |
| In reply to | #1470049 |
Initial Landlock Kconfig needed to split the Landlock eBPF and seccomp parts to ease the review. Signed-off-by: Mickaël Salaün <mic@digikod.net> Cc: James Morris <james.l.morris@oracle.com> Cc: Kees Cook <keescook@chromium.org> Cc: Serge E. Hallyn <serge@hallyn.com> --- security/Kconfig | 1 + security/landlock/Kconfig | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 security/landlock/Kconfig diff --git a/security/Kconfig b/security/Kconfig index 176758cdfa57..be6c549dd0ca 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -124,6 +124,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/landlock/Kconfig b/security/landlock/Kconfig new file mode 100644 index 000000000000..dc8328d216d7 --- /dev/null +++ b/security/landlock/Kconfig @@ -0,0 +1,16 @@ +config SECURITY_LANDLOCK + bool "Landlock sandbox support" + depends on SECURITY + select BPF_SYSCALL + select SECCOMP + default y + help + Landlock is a stacked LSM which allows any user to load a security policy + to restrict their processes (i.e. create a sandbox). The policy is a list + of stacked eBPF programs for some LSM hooks. Each program can do some + access comparison to check if an access request is legitimate. + + Further information about eBPF can be found in + Documentation/networking/filter.txt + + If you are unsure how to answer this question, answer Y. -- 2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 07/10] landlock: Add errno check |
| Message-ID | <sa0oq-8hT-23@gated-at.bofh.it> |
| In reply to | #1470049 |
Add a max errno value.
This is not strictly needed but should improve reliability.
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
---
include/uapi/asm-generic/errno-base.h | 1 +
security/landlock/lsm.c | 6 +++---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/include/uapi/asm-generic/errno-base.h b/include/uapi/asm-generic/errno-base.h
index 65115978510f..43407a403e72 100644
--- a/include/uapi/asm-generic/errno-base.h
+++ b/include/uapi/asm-generic/errno-base.h
@@ -35,5 +35,6 @@
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
+#define _ERRNO_LAST ERANGE
#endif
diff --git a/security/landlock/lsm.c b/security/landlock/lsm.c
index aa9d4a64826e..322309068066 100644
--- a/security/landlock/lsm.c
+++ b/security/landlock/lsm.c
@@ -11,7 +11,6 @@
#include <asm/current.h>
#include <linux/bpf.h> /* enum bpf_reg_type, struct landlock_data */
#include <linux/cred.h>
-#include <linux/err.h> /* MAX_ERRNO */
#include <linux/filter.h> /* struct bpf_prog, BPF_PROG_RUN() */
#include <linux/kernel.h> /* FIELD_SIZEOF() */
#include <linux/lsm_hooks.h>
@@ -104,8 +103,9 @@ static int landlock_run_prog(__u64 args[6])
}
}
if (!ret) {
- if (cur_ret > MAX_ERRNO)
- ret = MAX_ERRNO;
+ /* check errno to not mess with kernel code */
+ if (cur_ret > _ERRNO_LAST)
+ ret = EPERM;
else
ret = cur_ret;
}
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-25 13:20 +0200 |
| Subject | Re: [RFC v2 07/10] landlock: Add errno check |
| Message-ID | <sa0Rr-fC-21@gated-at.bofh.it> |
| In reply to | #1470056 |
On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: > Add a max errno value. > > This is not strictly needed but should improve reliability. > > Signed-off-by: Mickaël Salaün <mic@digikod.net> > Cc: Arnd Bergmann <arnd@arndb.de> > Cc: Serge E. Hallyn <serge@hallyn.com> > Cc: James Morris <james.l.morris@oracle.com> > Cc: Kees Cook <keescook@chromium.org> > --- > include/uapi/asm-generic/errno-base.h | 1 + > security/landlock/lsm.c | 6 +++--- > 2 files changed, 4 insertions(+), 3 deletions(-) > > diff --git a/include/uapi/asm-generic/errno-base.h b/include/uapi/asm-generic/errno-base.h > index 65115978510f..43407a403e72 100644 > --- a/include/uapi/asm-generic/errno-base.h > +++ b/include/uapi/asm-generic/errno-base.h > @@ -35,5 +35,6 @@ > #define EPIPE 32 /* Broken pipe */ > #define EDOM 33 /* Math argument out of domain of func */ > #define ERANGE 34 /* Math result not representable */ > +#define _ERRNO_LAST ERANGE At the very least this needs a more sensible name.
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 12:50 +0200 |
| Subject | [RFC v2 06/10] landlock: Add LSM hooks |
| Message-ID | <sa0oq-8hT-25@gated-at.bofh.it> |
| In reply to | #1470049 |
Add LSM hooks which can be used by userland through Landlock (eBPF)
programs. This programs are limited to a whitelist of functions (cf.
next commit). The eBPF program context is depicted by the struct
landlock_data (cf. include/uapi/linux/bpf.h):
* hook: LSM hook ID (useful when using the same program for multiple LSM
hooks);
* cookie: the 16-bit value from the seccomp filter that triggered this
Landlock program;
* args[6]: array of LSM hook arguments.
The LSM hook arguments can contain raw values as integers or
(unleakable) pointers. The only way to use the pointers are to pass them
to an eBPF function according to their types (e.g. the
bpf_landlock_cmp_fs_beneath_with_struct_file function can use a struct
file pointer).
For now, there is three hooks for file system access control:
* file_open;
* file_permission;
* mmap_file.
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Will Drewry <wad@chromium.org>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/bpf.h | 7 ++
include/linux/lsm_hooks.h | 5 ++
include/uapi/linux/bpf.h | 20 +++++
kernel/bpf/syscall.c | 3 +
kernel/bpf/verifier.c | 8 ++
kernel/seccomp.c | 7 +-
security/Makefile | 2 +
security/landlock/Makefile | 3 +
security/landlock/lsm.c | 211 +++++++++++++++++++++++++++++++++++++++++++++
security/security.c | 1 +
10 files changed, 265 insertions(+), 2 deletions(-)
create mode 100644 security/landlock/Makefile
create mode 100644 security/landlock/lsm.c
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 9a5b388be099..557e7efdf0cd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -81,6 +81,9 @@ enum bpf_arg_type {
ARG_PTR_TO_CTX, /* pointer to context */
ARG_ANYTHING, /* any (initialized) argument is ok */
+
+ ARG_PTR_TO_STRUCT_FILE, /* pointer to struct file */
+ ARG_PTR_TO_STRUCT_CRED, /* pointer to struct cred */
};
/* type of values returned from helper functions */
@@ -139,6 +142,10 @@ enum bpf_reg_type {
*/
PTR_TO_PACKET,
PTR_TO_PACKET_END, /* skb->data + headlen */
+
+ /* Landlock */
+ PTR_TO_STRUCT_FILE,
+ PTR_TO_STRUCT_CRED,
};
struct bpf_prog;
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7ae397669d8b..6792ae8fb53d 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1898,5 +1898,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/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a60eedc17d40..983d14e910ff 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -102,6 +102,9 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SCHED_CLS,
BPF_PROG_TYPE_SCHED_ACT,
BPF_PROG_TYPE_TRACEPOINT,
+ BPF_PROG_TYPE_LANDLOCK_FILE_OPEN,
+ BPF_PROG_TYPE_LANDLOCK_FILE_PERMISSION,
+ BPF_PROG_TYPE_LANDLOCK_MMAP_FILE,
};
#define BPF_PSEUDO_MAP_FD 1
@@ -404,4 +407,21 @@ struct landlock_handle {
};
} __attribute__((aligned(8)));
+/**
+ * struct landlock_data
+ *
+ * @hook: LSM hook ID
+ * @cookie: value set by a seccomp-filter return value RET_LANDLOCK. This come
+ * from a trusted seccomp-bpf program: the same process that loaded
+ * this Landlock hook program.
+ * @args: LSM hook arguments, see include/linux/lsm_hooks.h for there
+ * description and the LANDLOCK_HOOK* definitions from
+ * security/landlock/lsm.c for their types.
+ */
+struct landlock_data {
+ __u32 hook;
+ __u16 cookie;
+ __u64 args[6];
+};
+
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 32a10ef4b878..6b8bfc34c751 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -719,6 +719,9 @@ static int bpf_prog_load(union bpf_attr *attr)
switch (type) {
case BPF_PROG_TYPE_SOCKET_FILTER:
+ case BPF_PROG_TYPE_LANDLOCK_FILE_OPEN:
+ case BPF_PROG_TYPE_LANDLOCK_FILE_PERMISSION:
+ case BPF_PROG_TYPE_LANDLOCK_MMAP_FILE:
break;
default:
if (!capable(CAP_SYS_ADMIN))
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index c15f6cc28e00..2931e2efcc10 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -244,6 +244,8 @@ static const char * const reg_type_str[] = {
[CONST_IMM] = "imm",
[PTR_TO_PACKET] = "pkt",
[PTR_TO_PACKET_END] = "pkt_end",
+ [PTR_TO_STRUCT_FILE] = "struct_file",
+ [PTR_TO_STRUCT_CRED] = "struct_cred",
};
static void print_verifier_state(struct verifier_state *state)
@@ -554,6 +556,8 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case PTR_TO_PACKET_END:
case FRAME_PTR:
case CONST_PTR_TO_MAP:
+ case PTR_TO_STRUCT_FILE:
+ case PTR_TO_STRUCT_CRED:
return true;
default:
return false;
@@ -943,6 +947,10 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
expected_type = CONST_PTR_TO_MAP;
} else if (arg_type == ARG_PTR_TO_CTX) {
expected_type = PTR_TO_CTX;
+ } else if (arg_type == ARG_PTR_TO_STRUCT_FILE) {
+ expected_type = PTR_TO_STRUCT_FILE;
+ } else if (arg_type == ARG_PTR_TO_STRUCT_CRED) {
+ expected_type = PTR_TO_STRUCT_CRED;
} else if (arg_type == ARG_PTR_TO_STACK ||
arg_type == ARG_PTR_TO_RAW_STACK) {
expected_type = PTR_TO_STACK;
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 5df7274c7ec3..3395e370cd47 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -36,7 +36,7 @@
#include <linux/uaccess.h>
#ifdef CONFIG_SECURITY_LANDLOCK
-#include <linux/bpf.h> /* bpf_prog_put() */
+#include <linux/bpf.h> /* bpf_prog_put(), BPF_PROG_TYPE_LANDLOCK_* */
#endif /* CONFIG_SECURITY_LANDLOCK */
/**
@@ -960,7 +960,10 @@ static long landlock_set_hook(unsigned int flags, const char __user *user_bpf_fd
if (IS_ERR(prog))
return PTR_ERR(prog);
switch (prog->type) {
- /* TODO: add LSM hooks */
+ case BPF_PROG_TYPE_LANDLOCK_FILE_OPEN:
+ case BPF_PROG_TYPE_LANDLOCK_FILE_PERMISSION:
+ case BPF_PROG_TYPE_LANDLOCK_MMAP_FILE:
+ break;
default:
result = -EINVAL;
goto put_prog;
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/Makefile b/security/landlock/Makefile
new file mode 100644
index 000000000000..59669d70bc7e
--- /dev/null
+++ b/security/landlock/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
+
+landlock-y := lsm.o
diff --git a/security/landlock/lsm.c b/security/landlock/lsm.c
new file mode 100644
index 000000000000..aa9d4a64826e
--- /dev/null
+++ b/security/landlock/lsm.c
@@ -0,0 +1,211 @@
+/*
+ * Landlock LSM
+ *
+ * Copyright (C) 2016 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/current.h>
+#include <linux/bpf.h> /* enum bpf_reg_type, struct landlock_data */
+#include <linux/cred.h>
+#include <linux/err.h> /* MAX_ERRNO */
+#include <linux/filter.h> /* struct bpf_prog, BPF_PROG_RUN() */
+#include <linux/kernel.h> /* FIELD_SIZEOF() */
+#include <linux/lsm_hooks.h>
+#include <linux/seccomp.h> /* struct seccomp_* */
+
+#define LANDLOCK_HOOK_INIT(NAME) LSM_HOOK_INIT(NAME, landlock_hook_##NAME)
+
+#define LANDLOCK_HOOKx(X, NAME, CNAME, ...) \
+ static inline int landlock_hook_##NAME( \
+ LANDLOCK_MAP(X, LANDLOCK_ARG_TA, __VA_ARGS__)) \
+ { \
+ __u64 args[6] = { \
+ LANDLOCK_MAP(X, LANDLOCK_ARG_A, __VA_ARGS__) \
+ }; \
+ return landlock_run_prog(args); \
+ } \
+ static inline bool bpf_landlock_##NAME##_is_valid_access( \
+ int off, int size, enum bpf_access_type type, \
+ enum bpf_reg_type *reg_type) \
+ { \
+ enum bpf_reg_type arg_types[6] = { \
+ LANDLOCK_MAP(X, LANDLOCK_ARG_D, __VA_ARGS__) \
+ }; \
+ return __is_valid_access(off, size, type, reg_type, arg_types); \
+ } \
+ static const struct bpf_verifier_ops bpf_landlock_##NAME##_ops = { \
+ .get_func_proto = bpf_landlock_func_proto, \
+ .is_valid_access = bpf_landlock_##NAME##_is_valid_access, \
+ .convert_ctx_access = landlock_convert_ctx_access, \
+ }; \
+ static struct bpf_prog_type_list bpf_landlock_##NAME##_type __read_mostly = { \
+ .ops = &bpf_landlock_##NAME##_ops, \
+ .type = BPF_PROG_TYPE_LANDLOCK_##CNAME, \
+ }; \
+ static int __init register_landlock_##NAME##_filter_ops(void) \
+ { \
+ bpf_register_prog_type(&bpf_landlock_##NAME##_type); \
+ return 0; \
+ } \
+ late_initcall(register_landlock_##NAME##_filter_ops);
+
+#define LANDLOCK_HOOK1(NAME, ...) LANDLOCK_HOOKx(1, NAME, __VA_ARGS__)
+#define LANDLOCK_HOOK2(NAME, ...) LANDLOCK_HOOKx(2, NAME, __VA_ARGS__)
+#define LANDLOCK_HOOK3(NAME, ...) LANDLOCK_HOOKx(3, NAME, __VA_ARGS__)
+#define LANDLOCK_HOOK4(NAME, ...) LANDLOCK_HOOKx(4, NAME, __VA_ARGS__)
+#define LANDLOCK_HOOK5(NAME, ...) LANDLOCK_HOOKx(5, NAME, __VA_ARGS__)
+#define LANDLOCK_HOOK6(NAME, ...) LANDLOCK_HOOKx(6, NAME, __VA_ARGS__)
+
+#define LANDLOCK_MAP0(m,...)
+#define LANDLOCK_MAP1(m,d,t,a) m(d,t,a)
+#define LANDLOCK_MAP2(m,d,t,a,...) m(d,t,a), LANDLOCK_MAP1(m,__VA_ARGS__)
+#define LANDLOCK_MAP3(m,d,t,a,...) m(d,t,a), LANDLOCK_MAP2(m,__VA_ARGS__)
+#define LANDLOCK_MAP4(m,d,t,a,...) m(d,t,a), LANDLOCK_MAP3(m,__VA_ARGS__)
+#define LANDLOCK_MAP5(m,d,t,a,...) m(d,t,a), LANDLOCK_MAP4(m,__VA_ARGS__)
+#define LANDLOCK_MAP6(m,d,t,a,...) m(d,t,a), LANDLOCK_MAP5(m,__VA_ARGS__)
+#define LANDLOCK_MAP(n,...) LANDLOCK_MAP##n(__VA_ARGS__)
+
+#define LANDLOCK_ARG_D(d,t,a) d
+#define LANDLOCK_ARG_TA(d,t,a) t a
+#define LANDLOCK_ARG_A(d,t,a) (u64)a
+
+
+static int landlock_run_prog(__u64 args[6])
+{
+ u32 cur_ret = 0, ret = 0;
+ struct seccomp_landlock_ret *landlock_ret;
+ struct seccomp_landlock_prog *prog;
+
+ /* the hook ID is faked by landlock_convert_ctx_access() */
+ struct landlock_data ctx = {
+ .args[0] = args[0],
+ .args[1] = args[1],
+ .args[2] = args[2],
+ .args[3] = args[3],
+ .args[4] = args[4],
+ .args[5] = args[5],
+ };
+
+ /* TODO: use lockless_dereference()? */
+ /* run all the triggered Landlock programs */
+ for (landlock_ret = current->seccomp.landlock_ret;
+ landlock_ret; landlock_ret = landlock_ret->prev) {
+ if (landlock_ret->triggered) {
+ ctx.cookie = landlock_ret->cookie;
+ for (prog = current->seccomp.landlock_prog;
+ prog; prog = prog->prev) {
+ if (prog->filter == landlock_ret->filter) {
+ cur_ret = BPF_PROG_RUN(prog->prog, (void *)&ctx);
+ break;
+ }
+ }
+ if (!ret) {
+ if (cur_ret > MAX_ERRNO)
+ ret = MAX_ERRNO;
+ else
+ ret = cur_ret;
+ }
+ }
+ }
+ return -ret;
+}
+
+static const struct bpf_func_proto *bpf_landlock_func_proto(
+ enum bpf_func_id func_id)
+{
+ return NULL;
+}
+
+static u32 landlock_convert_ctx_access(enum bpf_access_type type, int dst_reg,
+ int src_reg, int ctx_off,
+ struct bpf_insn *insn_buf,
+ struct bpf_prog *prog)
+{
+ struct bpf_insn *insn = insn_buf;
+
+ /* only handle 32-bit values */
+ switch (ctx_off) {
+ case offsetof(struct landlock_data, hook):
+ *insn++ = BPF_MOV32_IMM(dst_reg, prog->type);
+ break;
+ default:
+ return 1;
+ }
+
+ return insn - insn_buf;
+}
+
+static bool __is_valid_access(int off, int size, enum bpf_access_type type,
+ enum bpf_reg_type *reg_type, enum bpf_reg_type arg_types[6])
+{
+ int arg_nb, expected_size;
+
+ if (type != BPF_READ)
+ return false;
+ if (off < 0 || off >= sizeof(struct landlock_data))
+ return false;
+
+ switch (off) {
+ case offsetof(struct landlock_data, cookie):
+ expected_size = sizeof(__u16);
+ break;
+ case offsetof(struct landlock_data, hook):
+ expected_size = sizeof(__u32);
+ break;
+ case offsetof(struct landlock_data, args[0]) ...
+ offsetof(struct landlock_data, args[5]):
+ expected_size = sizeof(__u64);
+ break;
+ default:
+ return false;
+ }
+ if (expected_size != size)
+ return false;
+
+ /* check pointer type */
+ switch (off) {
+ case offsetof(struct landlock_data, args[0]) ...
+ offsetof(struct landlock_data, args[5]):
+ arg_nb = (off - offsetof(struct landlock_data, args[0]))
+ / FIELD_SIZEOF(struct landlock_data, args[0]);
+ *reg_type = arg_types[arg_nb];
+ if (*reg_type == NOT_INIT)
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+LANDLOCK_HOOK2(file_open, FILE_OPEN,
+ PTR_TO_STRUCT_FILE, struct file *, file,
+ PTR_TO_STRUCT_CRED, const struct cred *, cred
+)
+
+LANDLOCK_HOOK2(file_permission, FILE_PERMISSION,
+ PTR_TO_STRUCT_FILE, struct file *, file,
+ UNKNOWN_VALUE, int, mask
+)
+
+LANDLOCK_HOOK4(mmap_file, MMAP_FILE,
+ PTR_TO_STRUCT_FILE, struct file *, file,
+ UNKNOWN_VALUE, unsigned long, reqprot,
+ UNKNOWN_VALUE, unsigned long, prot,
+ UNKNOWN_VALUE, unsigned long, flags
+)
+
+static struct security_hook_list landlock_hooks[] = {
+ LANDLOCK_HOOK_INIT(file_open),
+ LANDLOCK_HOOK_INIT(file_permission),
+ LANDLOCK_HOOK_INIT(mmap_file),
+};
+
+void __init landlock_add_hooks(void)
+{
+ pr_info("landlock: Becoming ready for sandboxing\n");
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks));
+}
diff --git a/security/security.c b/security/security.c
index 709569305d32..d918c5ca8b81 100644
--- a/security/security.c
+++ b/security/security.c
@@ -61,6 +61,7 @@ int __init security_init(void)
capability_add_hooks();
yama_add_hooks();
loadpin_add_hooks();
+ landlock_add_hooks();
/*
* Load all the remaining security modules.
--
2.8.1
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-30 21:00 +0200 |
| Subject | Re: [RFC v2 06/10] landlock: Add LSM hooks |
| Message-ID | <sbWql-b6-11@gated-at.bofh.it> |
| In reply to | #1470057 |
On Aug 25, 2016 12:34 PM, "Mickaël Salaün" <mic@digikod.net> wrote: > > Add LSM hooks which can be used by userland through Landlock (eBPF) > programs. This programs are limited to a whitelist of functions (cf. > next commit). The eBPF program context is depicted by the struct > landlock_data (cf. include/uapi/linux/bpf.h): > * hook: LSM hook ID (useful when using the same program for multiple LSM > hooks); > * cookie: the 16-bit value from the seccomp filter that triggered this > Landlock program; > * args[6]: array of LSM hook arguments. > > The LSM hook arguments can contain raw values as integers or > (unleakable) pointers. The only way to use the pointers are to pass them > to an eBPF function according to their types (e.g. the > bpf_landlock_cmp_fs_beneath_with_struct_file function can use a struct > file pointer). > > For now, there is three hooks for file system access control: > * file_open; > * file_permission; > * mmap_file. > What's the purpose of exposing struct cred * to userspace? It's primarily just an optimization to save a bit of RAM, and it's a dubious optimization at that. What are you using it for? Would it make more sense to use struct task_struct * or struct pid * instead? Also, exposing struct cred * has a really weird side-effect: it allows (maybe even encourages) checking for pointer equality between two struct cred * objects. Doing so will have erratic results.
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-30 22:20 +0200 |
| Subject | Re: [RFC v2 06/10] landlock: Add LSM hooks |
| Message-ID | <sbXFM-1b9-9@gated-at.bofh.it> |
| In reply to | #1472779 |
[Multipart message — attachments visible in raw view] — view raw
On 30/08/2016 20:56, Andy Lutomirski wrote: > On Aug 25, 2016 12:34 PM, "Mickaël Salaün" <mic@digikod.net> wrote: >> >> Add LSM hooks which can be used by userland through Landlock (eBPF) >> programs. This programs are limited to a whitelist of functions (cf. >> next commit). The eBPF program context is depicted by the struct >> landlock_data (cf. include/uapi/linux/bpf.h): >> * hook: LSM hook ID (useful when using the same program for multiple LSM >> hooks); >> * cookie: the 16-bit value from the seccomp filter that triggered this >> Landlock program; >> * args[6]: array of LSM hook arguments. >> >> The LSM hook arguments can contain raw values as integers or >> (unleakable) pointers. The only way to use the pointers are to pass them >> to an eBPF function according to their types (e.g. the >> bpf_landlock_cmp_fs_beneath_with_struct_file function can use a struct >> file pointer). >> >> For now, there is three hooks for file system access control: >> * file_open; >> * file_permission; >> * mmap_file. >> > > What's the purpose of exposing struct cred * to userspace? It's > primarily just an optimization to save a bit of RAM, and it's a > dubious optimization at that. What are you using it for? Would it > make more sense to use struct task_struct * or struct pid * instead? > > Also, exposing struct cred * has a really weird side-effect: it allows > (maybe even encourages) checking for pointer equality between two > struct cred * objects. Doing so will have erratic results. > The pointers exposed in the ePBF context are not directly readable by an unprivileged eBPF program thanks to the strong typing of the Landlock context and the static eBPF verification. There is no way to leak a kernel pointer to userspace from an unprivileged eBPF program: pointer arithmetic and comparison are prohibited. Pointers can only be pass as argument to dedicated eBPF functions. For now, struct cred * is simply not used by any eBPF function and then not usable at all. It only exist here because I map the LSM hook arguments in a generic/automatic way to the eBPF context. I'm planning to extend the Landlock context with extra pointers, whatever the LSM hook. We could then use task_struct, skb or any other kernel objects, in a safe way, with dedicated functions.
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-30 22:20 +0200 |
| Subject | Re: [RFC v2 06/10] landlock: Add LSM hooks |
| Message-ID | <sbXFM-1b9-21@gated-at.bofh.it> |
| In reply to | #1472835 |
On Tue, Aug 30, 2016 at 1:10 PM, Mickaël Salaün <mic@digikod.net> wrote: > > On 30/08/2016 20:56, Andy Lutomirski wrote: >> On Aug 25, 2016 12:34 PM, "Mickaël Salaün" <mic@digikod.net> wrote: >>> >>> Add LSM hooks which can be used by userland through Landlock (eBPF) >>> programs. This programs are limited to a whitelist of functions (cf. >>> next commit). The eBPF program context is depicted by the struct >>> landlock_data (cf. include/uapi/linux/bpf.h): >>> * hook: LSM hook ID (useful when using the same program for multiple LSM >>> hooks); >>> * cookie: the 16-bit value from the seccomp filter that triggered this >>> Landlock program; >>> * args[6]: array of LSM hook arguments. >>> >>> The LSM hook arguments can contain raw values as integers or >>> (unleakable) pointers. The only way to use the pointers are to pass them >>> to an eBPF function according to their types (e.g. the >>> bpf_landlock_cmp_fs_beneath_with_struct_file function can use a struct >>> file pointer). >>> >>> For now, there is three hooks for file system access control: >>> * file_open; >>> * file_permission; >>> * mmap_file. >>> >> >> What's the purpose of exposing struct cred * to userspace? It's >> primarily just an optimization to save a bit of RAM, and it's a >> dubious optimization at that. What are you using it for? Would it >> make more sense to use struct task_struct * or struct pid * instead? >> >> Also, exposing struct cred * has a really weird side-effect: it allows >> (maybe even encourages) checking for pointer equality between two >> struct cred * objects. Doing so will have erratic results. >> > > The pointers exposed in the ePBF context are not directly readable by an > unprivileged eBPF program thanks to the strong typing of the Landlock > context and the static eBPF verification. There is no way to leak a > kernel pointer to userspace from an unprivileged eBPF program: pointer > arithmetic and comparison are prohibited. Pointers can only be pass as > argument to dedicated eBPF functions. I'm not talking about leaking the value -- I'm talking about leaking the predicate (a == b) for two struct cred pointers. That predicate shouldn't be available because it has very odd effects. > > For now, struct cred * is simply not used by any eBPF function and then > not usable at all. It only exist here because I map the LSM hook > arguments in a generic/automatic way to the eBPF context. Maybe remove it from this patch set then? --Andy
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-30 22:30 +0200 |
| Subject | Re: [RFC v2 06/10] landlock: Add LSM hooks |
| Message-ID | <sbXPr-1hr-7@gated-at.bofh.it> |
| In reply to | #1472838 |
[Multipart message — attachments visible in raw view] — view raw
On 30/08/2016 22:18, Andy Lutomirski wrote: > On Tue, Aug 30, 2016 at 1:10 PM, Mickaël Salaün <mic@digikod.net> wrote: >> >> On 30/08/2016 20:56, Andy Lutomirski wrote: >>> On Aug 25, 2016 12:34 PM, "Mickaël Salaün" <mic@digikod.net> wrote: >>>> >>>> Add LSM hooks which can be used by userland through Landlock (eBPF) >>>> programs. This programs are limited to a whitelist of functions (cf. >>>> next commit). The eBPF program context is depicted by the struct >>>> landlock_data (cf. include/uapi/linux/bpf.h): >>>> * hook: LSM hook ID (useful when using the same program for multiple LSM >>>> hooks); >>>> * cookie: the 16-bit value from the seccomp filter that triggered this >>>> Landlock program; >>>> * args[6]: array of LSM hook arguments. >>>> >>>> The LSM hook arguments can contain raw values as integers or >>>> (unleakable) pointers. The only way to use the pointers are to pass them >>>> to an eBPF function according to their types (e.g. the >>>> bpf_landlock_cmp_fs_beneath_with_struct_file function can use a struct >>>> file pointer). >>>> >>>> For now, there is three hooks for file system access control: >>>> * file_open; >>>> * file_permission; >>>> * mmap_file. >>>> >>> >>> What's the purpose of exposing struct cred * to userspace? It's >>> primarily just an optimization to save a bit of RAM, and it's a >>> dubious optimization at that. What are you using it for? Would it >>> make more sense to use struct task_struct * or struct pid * instead? >>> >>> Also, exposing struct cred * has a really weird side-effect: it allows >>> (maybe even encourages) checking for pointer equality between two >>> struct cred * objects. Doing so will have erratic results. >>> >> >> The pointers exposed in the ePBF context are not directly readable by an >> unprivileged eBPF program thanks to the strong typing of the Landlock >> context and the static eBPF verification. There is no way to leak a >> kernel pointer to userspace from an unprivileged eBPF program: pointer >> arithmetic and comparison are prohibited. Pointers can only be pass as >> argument to dedicated eBPF functions. > > I'm not talking about leaking the value -- I'm talking about leaking > the predicate (a == b) for two struct cred pointers. That predicate > shouldn't be available because it has very odd effects. I'm pretty sure this case is covered with the impossibility of doing pointers comparison. > >> >> For now, struct cred * is simply not used by any eBPF function and then >> not usable at all. It only exist here because I map the LSM hook >> arguments in a generic/automatic way to the eBPF context. > > Maybe remove it from this patch set then? Well, this is done with the LANDLOCK_HOOK* macros but I will remove it.
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-25 13:30 +0200 |
| Message-ID | <sa117-jg-9@gated-at.bofh.it> |
| In reply to | #1470049 |
On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: > Hi, > > This series is a proof of concept to fill some missing part of seccomp as the > ability to check syscall argument pointers or creating more dynamic security > policies. 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 the Seatbelt/XNU Sandbox or the > OpenBSD Pledge. This kind of sandbox help to mitigate the security impact of > bugs or unexpected/malicious behaviors in userland applications. > Maybe I'm missing an obvious description, but: do you have a description of the eBPF API to landlock? What function do you provide, when is it called, what functions can it call, what does the fancy new arraymap do, etc? --Andy
[toc] | [prev] | [next] | [standalone]
| From | Mickaël Salaün <mic@digikod.net> |
|---|---|
| Date | 2016-08-25 16:00 +0200 |
| Message-ID | <sa3mh-1Ej-3@gated-at.bofh.it> |
| In reply to | #1470075 |
[Multipart message — attachments visible in raw view] — view raw
On 25/08/2016 13:05, Andy Lutomirski wrote: > On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: >> Hi, >> >> This series is a proof of concept to fill some missing part of seccomp as the >> ability to check syscall argument pointers or creating more dynamic security >> policies. 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 the Seatbelt/XNU Sandbox or the >> OpenBSD Pledge. This kind of sandbox help to mitigate the security impact of >> bugs or unexpected/malicious behaviors in userland applications. >> > > Maybe I'm missing an obvious description, but: do you have a > description of the eBPF API to landlock? What function do you > provide, when is it called, what functions can it call, what does the > fancy new arraymap do, etc? > > --Andy > The eBPF context is described in "[RFC v2 06/10] landlock: Add LSM hooks". The provided eBPF functions are described in "[RFC v2 08/10] landlock: Handle file system comparisons" (bpf_landlock_cmp_fs_prop_with_struct_file and bpf_landlock_cmp_fs_beneath_with_struct_file) and "[RFC v2 09/10] landlock: Handle cgroups" (bpf_landlock_cmp_cgroup_beneath). The function descriptions are summarized in include/uapi/linux/bpf.h . This functions can be called by an eBPF program of type BPF_PROG_TYPE_LANDLOCK_FILE_OPEN, BPF_PROG_TYPE_LANDLOCK_FILE_PERMISSION and BPF_PROG_TYPE_LANDLOCK_MMAP_FILE as described in "[RFC v2 06/10] landlock: Add LSM hooks". I tried to split the commits as much as possible to ease the review. The "[RFC v2 10/10] samples/landlock: Add sandbox example" may help to see the whole picture. Hope this helps, Mickaël
[toc] | [prev] | [next] | [standalone]
| From | Andy Lutomirski <luto@amacapital.net> |
|---|---|
| Date | 2016-08-27 09:50 +0200 |
| Message-ID | <saGxk-1I7-13@gated-at.bofh.it> |
| In reply to | #1470049 |
On Thu, Aug 25, 2016 at 3:32 AM, Mickaël Salaün <mic@digikod.net> wrote: > Hi, > > This series is a proof of concept to fill some missing part of seccomp as the > ability to check syscall argument pointers or creating more dynamic security > policies. 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 the Seatbelt/XNU Sandbox or the > OpenBSD Pledge. This kind of sandbox help to mitigate the security impact of > bugs or unexpected/malicious behaviors in userland applications. > > The first RFC [1] was focused on extending seccomp while staying at the syscall > level. This brought a working PoC but with some (mitigated) ToCToU race > conditions due to the seccomp ptrace hole (now fixed) and the non-atomic > syscall argument evaluation (hence the LSM hooks). > > > # Landlock LSM > > This second RFC is a fresh revamp of the code while keeping some working ideas. > This series is mainly focused on LSM hooks, while keeping the possibility to > tied them to syscalls. This new code removes all race conditions by design. It > now use eBPF instead of a subset of cBPF (as used by seccomp-bpf). This allow > to remove the previous stacked cBPF hack to do complex access checks thanks to > dedicated eBPF functions. An eBPF program is still very limited (i.e. can only > call a whitelist of functions) and can not do a denial of service (i.e. no > loop). The other major improvement is the replacement of the previous custom > checker groups of syscall arguments with a new dedicated eBPF map to collect > and compare Landlock handles with system resources (e.g. files or network > connections). > > The approach taken is to add the minimum amount of code while still allowing > the userland to create quite complex access rules. A dedicated security policy > language such as used by SELinux, AppArmor and other major LSMs is a lot of > code and dedicated to a trusted process (i.e. root/administrator). > I think there might be a problem with the current design. If I add a seccomp filter that uses RET_LANDLOCK and some landlock filters, what happens if a second seccomp filter *also* uses RET_LANDLOCK? I think they'll interfere with each other. It might end up being necessary to require only one landlock seccomp layer at a time or to find a way to stick all the filters in a layer together with the LSM callbacks or maybe to just drop RET_LANDLOCK and let the callbacks look at the syscall args. BTW, what happens if an LSM hook is called outside a syscall context, e.g. from a page fault? > > > # Sandbox example with conditional access control depending on cgroup > > $ mkdir /sys/fs/cgroup/sandboxed > $ ls /home > user1 > $ LANDLOCK_CGROUPS='/sys/fs/cgroup/sandboxed' \ > LANDLOCK_ALLOWED='/bin:/lib:/usr:/tmp:/proc/self/fd/0' \ > ./sandbox /bin/sh -i > $ ls /home > user1 > $ echo $$ > /sys/fs/cgroup/sandboxed/cgroup.procs > $ ls /home > ls: cannot open directory '/home': Permission denied > Something occurs to me that isn't strictly relevant to landlock but may be relevant to unprivileged cgroups: can you cause trouble by setting up a nastily-configured cgroup and running a setuid program in it?
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web