LKML Archive on lore.kernel.org
 help / Atom feed
* [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
@ 2017-03-28 23:46 Mickaël Salaün
  2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
                   ` (11 more replies)
  0 siblings, 12 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

Hi,

This sixth series add some changes to the previous one [1], including a simpler
rule inheritance hierarchy (similar to seccomp-bpf), a ptrace scope protection,
some file renaming (better feature identification per file), a future-proof
eBPF subtype and miscellaneous cosmetic fixes.

This is the first step of the roadmap discussed at LPC [2] (with the
inheritance feature included).  While the intended final goal is to allow
unprivileged users to use Landlock, this series allows only a process with
global CAP_SYS_ADMIN to load and enforce a rule.  This may help to get feedback
and avoid unexpected behaviors.  The documentation patch contains some kernel
documentation and explanations on how to use Landlock.  The compiled
documentation can be found here:
https://landlock-lsm.github.io/linux-doc/landlock-v6/security/landlock/index.html

This series can be applied on top of net-next: cc628c9680c2 ("Merge tag
'mlx5e-failsafe' of
git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux").  This can be
tested with CONFIG_SECCOMP_FILTER and CONFIG_SECURITY_LANDLOCK.  I would really
appreciate constructive comments on the usability, architecture, code, userland
API or use cases.


# Landlock LSM

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

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

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


# Landlock events and rule enforcement

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

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

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


# Sandbox example with a read-only filesystem

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

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


# Warning on read-only filesystems

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


# Frequently asked questions

## Why is seccomp-bpf not enough?

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

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


## Why use the seccomp(2) syscall?

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


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

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

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


# Changes since v5

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


# Changes since v4

* revamp Landlock to not expose an LSM hook interface but wrap and abstract
  them with Landlock events (currently one for all filesystem related
  operations: LANDLOCK_SUBTYPE_EVENT_FS)
* wrap all filesystem kernel objects through the same FS handle (struct
  landlock_handle_fs): struct file, struct inode, struct path and struct dentry
* a rule don't return an errno code but only a boolean to allow or deny an
  access request
* handle all filesystem related LSM hooks
* add some tests and a sample:
  * BPF context tests
  * Landlock sandboxing tests and sample
  * write Landlock rules in C and compile them with LLVM
* change field names of eBPF program subtype
* remove arraymap of handles for now (will be replaced with a revamped map)
* remove cgroup handling for now
* add user and kernel documentation
* rebase on net-next (which includes some needed commits already upstreamed)


# Changes since v3

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


# Changes since v2

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


# Changes since v1

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


[1] https://lkml.kernel.org/r/20170222012632.4196-1-mic@digikod.net
[2] https://lkml.kernel.org/r/5828776A.1010104@digikod.net
[3] https://lkml.kernel.org/r/1477390454-12553-1-git-send-email-daniel@zonque.org
[4] https://lkml.kernel.org/r/20160829114542.GA20836@ircssh.c.rugged-nimbus-611.internal
[5] https://lkml.kernel.org/r/20161221231506.19800-1-mic@digikod.net

Regards,

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

 Documentation/security/index.rst                   |   1 +
 Documentation/security/landlock/index.rst          |  19 +
 Documentation/security/landlock/kernel.rst         | 132 +++++
 Documentation/security/landlock/user.rst           | 307 +++++++++++
 include/linux/bpf.h                                |  40 +-
 include/linux/filter.h                             |   2 +
 include/linux/landlock.h                           |  59 +++
 include/linux/lsm_hooks.h                          |   5 +
 include/linux/seccomp.h                            |  12 +-
 include/uapi/linux/bpf.h                           | 126 ++++-
 include/uapi/linux/seccomp.h                       |   1 +
 kernel/bpf/Makefile                                |   2 +-
 kernel/bpf/helpers_fs.c                            |  52 ++
 kernel/bpf/syscall.c                               |  89 ++--
 kernel/bpf/verifier.c                              |  22 +-
 kernel/fork.c                                      |  16 +-
 kernel/seccomp.c                                   |  26 +-
 kernel/trace/bpf_trace.c                           |  15 +-
 net/core/filter.c                                  |  48 +-
 samples/bpf/Makefile                               |   4 +
 samples/bpf/bpf_helpers.h                          |   2 +
 samples/bpf/bpf_load.c                             |  34 +-
 samples/bpf/cookie_uid_helper_example.c            |   2 +-
 samples/bpf/fds_example.c                          |   2 +-
 samples/bpf/landlock1_kern.c                       |  46 ++
 samples/bpf/landlock1_user.c                       | 102 ++++
 samples/bpf/sock_example.c                         |   3 +-
 samples/bpf/test_cgrp2_attach.c                    |   2 +-
 samples/bpf/test_cgrp2_attach2.c                   |   2 +-
 samples/bpf/test_cgrp2_sock.c                      |   2 +-
 security/Kconfig                                   |   1 +
 security/Makefile                                  |   2 +
 security/landlock/Kconfig                          |  18 +
 security/landlock/Makefile                         |   5 +
 security/landlock/common.h                         |  25 +
 security/landlock/hooks.c                          | 152 ++++++
 security/landlock/hooks.h                          | 182 +++++++
 security/landlock/hooks_fs.c                       | 563 +++++++++++++++++++++
 security/landlock/hooks_fs.h                       |  19 +
 security/landlock/hooks_ptrace.c                   | 126 +++++
 security/landlock/hooks_ptrace.h                   |  11 +
 security/landlock/init.c                           | 145 ++++++
 security/landlock/providers.c                      | 232 +++++++++
 security/security.c                                |   7 +-
 tools/include/uapi/linux/bpf.h                     | 126 ++++-
 tools/lib/bpf/bpf.c                                |   5 +-
 tools/lib/bpf/bpf.h                                |   2 +-
 tools/lib/bpf/libbpf.c                             |   4 +-
 tools/perf/tests/bpf.c                             |   2 +-
 tools/testing/selftests/Makefile                   |   1 +
 tools/testing/selftests/bpf/test_tag.c             |   2 +-
 tools/testing/selftests/bpf/test_verifier.c        |  80 ++-
 tools/testing/selftests/landlock/.gitignore        |   4 +
 tools/testing/selftests/landlock/Makefile          |  47 ++
 tools/testing/selftests/landlock/rules/Makefile    |  52 ++
 tools/testing/selftests/landlock/rules/README.rst  |   1 +
 .../testing/selftests/landlock/rules/bpf_helpers.h |   1 +
 .../testing/selftests/landlock/rules/fs_no_open.c  |  31 ++
 .../selftests/landlock/rules/fs_read_only.c        |  31 ++
 tools/testing/selftests/landlock/test.h            |  35 ++
 tools/testing/selftests/landlock/test_base.c       |  31 ++
 tools/testing/selftests/landlock/test_fs.c         | 305 +++++++++++
 tools/testing/selftests/landlock/test_ptrace.c     | 161 ++++++
 tools/testing/selftests/seccomp/test_harness.h     |   8 +-
 64 files changed, 3502 insertions(+), 90 deletions(-)
 create mode 100644 Documentation/security/landlock/index.rst
 create mode 100644 Documentation/security/landlock/kernel.rst
 create mode 100644 Documentation/security/landlock/user.rst
 create mode 100644 include/linux/landlock.h
 create mode 100644 kernel/bpf/helpers_fs.c
 create mode 100644 samples/bpf/landlock1_kern.c
 create mode 100644 samples/bpf/landlock1_user.c
 create mode 100644 security/landlock/Kconfig
 create mode 100644 security/landlock/Makefile
 create mode 100644 security/landlock/common.h
 create mode 100644 security/landlock/hooks.c
 create mode 100644 security/landlock/hooks.h
 create mode 100644 security/landlock/hooks_fs.c
 create mode 100644 security/landlock/hooks_fs.h
 create mode 100644 security/landlock/hooks_ptrace.c
 create mode 100644 security/landlock/hooks_ptrace.h
 create mode 100644 security/landlock/init.c
 create mode 100644 security/landlock/providers.c
 create mode 100644 tools/testing/selftests/landlock/.gitignore
 create mode 100644 tools/testing/selftests/landlock/Makefile
 create mode 100644 tools/testing/selftests/landlock/rules/Makefile
 create mode 120000 tools/testing/selftests/landlock/rules/README.rst
 create mode 120000 tools/testing/selftests/landlock/rules/bpf_helpers.h
 create mode 100644 tools/testing/selftests/landlock/rules/fs_no_open.c
 create mode 100644 tools/testing/selftests/landlock/rules/fs_read_only.c
 create mode 100644 tools/testing/selftests/landlock/test.h
 create mode 100644 tools/testing/selftests/landlock/test_base.c
 create mode 100644 tools/testing/selftests/landlock/test_fs.c
 create mode 100644 tools/testing/selftests/landlock/test_ptrace.c

-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-03-29 13:48   ` kbuild test robot
  2017-04-18 21:48   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock Mickaël Salaün
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

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

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

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

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

Changes since v3:
* remove the "origin" field
* add an "option" field
* cleanup comments

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Link: https://lkml.kernel.org/r/20160827205559.GA43880@ast-mbp.thefacebook.com
---
 include/linux/bpf.h                         |  7 ++-
 include/linux/filter.h                      |  2 +
 include/uapi/linux/bpf.h                    | 11 ++++
 kernel/bpf/syscall.c                        | 89 +++++++++++++++++++----------
 kernel/bpf/verifier.c                       | 16 +++++-
 kernel/trace/bpf_trace.c                    | 15 +++--
 net/core/filter.c                           | 48 ++++++++++------
 samples/bpf/bpf_load.c                      |  3 +-
 samples/bpf/cookie_uid_helper_example.c     |  2 +-
 samples/bpf/fds_example.c                   |  2 +-
 samples/bpf/sock_example.c                  |  3 +-
 samples/bpf/test_cgrp2_attach.c             |  2 +-
 samples/bpf/test_cgrp2_attach2.c            |  2 +-
 samples/bpf/test_cgrp2_sock.c               |  2 +-
 tools/include/uapi/linux/bpf.h              | 11 ++++
 tools/lib/bpf/bpf.c                         |  5 +-
 tools/lib/bpf/bpf.h                         |  2 +-
 tools/lib/bpf/libbpf.c                      |  4 +-
 tools/perf/tests/bpf.c                      |  2 +-
 tools/testing/selftests/bpf/test_tag.c      |  2 +-
 tools/testing/selftests/bpf/test_verifier.c | 16 +++++-
 21 files changed, 175 insertions(+), 71 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2ae39a3e9ead..1cb407bd8ef7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -156,19 +156,22 @@ struct bpf_prog;
 
 struct bpf_verifier_ops {
 	/* return eBPF function prototype for verification */
-	const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id);
+	const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id,
+				      union bpf_prog_subtype *prog_subtype);
 
 	/* return true if 'size' wide access at offset 'off' within bpf_context
 	 * with 'type' (read or write) is allowed
 	 */
 	bool (*is_valid_access)(int off, int size, enum bpf_access_type type,
-				enum bpf_reg_type *reg_type);
+				enum bpf_reg_type *reg_type,
+				union bpf_prog_subtype *prog_subtype);
 	int (*gen_prologue)(struct bpf_insn *insn, bool direct_write,
 			    const struct bpf_prog *prog);
 	u32 (*convert_ctx_access)(enum bpf_access_type type,
 				  const struct bpf_insn *src,
 				  struct bpf_insn *dst,
 				  struct bpf_prog *prog);
+	bool (*is_valid_subtype)(union bpf_prog_subtype *prog_subtype);
 };
 
 struct bpf_prog_type_list {
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 511fe910bf1d..f5eb40a9aaa1 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -419,6 +419,8 @@ struct bpf_prog {
 	enum bpf_prog_type	type;		/* Type of BPF program */
 	u32			len;		/* Number of filter blocks */
 	u8			tag[BPF_TAG_SIZE];
+	u8			has_subtype;
+	union bpf_prog_subtype	subtype;	/* Fine-grained verifications */
 	struct bpf_prog_aux	*aux;		/* Auxiliary fields */
 	struct sock_fprog_kern	*orig_prog;	/* Original BPF program */
 	unsigned int		(*bpf_func)(const void *ctx,
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 28317a04c34d..0eb71ab9b4fd 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -147,6 +147,15 @@ enum bpf_attach_type {
  */
 #define BPF_F_NO_COMMON_LRU	(1U << 1)
 
+union bpf_prog_subtype {
+	struct {
+		__u32		version; /* cf. documentation */
+		__u32		event; /* enum landlock_subtype_event */
+		__aligned_u64	ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+		__aligned_u64	option; /* LANDLOCK_SUBTYPE_OPTION_* */
+	} landlock_rule;
+} __attribute__((aligned(8)));
+
 union bpf_attr {
 	struct { /* anonymous struct used by BPF_MAP_CREATE command */
 		__u32	map_type;	/* one of enum bpf_map_type */
@@ -176,6 +185,8 @@ union bpf_attr {
 		__u32		log_size;	/* size of user buffer */
 		__aligned_u64	log_buf;	/* user supplied buffer */
 		__u32		kern_version;	/* checked when prog_type=kprobe */
+		__aligned_u64	prog_subtype;	/* bpf_prog_subtype address */
+		__u32		prog_subtype_size;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index c35ebfe6d84d..3d07b10ade5e 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -783,8 +783,44 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type)
 }
 EXPORT_SYMBOL_GPL(bpf_prog_get_type);
 
+static int check_user_buf(void __user *uptr, unsigned int size_req,
+			  unsigned int size_max)
+{
+	if (!access_ok(VERIFY_READ, uptr, 1))
+		return -EFAULT;
+
+	if (size_req > PAGE_SIZE)	/* silly large */
+		return -E2BIG;
+
+	/* If we're handed a bigger struct than we know of,
+	 * ensure all the unknown bits are 0 - i.e. new
+	 * user-space does not rely on any kernel feature
+	 * extensions we dont know about yet.
+	 */
+	if (size_req > size_max) {
+		unsigned char __user *addr;
+		unsigned char __user *end;
+		unsigned char val;
+		int err;
+
+		addr = uptr + size_max;
+		end  = uptr + size_req;
+
+		for (; addr < end; addr++) {
+			err = get_user(val, addr);
+			if (err)
+				return err;
+			if (val)
+				return -E2BIG;
+		}
+		return size_max;
+	}
+
+	return size_req;
+}
+
 /* last field in 'union bpf_attr' used by this command */
-#define	BPF_PROG_LOAD_LAST_FIELD kern_version
+#define	BPF_PROG_LOAD_LAST_FIELD prog_subtype_size
 
 static int bpf_prog_load(union bpf_attr *attr)
 {
@@ -843,6 +879,26 @@ static int bpf_prog_load(union bpf_attr *attr)
 	if (err < 0)
 		goto free_prog;
 
+	/* copy eBPF program subtype from user space */
+	if (attr->prog_subtype) {
+		__u32 size;
+
+		size = check_user_buf((void __user *)attr->prog_subtype,
+				      attr->prog_subtype_size,
+				      sizeof(prog->subtype));
+		if (size < 0) {
+			err = size;
+			goto free_prog;
+		}
+		/* prog->subtype is __GFP_ZERO */
+		if (copy_from_user(&prog->subtype,
+				   u64_to_user_ptr(attr->prog_subtype), size)
+				   != 0)
+			return -EFAULT;
+		prog->has_subtype = 1;
+	} else if (attr->prog_subtype_size != 0)
+		return -EINVAL;
+
 	/* run eBPF verifier */
 	err = bpf_check(&prog, attr);
 	if (err < 0)
@@ -981,34 +1037,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
 	if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled)
 		return -EPERM;
 
-	if (!access_ok(VERIFY_READ, uattr, 1))
-		return -EFAULT;
-
-	if (size > PAGE_SIZE)	/* silly large */
-		return -E2BIG;
-
-	/* If we're handed a bigger struct than we know of,
-	 * ensure all the unknown bits are 0 - i.e. new
-	 * user-space does not rely on any kernel feature
-	 * extensions we dont know about yet.
-	 */
-	if (size > sizeof(attr)) {
-		unsigned char __user *addr;
-		unsigned char __user *end;
-		unsigned char val;
-
-		addr = (void __user *)uattr + sizeof(attr);
-		end  = (void __user *)uattr + size;
-
-		for (; addr < end; addr++) {
-			err = get_user(val, addr);
-			if (err)
-				return err;
-			if (val)
-				return -E2BIG;
-		}
-		size = sizeof(attr);
-	}
+	size = check_user_buf((void __user *)uattr, size, sizeof(attr));
+	if (size < 0)
+		return size;
 
 	/* copy attributes from user space, may be less than sizeof(bpf_attr) */
 	if (copy_from_user(&attr, uattr, size) != 0)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 09923cc5c7c7..1f44f7ce35f4 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -742,7 +742,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int off, int size,
 		return 0;
 
 	if (env->prog->aux->ops->is_valid_access &&
-	    env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) {
+	    env->prog->aux->ops->is_valid_access(off, size, t, reg_type,
+						 &env->prog->subtype)) {
 		/* remember the offset of last byte accessed in ctx */
 		if (env->prog->aux->max_ctx_offset < off + size)
 			env->prog->aux->max_ctx_offset = off + size;
@@ -1296,7 +1297,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
 	}
 
 	if (env->prog->aux->ops->get_func_proto)
-		fn = env->prog->aux->ops->get_func_proto(func_id);
+		fn = env->prog->aux->ops->get_func_proto(func_id,
+							 &env->prog->subtype);
 
 	if (!fn) {
 		verbose("unknown func %s#%d\n", func_id_name(func_id), func_id);
@@ -3365,7 +3367,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
 		}
 
 patch_call_imm:
-		fn = prog->aux->ops->get_func_proto(insn->imm);
+		fn = prog->aux->ops->get_func_proto(insn->imm, &prog->subtype);
 		/* all functions that have prototype and verifier allowed
 		 * programs to call them, must be real in-kernel functions
 		 */
@@ -3408,6 +3410,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
 	struct bpf_verifier_env *env;
 	int ret = -EINVAL;
 
+	if ((*prog)->aux->ops->is_valid_subtype) {
+		if (!(*prog)->aux->ops->is_valid_subtype(&(*prog)->subtype))
+			return -EINVAL;
+	} else if ((*prog)->has_subtype) {
+		/* do not accept a subtype if the program does not handle it */
+		return -EINVAL;
+	}
+
 	/* 'struct bpf_verifier_env' can be global, but since it's not small,
 	 * allocate/free it every time bpf_check() is called
 	 */
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index cee9802cf3e0..e71ee1bb7abf 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -469,7 +469,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
 	}
 }
 
-static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id,
+		union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_perf_event_output:
@@ -483,7 +484,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
 
 /* bpf+kprobe programs can access fields of 'struct pt_regs' */
 static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-					enum bpf_reg_type *reg_type)
+					enum bpf_reg_type *reg_type,
+					union bpf_prog_subtype *prog_subtype)
 {
 	if (off < 0 || off >= sizeof(struct pt_regs))
 		return false;
@@ -558,7 +560,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
 	.arg3_type	= ARG_ANYTHING,
 };
 
-static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id,
+		union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_perf_event_output:
@@ -571,7 +574,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
 }
 
 static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-				    enum bpf_reg_type *reg_type)
+				    enum bpf_reg_type *reg_type,
+				    union bpf_prog_subtype *prog_subtype)
 {
 	if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
 		return false;
@@ -595,7 +599,8 @@ static struct bpf_prog_type_list tracepoint_tl __ro_after_init = {
 };
 
 static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-				    enum bpf_reg_type *reg_type)
+				    enum bpf_reg_type *reg_type,
+				    union bpf_prog_subtype *prog_subtype)
 {
 	if (off < 0 || off >= sizeof(struct bpf_perf_event_data))
 		return false;
diff --git a/net/core/filter.c b/net/core/filter.c
index dfb9f61a2fd5..c7ef3938a598 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2638,7 +2638,8 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
 };
 
 static const struct bpf_func_proto *
-bpf_base_func_proto(enum bpf_func_id func_id)
+bpf_base_func_proto(enum bpf_func_id func_id,
+		    union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_map_lookup_elem:
@@ -2666,7 +2667,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
 }
 
 static const struct bpf_func_proto *
-sk_filter_func_proto(enum bpf_func_id func_id)
+sk_filter_func_proto(enum bpf_func_id func_id,
+		     union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_skb_load_bytes:
@@ -2676,12 +2678,13 @@ sk_filter_func_proto(enum bpf_func_id func_id)
 	case BPF_FUNC_get_socket_uid:
 		return &bpf_get_socket_uid_proto;
 	default:
-		return bpf_base_func_proto(func_id);
+		return bpf_base_func_proto(func_id, prog_subtype);
 	}
 }
 
 static const struct bpf_func_proto *
-tc_cls_act_func_proto(enum bpf_func_id func_id)
+tc_cls_act_func_proto(enum bpf_func_id func_id,
+		      union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_skb_store_bytes:
@@ -2739,12 +2742,13 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
 	case BPF_FUNC_get_socket_uid:
 		return &bpf_get_socket_uid_proto;
 	default:
-		return bpf_base_func_proto(func_id);
+		return bpf_base_func_proto(func_id, prog_subtype);
 	}
 }
 
 static const struct bpf_func_proto *
-xdp_func_proto(enum bpf_func_id func_id)
+xdp_func_proto(enum bpf_func_id func_id,
+	       union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_perf_event_output:
@@ -2754,23 +2758,25 @@ xdp_func_proto(enum bpf_func_id func_id)
 	case BPF_FUNC_xdp_adjust_head:
 		return &bpf_xdp_adjust_head_proto;
 	default:
-		return bpf_base_func_proto(func_id);
+		return bpf_base_func_proto(func_id, prog_subtype);
 	}
 }
 
 static const struct bpf_func_proto *
-cg_skb_func_proto(enum bpf_func_id func_id)
+cg_skb_func_proto(enum bpf_func_id func_id,
+		  union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_skb_load_bytes:
 		return &bpf_skb_load_bytes_proto;
 	default:
-		return bpf_base_func_proto(func_id);
+		return bpf_base_func_proto(func_id, prog_subtype);
 	}
 }
 
 static const struct bpf_func_proto *
-lwt_inout_func_proto(enum bpf_func_id func_id)
+lwt_inout_func_proto(enum bpf_func_id func_id,
+		     union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_skb_load_bytes:
@@ -2792,12 +2798,13 @@ lwt_inout_func_proto(enum bpf_func_id func_id)
 	case BPF_FUNC_skb_under_cgroup:
 		return &bpf_skb_under_cgroup_proto;
 	default:
-		return bpf_base_func_proto(func_id);
+		return bpf_base_func_proto(func_id, prog_subtype);
 	}
 }
 
 static const struct bpf_func_proto *
-lwt_xmit_func_proto(enum bpf_func_id func_id)
+lwt_xmit_func_proto(enum bpf_func_id func_id,
+		    union bpf_prog_subtype *prog_subtype)
 {
 	switch (func_id) {
 	case BPF_FUNC_skb_get_tunnel_key:
@@ -2827,7 +2834,7 @@ lwt_xmit_func_proto(enum bpf_func_id func_id)
 	case BPF_FUNC_set_hash_invalid:
 		return &bpf_set_hash_invalid_proto;
 	default:
-		return lwt_inout_func_proto(func_id);
+		return lwt_inout_func_proto(func_id, prog_subtype);
 	}
 }
 
@@ -2857,7 +2864,8 @@ static bool __is_valid_access(int off, int size)
 
 static bool sk_filter_is_valid_access(int off, int size,
 				      enum bpf_access_type type,
-				      enum bpf_reg_type *reg_type)
+				      enum bpf_reg_type *reg_type,
+				      union bpf_prog_subtype *prog_subtype)
 {
 	switch (off) {
 	case offsetof(struct __sk_buff, tc_classid):
@@ -2881,7 +2889,8 @@ static bool sk_filter_is_valid_access(int off, int size,
 
 static bool lwt_is_valid_access(int off, int size,
 				enum bpf_access_type type,
-				enum bpf_reg_type *reg_type)
+				enum bpf_reg_type *reg_type,
+				union bpf_prog_subtype *prog_subtype)
 {
 	switch (off) {
 	case offsetof(struct __sk_buff, tc_classid):
@@ -2914,7 +2923,8 @@ static bool lwt_is_valid_access(int off, int size,
 
 static bool sock_filter_is_valid_access(int off, int size,
 					enum bpf_access_type type,
-					enum bpf_reg_type *reg_type)
+					enum bpf_reg_type *reg_type,
+					union bpf_prog_subtype *prog_subtype)
 {
 	if (type == BPF_WRITE) {
 		switch (off) {
@@ -2977,7 +2987,8 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write,
 
 static bool tc_cls_act_is_valid_access(int off, int size,
 				       enum bpf_access_type type,
-				       enum bpf_reg_type *reg_type)
+				       enum bpf_reg_type *reg_type,
+				       union bpf_prog_subtype *prog_subtype)
 {
 	if (type == BPF_WRITE) {
 		switch (off) {
@@ -3019,7 +3030,8 @@ static bool __is_valid_xdp_access(int off, int size)
 
 static bool xdp_is_valid_access(int off, int size,
 				enum bpf_access_type type,
-				enum bpf_reg_type *reg_type)
+				enum bpf_reg_type *reg_type,
+				union bpf_prog_subtype *prog_subtype)
 {
 	if (type == BPF_WRITE)
 		return false;
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index dcdce1270d38..4a3460d7c01f 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -73,6 +73,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 	char buf[256];
 	int fd, efd, err, id;
 	struct perf_event_attr attr = {};
+	union bpf_prog_subtype *st = NULL;
 
 	attr.type = PERF_TYPE_TRACEPOINT;
 	attr.sample_type = PERF_SAMPLE_RAW;
@@ -99,7 +100,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 	}
 
 	fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version,
-			      bpf_log_buf, BPF_LOG_BUF_SIZE);
+			      bpf_log_buf, BPF_LOG_BUF_SIZE, st);
 	if (fd < 0) {
 		printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf);
 		return -1;
diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c
index f6e5e58931c5..7427428312d6 100644
--- a/samples/bpf/cookie_uid_helper_example.c
+++ b/samples/bpf/cookie_uid_helper_example.c
@@ -148,7 +148,7 @@ static void prog_load(void)
 	};
 	prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
 					ARRAY_SIZE(prog), "GPL", 0,
-					log_buf, sizeof(log_buf));
+					log_buf, sizeof(log_buf), NULL);
 	if (prog_fd < 0)
 		error(1, errno, "failed to load prog\n%s\n", log_buf);
 }
diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c
index e29bd52ff9e8..0f4f5f6a9f9f 100644
--- a/samples/bpf/fds_example.c
+++ b/samples/bpf/fds_example.c
@@ -62,7 +62,7 @@ static int bpf_prog_create(const char *object)
 	} else {
 		return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
 					insns, insns_cnt, "GPL", 0,
-					bpf_log_buf, BPF_LOG_BUF_SIZE);
+					bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
 	}
 }
 
diff --git a/samples/bpf/sock_example.c b/samples/bpf/sock_example.c
index 6fc6e193ef1b..3778f66deb76 100644
--- a/samples/bpf/sock_example.c
+++ b/samples/bpf/sock_example.c
@@ -60,7 +60,8 @@ static int test_sock(void)
 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
 
 	prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt,
-				   "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+				   "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE,
+				   NULL);
 	if (prog_fd < 0) {
 		printf("failed to load prog '%s'\n", strerror(errno));
 		goto cleanup;
diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c
index 4bfcaf93fcf3..f8a91d2b7896 100644
--- a/samples/bpf/test_cgrp2_attach.c
+++ b/samples/bpf/test_cgrp2_attach.c
@@ -72,7 +72,7 @@ static int prog_load(int map_fd, int verdict)
 
 	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
 				prog, insns_cnt, "GPL", 0,
-				bpf_log_buf, BPF_LOG_BUF_SIZE);
+				bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
 }
 
 static int usage(const char *argv0)
diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c
index 3049b1f26267..31a0f4bd665f 100644
--- a/samples/bpf/test_cgrp2_attach2.c
+++ b/samples/bpf/test_cgrp2_attach2.c
@@ -45,7 +45,7 @@ static int prog_load(int verdict)
 
 	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
 			       prog, insns_cnt, "GPL", 0,
-			       bpf_log_buf, BPF_LOG_BUF_SIZE);
+			       bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
 
 	if (ret < 0) {
 		log_err("Loading program");
diff --git a/samples/bpf/test_cgrp2_sock.c b/samples/bpf/test_cgrp2_sock.c
index c3cfb23e23b5..697f2db30e6a 100644
--- a/samples/bpf/test_cgrp2_sock.c
+++ b/samples/bpf/test_cgrp2_sock.c
@@ -38,7 +38,7 @@ static int prog_load(int idx)
 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
 
 	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt,
-				"GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+				"GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
 }
 
 static int usage(const char *argv0)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 1ea08ce35567..725c9a7d3dd9 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -147,6 +147,15 @@ enum bpf_attach_type {
  */
 #define BPF_F_NO_COMMON_LRU	(1U << 1)
 
+union bpf_prog_subtype {
+	struct {
+		__u32		version; /* cf. documentation */
+		__u32		event; /* enum landlock_subtype_event */
+		__aligned_u64	ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+		__aligned_u64	option; /* LANDLOCK_SUBTYPE_OPTION_* */
+	} landlock_rule;
+} __attribute__((aligned(8)));
+
 union bpf_attr {
 	struct { /* anonymous struct used by BPF_MAP_CREATE command */
 		__u32	map_type;	/* one of enum bpf_map_type */
@@ -176,6 +185,8 @@ union bpf_attr {
 		__u32		log_size;	/* size of user buffer */
 		__aligned_u64	log_buf;	/* user supplied buffer */
 		__u32		kern_version;	/* checked when prog_type=kprobe */
+		__aligned_u64	prog_subtype;	/* bpf_prog_subtype address */
+		__u32		prog_subtype_size;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 9b58d20e8c93..34fa03c95389 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -88,7 +88,8 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
 
 int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 		     size_t insns_cnt, const char *license,
-		     __u32 kern_version, char *log_buf, size_t log_buf_sz)
+		     __u32 kern_version, char *log_buf, size_t log_buf_sz,
+		     const union bpf_prog_subtype *subtype)
 {
 	int fd;
 	union bpf_attr attr;
@@ -102,6 +103,8 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 	attr.log_size = 0;
 	attr.log_level = 0;
 	attr.kern_version = kern_version;
+	attr.prog_subtype = ptr_to_u64(subtype);
+	attr.prog_subtype_size = subtype ? sizeof(*subtype) : 0;
 
 	fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 	if (fd >= 0 || !log_buf || !log_buf_sz)
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 93f021932623..dfb320653ac1 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -34,7 +34,7 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
 int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 		     size_t insns_cnt, const char *license,
 		     __u32 kern_version, char *log_buf,
-		     size_t log_buf_sz);
+		     size_t log_buf_sz, const union bpf_prog_subtype *subtype);
 
 int bpf_map_update_elem(int fd, const void *key, const void *value,
 			__u64 flags);
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ac6eb863b2a4..645b0a96e66f 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -995,7 +995,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
 		pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
 
 	ret = bpf_load_program(type, insns, insns_cnt, license,
-			       kern_version, log_buf, BPF_LOG_BUF_SIZE);
+			       kern_version, log_buf, BPF_LOG_BUF_SIZE, NULL);
 
 	if (ret >= 0) {
 		*pfd = ret;
@@ -1022,7 +1022,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
 
 			fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
 					      insns_cnt, license, kern_version,
-					      NULL, 0);
+					      NULL, 0, NULL);
 			if (fd >= 0) {
 				close(fd);
 				ret = -LIBBPF_ERRNO__PROGTYPE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 1a04fe77487d..8ae07f5ecdc0 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -306,7 +306,7 @@ static int check_env(void)
 
 	err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
 			       sizeof(insns) / sizeof(insns[0]),
-			       license, kver_int, NULL, 0);
+			       license, kver_int, NULL, 0, NULL);
 	if (err < 0) {
 		pr_err("Missing basic BPF support, skip this test: %s\n",
 		       strerror(errno));
diff --git a/tools/testing/selftests/bpf/test_tag.c b/tools/testing/selftests/bpf/test_tag.c
index de409fc50c35..cf7892c87b5a 100644
--- a/tools/testing/selftests/bpf/test_tag.c
+++ b/tools/testing/selftests/bpf/test_tag.c
@@ -57,7 +57,7 @@ static int bpf_try_load_prog(int insns, int fd_map,
 
 	bpf_filler(insns, fd_map);
 	fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0,
-				   NULL, 0);
+				   NULL, 0, NULL);
 	assert(fd_prog > 0);
 	if (fd_map > 0)
 		bpf_filler(insns, 0);
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index f4f43c98cf7f..daa87dd7c80e 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -55,6 +55,8 @@ struct bpf_test {
 		REJECT
 	} result, result_unpriv;
 	enum bpf_prog_type prog_type;
+	bool has_prog_subtype;
+	union bpf_prog_subtype prog_subtype;
 };
 
 /* Note we want this to be 64 bit aligned so that the end of our array is
@@ -4524,6 +4526,16 @@ static struct bpf_test tests[] = {
 		.errstr = "R1 type=map_value_or_null expected=map_ptr",
 		.result = REJECT,
 	},
+	{
+		"superfluous subtype",
+		.insns = {
+			BPF_MOV32_IMM(BPF_REG_0, 0),
+			BPF_EXIT_INSN(),
+		},
+		.errstr = "",
+		.result = REJECT,
+		.has_prog_subtype = true,
+	},
 };
 
 static int probe_filter_length(const struct bpf_insn *fp)
@@ -4639,6 +4651,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 	int fd_prog, expected_ret;
 	const char *expected_err;
 	int i;
+	union bpf_prog_subtype *prog_subtype =
+		test->has_prog_subtype ? &test->prog_subtype : NULL;
 
 	for (i = 0; i < MAX_NR_MAPS; i++)
 		map_fds[i] = -1;
@@ -4647,7 +4661,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 
 	fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
 				   prog, prog_len, "GPL", 0, bpf_vlog,
-				   sizeof(bpf_vlog));
+				   sizeof(bpf_vlog), prog_subtype);
 
 	expected_ret = unpriv && test->result_unpriv != UNDEF ?
 		       test->result_unpriv : test->result;
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
  2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-16 21:57   ` Mickaël Salaün
  2017-04-18 21:58   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 03/11] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode() Mickaël Salaün
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

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

Add an initial Landlock Kconfig.

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

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

Changes since v3:
* split commit
* revamp the landlock_context:
  * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
    cross-check action with the event type
  * replace args array with dedicated fields to ease the addition of new
    fields

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---
 include/linux/landlock.h       |  23 ++++++++
 include/uapi/linux/bpf.h       | 105 +++++++++++++++++++++++++++++++++++
 security/Kconfig               |   1 +
 security/Makefile              |   2 +
 security/landlock/Kconfig      |  18 ++++++
 security/landlock/Makefile     |   3 +
 security/landlock/common.h     |  25 +++++++++
 security/landlock/init.c       | 123 +++++++++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 105 +++++++++++++++++++++++++++++++++++
 9 files changed, 405 insertions(+)
 create mode 100644 include/linux/landlock.h
 create mode 100644 security/landlock/Kconfig
 create mode 100644 security/landlock/Makefile
 create mode 100644 security/landlock/common.h
 create mode 100644 security/landlock/init.c

diff --git a/include/linux/landlock.h b/include/linux/landlock.h
new file mode 100644
index 000000000000..53013dc374fe
--- /dev/null
+++ b/include/linux/landlock.h
@@ -0,0 +1,23 @@
+/*
+ * Landlock LSM - public kernel headers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_LANDLOCK_H
+#define _LINUX_LANDLOCK_H
+#ifdef CONFIG_SECURITY_LANDLOCK
+
+/*
+ * This is not intended for the UAPI headers. Each userland software should use
+ * a static minimal version for the required features as explained in the
+ * documentation.
+ */
+#define LANDLOCK_VERSION 1
+
+#endif /* CONFIG_SECURITY_LANDLOCK */
+#endif /* _LINUX_LANDLOCK_H */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 0eb71ab9b4fd..619b1f8707cc 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_IN,
 	BPF_PROG_TYPE_LWT_OUT,
 	BPF_PROG_TYPE_LWT_XMIT,
+	BPF_PROG_TYPE_LANDLOCK,
 };
 
 enum bpf_attach_type {
@@ -661,4 +662,108 @@ struct xdp_md {
 	__u32 data_end;
 };
 
+/**
+ * enum landlock_subtype_event - event occurring when an action is performed on
+ * a particular kernel object
+ *
+ * An event is a policy decision point which exposes the same context type
+ * (especially the same arg[0-9] field types) for each rule execution.
+ *
+ * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
+ * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
+ */
+enum landlock_subtype_event {
+	LANDLOCK_SUBTYPE_EVENT_UNSPEC,
+	LANDLOCK_SUBTYPE_EVENT_FS,
+};
+#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS
+
+/**
+ * DOC: landlock_subtype_access
+ *
+ * eBPF context and functions allowed for a rule
+ *
+ * - LANDLOCK_SUBTYPE_ABILITY_WRITE: allows to directly send notification to
+ *   userland (e.g. through a map), which may leaks sensitive information
+ * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
+ *   logs), which may be dangerous and should only be used for rule testing
+ */
+#define LANDLOCK_SUBTYPE_ABILITY_WRITE		(1ULL << 0)
+#define LANDLOCK_SUBTYPE_ABILITY_DEBUG		(1ULL << 1)
+#define _LANDLOCK_SUBTYPE_ABILITY_NB		2
+#define _LANDLOCK_SUBTYPE_ABILITY_MASK		((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)
+
+/*
+ * Future options for a Landlock rule (e.g. run even if a previous rule denied
+ * an action).
+ */
+#define _LANDLOCK_SUBTYPE_OPTION_NB		0
+#define _LANDLOCK_SUBTYPE_OPTION_MASK		((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)
+
+/*
+ * Status visible in the @status field of a context (e.g. already called in
+ * this syscall session, with same args...).
+ *
+ * The @status field exposed to a rule shall depend on the rule version.
+ */
+#define _LANDLOCK_SUBTYPE_STATUS_NB		0
+#define _LANDLOCK_SUBTYPE_STATUS_MASK		((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)
+
+/**
+ * DOC: landlock_action_fs
+ *
+ * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
+ * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
+ *   include mount actions)
+ * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
+ * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
+ * - %LANDLOCK_ACTION_FS_GET: open or receive a file
+ * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
+ *
+ * Each of the following actions are specific to syscall multiplexers. They
+ * fill the syscall_cmd field from &struct landlock_context with their custom
+ * command.
+ *
+ * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
+ * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
+ * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
+ */
+#define LANDLOCK_ACTION_FS_EXEC			(1ULL << 0)
+#define LANDLOCK_ACTION_FS_WRITE		(1ULL << 1)
+#define LANDLOCK_ACTION_FS_READ			(1ULL << 2)
+#define LANDLOCK_ACTION_FS_NEW			(1ULL << 3)
+#define LANDLOCK_ACTION_FS_GET			(1ULL << 4)
+#define LANDLOCK_ACTION_FS_REMOVE		(1ULL << 5)
+#define LANDLOCK_ACTION_FS_IOCTL		(1ULL << 6)
+#define LANDLOCK_ACTION_FS_LOCK			(1ULL << 7)
+#define LANDLOCK_ACTION_FS_FCNTL		(1ULL << 8)
+#define _LANDLOCK_ACTION_FS_NB			9
+#define _LANDLOCK_ACTION_FS_MASK		((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
+
+
+/**
+ * struct landlock_context - context accessible to a Landlock rule
+ *
+ * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
+ * @arch: indicates system call convention as an AUDIT_ARCH_* value
+ *        as defined in <linux/audit.h>
+ * @syscall_nr: the system call number called by the current process (may be
+ *              useful to debug: find out from which syscall this request came
+ *              from)
+ * @syscall_cmd: contains the command used by a multiplexer syscall (e.g.
+ *               ioctl, fcntl, flock)
+ * @event: event type (&enum landlock_subtype_event)
+ * @arg1: event's first optional argument
+ * @arg2: event's second optional argument
+ */
+struct landlock_context {
+	__u64 status;
+	__u32 arch;
+	__u32 syscall_nr;
+	__u32 syscall_cmd;
+	__u32 event;
+	__u64 arg1;
+	__u64 arg2;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/security/Kconfig b/security/Kconfig
index d900f47eaa68..fb9eb0ac1ee4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -199,6 +199,7 @@ source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/landlock/Kconfig
 
 source security/integrity/Kconfig
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cdb8e19..3fdc2f19dc48 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
 subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
+subdir-$(CONFIG_SECURITY_LANDLOCK)		+= landlock
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
 obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
+obj-$(CONFIG_SECURITY_LANDLOCK)	+= landlock/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
new file mode 100644
index 000000000000..aa5808e116f1
--- /dev/null
+++ b/security/landlock/Kconfig
@@ -0,0 +1,18 @@
+config SECURITY_LANDLOCK
+	bool "Landlock sandbox support"
+	depends on SECURITY
+	depends on BPF_SYSCALL
+	depends on SECCOMP_FILTER
+	default y
+	help
+	  Landlock is a stackable LSM which allows to load a security policy to
+	  restrict processes (i.e. create a sandbox). The policy is a list of
+	  stacked eBPF programs, called rules, dedicated to restrict access to
+	  a type of kernel object (e.g. file).
+
+	  You need to enable seccomp filter to apply a security policy to a
+	  process hierarchy (e.g. application with built-in sandboxing).
+
+	  See Documentation/security/landlock/ for further information.
+
+	  If you are unsure how to answer this question, answer Y.
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
new file mode 100644
index 000000000000..7205f9a7a2ee
--- /dev/null
+++ b/security/landlock/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
+
+landlock-y := init.o
diff --git a/security/landlock/common.h b/security/landlock/common.h
new file mode 100644
index 000000000000..a2483405349f
--- /dev/null
+++ b/security/landlock/common.h
@@ -0,0 +1,25 @@
+/*
+ * Landlock LSM - private headers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SECURITY_LANDLOCK_COMMON_H
+#define _SECURITY_LANDLOCK_COMMON_H
+
+/**
+ * get_index - get an index for the rules of struct landlock_events
+ *
+ * @event: a Landlock event type
+ */
+static inline int get_index(enum landlock_subtype_event event)
+{
+	/* event ID > 0 for loaded programs */
+	return event - 1;
+}
+
+#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/init.c b/security/landlock/init.c
new file mode 100644
index 000000000000..0a97026f1c07
--- /dev/null
+++ b/security/landlock/init.c
@@ -0,0 +1,123 @@
+/*
+ * Landlock LSM - init
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h> /* enum bpf_access_type */
+#include <linux/capability.h> /* capable */
+#include <linux/landlock.h> /* LANDLOCK_VERSION */
+
+
+static inline bool bpf_landlock_is_valid_access(int off, int size,
+		enum bpf_access_type type, enum bpf_reg_type *reg_type,
+		union bpf_prog_subtype *prog_subtype)
+{
+	if (WARN_ON(!prog_subtype))
+		return false;
+
+	switch (prog_subtype->landlock_rule.event) {
+	case LANDLOCK_SUBTYPE_EVENT_FS:
+	case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
+	default:
+		return false;
+	}
+}
+
+static inline bool bpf_landlock_is_valid_subtype(
+		union bpf_prog_subtype *prog_subtype)
+{
+	if (WARN_ON(!prog_subtype))
+		return false;
+
+	switch (prog_subtype->landlock_rule.event) {
+	case LANDLOCK_SUBTYPE_EVENT_FS:
+		break;
+	case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
+	default:
+		return false;
+	}
+
+	if (!prog_subtype->landlock_rule.version ||
+			prog_subtype->landlock_rule.version > LANDLOCK_VERSION)
+		return false;
+	if (!prog_subtype->landlock_rule.event ||
+			prog_subtype->landlock_rule.event > _LANDLOCK_SUBTYPE_EVENT_LAST)
+		return false;
+	if (prog_subtype->landlock_rule.ability & ~_LANDLOCK_SUBTYPE_ABILITY_MASK)
+		return false;
+	if (prog_subtype->landlock_rule.option & ~_LANDLOCK_SUBTYPE_OPTION_MASK)
+		return false;
+
+	/* check ability flags */
+	if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_WRITE &&
+			!capable(CAP_SYS_ADMIN))
+		return false;
+	if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_DEBUG &&
+			!capable(CAP_SYS_ADMIN))
+		return false;
+
+	return true;
+}
+
+static inline const struct bpf_func_proto *bpf_landlock_func_proto(
+		enum bpf_func_id func_id, union bpf_prog_subtype *prog_subtype)
+{
+	bool event_fs = (prog_subtype->landlock_rule.event ==
+			LANDLOCK_SUBTYPE_EVENT_FS);
+	bool ability_write = !!(prog_subtype->landlock_rule.ability &
+			LANDLOCK_SUBTYPE_ABILITY_WRITE);
+	bool ability_debug = !!(prog_subtype->landlock_rule.ability &
+			LANDLOCK_SUBTYPE_ABILITY_DEBUG);
+
+	switch (func_id) {
+	case BPF_FUNC_map_lookup_elem:
+		return &bpf_map_lookup_elem_proto;
+
+	/* ability_write */
+	case BPF_FUNC_map_delete_elem:
+		if (ability_write)
+			return &bpf_map_delete_elem_proto;
+		return NULL;
+	case BPF_FUNC_map_update_elem:
+		if (ability_write)
+			return &bpf_map_update_elem_proto;
+		return NULL;
+
+	/* ability_debug */
+	case BPF_FUNC_get_current_comm:
+		if (ability_debug)
+			return &bpf_get_current_comm_proto;
+		return NULL;
+	case BPF_FUNC_get_current_pid_tgid:
+		if (ability_debug)
+			return &bpf_get_current_pid_tgid_proto;
+		return NULL;
+	case BPF_FUNC_get_current_uid_gid:
+		if (ability_debug)
+			return &bpf_get_current_uid_gid_proto;
+		return NULL;
+	case BPF_FUNC_trace_printk:
+		if (ability_debug)
+			return bpf_get_trace_printk_proto();
+		return NULL;
+
+	default:
+		return NULL;
+	}
+}
+
+static const struct bpf_verifier_ops bpf_landlock_ops = {
+	.get_func_proto	= bpf_landlock_func_proto,
+	.is_valid_access = bpf_landlock_is_valid_access,
+	.is_valid_subtype = bpf_landlock_is_valid_subtype,
+};
+
+static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
+	.ops = &bpf_landlock_ops,
+	.type = BPF_PROG_TYPE_LANDLOCK,
+};
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 725c9a7d3dd9..cdb13134f522 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_IN,
 	BPF_PROG_TYPE_LWT_OUT,
 	BPF_PROG_TYPE_LWT_XMIT,
+	BPF_PROG_TYPE_LANDLOCK,
 };
 
 enum bpf_attach_type {
@@ -649,4 +650,108 @@ struct xdp_md {
 	__u32 data_end;
 };
 
+/**
+ * enum landlock_subtype_event - event occurring when an action is performed on
+ * a particular kernel object
+ *
+ * An event is a policy decision point which exposes the same context type
+ * (especially the same arg[0-9] field types) for each rule execution.
+ *
+ * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
+ * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
+ */
+enum landlock_subtype_event {
+	LANDLOCK_SUBTYPE_EVENT_UNSPEC,
+	LANDLOCK_SUBTYPE_EVENT_FS,
+};
+#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS
+
+/**
+ * DOC: landlock_subtype_access
+ *
+ * eBPF context and functions allowed for a rule
+ *
+ * - LANDLOCK_SUBTYPE_ABILITY_WRITE: allows to directly send notification to
+ *   userland (e.g. through a map), which may leaks sensitive information
+ * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing
+ *   logs), which may be dangerous and should only be used for rule testing
+ */
+#define LANDLOCK_SUBTYPE_ABILITY_WRITE		(1ULL << 0)
+#define LANDLOCK_SUBTYPE_ABILITY_DEBUG		(1ULL << 1)
+#define _LANDLOCK_SUBTYPE_ABILITY_NB		2
+#define _LANDLOCK_SUBTYPE_ABILITY_MASK		((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1)
+
+/*
+ * Future options for a Landlock rule (e.g. run even if a previous rule denied
+ * an action).
+ */
+#define _LANDLOCK_SUBTYPE_OPTION_NB		0
+#define _LANDLOCK_SUBTYPE_OPTION_MASK		((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1)
+
+/*
+ * Status visible in the @status field of a context (e.g. already called in
+ * this syscall session, with same args...).
+ *
+ * The @status field exposed to a rule shall depend on the rule version.
+ */
+#define _LANDLOCK_SUBTYPE_STATUS_NB		0
+#define _LANDLOCK_SUBTYPE_STATUS_MASK		((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1)
+
+/**
+ * DOC: landlock_action_fs
+ *
+ * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
+ * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
+ *   include mount actions)
+ * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
+ * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
+ * - %LANDLOCK_ACTION_FS_GET: open or receive a file
+ * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
+ *
+ * Each of the following actions are specific to syscall multiplexers. They
+ * fill the syscall_cmd field from &struct landlock_context with their custom
+ * command.
+ *
+ * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
+ * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
+ * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
+ */
+#define LANDLOCK_ACTION_FS_EXEC			(1ULL << 0)
+#define LANDLOCK_ACTION_FS_WRITE		(1ULL << 1)
+#define LANDLOCK_ACTION_FS_READ			(1ULL << 2)
+#define LANDLOCK_ACTION_FS_NEW			(1ULL << 3)
+#define LANDLOCK_ACTION_FS_GET			(1ULL << 4)
+#define LANDLOCK_ACTION_FS_REMOVE		(1ULL << 5)
+#define LANDLOCK_ACTION_FS_IOCTL		(1ULL << 6)
+#define LANDLOCK_ACTION_FS_LOCK			(1ULL << 7)
+#define LANDLOCK_ACTION_FS_FCNTL		(1ULL << 8)
+#define _LANDLOCK_ACTION_FS_NB			9
+#define _LANDLOCK_ACTION_FS_MASK		((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
+
+
+/**
+ * struct landlock_context - context accessible to a Landlock rule
+ *
+ * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
+ * @arch: indicates system call convention as an AUDIT_ARCH_* value
+ *        as defined in <linux/audit.h>
+ * @syscall_nr: the system call number called by the current process (may be
+ *              useful to debug: find out from which syscall this request came
+ *              from)
+ * @syscall_cmd: contains the command used by a multiplexer syscall (e.g.
+ *               ioctl, fcntl, flock)
+ * @event: event type (&enum landlock_subtype_event)
+ * @arg1: event's first optional argument
+ * @arg2: event's second optional argument
+ */
+struct landlock_context {
+	__u64 status;
+	__u32 arch;
+	__u32 syscall_nr;
+	__u32 syscall_cmd;
+	__u32 event;
+	__u64 arg1;
+	__u64 arg2;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 03/11] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode()
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
  2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
  2017-03-28 23:46 ` [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-03-28 23:46 ` [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem Mickaël Salaün
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

Changes since v5:
* cosmetic fixes and rebase

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

Changes since v3:
* remove bpf_landlock_cmp_fs_prop() (suggested by Alexie Starovoitov)
* add hooks dealing with struct inode and struct path pointers:
  inode_permission and inode_getattr
* add abstraction over eBPF helper arguments thanks to wrapping structs
* add bpf_landlock_get_fs_mode() helper to check file type and mode
* merge WARN_ON() (suggested by Kees Cook)
* fix and update bpf_helpers.h
* use BPF_CALL_* for eBPF helpers (suggested by Alexie Starovoitov)
* make handle arraymap safe (RCU) and remove buggy synchronize_rcu()
* factor out the arraymay walk
* use size_t to index array (suggested by Jann Horn)

Changes since v2:
* add MNT_INTERNAL check to only add file handle from user-visible FS
  (e.g. no anonymous inode)
* replace struct file* with struct path* in map_landlock_handle
* add BPF protos
* fix bpf_landlock_cmp_fs_prop_with_struct_file()

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: Jann Horn <jann@thejh.net>
---
 include/linux/bpf.h            | 33 +++++++++++++++++++++++++++
 include/uapi/linux/bpf.h       | 10 +++++++-
 kernel/bpf/Makefile            |  2 +-
 kernel/bpf/helpers_fs.c        | 52 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/verifier.c          |  6 +++++
 samples/bpf/bpf_helpers.h      |  2 ++
 security/landlock/init.c       |  6 +++++
 tools/include/uapi/linux/bpf.h | 10 +++++++-
 8 files changed, 118 insertions(+), 3 deletions(-)
 create mode 100644 kernel/bpf/helpers_fs.c

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1cb407bd8ef7..a2e53c22e450 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -15,6 +15,11 @@
 #include <linux/err.h>
 #include <linux/rbtree_latch.h>
 
+/* FS helpers */
+#include <linux/dcache.h> /* struct dentry */
+#include <linux/fs.h> /* struct file, struct inode */
+#include <linux/path.h> /* struct path */
+
 struct perf_event;
 struct bpf_map;
 
@@ -84,6 +89,8 @@ enum bpf_arg_type {
 
 	ARG_PTR_TO_CTX,		/* pointer to context */
 	ARG_ANYTHING,		/* any (initialized) argument is ok */
+
+	ARG_CONST_PTR_TO_HANDLE_FS,	/* pointer to an abstract FS struct */
 };
 
 /* type of values returned from helper functions */
@@ -150,6 +157,9 @@ enum bpf_reg_type {
 	 * map element.
 	 */
 	PTR_TO_MAP_VALUE_ADJ,
+
+	/* FS helpers */
+	CONST_PTR_TO_HANDLE_FS,
 };
 
 struct bpf_prog;
@@ -222,6 +232,26 @@ struct bpf_event_entry {
 	struct rcu_head rcu;
 };
 
+/* FS helpers */
+enum bpf_handle_fs_type {
+	BPF_HANDLE_FS_TYPE_NONE,
+	BPF_HANDLE_FS_TYPE_FILE,
+	BPF_HANDLE_FS_TYPE_INODE,
+	BPF_HANDLE_FS_TYPE_PATH,
+	BPF_HANDLE_FS_TYPE_DENTRY,
+};
+
+struct bpf_handle_fs {
+	enum bpf_handle_fs_type type;
+	union {
+		struct file *file;
+		struct inode *inode;
+		const struct path *path;
+		struct dentry *dentry;
+	};
+};
+
+
 u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5);
 u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 
@@ -362,6 +392,9 @@ extern const struct bpf_func_proto bpf_skb_vlan_push_proto;
 extern const struct bpf_func_proto bpf_skb_vlan_pop_proto;
 extern const struct bpf_func_proto bpf_get_stackid_proto;
 
+/* FS helpers */
+extern const struct bpf_func_proto bpf_handle_fs_get_mode_proto;
+
 /* Shared helpers among cBPF and eBPF. */
 void bpf_user_rnd_init_once(void);
 u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 619b1f8707cc..d35948634667 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -483,6 +483,13 @@ union bpf_attr {
  *     @skb: pointer to skb
  *     Return: uid of the socket owner on success or 0 if the socket pointer
  *     inside sk_buff is NULL
+ *
+ * s64 bpf_handle_fs_get_mode(handle_fs)
+ *     Get the mode of a struct bpf_handle_fs
+ *     fs: struct bpf_handle_fs address
+ *     Return:
+ *       >= 0 file mode
+ *       < 0 error
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -532,7 +539,8 @@ union bpf_attr {
 	FN(xdp_adjust_head),		\
 	FN(probe_read_str),		\
 	FN(get_socket_cookie),		\
-	FN(get_socket_uid),
+	FN(get_socket_uid),		\
+	FN(handle_fs_get_mode),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index e1e5e658f2db..2378a0e8fc87 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -1,6 +1,6 @@
 obj-y := core.o
 
-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o helpers_fs.o
 obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
 ifeq ($(CONFIG_PERF_EVENTS),y)
 obj-$(CONFIG_BPF_SYSCALL) += stackmap.o
diff --git a/kernel/bpf/helpers_fs.c b/kernel/bpf/helpers_fs.c
new file mode 100644
index 000000000000..d524d382adeb
--- /dev/null
+++ b/kernel/bpf/helpers_fs.c
@@ -0,0 +1,52 @@
+/*
+ * BPF filesystem helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h> /* struct bpf_handle_fs */
+#include <linux/errno.h>
+#include <linux/filter.h> /* BPF_CALL*() */
+
+BPF_CALL_1(bpf_handle_fs_get_mode, struct bpf_handle_fs *, handle_fs)
+{
+	if (WARN_ON(!handle_fs))
+		return -EFAULT;
+	if (!handle_fs->file) {
+		/* file can be null for anonymous mmap */
+		WARN_ON(handle_fs->type != BPF_HANDLE_FS_TYPE_FILE);
+		return -ENOENT;
+	}
+	switch (handle_fs->type) {
+	case BPF_HANDLE_FS_TYPE_FILE:
+		if (WARN_ON(!handle_fs->file->f_inode))
+			return -ENOENT;
+		return handle_fs->file->f_inode->i_mode;
+	case BPF_HANDLE_FS_TYPE_INODE:
+		return handle_fs->inode->i_mode;
+	case BPF_HANDLE_FS_TYPE_PATH:
+		if (WARN_ON(!handle_fs->path->dentry ||
+				!handle_fs->path->dentry->d_inode))
+			return -ENOENT;
+		return handle_fs->path->dentry->d_inode->i_mode;
+	case BPF_HANDLE_FS_TYPE_DENTRY:
+		if (WARN_ON(!handle_fs->dentry->d_inode))
+			return -ENOENT;
+		return handle_fs->dentry->d_inode->i_mode;
+	case BPF_HANDLE_FS_TYPE_NONE:
+	default:
+		WARN_ON(1);
+		return -EFAULT;
+	}
+}
+
+const struct bpf_func_proto bpf_handle_fs_get_mode_proto = {
+	.func		= bpf_handle_fs_get_mode,
+	.gpl_only	= true,
+	.ret_type	= RET_INTEGER,
+	.arg1_type	= ARG_CONST_PTR_TO_HANDLE_FS,
+};
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1f44f7ce35f4..2790e9ffd85a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -191,6 +191,7 @@ static const char * const reg_type_str[] = {
 	[CONST_IMM]		= "imm",
 	[PTR_TO_PACKET]		= "pkt",
 	[PTR_TO_PACKET_END]	= "pkt_end",
+	[CONST_PTR_TO_HANDLE_FS] = "handle_fs",
 };
 
 #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
@@ -548,6 +549,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
 	case PTR_TO_PACKET_END:
 	case FRAME_PTR:
 	case CONST_PTR_TO_MAP:
+	case CONST_PTR_TO_HANDLE_FS:
 		return true;
 	default:
 		return false;
@@ -1054,6 +1056,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
 		expected_type = PTR_TO_CTX;
 		if (type != expected_type)
 			goto err_type;
+	} else if (arg_type == ARG_CONST_PTR_TO_HANDLE_FS) {
+		expected_type = CONST_PTR_TO_HANDLE_FS;
+		if (type != expected_type)
+			goto err_type;
 	} else if (arg_type == ARG_PTR_TO_MEM ||
 		   arg_type == ARG_PTR_TO_UNINIT_MEM) {
 		expected_type = PTR_TO_STACK;
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h
index 52de9d88c021..98dc4988cede 100644
--- a/samples/bpf/bpf_helpers.h
+++ b/samples/bpf/bpf_helpers.h
@@ -59,6 +59,8 @@ static unsigned long long (*bpf_get_prandom_u32)(void) =
 	(void *) BPF_FUNC_get_prandom_u32;
 static int (*bpf_xdp_adjust_head)(void *ctx, int offset) =
 	(void *) BPF_FUNC_xdp_adjust_head;
+static long long (*bpf_handle_fs_get_mode)(void *handle_fs) =
+	(void *) BPF_FUNC_handle_fs_get_mode;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
diff --git a/security/landlock/init.c b/security/landlock/init.c
index 0a97026f1c07..914895d08320 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -78,6 +78,12 @@ static inline const struct bpf_func_proto *bpf_landlock_func_proto(
 	case BPF_FUNC_map_lookup_elem:
 		return &bpf_map_lookup_elem_proto;
 
+	/* event_fs */
+	case BPF_FUNC_handle_fs_get_mode:
+		if (event_fs)
+			return &bpf_handle_fs_get_mode_proto;
+		return NULL;
+
 	/* ability_write */
 	case BPF_FUNC_map_delete_elem:
 		if (ability_write)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index cdb13134f522..7b784d8b6be5 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -471,6 +471,13 @@ union bpf_attr {
  *     Return:
  *       > 0 length of the string including the trailing NUL on success
  *       < 0 error
+ *
+ * s64 bpf_handle_fs_get_mode(handle_fs)
+ *     Get the mode of a struct bpf_handle_fs
+ *     fs: struct bpf_handle_fs address
+ *     Return:
+ *       >= 0 file mode
+ *       < 0 error
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -520,7 +527,8 @@ union bpf_attr {
 	FN(xdp_adjust_head),		\
 	FN(probe_read_str),		\
 	FN(get_socket_cookie),		\
-	FN(get_socket_uid),
+	FN(get_socket_uid),		\
+	FN(handle_fs_get_mode),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (2 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 03/11] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode() Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-03-29 15:18   ` kbuild test robot
  2017-04-18 22:17   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp() Mickaël Salaün
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

Handle 33 filesystem-related LSM hooks for the Landlock filesystem
event: LANDLOCK_SUBTYPE_EVENT_FS.

A Landlock event wrap LSM hooks for similar kernel object types (e.g.
struct file, struct path...). Multiple LSM hooks can trigger the same
Landlock event.

Landlock handle nine coarse-grained actions: read, write, execute, new,
get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
access control in a way that can be extended in the future.

The Landlock LSM hook registration is done after other LSM to only run
actions from user-space, via eBPF programs, if the access was granted by
major (privileged) LSMs.

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

Changes since v4:
* add LSM hook abstraction called Landlock event
  * use the compiler type checking to verify hooks use by an event
  * handle all filesystem related LSM hooks (e.g. file_permission,
    mmap_file, sb_mount...)
* register BPF programs for Landlock just after LSM hooks registration
* move hooks registration after other LSMs
* add failsafes to check if a hook is not used by the kernel
* allow partial raw value access form the context (needed for programs
  generated by LLVM)

Changes since v3:
* split commit
* add hooks dealing with struct inode and struct path pointers:
  inode_permission and inode_getattr
* add abstraction over eBPF helper arguments thanks to wrapping structs

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---
 include/linux/lsm_hooks.h    |   5 +
 security/landlock/Makefile   |   4 +-
 security/landlock/hooks.c    | 115 +++++++++
 security/landlock/hooks.h    | 177 ++++++++++++++
 security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
 security/landlock/hooks_fs.h |  19 ++
 security/landlock/init.c     |  13 +
 security/security.c          |   7 +-
 8 files changed, 901 insertions(+), 2 deletions(-)
 create mode 100644 security/landlock/hooks.c
 create mode 100644 security/landlock/hooks.h
 create mode 100644 security/landlock/hooks_fs.c
 create mode 100644 security/landlock/hooks_fs.h

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index e29d4c62a3c8..884289166a0e 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1920,5 +1920,10 @@ void __init loadpin_add_hooks(void);
 #else
 static inline void loadpin_add_hooks(void) { };
 #endif
+#ifdef CONFIG_SECURITY_LANDLOCK
+extern void __init landlock_add_hooks(void);
+#else
+static inline void __init landlock_add_hooks(void) { }
+#endif /* CONFIG_SECURITY_LANDLOCK */
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 7205f9a7a2ee..c0db504a6335 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,3 +1,5 @@
+ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
+
 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
 
-landlock-y := init.o
+landlock-y := init.o hooks.o hooks_fs.o
diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
new file mode 100644
index 000000000000..eaee8162ff70
--- /dev/null
+++ b/security/landlock/hooks.c
@@ -0,0 +1,115 @@
+/*
+ * Landlock LSM - hooks helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/current.h>
+#include <asm/processor.h> /* task_pt_regs() */
+#include <asm/syscall.h> /* syscall_get_nr(), syscall_get_arch() */
+#include <linux/bpf.h> /* enum bpf_access_type, struct landlock_context */
+#include <linux/err.h> /* EPERM */
+#include <linux/filter.h> /* BPF_PROG_RUN() */
+#include <linux/landlock.h> /* struct landlock_rule */
+#include <linux/lsm_hooks.h>
+#include <linux/rculist.h> /* list_add_tail_rcu */
+#include <linux/stddef.h> /* offsetof */
+
+#include "common.h" /* get_index() */
+#include "hooks.h" /* CTX_ARG_NB */
+
+
+__init void landlock_register_hooks(struct security_hook_list *hooks, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		hooks[i].lsm = "landlock";
+		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+	}
+}
+
+bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
+		enum bpf_reg_type *reg_type,
+		enum bpf_reg_type ctx_types[CTX_ARG_NB],
+		union bpf_prog_subtype *prog_subtype)
+{
+	int max_size;
+
+	if (type != BPF_READ)
+		return false;
+	if (off < 0 || off >= sizeof(struct landlock_context))
+		return false;
+	if (size <= 0 || size > sizeof(__u64))
+		return false;
+
+	/* set max size */
+	switch (off) {
+	case offsetof(struct landlock_context, arch):
+	case offsetof(struct landlock_context, syscall_nr):
+	case offsetof(struct landlock_context, syscall_cmd):
+	case offsetof(struct landlock_context, event):
+		max_size = sizeof(__u32);
+		break;
+	case offsetof(struct landlock_context, status):
+	case offsetof(struct landlock_context, arg1):
+	case offsetof(struct landlock_context, arg2):
+		max_size = sizeof(__u64);
+		break;
+	default:
+		return false;
+	}
+
+	/* set register type */
+	switch (off) {
+	case offsetof(struct landlock_context, arg1):
+		*reg_type = ctx_types[0];
+		break;
+	case offsetof(struct landlock_context, arg2):
+		*reg_type = ctx_types[1];
+		break;
+	default:
+		*reg_type = UNKNOWN_VALUE;
+	}
+
+	/* check memory range access */
+	switch (*reg_type) {
+	case NOT_INIT:
+		return false;
+	case UNKNOWN_VALUE:
+	case CONST_IMM:
+		/* allow partial raw value */
+		if (size > max_size)
+			return false;
+		break;
+	default:
+		/* deny partial pointer */
+		if (size != max_size)
+			return false;
+	}
+
+	return true;
+}
+
+int landlock_decide(enum landlock_subtype_event event,
+		__u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook)
+{
+	bool deny = false;
+	u32 event_idx = get_index(event);
+
+	struct landlock_context ctx = {
+		.status = 0,
+		.arch = syscall_get_arch(),
+		.syscall_nr = syscall_get_nr(current, task_pt_regs(current)),
+		.syscall_cmd = cmd,
+		.event = event,
+		.arg1 = ctx_values[0],
+		.arg2 = ctx_values[1],
+	};
+
+	return deny ? -EPERM : 0;
+}
diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
new file mode 100644
index 000000000000..2e180f6ed86b
--- /dev/null
+++ b/security/landlock/hooks.h
@@ -0,0 +1,177 @@
+/*
+ * Landlock LSM - hooks helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/current.h>
+#include <linux/bpf.h> /* enum bpf_access_type */
+#include <linux/lsm_hooks.h>
+#include <linux/sched.h> /* struct task_struct */
+
+/* separators */
+#define SEP_COMMA() ,
+#define SEP_SPACE()
+#define SEP_AND() &&
+
+#define MAP2x1(s, m, x1, x2, ...) m(x1, x2)
+#define MAP2x2(s, m, x1, x2, ...) m(x1, x2) s() MAP2x1(s, m, __VA_ARGS__)
+#define MAP2x3(s, m, x1, x2, ...) m(x1, x2) s() MAP2x2(s, m, __VA_ARGS__)
+#define MAP2x4(s, m, x1, x2, ...) m(x1, x2) s() MAP2x3(s, m, __VA_ARGS__)
+#define MAP2x5(s, m, x1, x2, ...) m(x1, x2) s() MAP2x4(s, m, __VA_ARGS__)
+#define MAP2x6(s, m, x1, x2, ...) m(x1, x2) s() MAP2x5(s, m, __VA_ARGS__)
+#define MAP2x(n, ...) MAP2x##n(__VA_ARGS__)
+
+#define MAP1x1(s, m, x1, ...) m(x1)
+#define MAP1x2(s, m, x1, ...) m(x1) s() MAP1x1(s, m, __VA_ARGS__)
+#define MAP1x(n, ...) MAP1x##n(__VA_ARGS__)
+
+#define SKIP2x1(x1, x2, ...) __VA_ARGS__
+#define SKIP2x2(x1, x2, ...) SKIP2x1(__VA_ARGS__)
+#define SKIP2x3(x1, x2, ...) SKIP2x2(__VA_ARGS__)
+#define SKIP2x4(x1, x2, ...) SKIP2x3(__VA_ARGS__)
+#define SKIP2x5(x1, x2, ...) SKIP2x4(__VA_ARGS__)
+#define SKIP2x6(x1, x2, ...) SKIP2x5(__VA_ARGS__)
+#define SKIP2x(n, ...) SKIP2x##n(__VA_ARGS__)
+
+/* LSM hook argument helpers */
+#define MAP_HOOK_COMMA(n, ...) MAP2x(n, SEP_COMMA, __VA_ARGS__)
+
+#define GET_HOOK_TA(t, a) t a
+
+/* Landlock event argument helpers  */
+#define MAP_EVENT_COMMA(h, n, m, ...) MAP2x(n, SEP_COMMA, m, SKIP2x(h, __VA_ARGS__))
+#define MAP_EVENT_SPACE(h, n, m, ...) MAP2x(n, SEP_SPACE, m, SKIP2x(h, __VA_ARGS__))
+#define MAP_EVENT_AND(h, n, m, ...) MAP2x(n, SEP_AND, m, SKIP2x(h, __VA_ARGS__))
+
+#define GET_CMD(h, n, ...) SKIP2x(n, SKIP2x(h, __VA_ARGS__))
+
+#define EXPAND_TYPE(d) d##_TYPE
+#define EXPAND_BPF(d) d##_BPF
+#define EXPAND_C(d) d##_C
+
+#define GET_TYPE_BPF(t) EXPAND_BPF(t)
+#define GET_TYPE_C(t) EXPAND_C(t) *
+
+#define GET_EVENT_C(d, a) GET_TYPE_C(EXPAND_TYPE(d))
+#define GET_EVENT_U64(d, a) ((u64)(d##_VAL(a)))
+#define GET_EVENT_DEC(d, a) d##_DEC(a)
+#define GET_EVENT_OK(d, a) d##_OK(a)
+
+/**
+ * HOOK_ACCESS
+ *
+ * @EVENT: Landlock event name
+ * @NA: number of event arguments
+ *
+ * The __consistent_##EVENT() extern functions and __wrapcheck_* types are
+ * useful to catch inconsistencies in LSM hook definitions thanks to the
+ * compiler type checking.
+ */
+#define HOOK_ACCESS(EVENT, NA, ...)					\
+	inline bool landlock_is_valid_access_event_##EVENT(		\
+			int off, int size, enum bpf_access_type type,	\
+			enum bpf_reg_type *reg_type,			\
+			union bpf_prog_subtype *prog_subtype)		\
+	{								\
+		enum bpf_reg_type _ctx_types[CTX_ARG_NB] = {		\
+			MAP1x(NA, SEP_COMMA, GET_TYPE_BPF, __VA_ARGS__)	\
+		};							\
+		return landlock_is_valid_access(off, size, type,	\
+				reg_type, _ctx_types, prog_subtype);	\
+	}								\
+	extern void __consistent_##EVENT(				\
+			MAP1x(NA, SEP_COMMA, GET_TYPE_C, __VA_ARGS__))
+
+/**
+ * HOOK_NEW
+ *
+ * @INST: event instance for this hook
+ * @EVENT: Landlock event name
+ * @NE: number of event arguments
+ * @HOOK: LSM hook name
+ * @NH: number of hook arguments
+ */
+#define HOOK_NEW(INST, EVENT, NE, HOOK, NH, ...)			\
+	static int landlock_hook_##EVENT##_##HOOK##_##INST(		\
+			MAP_HOOK_COMMA(NH, GET_HOOK_TA, __VA_ARGS__))	\
+	{								\
+		if (!landlocked(current))				\
+			return 0;					\
+		if (!(MAP_EVENT_AND(NH, NE, GET_EVENT_OK,		\
+						__VA_ARGS__)))		\
+			return 0;					\
+		{							\
+		MAP_EVENT_SPACE(NH, NE, GET_EVENT_DEC, __VA_ARGS__)	\
+		__u64 _ctx_values[CTX_ARG_NB] = {			\
+			MAP_EVENT_COMMA(NH, NE, GET_EVENT_U64,		\
+					__VA_ARGS__)			\
+		};							\
+		u32 _cmd = GET_CMD(NH, NE, __VA_ARGS__);		\
+		return landlock_decide(LANDLOCK_SUBTYPE_EVENT_##EVENT,	\
+				_ctx_values, _cmd, #HOOK);		\
+		}							\
+	}								\
+	extern void __consistent_##EVENT(MAP_EVENT_COMMA(		\
+				NH, NE, GET_EVENT_C, __VA_ARGS__))
+
+/*
+ * The WRAP_TYPE_* definitions group the bpf_reg_type enum value and the C
+ * type. This C type may remains unused except to catch inconsistencies in LSM
+ * hook definitions thanks to the compiler type checking.
+ */
+
+/* WRAP_TYPE_NONE */
+#define WRAP_TYPE_NONE_BPF	NOT_INIT
+#define WRAP_TYPE_NONE_C	struct __wrapcheck_none
+WRAP_TYPE_NONE_C;
+
+/* WRAP_TYPE_RAW */
+#define WRAP_TYPE_RAW_BPF	UNKNOWN_VALUE
+#define WRAP_TYPE_RAW_C		struct __wrapcheck_raw
+WRAP_TYPE_RAW_C;
+
+/*
+ * The WRAP_ARG_* definitions group the LSM hook argument type (C and BPF), the
+ * wrapping struct declaration (if any) and the value to copy to the BPF
+ * context. This definitions may be used thanks to the EXPAND_* helpers.
+ *
+ * WRAP_ARG_*_TYPE: type for BPF and C (cf. WRAP_TYPE_*)
+ * WRAP_ARG_*_DEC: declare a wrapper
+ * WRAP_ARG_*_VAL: get this wrapper's address
+ * WRAP_ARG_*_OK: check if the argument is usable
+ */
+
+/* WRAP_ARG_NONE */
+#define WRAP_ARG_NONE_TYPE	WRAP_TYPE_NONE
+#define WRAP_ARG_NONE_DEC(arg)
+#define WRAP_ARG_NONE_VAL(arg)	0
+#define WRAP_ARG_NONE_OK(arg)	(!WARN_ON(true))
+
+/* WRAP_ARG_RAW */
+#define WRAP_ARG_RAW_TYPE	WRAP_TYPE_RAW
+#define WRAP_ARG_RAW_DEC(arg)
+#define WRAP_ARG_RAW_VAL(arg)	arg
+#define WRAP_ARG_RAW_OK(arg)	(true)
+
+
+#define CTX_ARG_NB 2
+
+static inline bool landlocked(const struct task_struct *task)
+{
+	return false;
+}
+
+__init void landlock_register_hooks(struct security_hook_list *hooks, int count);
+
+bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
+		enum bpf_reg_type *reg_type,
+		enum bpf_reg_type ctx_types[CTX_ARG_NB],
+		union bpf_prog_subtype *prog_subtype);
+
+int landlock_decide(enum landlock_subtype_event event,
+		__u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook);
diff --git a/security/landlock/hooks_fs.c b/security/landlock/hooks_fs.c
new file mode 100644
index 000000000000..6578f21783c7
--- /dev/null
+++ b/security/landlock/hooks_fs.c
@@ -0,0 +1,563 @@
+/*
+ * Landlock LSM - filesystem hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h> /* ARRAY_SIZE */
+#include <linux/lsm_hooks.h>
+#include <linux/types.h> /* uintptr_t */
+
+/* permissions translation */
+#include <linux/fs.h> /* MAY_* */
+#include <linux/mman.h> /* PROT_* */
+
+/* hook arguments */
+#include <linux/cred.h>
+#include <linux/dcache.h> /* struct dentry */
+#include <linux/fs.h> /* struct inode, struct iattr */
+#include <linux/mm_types.h> /* struct vm_area_struct */
+#include <linux/mount.h> /* struct vfsmount */
+#include <linux/path.h> /* struct path */
+#include <linux/sched.h> /* struct task_struct */
+#include <linux/time.h> /* struct timespec */
+
+#include "hooks.h"
+
+#include "hooks_fs.h"
+
+
+#define HOOK_NEW_FS(...) HOOK_NEW(1, FS, 2, __VA_ARGS__, 0)
+#define HOOK_NEW_FS2(...) HOOK_NEW(2, FS, 2, __VA_ARGS__, 0)
+#define HOOK_NEW_FS3(...) HOOK_NEW(3, FS, 2, __VA_ARGS__, 0)
+#define HOOK_NEW_FS4(...) HOOK_NEW(4, FS, 2, __VA_ARGS__, 0)
+#define HOOK_NEW_FS_CMD(...) HOOK_NEW(1, FS, 2, __VA_ARGS__)
+#define HOOK_INIT_FS(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_1)
+#define HOOK_INIT_FS2(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_2)
+#define HOOK_INIT_FS3(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_3)
+#define HOOK_INIT_FS4(HOOK) LSM_HOOK_INIT(HOOK, landlock_hook_FS_##HOOK##_4)
+
+/* WRAP_TYPE_FS */
+#define WRAP_TYPE_FS_BPF	CONST_PTR_TO_HANDLE_FS
+#define WRAP_TYPE_FS_C		const struct bpf_handle_fs
+
+/* WRAP_ARG_FILE */
+#define WRAP_ARG_FILE_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_FILE_DEC(arg)					\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_FILE, .file = arg };
+#define WRAP_ARG_FILE_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_FILE_OK(arg)	(arg)
+
+/* WRAP_ARG_VMAF */
+#define WRAP_ARG_VMAF_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_VMAF_DEC(arg)					\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_FILE, .file = arg->vm_file };
+#define WRAP_ARG_VMAF_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_VMAF_OK(arg)	(arg && arg->vm_file)
+
+/* WRAP_ARG_INODE */
+#define WRAP_ARG_INODE_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_INODE_DEC(arg)					\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_INODE, .inode = arg };
+#define WRAP_ARG_INODE_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_INODE_OK(arg)	(arg)
+
+/* WRAP_ARG_PATH */
+#define WRAP_ARG_PATH_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_PATH_DEC(arg)					\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_PATH, .path = arg };
+#define WRAP_ARG_PATH_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_PATH_OK(arg)	(arg)
+
+/* WRAP_ARG_DENTRY */
+#define WRAP_ARG_DENTRY_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_DENTRY_DEC(arg)				\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg };
+#define WRAP_ARG_DENTRY_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_DENTRY_OK(arg)	(arg)
+
+/* WRAP_ARG_SB */
+#define WRAP_ARG_SB_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_SB_DEC(arg)					\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg->s_root };
+#define WRAP_ARG_SB_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_SB_OK(arg)	(arg && arg->s_root)
+
+/* WRAP_ARG_MNTROOT */
+#define WRAP_ARG_MNTROOT_TYPE	WRAP_TYPE_FS
+#define WRAP_ARG_MNTROOT_DEC(arg)				\
+	EXPAND_C(WRAP_TYPE_FS) wrap_##arg =			\
+	{ .type = BPF_HANDLE_FS_TYPE_DENTRY, .dentry = arg->mnt_root };
+#define WRAP_ARG_MNTROOT_VAL(arg)	((uintptr_t)&wrap_##arg)
+#define WRAP_ARG_MNTROOT_OK(arg)	(arg && arg->mnt_root)
+
+
+static inline u64 fs_may_to_access(int fs_may)
+{
+	u64 ret = 0;
+
+	if (fs_may & MAY_EXEC)
+		ret |= LANDLOCK_ACTION_FS_EXEC;
+	if (fs_may & MAY_READ)
+		ret |= LANDLOCK_ACTION_FS_READ;
+	if (fs_may & MAY_WRITE)
+		ret |= LANDLOCK_ACTION_FS_WRITE;
+	if (fs_may & MAY_APPEND)
+		ret |= LANDLOCK_ACTION_FS_WRITE;
+	if (fs_may & MAY_OPEN)
+		ret |= LANDLOCK_ACTION_FS_GET;
+	/* ignore MAY_CHDIR and MAY_ACCESS */
+
+	return ret;
+}
+
+static u64 mem_prot_to_access(unsigned long prot, bool private)
+{
+	u64 ret = 0;
+
+	/* private mapping do not write to files */
+	if (!private && (prot & PROT_WRITE))
+		ret |= LANDLOCK_ACTION_FS_WRITE;
+	if (prot & PROT_READ)
+		ret |= LANDLOCK_ACTION_FS_READ;
+	if (prot & PROT_EXEC)
+		ret |= LANDLOCK_ACTION_FS_EXEC;
+
+	return ret;
+}
+
+/* hook definitions */
+
+HOOK_ACCESS(FS, 2, WRAP_TYPE_FS, WRAP_TYPE_RAW);
+
+/* binder_* hooks */
+
+HOOK_NEW_FS(binder_transfer_file, 3,
+	struct task_struct *, from,
+	struct task_struct *, to,
+	struct file *, file,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+/* sb_* hooks */
+
+HOOK_NEW_FS(sb_statfs, 1,
+	struct dentry *, dentry,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+/*
+ * Being able to mount on a path means being able to override the underlying
+ * filesystem view of this path, hence the need for a write access right.
+ */
+HOOK_NEW_FS(sb_mount, 5,
+	const char *, dev_name,
+	const struct path *, path,
+	const char *, type,
+	unsigned long, flags,
+	void *, data,
+	WRAP_ARG_PATH, path,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS(sb_remount, 2,
+	struct super_block *, sb,
+	void *, data,
+	WRAP_ARG_SB, sb,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS(sb_umount, 2,
+	struct vfsmount *, mnt,
+	int, flags,
+	WRAP_ARG_MNTROOT, mnt,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+/*
+ * The old_path is similar to a destination mount point.
+ */
+HOOK_NEW_FS(sb_pivotroot, 2,
+	const struct path *, old_path,
+	const struct path *, new_path,
+	WRAP_ARG_PATH, old_path,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+/* inode_* hooks */
+
+/* a directory inode contains only one dentry */
+HOOK_NEW_FS(inode_create, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_create, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_link, 3,
+	struct dentry *, old_dentry,
+	struct inode *, dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_DENTRY, old_dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS2(inode_link, 3,
+	struct dentry *, old_dentry,
+	struct inode *, dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS3(inode_link, 3,
+	struct dentry *, old_dentry,
+	struct inode *, dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_DENTRY, new_dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_unlink, 2,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_unlink, 2,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
+);
+
+HOOK_NEW_FS(inode_symlink, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	const char *, old_name,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_symlink, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	const char *, old_name,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_mkdir, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_mkdir, 3,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_rmdir, 2,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_rmdir, 2,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
+);
+
+HOOK_NEW_FS(inode_mknod, 4,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	dev_t, dev,
+	WRAP_ARG_INODE, dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_mknod, 4,
+	struct inode *, dir,
+	struct dentry *, dentry,
+	umode_t, mode,
+	dev_t, dev,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_rename, 4,
+	struct inode *, old_dir,
+	struct dentry *, old_dentry,
+	struct inode *, new_dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_INODE, old_dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS2(inode_rename, 4,
+	struct inode *, old_dir,
+	struct dentry *, old_dentry,
+	struct inode *, new_dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_DENTRY, old_dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_REMOVE
+);
+
+HOOK_NEW_FS3(inode_rename, 4,
+	struct inode *, old_dir,
+	struct dentry *, old_dentry,
+	struct inode *, new_dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_INODE, new_dir,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS4(inode_rename, 4,
+	struct inode *, old_dir,
+	struct dentry *, old_dentry,
+	struct inode *, new_dir,
+	struct dentry *, new_dentry,
+	WRAP_ARG_DENTRY, new_dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_NEW
+);
+
+HOOK_NEW_FS(inode_readlink, 1,
+	struct dentry *, dentry,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+// XXX: handle inode?
+HOOK_NEW_FS(inode_follow_link, 3,
+	struct dentry *, dentry,
+	struct inode *, inode,
+	bool, rcu,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS(inode_permission, 2,
+	struct inode *, inode,
+	int, mask,
+	WRAP_ARG_INODE, inode,
+	WRAP_ARG_RAW, fs_may_to_access(mask)
+);
+
+HOOK_NEW_FS(inode_setattr, 2,
+	struct dentry *, dentry,
+	struct iattr *, attr,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS(inode_getattr, 1,
+	const struct path *, path,
+	WRAP_ARG_PATH, path,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS(inode_setxattr, 5,
+	struct dentry *, dentry,
+	const char *, name,
+	const void *, value,
+	size_t, size,
+	int, flags,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS(inode_getxattr, 2,
+	struct dentry *, dentry,
+	const char *, name,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS(inode_listxattr, 1,
+	struct dentry *, dentry,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS(inode_removexattr, 2,
+	struct dentry *, dentry,
+	const char *, name,
+	WRAP_ARG_DENTRY, dentry,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+HOOK_NEW_FS(inode_getsecurity, 4,
+	struct inode *, inode,
+	const char *, name,
+	void **, buffer,
+	bool, alloc,
+	WRAP_ARG_INODE, inode,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_READ
+);
+
+HOOK_NEW_FS(inode_setsecurity, 5,
+	struct inode *, inode,
+	const char *, name,
+	const void *, value,
+	size_t, size,
+	int, flag,
+	WRAP_ARG_INODE, inode,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_WRITE
+);
+
+/* file_* hooks */
+
+HOOK_NEW_FS(file_permission, 2,
+	struct file *, file,
+	int, mask,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, fs_may_to_access(mask)
+);
+
+/*
+ * An ioctl command can be a read or a write. This can be checked with _IOC*()
+ * for some commands but a Landlock rule should check the ioctl command to
+ * whitelist them.
+ */
+HOOK_NEW_FS_CMD(file_ioctl, 3,
+	struct file *, file,
+	unsigned int, cmd,
+	unsigned long, arg,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_IOCTL,
+	cmd
+);
+
+HOOK_NEW_FS_CMD(file_lock, 2,
+	struct file *, file,
+	unsigned int, cmd,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_LOCK,
+	cmd
+);
+
+HOOK_NEW_FS_CMD(file_fcntl, 3,
+	struct file *, file,
+	unsigned int, cmd,
+	unsigned long, arg,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_FCNTL,
+	cmd
+);
+
+HOOK_NEW_FS(mmap_file, 4,
+	struct file *, file,
+	unsigned long, reqprot,
+	unsigned long, prot,
+	unsigned long, flags,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, mem_prot_to_access(prot, flags & MAP_PRIVATE)
+);
+
+HOOK_NEW_FS(file_mprotect, 3,
+	struct vm_area_struct *, vma,
+	unsigned long, reqprot,
+	unsigned long, prot,
+	WRAP_ARG_VMAF, vma,
+	WRAP_ARG_RAW, mem_prot_to_access(prot, !(vma->vm_flags & VM_SHARED))
+);
+
+HOOK_NEW_FS(file_receive, 1,
+	struct file *, file,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_GET
+);
+
+HOOK_NEW_FS(file_open, 2,
+	struct file *, file,
+	const struct cred *, cred,
+	WRAP_ARG_FILE, file,
+	WRAP_ARG_RAW, LANDLOCK_ACTION_FS_GET
+);
+
+static struct security_hook_list landlock_hooks[] = {
+	HOOK_INIT_FS(binder_transfer_file),
+
+	HOOK_INIT_FS(sb_statfs),
+	HOOK_INIT_FS(sb_mount),
+	HOOK_INIT_FS(sb_remount),
+	HOOK_INIT_FS(sb_umount),
+	HOOK_INIT_FS(sb_pivotroot),
+
+	HOOK_INIT_FS(inode_create),
+	HOOK_INIT_FS2(inode_create),
+	HOOK_INIT_FS(inode_link),
+	HOOK_INIT_FS2(inode_link),
+	HOOK_INIT_FS3(inode_link),
+	HOOK_INIT_FS(inode_unlink),
+	HOOK_INIT_FS2(inode_unlink),
+	HOOK_INIT_FS(inode_symlink),
+	HOOK_INIT_FS2(inode_symlink),
+	HOOK_INIT_FS(inode_mkdir),
+	HOOK_INIT_FS2(inode_mkdir),
+	HOOK_INIT_FS(inode_rmdir),
+	HOOK_INIT_FS2(inode_rmdir),
+	HOOK_INIT_FS(inode_mknod),
+	HOOK_INIT_FS2(inode_mknod),
+	HOOK_INIT_FS(inode_rename),
+	HOOK_INIT_FS2(inode_rename),
+	HOOK_INIT_FS3(inode_rename),
+	HOOK_INIT_FS4(inode_rename),
+	HOOK_INIT_FS(inode_readlink),
+	HOOK_INIT_FS(inode_follow_link),
+	HOOK_INIT_FS(inode_permission),
+	HOOK_INIT_FS(inode_setattr),
+	HOOK_INIT_FS(inode_getattr),
+	HOOK_INIT_FS(inode_setxattr),
+	HOOK_INIT_FS(inode_getxattr),
+	HOOK_INIT_FS(inode_listxattr),
+	HOOK_INIT_FS(inode_removexattr),
+	HOOK_INIT_FS(inode_getsecurity),
+	HOOK_INIT_FS(inode_setsecurity),
+
+	HOOK_INIT_FS(file_permission),
+	HOOK_INIT_FS(file_ioctl),
+	HOOK_INIT_FS(file_lock),
+	HOOK_INIT_FS(file_fcntl),
+	HOOK_INIT_FS(mmap_file),
+	HOOK_INIT_FS(file_mprotect),
+	HOOK_INIT_FS(file_receive),
+	HOOK_INIT_FS(file_open),
+};
+
+__init void landlock_add_hooks_fs(void)
+{
+	landlock_register_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks));
+}
diff --git a/security/landlock/hooks_fs.h b/security/landlock/hooks_fs.h
new file mode 100644
index 000000000000..093c72bb91dc
--- /dev/null
+++ b/security/landlock/hooks_fs.h
@@ -0,0 +1,19 @@
+/*
+ * Landlock LSM - filesystem hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h> /* enum bpf_access_type */
+
+
+bool landlock_is_valid_access_event_FS(
+		int off, int size, enum bpf_access_type type,
+		enum bpf_reg_type *reg_type,
+		union bpf_prog_subtype *prog_subtype);
+
+__init void landlock_add_hooks_fs(void);
diff --git a/security/landlock/init.c b/security/landlock/init.c
index 914895d08320..1c2750e12dfa 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -11,6 +11,9 @@
 #include <linux/bpf.h> /* enum bpf_access_type */
 #include <linux/capability.h> /* capable */
 #include <linux/landlock.h> /* LANDLOCK_VERSION */
+#include <linux/lsm_hooks.h>
+
+#include "hooks_fs.h"
 
 
 static inline bool bpf_landlock_is_valid_access(int off, int size,
@@ -22,6 +25,8 @@ static inline bool bpf_landlock_is_valid_access(int off, int size,
 
 	switch (prog_subtype->landlock_rule.event) {
 	case LANDLOCK_SUBTYPE_EVENT_FS:
+		return landlock_is_valid_access_event_FS(off, size, type,
+				reg_type, prog_subtype);
 	case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
 	default:
 		return false;
@@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
 	.ops = &bpf_landlock_ops,
 	.type = BPF_PROG_TYPE_LANDLOCK,
 };
+
+void __init landlock_add_hooks(void)
+{
+	pr_info("landlock: Version %u", LANDLOCK_VERSION);
+	landlock_add_hooks_fs();
+	security_add_hooks(NULL, 0, "landlock");
+	bpf_register_prog_type(&bpf_landlock_type);
+}
diff --git a/security/security.c b/security/security.c
index d0e07f269b2d..a3e9f4625991 100644
--- a/security/security.c
+++ b/security/security.c
@@ -64,10 +64,15 @@ int __init security_init(void)
 	loadpin_add_hooks();
 
 	/*
-	 * Load all the remaining security modules.
+	 * Load all remaining privileged security modules.
 	 */
 	do_security_initcalls();
 
+	/*
+	 * Load potentially-unprivileged security modules at the end.
+	 */
+	landlock_add_hooks();
+
 	return 0;
 }
 
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (3 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-18 22:23   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy Mickaël Salaün
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

The semantic is unchanged. This will be useful for the Landlock
integration with seccomp (next commit).

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>
---
 include/linux/seccomp.h |  4 ++--
 kernel/fork.c           |  2 +-
 kernel/seccomp.c        | 18 +++++++++++++-----
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index ecc296c137cd..e25aee2cdfc0 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s)
 #endif /* CONFIG_SECCOMP */
 
 #ifdef CONFIG_SECCOMP_FILTER
-extern void put_seccomp_filter(struct task_struct *tsk);
+extern void put_seccomp(struct task_struct *tsk);
 extern void get_seccomp_filter(struct task_struct *tsk);
 #else  /* CONFIG_SECCOMP_FILTER */
-static inline void put_seccomp_filter(struct task_struct *tsk)
+static inline void put_seccomp(struct task_struct *tsk)
 {
 	return;
 }
diff --git a/kernel/fork.c b/kernel/fork.c
index 6c463c80e93d..a27d8e67ce33 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -363,7 +363,7 @@ void free_task(struct task_struct *tsk)
 #endif
 	rt_mutex_debug_task_free(tsk);
 	ftrace_graph_exit_task(tsk);
-	put_seccomp_filter(tsk);
+	put_seccomp(tsk);
 	arch_release_task_struct(tsk);
 	if (tsk->flags & PF_KTHREAD)
 		free_kthread_struct(tsk);
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 65f61077ad50..326f79e32127 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -64,6 +64,8 @@ struct seccomp_filter {
 /* Limit any path through the tree to 256KB worth of instructions. */
 #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
 
+static void put_seccomp_filter(struct seccomp_filter *filter);
+
 /*
  * Endianness is explicitly ignored and left for BPF program authors to manage
  * as per the specific architecture.
@@ -314,7 +316,7 @@ static inline void seccomp_sync_threads(void)
 		 * current's path will hold a reference.  (This also
 		 * allows a put before the assignment.)
 		 */
-		put_seccomp_filter(thread);
+		put_seccomp_filter(thread->seccomp.filter);
 		smp_store_release(&thread->seccomp.filter,
 				  caller->seccomp.filter);
 
@@ -476,10 +478,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter)
 	}
 }
 
-/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
-void put_seccomp_filter(struct task_struct *tsk)
+/* put_seccomp_filter - decrements the ref count of a filter */
+static void put_seccomp_filter(struct seccomp_filter *filter)
 {
-	struct seccomp_filter *orig = tsk->seccomp.filter;
+	struct seccomp_filter *orig = filter;
+
 	/* Clean up single-reference branches iteratively. */
 	while (orig && atomic_dec_and_test(&orig->usage)) {
 		struct seccomp_filter *freeme = orig;
@@ -488,6 +491,11 @@ void put_seccomp_filter(struct task_struct *tsk)
 	}
 }
 
+void put_seccomp(struct task_struct *tsk)
+{
+	put_seccomp_filter(tsk->seccomp.filter);
+}
+
 static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
 {
 	memset(info, 0, sizeof(*info));
@@ -914,7 +922,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
 	if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
 		ret = -EFAULT;
 
-	put_seccomp_filter(task);
+	put_seccomp_filter(task->seccomp.filter);
 	return ret;
 
 out:
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (4 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp() Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-03-29 10:35   ` [kernel-hardening] " Djalal Harouni
  2017-04-18 22:53   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 07/11] landlock: Add ptrace restrictions Mickaël Salaün
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev, Andrew Morton

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

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

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

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

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

Changes since v2:
* Landlock programs can now be run without seccomp filter but for any
  syscall (from the process) or interruption
* move Landlock related functions and structs into security/landlock/*
  (to manage cgroups as well)
* fix seccomp filter handling: run Landlock programs for each of their
  legitimate seccomp filter
* properly clean up all seccomp results
* cosmetic changes to ease the understanding
* fix some ifdef

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: Will Drewry <wad@chromium.org>
Link: https://lkml.kernel.org/r/c10a503d-5e35-7785-2f3d-25ed8dd63fab@digikod.net
---
 include/linux/landlock.h      |  36 +++++++
 include/linux/seccomp.h       |   8 ++
 include/uapi/linux/seccomp.h  |   1 +
 kernel/fork.c                 |  14 ++-
 kernel/seccomp.c              |   8 ++
 security/landlock/Makefile    |   2 +-
 security/landlock/hooks.c     |  37 +++++++
 security/landlock/hooks.h     |   5 +
 security/landlock/init.c      |   3 +-
 security/landlock/providers.c | 232 ++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 342 insertions(+), 4 deletions(-)
 create mode 100644 security/landlock/providers.c

diff --git a/include/linux/landlock.h b/include/linux/landlock.h
index 53013dc374fe..c40ee78e86e0 100644
--- a/include/linux/landlock.h
+++ b/include/linux/landlock.h
@@ -12,6 +12,9 @@
 #define _LINUX_LANDLOCK_H
 #ifdef CONFIG_SECURITY_LANDLOCK
 
+#include <linux/bpf.h>	/* _LANDLOCK_SUBTYPE_EVENT_LAST */
+#include <linux/types.h> /* atomic_t */
+
 /*
  * This is not intended for the UAPI headers. Each userland software should use
  * a static minimal version for the required features as explained in the
@@ -19,5 +22,38 @@
  */
 #define LANDLOCK_VERSION 1
 
+struct landlock_rule {
+	atomic_t usage;
+	struct landlock_rule *prev;
+	struct bpf_prog *prog;
+};
+
+/**
+ * struct landlock_events - Landlock event rules enforced on a thread
+ *
+ * This is used for low performance impact when forking a process. Instead of
+ * copying the full array and incrementing the usage of each entries, only
+ * create a pointer to &struct landlock_events and increments its usage. When
+ * appending a new rule, if &struct landlock_events is shared with other tasks,
+ * then duplicate it and append the rule to this new &struct landlock_events.
+ *
+ * @usage: reference count to manage the object lifetime. When a thread need to
+ *         add Landlock rules and if @usage is greater than 1, then the thread
+ *         must duplicate &struct landlock_events to not change the children's
+ *         rules as well.
+ * @rules: array of non-NULL &struct landlock_rule pointers
+ */
+struct landlock_events {
+	atomic_t usage;
+	struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST];
+};
+
+void put_landlock_events(struct landlock_events *events);
+
+#ifdef CONFIG_SECCOMP_FILTER
+int landlock_seccomp_append_prog(unsigned int flags,
+		const char __user *user_bpf_fd);
+#endif /* CONFIG_SECCOMP_FILTER */
+
 #endif /* CONFIG_SECURITY_LANDLOCK */
 #endif /* _LINUX_LANDLOCK_H */
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index e25aee2cdfc0..9a38de3c0e72 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -10,6 +10,10 @@
 #include <linux/thread_info.h>
 #include <asm/seccomp.h>
 
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+struct landlock_events;
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
+
 struct seccomp_filter;
 /**
  * struct seccomp - the state of a seccomp'ed process
@@ -18,6 +22,7 @@ struct seccomp_filter;
  *         system calls available to a process.
  * @filter: must always point to a valid seccomp-filter or NULL as it is
  *          accessed without locking during system call entry.
+ * @landlock_events: contains an array of Landlock rules.
  *
  *          @filter must only be accessed from the context of current as there
  *          is no read locking.
@@ -25,6 +30,9 @@ struct seccomp_filter;
 struct seccomp {
 	int mode;
 	struct seccomp_filter *filter;
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+	struct landlock_events *landlock_events;
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
 };
 
 #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index 0f238a43ff1e..74891cf60ca6 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_APPEND_LANDLOCK_RULE	2
 
 /* Valid flags for SECCOMP_SET_MODE_FILTER */
 #define SECCOMP_FILTER_FLAG_TSYNC	1
diff --git a/kernel/fork.c b/kernel/fork.c
index a27d8e67ce33..14c09486c565 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -47,6 +47,7 @@
 #include <linux/security.h>
 #include <linux/hugetlb.h>
 #include <linux/seccomp.h>
+#include <linux/landlock.h>
 #include <linux/swap.h>
 #include <linux/syscalls.h>
 #include <linux/jiffies.h>
@@ -528,7 +529,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 	 * the usage counts on the error path calling free_task.
 	 */
 	tsk->seccomp.filter = NULL;
-#endif
+#ifdef CONFIG_SECURITY_LANDLOCK
+	tsk->seccomp.landlock_events = NULL;
+#endif /* CONFIG_SECURITY_LANDLOCK */
+#endif /* CONFIG_SECCOMP */
 
 	setup_thread_stack(tsk, orig);
 	clear_user_return_notifier(tsk);
@@ -1405,7 +1409,13 @@ 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;
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+	p->seccomp.landlock_events = current->seccomp.landlock_events;
+	if (p->seccomp.landlock_events)
+		atomic_inc(&p->seccomp.landlock_events->usage);
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
 
 	/*
 	 * Explicitly enable no_new_privs here in case it got set
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 326f79e32127..d122829e6da1 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -34,6 +34,7 @@
 #include <linux/security.h>
 #include <linux/tracehook.h>
 #include <linux/uaccess.h>
+#include <linux/landlock.h>
 
 /**
  * struct seccomp_filter - container for seccomp BPF programs
@@ -494,6 +495,9 @@ 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_events(tsk->seccomp.landlock_events);
+#endif /* CONFIG_SECURITY_LANDLOCK */
 }
 
 static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
@@ -813,6 +817,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);
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
+	case SECCOMP_APPEND_LANDLOCK_RULE:
+		return landlock_seccomp_append_prog(flags, uargs);
+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
 	default:
 		return -EINVAL;
 	}
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index c0db504a6335..da8ba8b5183e 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
 
 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
 
-landlock-y := init.o hooks.o hooks_fs.o
+landlock-y := init.o providers.o hooks.o hooks_fs.o
diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
index eaee8162ff70..4fa7d0b38d41 100644
--- a/security/landlock/hooks.c
+++ b/security/landlock/hooks.c
@@ -95,6 +95,38 @@ bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
 	return true;
 }
 
+/**
+ * landlock_event_deny - run Landlock rules tied to an event
+ *
+ * @event_idx: event index in the rules array
+ * @ctx: non-NULL eBPF context
+ * @events: Landlock events pointer
+ *
+ * Return true if at least one rule deny the event.
+ */
+static bool landlock_event_deny(u32 event_idx, const struct landlock_context *ctx,
+		struct landlock_events *events)
+{
+	struct landlock_rule *rule;
+
+	if (!events)
+		return false;
+
+	for (rule = events->rules[event_idx]; rule; rule = rule->prev) {
+		u32 ret;
+
+		if (WARN_ON(!rule->prog))
+			continue;
+		rcu_read_lock();
+		ret = BPF_PROG_RUN(rule->prog, (void *)ctx);
+		rcu_read_unlock();
+		/* deny access if a program returns a value different than 0 */
+		if (ret)
+			return true;
+	}
+	return false;
+}
+
 int landlock_decide(enum landlock_subtype_event event,
 		__u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook)
 {
@@ -111,5 +143,10 @@ int landlock_decide(enum landlock_subtype_event event,
 		.arg2 = ctx_values[1],
 	};
 
+#ifdef CONFIG_SECCOMP_FILTER
+	deny = landlock_event_deny(event_idx, &ctx,
+			current->seccomp.landlock_events);
+#endif /* CONFIG_SECCOMP_FILTER */
+
 	return deny ? -EPERM : 0;
 }
diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
index 2e180f6ed86b..dd0486a4c284 100644
--- a/security/landlock/hooks.h
+++ b/security/landlock/hooks.h
@@ -12,6 +12,7 @@
 #include <linux/bpf.h> /* enum bpf_access_type */
 #include <linux/lsm_hooks.h>
 #include <linux/sched.h> /* struct task_struct */
+#include <linux/seccomp.h>
 
 /* separators */
 #define SEP_COMMA() ,
@@ -163,7 +164,11 @@ WRAP_TYPE_RAW_C;
 
 static inline bool landlocked(const struct task_struct *task)
 {
+#ifdef CONFIG_SECCOMP_FILTER
+	return !!(task->seccomp.landlock_events);
+#else
 	return false;
+#endif /* CONFIG_SECCOMP_FILTER */
 }
 
 __init void landlock_register_hooks(struct security_hook_list *hooks, int count);
diff --git a/security/landlock/init.c b/security/landlock/init.c
index 1c2750e12dfa..ef8a3da69860 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -135,7 +135,8 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
 
 void __init landlock_add_hooks(void)
 {
-	pr_info("landlock: Version %u", LANDLOCK_VERSION);
+	pr_info("landlock: Version %u, ready to sandbox with %s\n",
+			LANDLOCK_VERSION, "seccomp");
 	landlock_add_hooks_fs();
 	security_add_hooks(NULL, 0, "landlock");
 	bpf_register_prog_type(&bpf_landlock_type);
diff --git a/security/landlock/providers.c b/security/landlock/providers.c
new file mode 100644
index 000000000000..6d867a39c947
--- /dev/null
+++ b/security/landlock/providers.c
@@ -0,0 +1,232 @@
+/*
+ * Landlock LSM - seccomp provider
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/page.h> /* PAGE_SIZE */
+#include <linux/atomic.h> /* atomic_*(), smp_store_release() */
+#include <linux/bpf.h> /* bpf_prog_put() */
+#include <linux/filter.h> /* struct bpf_prog */
+#include <linux/kernel.h> /* round_up() */
+#include <linux/landlock.h>
+#include <linux/sched.h> /* current_cred(), task_no_new_privs() */
+#include <linux/security.h> /* security_capable_noaudit() */
+#include <linux/slab.h> /* alloc(), kfree() */
+#include <linux/types.h> /* atomic_t */
+#include <linux/uaccess.h> /* copy_from_user() */
+
+#include "common.h"
+
+static void put_landlock_rule(struct landlock_rule *rule)
+{
+	struct landlock_rule *orig = rule;
+
+	/* clean up single-reference branches iteratively */
+	while (orig && atomic_dec_and_test(&orig->usage)) {
+		struct landlock_rule *freeme = orig;
+
+		bpf_prog_put(orig->prog);
+		orig = orig->prev;
+		kfree(freeme);
+	}
+}
+
+void put_landlock_events(struct landlock_events *events)
+{
+	if (events && atomic_dec_and_test(&events->usage)) {
+		size_t i;
+
+		for (i = 0; i < ARRAY_SIZE(events->rules); i++)
+			/* XXX: Do we need to use lockless_dereference() here? */
+			put_landlock_rule(events->rules[i]);
+		kfree(events);
+	}
+}
+
+static struct landlock_events *new_landlock_events(void)
+{
+	struct landlock_events *ret;
+
+	/* array filled with NULL values */
+	ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+	if (!ret)
+		return ERR_PTR(-ENOMEM);
+	atomic_set(&ret->usage, 1);
+	return ret;
+}
+
+static void add_landlock_rule(struct landlock_events *events,
+		struct landlock_rule *rule)
+{
+	/* subtype.landlock_rule.event > 0 for loaded programs */
+	u32 event_idx = get_index(rule->prog->subtype.landlock_rule.event);
+
+	rule->prev = events->rules[event_idx];
+	WARN_ON(atomic_read(&rule->usage));
+	atomic_set(&rule->usage, 1);
+	/* do not increment the previous rule usage */
+	smp_store_release(&events->rules[event_idx], rule);
+}
+
+/* limit Landlock events to 256KB */
+#define LANDLOCK_EVENTS_MAX_PAGES (1 << 6)
+
+/**
+ * landlock_append_prog - attach a Landlock rule to @current_events
+ *
+ * @current_events: landlock_events pointer, must be locked (if needed) to
+ *                  prevent a concurrent put/free. This pointer must not be
+ *                  freed after the call.
+ * @prog: non-NULL Landlock rule to append to @current_events. @prog will be
+ *        owned by landlock_append_prog() and freed if an error happened.
+ *
+ * Return @current_events or a new pointer when OK. Return a pointer error
+ * otherwise.
+ */
+static struct landlock_events *landlock_append_prog(
+		struct landlock_events *current_events, struct bpf_prog *prog)
+{
+	struct landlock_events *new_events = current_events;
+	unsigned long pages;
+	struct landlock_rule *rule;
+	u32 event_idx;
+
+	if (prog->type != BPF_PROG_TYPE_LANDLOCK) {
+		new_events = ERR_PTR(-EINVAL);
+		goto put_prog;
+	}
+
+	/* validate memory size allocation */
+	pages = prog->pages;
+	if (current_events) {
+		size_t i;
+
+		for (i = 0; i < ARRAY_SIZE(current_events->rules); i++) {
+			struct landlock_rule *walker_r;
+
+			for (walker_r = current_events->rules[i]; walker_r;
+					walker_r = walker_r->prev)
+				pages += walker_r->prog->pages;
+		}
+		/* count a struct landlock_events if we need to allocate one */
+		if (atomic_read(&current_events->usage) != 1)
+			pages += round_up(sizeof(*current_events), PAGE_SIZE) /
+				PAGE_SIZE;
+	}
+	if (pages > LANDLOCK_EVENTS_MAX_PAGES) {
+		new_events = ERR_PTR(-E2BIG);
+		goto put_prog;
+	}
+
+	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+	if (!rule) {
+		new_events = ERR_PTR(-ENOMEM);
+		goto put_prog;
+	}
+	rule->prog = prog;
+
+	/* subtype.landlock_rule.event > 0 for loaded programs */
+	event_idx = get_index(rule->prog->subtype.landlock_rule.event);
+
+	if (!new_events) {
+		/*
+		 * If there is no Landlock events used by the current task,
+		 * then create a new one.
+		 */
+		new_events = new_landlock_events();
+		if (IS_ERR(new_events))
+			goto put_rule;
+	} else if (atomic_read(&current_events->usage) > 1) {
+		/*
+		 * If the current task is not the sole user of its Landlock
+		 * events, then duplicate them.
+		 */
+		size_t i;
+
+		new_events = new_landlock_events();
+		if (IS_ERR(new_events))
+			goto put_rule;
+		for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) {
+			new_events->rules[i] =
+				lockless_dereference(current_events->rules[i]);
+			if (new_events->rules[i])
+				atomic_inc(&new_events->rules[i]->usage);
+		}
+
+		/*
+		 * Landlock events from the current task will not be freed here
+		 * because the usage is strictly greater than 1. It is only
+		 * prevented to be freed by another subject thanks to the
+		 * caller of landlock_append_prog() which should be locked if
+		 * needed.
+		 */
+		put_landlock_events(current_events);
+	}
+	add_landlock_rule(new_events, rule);
+	return new_events;
+
+put_prog:
+	bpf_prog_put(prog);
+	return new_events;
+
+put_rule:
+	put_landlock_rule(rule);
+	return new_events;
+}
+
+/**
+ * landlock_seccomp_append_prog - attach a Landlock rule to the current process
+ *
+ * current->seccomp.landlock_events is lazily allocated. When a process fork,
+ * only a pointer is copied. When a new event is added by a process, if there
+ * is other references to this process' landlock_events, then a new allocation
+ * is made to contain an array pointing to Landlock rule lists. This design
+ * enable low-performance impact and is memory efficient while keeping the
+ * property of append-only rules.
+ *
+ * @flags: not used for now, but could be used for TSYNC
+ * @user_bpf_fd: file descriptor pointing to a loaded Landlock rule
+ */
+#ifdef CONFIG_SECCOMP_FILTER
+int landlock_seccomp_append_prog(unsigned int flags,
+		const char __user *user_bpf_fd)
+{
+	struct landlock_events *new_events;
+	struct bpf_prog *prog;
+	int bpf_fd;
+
+	/* force no_new_privs to limit privilege escalation */
+	if (!task_no_new_privs(current))
+		return -EPERM;
+	/* will be removed in the future to allow unprivileged tasks */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	/* enable to check if Landlock is supported with early EFAULT */
+	if (!user_bpf_fd)
+		return -EFAULT;
+	if (flags)
+		return -EINVAL;
+	if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(bpf_fd)))
+		return -EFAULT;
+	prog = bpf_prog_get(bpf_fd);
+	if (IS_ERR(prog))
+		return PTR_ERR(prog);
+
+	/*
+	 * We don't need to lock anything for the current process hierarchy,
+	 * everything is guarded by the atomic counters.
+	 */
+	new_events = landlock_append_prog(current->seccomp.landlock_events,
+			prog);
+	/* @prog is managed/freed by landlock_append_prog() */
+	if (IS_ERR(new_events))
+		return PTR_ERR(new_events);
+	current->seccomp.landlock_events = new_events;
+	return 0;
+}
+#endif /* CONFIG_SECCOMP_FILTER */
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (5 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-10  6:48   ` [kernel-hardening] " Djalal Harouni
  2017-03-28 23:46 ` [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example Mickaël Salaün
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

New in v6

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---
 security/landlock/Makefile       |   2 +-
 security/landlock/hooks_ptrace.c | 126 +++++++++++++++++++++++++++++++++++++++
 security/landlock/hooks_ptrace.h |  11 ++++
 security/landlock/init.c         |   2 +
 4 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 security/landlock/hooks_ptrace.c
 create mode 100644 security/landlock/hooks_ptrace.h

diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index da8ba8b5183e..099a56ca4842 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
 
 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
 
-landlock-y := init.o providers.o hooks.o hooks_fs.o
+landlock-y := init.o providers.o hooks.o hooks_fs.o hooks_ptrace.o
diff --git a/security/landlock/hooks_ptrace.c b/security/landlock/hooks_ptrace.c
new file mode 100644
index 000000000000..8ab53baba9ad
--- /dev/null
+++ b/security/landlock/hooks_ptrace.c
@@ -0,0 +1,126 @@
+/*
+ * Landlock LSM - ptrace hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/current.h>
+#include <linux/kernel.h> /* ARRAY_SIZE */
+#include <linux/landlock.h> /* struct landlock_events */
+#include <linux/lsm_hooks.h>
+#include <linux/sched.h> /* struct task_struct */
+#include <linux/seccomp.h>
+
+#include "hooks.h" /* landlocked() */
+
+#include "hooks_ptrace.h"
+
+
+static bool landlock_events_are_subset(const struct landlock_events *parent,
+		const struct landlock_events *child)
+{
+	size_t i;
+
+	if (!parent || !child)
+		return false;
+	if (parent == child)
+		return true;
+
+	for (i = 0; i < ARRAY_SIZE(child->rules); i++) {
+		struct landlock_rule *walker;
+		bool found_parent = false;
+
+		if (!parent->rules[i])
+			continue;
+		for (walker = child->rules[i]; walker; walker = walker->prev) {
+			if (walker == parent->rules[i]) {
+				found_parent = true;
+				break;
+			}
+		}
+		if (!found_parent)
+			return false;
+	}
+	return true;
+}
+
+static bool landlock_task_has_subset_events(const struct task_struct *parent,
+		const struct task_struct *child)
+{
+#ifdef CONFIG_SECCOMP_FILTER
+	if (landlock_events_are_subset(parent->seccomp.landlock_events,
+				child->seccomp.landlock_events))
+		/* must be ANDed with other providers (i.e. cgroup) */
+		return true;
+#endif /* CONFIG_SECCOMP_FILTER */
+	return false;
+}
+
+/**
+ * landlock_ptrace_access_check - determine whether the current process may
+ *				  access another
+ *
+ * @child: the process to be accessed
+ * @mode: the mode of attachment
+ *
+ * If the current task has Landlock rules, then the child must have at least
+ * the same rules.  Else denied.
+ *
+ * Determine whether a process may access another, returning 0 if permission
+ * granted, -errno if denied.
+ */
+static int landlock_ptrace_access_check(struct task_struct *child,
+		unsigned int mode)
+{
+	if (!landlocked(current))
+		return 0;
+
+	if (!landlocked(child))
+		return -EPERM;
+
+	if (landlock_task_has_subset_events(current, child))
+		return 0;
+
+	return -EPERM;
+}
+
+/**
+ * landlock_ptrace_traceme - determine whether another process may trace the
+ *			     current one
+ *
+ * @parent: the task proposed to be the tracer
+ *
+ * If the parent has Landlock rules, then the current task must have the same
+ * or more rules.
+ * Else denied.
+ *
+ * Determine whether the nominated task is permitted to trace the current
+ * process, returning 0 if permission is granted, -errno if denied.
+ */
+static int landlock_ptrace_traceme(struct task_struct *parent)
+{
+	if (!landlocked(parent))
+		return 0;
+
+	if (!landlocked(current))
+		return -EPERM;
+
+	if (landlock_task_has_subset_events(parent, current))
+		return 0;
+
+	return -EPERM;
+}
+
+static struct security_hook_list landlock_hooks[] = {
+	LSM_HOOK_INIT(ptrace_access_check, landlock_ptrace_access_check),
+	LSM_HOOK_INIT(ptrace_traceme, landlock_ptrace_traceme),
+};
+
+__init void landlock_add_hooks_ptrace(void)
+{
+	landlock_register_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks));
+}
diff --git a/security/landlock/hooks_ptrace.h b/security/landlock/hooks_ptrace.h
new file mode 100644
index 000000000000..15b1f3479e0e
--- /dev/null
+++ b/security/landlock/hooks_ptrace.h
@@ -0,0 +1,11 @@
+/*
+ * Landlock LSM - ptrace hooks
+ *
+ * Copyright © 2017 Mickaël Salaün <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.
+ */
+
+__init void landlock_add_hooks_ptrace(void);
diff --git a/security/landlock/init.c b/security/landlock/init.c
index ef8a3da69860..c8bce3142a32 100644
--- a/security/landlock/init.c
+++ b/security/landlock/init.c
@@ -14,6 +14,7 @@
 #include <linux/lsm_hooks.h>
 
 #include "hooks_fs.h"
+#include "hooks_ptrace.h"
 
 
 static inline bool bpf_landlock_is_valid_access(int off, int size,
@@ -137,6 +138,7 @@ void __init landlock_add_hooks(void)
 {
 	pr_info("landlock: Version %u, ready to sandbox with %s\n",
 			LANDLOCK_VERSION, "seccomp");
+	landlock_add_hooks_ptrace();
 	landlock_add_hooks_fs();
 	security_add_hooks(NULL, 0, "landlock");
 	bpf_register_prog_type(&bpf_landlock_type);
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (6 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 07/11] landlock: Add ptrace restrictions Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-18 23:06   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism Mickaël Salaün
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

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

Changes since v5:
* cosmetic fixes
* rebase

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

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

Changes since v2:
* use BPF_PROG_ATTACH for cgroup handling

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---
 samples/bpf/Makefile         |   4 ++
 samples/bpf/bpf_load.c       |  31 +++++++++++--
 samples/bpf/landlock1_kern.c |  46 +++++++++++++++++++
 samples/bpf/landlock1_user.c | 102 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 179 insertions(+), 4 deletions(-)
 create mode 100644 samples/bpf/landlock1_kern.c
 create mode 100644 samples/bpf/landlock1_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index d42b495b0992..4743674a3fa3 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -36,6 +36,7 @@ hostprogs-y += lwt_len_hist
 hostprogs-y += xdp_tx_iptunnel
 hostprogs-y += test_map_in_map
 hostprogs-y += per_socket_stats_example
+hostprogs-y += landlock1
 
 # Libbpf dependencies
 LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -76,6 +77,7 @@ lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
 xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
 test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
 per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o
+landlock1-objs := bpf_load.o $(LIBBPF) landlock1_user.o
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
@@ -111,6 +113,7 @@ always += lwt_len_hist_kern.o
 always += xdp_tx_iptunnel_kern.o
 always += test_map_in_map_kern.o
 always += cookie_uid_helper_example.o
+always += landlock1_kern.o
 
 HOSTCFLAGS += -I$(objtree)/usr/include
 HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -146,6 +149,7 @@ HOSTLOADLIBES_tc_l2_redirect += -l elf
 HOSTLOADLIBES_lwt_len_hist += -l elf
 HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
 HOSTLOADLIBES_test_map_in_map += -lelf
+HOSTLOADLIBES_landlock1 += -lelf
 
 # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
 #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 4a3460d7c01f..3713e5e2e998 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -29,6 +29,8 @@
 
 static char license[128];
 static int kern_version;
+static union bpf_prog_subtype subtype = {};
+static bool has_subtype;
 static bool processed_sec[128];
 char bpf_log_buf[BPF_LOG_BUF_SIZE];
 int map_fd[MAX_MAPS];
@@ -68,6 +70,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 	bool is_perf_event = strncmp(event, "perf_event", 10) == 0;
 	bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0;
 	bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0;
+	bool is_landlock = strncmp(event, "landlock", 8) == 0;
 	size_t insns_cnt = size / sizeof(struct bpf_insn);
 	enum bpf_prog_type prog_type;
 	char buf[256];
@@ -94,6 +97,13 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 		prog_type = BPF_PROG_TYPE_CGROUP_SKB;
 	} else if (is_cgroup_sk) {
 		prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
+	} else if (is_landlock) {
+		prog_type = BPF_PROG_TYPE_LANDLOCK;
+		if (!has_subtype) {
+			printf("No subtype\n");
+			return -1;
+		}
+		st = &subtype;
 	} else {
 		printf("Unknown event '%s'\n", event);
 		return -1;
@@ -108,7 +118,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 
 	prog_fd[prog_cnt++] = fd;
 
-	if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
+	if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
+	    is_landlock)
 		return 0;
 
 	if (is_socket) {
@@ -294,6 +305,7 @@ int load_bpf_file(char *path)
 	kern_version = 0;
 	memset(license, 0, sizeof(license));
 	memset(processed_sec, 0, sizeof(processed_sec));
+	has_subtype = false;
 
 	if (elf_version(EV_CURRENT) == EV_NONE)
 		return 1;
@@ -339,6 +351,16 @@ int load_bpf_file(char *path)
 			processed_sec[i] = true;
 			if (load_maps(data->d_buf, data->d_size))
 				return 1;
+		} else if (strcmp(shname, "subtype") == 0) {
+			processed_sec[i] = true;
+			if (data->d_size != sizeof(union bpf_prog_subtype)) {
+				printf("invalid size of subtype section %zd\n",
+				       data->d_size);
+				return 1;
+			}
+			memcpy(&subtype, data->d_buf,
+			       sizeof(union bpf_prog_subtype));
+			has_subtype = true;
 		} else if (shdr.sh_type == SHT_SYMTAB) {
 			symbols = data;
 		}
@@ -376,14 +398,14 @@ int load_bpf_file(char *path)
 			    memcmp(shname_prog, "xdp", 3) == 0 ||
 			    memcmp(shname_prog, "perf_event", 10) == 0 ||
 			    memcmp(shname_prog, "socket", 6) == 0 ||
-			    memcmp(shname_prog, "cgroup/", 7) == 0)
+			    memcmp(shname_prog, "cgroup/", 7) == 0 ||
+			    memcmp(shname_prog, "landlock", 8) == 0)
 				load_and_attach(shname_prog, insns, data_prog->d_size);
 		}
 	}
 
 	/* load programs that don't use maps */
 	for (i = 1; i < ehdr.e_shnum; i++) {
-
 		if (processed_sec[i])
 			continue;
 
@@ -396,7 +418,8 @@ int load_bpf_file(char *path)
 		    memcmp(shname, "xdp", 3) == 0 ||
 		    memcmp(shname, "perf_event", 10) == 0 ||
 		    memcmp(shname, "socket", 6) == 0 ||
-		    memcmp(shname, "cgroup/", 7) == 0)
+		    memcmp(shname, "cgroup/", 7) == 0 ||
+		    memcmp(shname, "landlock", 8) == 0)
 			load_and_attach(shname, data->d_buf, data->d_size);
 	}
 
diff --git a/samples/bpf/landlock1_kern.c b/samples/bpf/landlock1_kern.c
new file mode 100644
index 000000000000..b8a9b0ca84c9
--- /dev/null
+++ b/samples/bpf/landlock1_kern.c
@@ -0,0 +1,46 @@
+/*
+ * Landlock rule - partial read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#define KBUILD_MODNAME "foo"
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/stat.h> /* S_ISCHR() */
+#include "bpf_helpers.h"
+
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+	char fmt_error[] = "landlock1: error: get_mode:%lld\n";
+	char fmt_name[] = "landlock1: syscall:%d\n";
+	long long ret;
+
+	if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
+		return 0;
+	ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
+	if (ret < 0) {
+		bpf_trace_printk(fmt_error, sizeof(fmt_error), ret);
+		return 1;
+	}
+	if (S_ISCHR(ret))
+		return 0;
+	bpf_trace_printk(fmt_name, sizeof(fmt_name), ctx->syscall_nr);
+	return 1;
+}
+
+SEC("subtype")
+static union bpf_prog_subtype _subtype = {
+	.landlock_rule = {
+		.version = 1,
+		.event = LANDLOCK_SUBTYPE_EVENT_FS,
+		.ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
+	}
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/samples/bpf/landlock1_user.c b/samples/bpf/landlock1_user.c
new file mode 100644
index 000000000000..6f79eb0ee6db
--- /dev/null
+++ b/samples/bpf/landlock1_user.c
@@ -0,0 +1,102 @@
+/*
+ * Landlock sandbox - partial read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include "bpf_load.h"
+#include "libbpf.h"
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h> /* open() */
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/prctl.h>
+#include <linux/seccomp.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#ifndef seccomp
+static int seccomp(unsigned int op, unsigned int flags, void *args)
+{
+	errno = 0;
+	return syscall(__NR_seccomp, op, flags, args);
+}
+#endif
+
+#define ARRAY_SIZE(a)	(sizeof(a) / sizeof(a[0]))
+#define MAX_ERRNO	4095
+
+
+struct landlock_rule {
+	enum landlock_subtype_event event;
+	struct bpf_insn *bpf;
+	size_t size;
+};
+
+static int apply_sandbox(int prog_fd)
+{
+	int ret = 0;
+
+	/* set up the test sandbox */
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+		perror("prctl(no_new_priv)");
+		return 1;
+	}
+	if (seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog_fd)) {
+		perror("seccomp(set_hook)");
+		ret = 1;
+	}
+	close(prog_fd);
+
+	return ret;
+}
+
+int main(int argc, char * const argv[], char * const *envp)
+{
+	char filename[256];
+	char *cmd_path;
+	char * const *cmd_argv;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]);
+		fprintf(stderr, "Launch a command in a read-only environment "
+				"(except for character devices).\n");
+		fprintf(stderr, "Display debug with: "
+				"cat /sys/kernel/debug/tracing/trace_pipe &\n");
+		return 1;
+	}
+
+	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+	if (load_bpf_file(filename)) {
+		printf("%s", bpf_log_buf);
+		return 1;
+	}
+	if (!prog_fd[0]) {
+		if (errno) {
+			printf("load_bpf_file: %s\n", strerror(errno));
+		} else {
+			printf("load_bpf_file: Error\n");
+		}
+		return 1;
+	}
+
+	if (apply_sandbox(prog_fd[0]))
+		return 1;
+	cmd_path = argv[1];
+	cmd_argv = argv + 1;
+	fprintf(stderr, "Launching a new sandboxed process.\n");
+	execve(cmd_path, cmd_argv, envp);
+	perror("execve");
+	return 1;
+}
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (7 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-19  0:02   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock Mickaël Salaün
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

This is useful to return an information about the error without being
able to write to TH_LOG_STREAM.

Helpers from test_harness.h may be useful outside of the seccomp
directory.

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Will Drewry <wad@chromium.org>
---
 tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
index a786c69c7584..77e407663e06 100644
--- a/tools/testing/selftests/seccomp/test_harness.h
+++ b/tools/testing/selftests/seccomp/test_harness.h
@@ -397,7 +397,7 @@ struct __test_metadata {
 	const char *name;
 	void (*fn)(struct __test_metadata *);
 	int termsig;
-	int passed;
+	__s8 passed;
 	int trigger; /* extra handler after the evaluation */
 	struct __test_metadata *prev, *next;
 };
@@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
 					"instead of by signal (code: %d)\n",
 					t->name,
 					WEXITSTATUS(status));
+			} else if (t->passed < 0) {
+				fprintf(TH_LOG_STREAM,
+					"%s: Failed at step #%d\n",
+					t->name,
+					t->passed * -1);
+				t->passed = 0;
 			}
 		} else if (WIFSIGNALED(status)) {
 			t->passed = 0;
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (8 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism Mickaël Salaün
@ 2017-03-28 23:46 ` Mickaël Salaün
  2017-04-18 23:16   ` Kees Cook
  2017-03-28 23:46 ` [PATCH net-next v6 11/11] landlock: Add user and kernel documentation " Mickaël Salaün
  2017-04-18 23:26 ` [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Kees Cook
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

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

Changes since v5:
* add subtype test
* add ptrace tests
* split and rename files
* cleanup and rebase

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Will Drewry <wad@chromium.org>
---
 tools/testing/selftests/Makefile                   |   1 +
 tools/testing/selftests/bpf/test_verifier.c        |  64 +++++
 tools/testing/selftests/landlock/.gitignore        |   4 +
 tools/testing/selftests/landlock/Makefile          |  47 ++++
 tools/testing/selftests/landlock/rules/Makefile    |  52 ++++
 tools/testing/selftests/landlock/rules/README.rst  |   1 +
 .../testing/selftests/landlock/rules/bpf_helpers.h |   1 +
 .../testing/selftests/landlock/rules/fs_no_open.c  |  31 +++
 .../selftests/landlock/rules/fs_read_only.c        |  31 +++
 tools/testing/selftests/landlock/test.h            |  35 +++
 tools/testing/selftests/landlock/test_base.c       |  31 +++
 tools/testing/selftests/landlock/test_fs.c         | 305 +++++++++++++++++++++
 tools/testing/selftests/landlock/test_ptrace.c     | 161 +++++++++++
 13 files changed, 764 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/.gitignore
 create mode 100644 tools/testing/selftests/landlock/Makefile
 create mode 100644 tools/testing/selftests/landlock/rules/Makefile
 create mode 120000 tools/testing/selftests/landlock/rules/README.rst
 create mode 120000 tools/testing/selftests/landlock/rules/bpf_helpers.h
 create mode 100644 tools/testing/selftests/landlock/rules/fs_no_open.c
 create mode 100644 tools/testing/selftests/landlock/rules/fs_read_only.c
 create mode 100644 tools/testing/selftests/landlock/test.h
 create mode 100644 tools/testing/selftests/landlock/test_base.c
 create mode 100644 tools/testing/selftests/landlock/test_fs.c
 create mode 100644 tools/testing/selftests/landlock/test_ptrace.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index d8593f1251ec..b584ad456428 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -12,6 +12,7 @@ TARGETS += gpio
 TARGETS += intel_pstate
 TARGETS += ipc
 TARGETS += kcmp
+TARGETS += landlock
 TARGETS += lib
 TARGETS += membarrier
 TARGETS += memfd
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index daa87dd7c80e..77255b14871e 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -4536,6 +4536,70 @@ static struct bpf_test tests[] = {
 		.result = REJECT,
 		.has_prog_subtype = true,
 	},
+	{
+		"missing subtype",
+		.insns = {
+			BPF_MOV32_IMM(BPF_REG_0, 0),
+			BPF_EXIT_INSN(),
+		},
+		.errstr = "",
+		.result = REJECT,
+		.prog_type = BPF_PROG_TYPE_LANDLOCK,
+	},
+	{
+		"landlock/fs: always accept",
+		.insns = {
+			BPF_MOV32_IMM(BPF_REG_0, 0),
+			BPF_EXIT_INSN(),
+		},
+		.result = ACCEPT,
+		.prog_type = BPF_PROG_TYPE_LANDLOCK,
+		.has_prog_subtype = true,
+		.prog_subtype = {
+			.landlock_rule = {
+				.version = 1,
+				.event = LANDLOCK_SUBTYPE_EVENT_FS,
+			}
+		},
+	},
+	{
+		"landlock/fs: read context",
+		.insns = {
+			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+			BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, status)),
+			/* test operations on raw values */
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, arch)),
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, syscall_nr)),
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, syscall_cmd)),
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, event)),
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, arg1)),
+			BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
+				offsetof(struct landlock_context, arg2)),
+			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+			BPF_MOV32_IMM(BPF_REG_0, 0),
+			BPF_EXIT_INSN(),
+		},
+		.result = ACCEPT,
+		.prog_type = BPF_PROG_TYPE_LANDLOCK,
+		.has_prog_subtype = true,
+		.prog_subtype = {
+			.landlock_rule = {
+				.version = 1,
+				.event = LANDLOCK_SUBTYPE_EVENT_FS,
+			}
+		},
+	},
 };
 
 static int probe_filter_length(const struct bpf_insn *fp)
diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
new file mode 100644
index 000000000000..25b9cd834c3c
--- /dev/null
+++ b/tools/testing/selftests/landlock/.gitignore
@@ -0,0 +1,4 @@
+/test_base
+/test_fs
+/test_ptrace
+/tmp_*
diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
new file mode 100644
index 000000000000..9a52c82d64fa
--- /dev/null
+++ b/tools/testing/selftests/landlock/Makefile
@@ -0,0 +1,47 @@
+LIBDIR := ../../../lib
+BPFOBJ := $(LIBDIR)/bpf/bpf.o
+LOADOBJ := ../../../../samples/bpf/bpf_load.o
+
+CFLAGS += -Wl,-no-as-needed -Wall -O2 -I../../../include/uapi -I$(LIBDIR)
+LDFLAGS += -lelf
+
+test_src = $(wildcard test_*.c)
+rule_src = $(wildcard rules/*.c)
+
+test_objs := $(test_src:.c=)
+rule_objs := $(rule_src:.c=.o)
+
+TEST_PROGS := $(test_objs)
+
+.PHONY: all clean clean_tmp force
+
+all: $(test_objs) $(rule_objs)
+
+# force a rebuild of BPFOBJ when its dependencies are updated
+force:
+
+$(BPFOBJ): force
+	$(MAKE) -C $(dir $(BPFOBJ))
+
+$(LOADOBJ):
+	$(MAKE) -C $(dir $(LOADOBJ))
+
+# minimize builds
+rules/modules.order: $(rule_src)
+	$(MAKE) -C rules
+	@touch $@
+
+$(rule_objs): rules/modules.order
+	@
+
+$(test_objs): $(BPFOBJ) $(LOADOBJ)
+
+include ../lib.mk
+
+clean_tmp:
+	$(RM) -r tmp_*
+
+clean: clean_tmp
+	$(MAKE) -C rules clean
+	$(RM) $(test_objs)
+
diff --git a/tools/testing/selftests/landlock/rules/Makefile b/tools/testing/selftests/landlock/rules/Makefile
new file mode 100644
index 000000000000..8d6ff960ff7c
--- /dev/null
+++ b/tools/testing/selftests/landlock/rules/Makefile
@@ -0,0 +1,52 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+# Tell kbuild to always build the programs
+always := fs_read_only.o
+always += fs_no_open.o
+
+EXTRA_CFLAGS = -Wall -Wextra
+
+# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
+#  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
+LLC ?= llc
+CLANG ?= clang
+
+# Verify LLVM compiler tools are available and bpf target is supported by llc
+.PHONY: all clean verify_cmds verify_target_bpf $(CLANG) $(LLC)
+
+# Trick to allow make to be run from this directory
+all:
+	$(MAKE) -C ../../../../../ $(CURDIR)/
+
+clean:
+	$(MAKE) -C ../../../../../ M=$(CURDIR) clean
+
+verify_cmds: $(CLANG) $(LLC)
+	@for TOOL in $^ ; do \
+		if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
+			echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
+			exit 1; \
+		else true; fi; \
+	done
+
+verify_target_bpf: verify_cmds
+	@if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
+		echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
+		echo "   NOTICE: LLVM version >= 3.7.1 required" ;\
+		exit 2; \
+	else true; fi
+
+%_kern.c: verify_target_bpf
+
+# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
+# But, there is no easy way to fix it, so just exclude it since it is
+# useless for BPF samples.
+$(obj)/%.o: $(src)/%.c
+	$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
+		-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
+		-Wno-compare-distinct-pointer-types \
+		-Wno-gnu-variable-sized-type-not-at-end \
+		-Wno-tautological-compare \
+		-O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
+
diff --git a/tools/testing/selftests/landlock/rules/README.rst b/tools/testing/selftests/landlock/rules/README.rst
new file mode 120000
index 000000000000..605f48aa6f72
--- /dev/null
+++ b/tools/testing/selftests/landlock/rules/README.rst
@@ -0,0 +1 @@
+../../../../../samples/bpf/README.rst
\ No newline at end of file
diff --git a/tools/testing/selftests/landlock/rules/bpf_helpers.h b/tools/testing/selftests/landlock/rules/bpf_helpers.h
new file mode 120000
index 000000000000..0aa1a521b39a
--- /dev/null
+++ b/tools/testing/selftests/landlock/rules/bpf_helpers.h
@@ -0,0 +1 @@
+../../../../../samples/bpf/bpf_helpers.h
\ No newline at end of file
diff --git a/tools/testing/selftests/landlock/rules/fs_no_open.c b/tools/testing/selftests/landlock/rules/fs_no_open.c
new file mode 100644
index 000000000000..c6ea305e58a7
--- /dev/null
+++ b/tools/testing/selftests/landlock/rules/fs_no_open.c
@@ -0,0 +1,31 @@
+/*
+ * Landlock rule - no-open filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <uapi/linux/bpf.h>
+#include "bpf_helpers.h"
+
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+	if (!(ctx->arg2 & LANDLOCK_ACTION_FS_GET))
+		return 0;
+	return 1;
+}
+
+SEC("subtype")
+static union bpf_prog_subtype _subtype = {
+	.landlock_rule = {
+		.version = 1,
+		.event = LANDLOCK_SUBTYPE_EVENT_FS,
+	}
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/tools/testing/selftests/landlock/rules/fs_read_only.c b/tools/testing/selftests/landlock/rules/fs_read_only.c
new file mode 100644
index 000000000000..212dda7c0c27
--- /dev/null
+++ b/tools/testing/selftests/landlock/rules/fs_read_only.c
@@ -0,0 +1,31 @@
+/*
+ * Landlock rule - read-only filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <uapi/linux/bpf.h>
+#include "bpf_helpers.h"
+
+SEC("landlock1")
+static int landlock_fs_prog1(struct landlock_context *ctx)
+{
+	if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
+		return 0;
+	return 1;
+}
+
+SEC("subtype")
+static union bpf_prog_subtype _subtype = {
+	.landlock_rule = {
+		.version = 1,
+		.event = LANDLOCK_SUBTYPE_EVENT_FS,
+	}
+};
+
+SEC("license")
+static const char _license[] = "GPL";
diff --git a/tools/testing/selftests/landlock/test.h b/tools/testing/selftests/landlock/test.h
new file mode 100644
index 000000000000..7a194815391b
--- /dev/null
+++ b/tools/testing/selftests/landlock/test.h
@@ -0,0 +1,35 @@
+/*
+ * Landlock helpers
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include "../seccomp/test_harness.h"
+#include "../../../../samples/bpf/bpf_load.h"
+
+#ifndef SECCOMP_APPEND_LANDLOCK_RULE
+#define SECCOMP_APPEND_LANDLOCK_RULE	2
+#endif
+
+#ifndef seccomp
+static int seccomp(unsigned int op, unsigned int flags, void *args)
+{
+	errno = 0;
+	return syscall(__NR_seccomp, op, flags, args);
+}
+#endif
+
+#define ASSERT_STEP(cond) \
+	{ \
+		step--; \
+		if (!(cond)) \
+			_exit(step); \
+	}
diff --git a/tools/testing/selftests/landlock/test_base.c b/tools/testing/selftests/landlock/test_base.c
new file mode 100644
index 000000000000..bdf056edee03
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_base.c
@@ -0,0 +1,31 @@
+/*
+ * Landlock tests - base
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+
+#include "test.h"
+
+TEST(seccomp_landlock)
+{
+	int ret;
+
+	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
+	ASSERT_EQ(0, ret) {
+		TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
+	}
+	ret = seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, NULL);
+	EXPECT_EQ(-1, ret);
+	EXPECT_EQ(EFAULT, errno) {
+		TH_LOG("Kernel does not support CONFIG_SECURITY_LANDLOCK");
+	}
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/test_fs.c b/tools/testing/selftests/landlock/test_fs.c
new file mode 100644
index 000000000000..e69eda433716
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_fs.c
@@ -0,0 +1,305 @@
+/*
+ * Landlock tests - filesystem
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include <fcntl.h> /* open() */
+#include <sys/mount.h>
+#include <sys/stat.h> /* mkdir() */
+#include <sys/mman.h> /* mmap() */
+
+#include "test.h"
+
+#define TMP_PREFIX "tmp_"
+
+struct layout1 {
+	int file_ro;
+	int file_rw;
+	int file_wo;
+};
+
+static void setup_layout1(struct __test_metadata *_metadata,
+		struct layout1 *l1)
+{
+	int fd;
+	char buf[] = "fs_read_only";
+
+	l1->file_ro = -1;
+	l1->file_rw = -1;
+	l1->file_wo = -1;
+
+	fd = open(TMP_PREFIX "file_created",
+			O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+	ASSERT_GE(fd, 0);
+	ASSERT_EQ(sizeof(buf), write(fd, buf, sizeof(buf)));
+	ASSERT_EQ(0, close(fd));
+
+	fd = mkdir(TMP_PREFIX "dir_created", 0600);
+	ASSERT_GE(fd, 0);
+	ASSERT_EQ(0, close(fd));
+
+	l1->file_ro = open(TMP_PREFIX "file_ro",
+			O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+	ASSERT_LE(0, l1->file_ro);
+	ASSERT_EQ(sizeof(buf), write(l1->file_ro, buf, sizeof(buf)));
+	ASSERT_EQ(0, close(l1->file_ro));
+	l1->file_ro = open(TMP_PREFIX "file_ro",
+			O_RDONLY | O_CLOEXEC, 0600);
+	ASSERT_LE(0, l1->file_ro);
+
+	l1->file_rw = open(TMP_PREFIX "file_rw",
+			O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
+	ASSERT_LE(0, l1->file_rw);
+	ASSERT_EQ(sizeof(buf), write(l1->file_rw, buf, sizeof(buf)));
+	ASSERT_EQ(0, lseek(l1->file_rw, 0, SEEK_SET));
+
+	l1->file_wo = open(TMP_PREFIX "file_wo",
+			O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
+	ASSERT_LE(0, l1->file_wo);
+	ASSERT_EQ(sizeof(buf), write(l1->file_wo, buf, sizeof(buf)));
+	ASSERT_EQ(0, lseek(l1->file_wo, 0, SEEK_SET));
+}
+
+static void cleanup_layout1(void)
+{
+	unlink(TMP_PREFIX "file_created");
+	unlink(TMP_PREFIX "file_ro");
+	unlink(TMP_PREFIX "file_rw");
+	unlink(TMP_PREFIX "file_wo");
+	unlink(TMP_PREFIX "should_not_exist");
+	rmdir(TMP_PREFIX "dir_created");
+}
+
+FIXTURE(fs_read_only) {
+	struct layout1 l1;
+	int prog;
+};
+
+FIXTURE_SETUP(fs_read_only)
+{
+	cleanup_layout1();
+	setup_layout1(_metadata, &self->l1);
+
+	ASSERT_EQ(0, load_bpf_file("rules/fs_read_only.o")) {
+		TH_LOG("%s", bpf_log_buf);
+	}
+	self->prog = prog_fd[0];
+	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
+		TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
+	}
+}
+
+FIXTURE_TEARDOWN(fs_read_only)
+{
+	EXPECT_EQ(0, close(self->prog));
+	/* cleanup_layout1() would be denied here */
+}
+
+TEST_F(fs_read_only, load_prog) {}
+
+TEST_F(fs_read_only, read_only_file)
+{
+	int fd;
+	int step = 0;
+	char buf_write[] = "should not be written";
+	char buf_read[2];
+
+	ASSERT_EQ(-1, write(self->l1.file_ro, buf_write, sizeof(buf_write)));
+	ASSERT_EQ(EBADF, errno);
+
+	ASSERT_EQ(-1, read(self->l1.file_wo, buf_read, sizeof(buf_read)));
+	ASSERT_EQ(EBADF, errno);
+
+	ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
+		TH_LOG("Failed to apply rule fs_read_only: %s",
+				strerror(errno));
+	}
+
+	fd = open(".", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
+	ASSERT_STEP(fd == -1);
+	ASSERT_STEP(errno != EOPNOTSUPP)
+	ASSERT_STEP(errno == EPERM);
+
+	fd = open(TMP_PREFIX "file_created",
+			O_RDONLY | O_CLOEXEC);
+	ASSERT_STEP(fd >= 0);
+	ASSERT_STEP(!close(fd));
+
+	fd = open(TMP_PREFIX "file_created",
+			O_RDWR | O_CLOEXEC);
+	ASSERT_STEP(fd == -1);
+	ASSERT_STEP(errno == EPERM);
+
+	fd = open(TMP_PREFIX "file_created",
+			O_WRONLY | O_CLOEXEC);
+	ASSERT_STEP(fd == -1);
+	ASSERT_STEP(errno == EPERM);
+
+	fd = open(TMP_PREFIX "should_not_exist",
+			O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+	ASSERT_STEP(fd == -1);
+	ASSERT_STEP(errno == EPERM);
+
+	ASSERT_STEP(-1 ==
+			write(self->l1.file_ro, buf_write, sizeof(buf_write)));
+	ASSERT_STEP(errno == EBADF);
+	ASSERT_STEP(sizeof(buf_read) ==
+			read(self->l1.file_ro, buf_read, sizeof(buf_read)));
+
+	ASSERT_STEP(-1 ==
+			write(self->l1.file_rw, buf_write, sizeof(buf_write)));
+	ASSERT_STEP(errno == EPERM);
+	ASSERT_STEP(sizeof(buf_read) ==
+			read(self->l1.file_rw, buf_read, sizeof(buf_read)));
+
+	ASSERT_STEP(-1 == write(self->l1.file_wo, buf_write, sizeof(buf_write)));
+	ASSERT_STEP(errno == EPERM);
+	ASSERT_STEP(-1 == read(self->l1.file_wo, buf_read, sizeof(buf_read)));
+	ASSERT_STEP(errno == EBADF);
+
+	ASSERT_STEP(-1 == unlink(TMP_PREFIX "file_created"));
+	ASSERT_STEP(errno == EPERM);
+	ASSERT_STEP(-1 == rmdir(TMP_PREFIX "dir_created"));
+	ASSERT_STEP(errno == EPERM);
+
+	ASSERT_STEP(0 == close(self->l1.file_ro));
+	ASSERT_STEP(0 == close(self->l1.file_rw));
+	ASSERT_STEP(0 == close(self->l1.file_wo));
+}
+
+TEST_F(fs_read_only, read_only_mount)
+{
+	int step = 0;
+
+	ASSERT_EQ(0, mount(".", TMP_PREFIX "dir_created",
+				NULL, MS_BIND, NULL));
+	ASSERT_EQ(0, umount2(TMP_PREFIX "dir_created", MNT_FORCE));
+
+	ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
+		TH_LOG("Failed to apply rule fs_read_only: %s",
+				strerror(errno));
+	}
+
+	ASSERT_STEP(-1 == mount(".", TMP_PREFIX "dir_created",
+				NULL, MS_BIND, NULL));
+	ASSERT_STEP(errno == EPERM);
+	ASSERT_STEP(-1 == umount("/"));
+	ASSERT_STEP(errno == EPERM);
+}
+
+TEST_F(fs_read_only, read_only_mem)
+{
+	int step = 0;
+	void *addr;
+
+	addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
+			MAP_SHARED, self->l1.file_rw, 0);
+	ASSERT_NE(NULL, addr);
+	ASSERT_EQ(0, munmap(addr, 1));
+
+	ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
+		TH_LOG("Failed to apply rule fs_read_only: %s",
+				strerror(errno));
+	}
+
+	addr = mmap(NULL, 1, PROT_READ, MAP_SHARED,
+			self->l1.file_rw, 0);
+	ASSERT_STEP(addr != NULL);
+	ASSERT_STEP(-1 == mprotect(addr, 1, PROT_WRITE));
+	ASSERT_STEP(errno == EPERM);
+	ASSERT_STEP(0 == munmap(addr, 1));
+
+	addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED,
+			self->l1.file_rw, 0);
+	ASSERT_STEP(addr != NULL);
+	ASSERT_STEP(errno == EPERM);
+
+	addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+			self->l1.file_rw, 0);
+	ASSERT_STEP(addr != NULL);
+	ASSERT_STEP(0 == munmap(addr, 1));
+}
+
+FIXTURE(fs_no_open) {
+	struct layout1 l1;
+	int prog;
+};
+
+FIXTURE_SETUP(fs_no_open)
+{
+	cleanup_layout1();
+	setup_layout1(_metadata, &self->l1);
+
+	ASSERT_EQ(0, load_bpf_file("rules/fs_no_open.o")) {
+		TH_LOG("%s", bpf_log_buf);
+	}
+	self->prog = prog_fd[0];
+	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
+		TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
+	}
+}
+
+FIXTURE_TEARDOWN(fs_no_open)
+{
+	EXPECT_EQ(0, close(self->prog));
+	cleanup_layout1();
+}
+
+static void landlocked_deny_open(struct __test_metadata *_metadata,
+		struct layout1 *l1)
+{
+	int fd;
+	void *addr;
+
+	fd = open(".", O_DIRECTORY | O_CLOEXEC);
+	ASSERT_EQ(-1, fd);
+	ASSERT_EQ(EPERM, errno);
+
+	addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
+			MAP_SHARED, l1->file_rw, 0);
+	ASSERT_NE(NULL, addr);
+	ASSERT_EQ(0, munmap(addr, 1));
+}
+
+TEST_F(fs_no_open, deny_open_for_hierarchy) {
+	int fd;
+	int status;
+	pid_t child;
+
+	fd = open(".", O_DIRECTORY | O_CLOEXEC);
+	ASSERT_LE(0, fd);
+	ASSERT_EQ(0, close(fd));
+
+	ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
+		TH_LOG("Failed to apply rule fs_no_open: %s", strerror(errno));
+	}
+
+	landlocked_deny_open(_metadata, &self->l1);
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (!child) {
+		landlocked_deny_open(_metadata, &self->l1);
+		_exit(1);
+	}
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_TRUE(WIFEXITED(status));
+	_exit(WEXITSTATUS(status));
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/test_ptrace.c b/tools/testing/selftests/landlock/test_ptrace.c
new file mode 100644
index 000000000000..0c940a7fd3d0
--- /dev/null
+++ b/tools/testing/selftests/landlock/test_ptrace.c
@@ -0,0 +1,161 @@
+/*
+ * Landlock tests - ptrace
+ *
+ * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#define _GNU_SOURCE
+#include <signal.h> /* raise */
+#include <sys/ptrace.h>
+#include <sys/types.h> /* waitpid */
+#include <sys/wait.h> /* waitpid */
+#include <unistd.h> /* fork, pipe */
+
+#include "test.h"
+
+static void apply_null_sandbox(struct __test_metadata *_metadata)
+{
+	const struct bpf_insn prog_accept[] = {
+		BPF_MOV32_IMM(BPF_REG_0, 0),
+		BPF_EXIT_INSN(),
+	};
+	const union bpf_prog_subtype subtype = {
+		.landlock_rule = {
+			.version = 1,
+			.event = LANDLOCK_SUBTYPE_EVENT_FS,
+		}
+	};
+	int prog;
+	char log[256] = "";
+
+	prog = bpf_load_program(BPF_PROG_TYPE_LANDLOCK,
+			(const struct bpf_insn *)&prog_accept,
+			sizeof(prog_accept) / sizeof(struct bpf_insn), "GPL",
+			0, log, sizeof(log), &subtype);
+	ASSERT_NE(-1, prog) {
+		TH_LOG("Failed to load minimal rule: %s\n%s",
+				strerror(errno), log);
+	}
+	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
+		TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
+	}
+	ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog)) {
+		TH_LOG("Failed to apply minimal rule: %s", strerror(errno));
+	}
+	EXPECT_EQ(0, close(prog));
+}
+
+/* PTRACE_TRACEME and PTRACE_ATTACH without Landlock rules effect */
+static void check_ptrace(struct __test_metadata *_metadata,
+		int sandbox_both, int sandbox_parent, int sandbox_child,
+		int expect_ptrace)
+{
+	pid_t child;
+	int status;
+	int pipefd[2];
+
+	ASSERT_EQ(0, pipe(pipefd));
+	if (sandbox_both)
+		apply_null_sandbox(_metadata);
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		char buf;
+
+		EXPECT_EQ(0, close(pipefd[1]));
+		if (sandbox_child)
+			apply_null_sandbox(_metadata);
+
+		/* test traceme */
+		ASSERT_EQ(expect_ptrace, ptrace(PTRACE_TRACEME));
+		if (expect_ptrace) {
+			ASSERT_EQ(EPERM, errno);
+		} else {
+			ASSERT_EQ(0, raise(SIGSTOP));
+		}
+
+		/* sync */
+		ASSERT_EQ(1, read(pipefd[0], &buf, 1)) {
+			TH_LOG("Failed to read() sync from parent");
+		}
+		ASSERT_EQ('.', buf);
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+	}
+
+	EXPECT_EQ(0, close(pipefd[0]));
+	if (sandbox_parent)
+		apply_null_sandbox(_metadata);
+
+	/* test traceme */
+	if (!expect_ptrace) {
+		ASSERT_EQ(child, waitpid(child, &status, 0));
+		ASSERT_EQ(1, WIFSTOPPED(status));
+		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
+	}
+	/* test attach */
+	ASSERT_EQ(expect_ptrace, ptrace(PTRACE_ATTACH, child, NULL, 0));
+	if (expect_ptrace) {
+		ASSERT_EQ(EPERM, errno);
+	} else {
+		ASSERT_EQ(child, waitpid(child, &status, 0));
+		ASSERT_EQ(1, WIFSTOPPED(status));
+		ASSERT_EQ(0, ptrace(PTRACE_CONT, child, NULL, 0));
+	}
+
+	/* sync */
+	ASSERT_EQ(1, write(pipefd[1], ".", 1)) {
+		TH_LOG("Failed to write() sync to child");
+	}
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	if (WIFSIGNALED(status) || WEXITSTATUS(status))
+		_metadata->passed = 0;
+}
+
+TEST(ptrace_allow_without_sandbox)
+{
+	/* no sandbox */
+	check_ptrace(_metadata, 0, 0, 0, 0);
+}
+
+TEST(ptrace_allow_with_one_sandbox)
+{
+	/* child sandbox */
+	check_ptrace(_metadata, 0, 0, 1, 0);
+}
+
+TEST(ptrace_allow_with_nested_sandbox)
+{
+	/* inherited and child sandbox */
+	check_ptrace(_metadata, 1, 0, 1, 0);
+}
+
+TEST(ptrace_deny_with_parent_sandbox)
+{
+	/* parent sandbox */
+	check_ptrace(_metadata, 0, 1, 0, -1);
+}
+
+TEST(ptrace_deny_with_nested_and_parent_sandbox)
+{
+	/* inherited and parent sandbox */
+	check_ptrace(_metadata, 1, 1, 0, -1);
+}
+
+TEST(ptrace_deny_with_forked_sandbox)
+{
+	/* inherited, parent and child sandbox */
+	check_ptrace(_metadata, 1, 1, 1, -1);
+}
+
+TEST(ptrace_deny_with_sibling_sandbox)
+{
+	/* parent and child sandbox */
+	check_ptrace(_metadata, 0, 1, 1, -1);
+}
+
+TEST_HARNESS_MAIN
-- 
2.11.0

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (9 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock Mickaël Salaün
@ 2017-03-28 23:46 ` " Mickaël Salaün
  2017-03-29 15:58   ` kbuild test robot
  2017-04-18 23:26 ` [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Kees Cook
  11 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mickaël Salaün, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, linux-api, linux-security-module,
	netdev

This documentation can be built with the Sphinx framework.

Changes since v5:
* update the rule hierarchy inheritance explanation
* briefly explain ctx->arg2
* add ptrace restrictions
* explain EPERM
* update example (subtype)
* use ":manpage:"

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---
 Documentation/security/index.rst           |   1 +
 Documentation/security/landlock/index.rst  |  19 ++
 Documentation/security/landlock/kernel.rst | 132 +++++++++++++
 Documentation/security/landlock/user.rst   | 307 +++++++++++++++++++++++++++++
 4 files changed, 459 insertions(+)
 create mode 100644 Documentation/security/landlock/index.rst
 create mode 100644 Documentation/security/landlock/kernel.rst
 create mode 100644 Documentation/security/landlock/user.rst

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

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-28 23:46 ` [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy Mickaël Salaün
@ 2017-03-29 10:35   ` " Djalal Harouni
  2017-03-31 21:15     ` Mickaël Salaün
  2017-04-18 22:53   ` Kees Cook
  1 sibling, 1 reply; 50+ messages in thread
From: Djalal Harouni @ 2017-03-29 10:35 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: linux-kernel, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, Linux API, LSM List, netdev,
	Andrew Morton, Tetsuo Handa

On Wed, Mar 29, 2017 at 1:46 AM, Mickaël Salaün <mic@digikod.net> wrote:
> The seccomp(2) syscall can be used by a task to apply a Landlock rule to
> itself. As a seccomp filter, a Landlock rule is enforced for the current
> task and all its future children. A rule is immutable and a task can
> only add new restricting rules to itself, forming a chain of rules.
>
> A Landlock rule is tied to a Landlock event. If the use of a kernel
> object is allowed by the other Linux security mechanisms (e.g. DAC,
> capabilities, other LSM), then a Landlock event related to this kind of
> object is triggered. The chain of rules for this event is then
> evaluated. Each rule return a 32-bit value which can deny the use of a
> kernel object with a non-zero value. If every rules of the chain return
> zero, then the use of the object is allowed.
>
> Changes since v5:
> * remove struct landlock_node and use a similar inheritance mechanisme
>   as seccomp-bpf (requested by Andy Lutomirski)
> * rename SECCOMP_ADD_LANDLOCK_RULE to SECCOMP_APPEND_LANDLOCK_RULE
> * rename file manager.c to providers.c
> * add comments
> * typo and cosmetic fixes
>
> Changes since v4:
> * merge manager and seccomp patches
> * return -EFAULT in seccomp(2) when user_bpf_fd is null to easely check
>   if Landlock is supported
> * only allow a process with the global CAP_SYS_ADMIN to use Landlock
>   (will be lifted in the future)
> * add an early check to exit as soon as possible if the current process
>   does not have Landlock rules
>
> Changes since v3:
> * remove the hard link with seccomp (suggested by Andy Lutomirski and
>   Kees Cook):
>   * remove the cookie which could imply multiple evaluation of Landlock
>     rules
>   * remove the origin field in struct landlock_data
> * remove documentation fix (merged upstream)
> * rename the new seccomp command to SECCOMP_ADD_LANDLOCK_RULE
> * internal renaming
> * split commit
> * new design to be able to inherit on the fly the parent rules
>
> Changes since v2:
> * Landlock programs can now be run without seccomp filter but for any
>   syscall (from the process) or interruption
> * move Landlock related functions and structs into security/landlock/*
>   (to manage cgroups as well)
> * fix seccomp filter handling: run Landlock programs for each of their
>   legitimate seccomp filter
> * properly clean up all seccomp results
> * cosmetic changes to ease the understanding
> * fix some ifdef
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Cc: Will Drewry <wad@chromium.org>
> Link: https://lkml.kernel.org/r/c10a503d-5e35-7785-2f3d-25ed8dd63fab@digikod.net
> ---
>  include/linux/landlock.h      |  36 +++++++
>  include/linux/seccomp.h       |   8 ++
>  include/uapi/linux/seccomp.h  |   1 +
>  kernel/fork.c                 |  14 ++-
>  kernel/seccomp.c              |   8 ++
>  security/landlock/Makefile    |   2 +-
>  security/landlock/hooks.c     |  37 +++++++
>  security/landlock/hooks.h     |   5 +
>  security/landlock/init.c      |   3 +-
>  security/landlock/providers.c | 232 ++++++++++++++++++++++++++++++++++++++++++
>  10 files changed, 342 insertions(+), 4 deletions(-)
>  create mode 100644 security/landlock/providers.c
>
> diff --git a/include/linux/landlock.h b/include/linux/landlock.h
> index 53013dc374fe..c40ee78e86e0 100644
> --- a/include/linux/landlock.h
> +++ b/include/linux/landlock.h
> @@ -12,6 +12,9 @@
>  #define _LINUX_LANDLOCK_H
>  #ifdef CONFIG_SECURITY_LANDLOCK
>
> +#include <linux/bpf.h> /* _LANDLOCK_SUBTYPE_EVENT_LAST */
> +#include <linux/types.h> /* atomic_t */
> +
>  /*
>   * This is not intended for the UAPI headers. Each userland software should use
>   * a static minimal version for the required features as explained in the
> @@ -19,5 +22,38 @@
>   */
>  #define LANDLOCK_VERSION 1
>
> +struct landlock_rule {
> +       atomic_t usage;
> +       struct landlock_rule *prev;
> +       struct bpf_prog *prog;
> +};
> +
> +/**
> + * struct landlock_events - Landlock event rules enforced on a thread
> + *
> + * This is used for low performance impact when forking a process. Instead of
> + * copying the full array and incrementing the usage of each entries, only
> + * create a pointer to &struct landlock_events and increments its usage. When
> + * appending a new rule, if &struct landlock_events is shared with other tasks,
> + * then duplicate it and append the rule to this new &struct landlock_events.
> + *
> + * @usage: reference count to manage the object lifetime. When a thread need to
> + *         add Landlock rules and if @usage is greater than 1, then the thread
> + *         must duplicate &struct landlock_events to not change the children's
> + *         rules as well.
> + * @rules: array of non-NULL &struct landlock_rule pointers
> + */
> +struct landlock_events {
> +       atomic_t usage;
> +       struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST];
> +};
> +
> +void put_landlock_events(struct landlock_events *events);
> +
> +#ifdef CONFIG_SECCOMP_FILTER
> +int landlock_seccomp_append_prog(unsigned int flags,
> +               const char __user *user_bpf_fd);
> +#endif /* CONFIG_SECCOMP_FILTER */
> +
>  #endif /* CONFIG_SECURITY_LANDLOCK */
>  #endif /* _LINUX_LANDLOCK_H */
> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
> index e25aee2cdfc0..9a38de3c0e72 100644
> --- a/include/linux/seccomp.h
> +++ b/include/linux/seccomp.h
> @@ -10,6 +10,10 @@
>  #include <linux/thread_info.h>
>  #include <asm/seccomp.h>
>
> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
> +struct landlock_events;
> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
> +
>  struct seccomp_filter;
>  /**
>   * struct seccomp - the state of a seccomp'ed process
> @@ -18,6 +22,7 @@ struct seccomp_filter;
>   *         system calls available to a process.
>   * @filter: must always point to a valid seccomp-filter or NULL as it is
>   *          accessed without locking during system call entry.
> + * @landlock_events: contains an array of Landlock rules.
>   *
>   *          @filter must only be accessed from the context of current as there
>   *          is no read locking.
> @@ -25,6 +30,9 @@ struct seccomp_filter;
>  struct seccomp {
>         int mode;
>         struct seccomp_filter *filter;
> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
> +       struct landlock_events *landlock_events;
> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>  };

Sorry if this was discussed before, but since this is mean to be a
stackable LSM, I'm wondering if later you could move the events from
seccomp, and go with a security_task_alloc() model [1] ?

Thanks!

[1] http://kernsec.org/pipermail/linux-security-module-archive/2017-March/000184.html

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
  2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
@ 2017-03-29 13:48   ` kbuild test robot
  2017-04-18 21:48   ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: kbuild test robot @ 2017-03-29 13:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: kbuild-all, linux-kernel, Mickaël Salaün,
	Alexei Starovoitov, Andy Lutomirski, Arnaldo Carvalho de Melo,
	Casey Schaufler, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Kees Cook,
	Paul Moore, Sargun Dhillon, Serge E . Hallyn, Shuah Khan,
	Tejun Heo, Thomas Graf, Will Drewry, kernel-hardening, linux-api,
	linux-security-module, netdev

[-- Attachment #1: Type: text/plain, Size: 1645 bytes --]

Hi Mickaël,

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Micka-l-Sala-n/Landlock-LSM-Toward-unprivileged-sandboxing/20170329-211258
config: i386-randconfig-x002-201713 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   kernel/bpf/syscall.c: In function 'bpf_prog_load':
>> kernel/bpf/syscall.c:886:25: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
      size = check_user_buf((void __user *)attr->prog_subtype,
                            ^

vim +886 kernel/bpf/syscall.c

   870	
   871		prog->orig_prog = NULL;
   872		prog->jited = 0;
   873	
   874		atomic_set(&prog->aux->refcnt, 1);
   875		prog->gpl_compatible = is_gpl ? 1 : 0;
   876	
   877		/* find program type: socket_filter vs tracing_filter */
   878		err = find_prog_type(type, prog);
   879		if (err < 0)
   880			goto free_prog;
   881	
   882		/* copy eBPF program subtype from user space */
   883		if (attr->prog_subtype) {
   884			__u32 size;
   885	
 > 886			size = check_user_buf((void __user *)attr->prog_subtype,
   887					      attr->prog_subtype_size,
   888					      sizeof(prog->subtype));
   889			if (size < 0) {
   890				err = size;
   891				goto free_prog;
   892			}
   893			/* prog->subtype is __GFP_ZERO */
   894			if (copy_from_user(&prog->subtype,

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 23016 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-03-28 23:46 ` [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem Mickaël Salaün
@ 2017-03-29 15:18   ` kbuild test robot
  2017-04-18 22:17   ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: kbuild test robot @ 2017-03-29 15:18 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: kbuild-all, linux-kernel, Mickaël Salaün,
	Alexei Starovoitov, Andy Lutomirski, Arnaldo Carvalho de Melo,
	Casey Schaufler, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Kees Cook,
	Paul Moore, Sargun Dhillon, Serge E . Hallyn, Shuah Khan,
	Tejun Heo, Thomas Graf, Will Drewry, kernel-hardening, linux-api,
	linux-security-module, netdev

[-- Attachment #1: Type: text/plain, Size: 3023 bytes --]

Hi Mickaël,

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Micka-l-Sala-n/Landlock-LSM-Toward-unprivileged-sandboxing/20170329-211258
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

   In file included from security/landlock/hooks.c:12:0:
   security/landlock/hooks.c: In function 'landlock_decide':
>> arch/x86/include/asm/processor.h:824:39: error: implicit declaration of function 'task_stack_page' [-Werror=implicit-function-declaration]
     unsigned long __ptr = (unsigned long)task_stack_page(task); \
                                          ^
>> security/landlock/hooks.c:107:41: note: in expansion of macro 'task_pt_regs'
      .syscall_nr = syscall_get_nr(current, task_pt_regs(current)),
                                            ^~~~~~~~~~~~
   security/landlock/hooks.c:104:26: warning: unused variable 'ctx' [-Wunused-variable]
     struct landlock_context ctx = {
                             ^~~
   security/landlock/hooks.c:102:6: warning: unused variable 'event_idx' [-Wunused-variable]
     u32 event_idx = get_index(event);
         ^~~~~~~~~
   cc1: some warnings being treated as errors

vim +/task_stack_page +824 arch/x86/include/asm/processor.h

2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  818   * Therefore beware: accessing the ss/esp fields of the
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  819   * "struct pt_regs" is possible, but they may contain the
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  820   * completely wrong values.
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  821   */
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  822  #define task_pt_regs(task) \
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  823  ({									\
5c39403e arch/x86/include/asm/processor.h Denys Vlasenko            2015-03-13 @824  	unsigned long __ptr = (unsigned long)task_stack_page(task);	\
5c39403e arch/x86/include/asm/processor.h Denys Vlasenko            2015-03-13  825  	__ptr += THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING;		\
5c39403e arch/x86/include/asm/processor.h Denys Vlasenko            2015-03-13  826  	((struct pt_regs *)__ptr) - 1;					\
2f66dcc9 include/asm-x86/processor.h      Glauber de Oliveira Costa 2008-01-30  827  })

:::::: The code at line 824 was first introduced by commit
:::::: 5c39403e004bec75ce0c549541be5479595d6ad0 x86/asm/entry: Simplify task_pt_regs() macro definition

:::::: TO: Denys Vlasenko <dvlasenk@redhat.com>
:::::: CC: Ingo Molnar <mingo@kernel.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 59015 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
  2017-03-28 23:46 ` [PATCH net-next v6 11/11] landlock: Add user and kernel documentation " Mickaël Salaün
@ 2017-03-29 15:58   ` kbuild test robot
  0 siblings, 0 replies; 50+ messages in thread
From: kbuild test robot @ 2017-03-29 15:58 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: kbuild-all, linux-kernel, Mickaël Salaün,
	Alexei Starovoitov, Andy Lutomirski, Arnaldo Carvalho de Melo,
	Casey Schaufler, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Kees Cook,
	Paul Moore, Sargun Dhillon, Serge E . Hallyn, Shuah Khan,
	Tejun Heo, Thomas Graf, Will Drewry, kernel-hardening, linux-api,
	linux-security-module, netdev

[-- Attachment #1: Type: text/plain, Size: 9285 bytes --]

Hi Mickaël,

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Micka-l-Sala-n/Landlock-LSM-Toward-unprivileged-sandboxing/20170329-211258
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

   include/linux/init.h:1: warning: no structured comments found
   kernel/sched/core.c:2085: warning: No description found for parameter 'rf'
   kernel/sched/core.c:2085: warning: Excess function parameter 'cookie' description in 'try_to_wake_up_local'
   include/linux/kthread.h:26: warning: Excess function parameter '...' description in 'kthread_create'
   kernel/sys.c:1: warning: no structured comments found
   include/linux/device.h:969: warning: No description found for parameter 'dma_ops'
   drivers/dma-buf/seqno-fence.c:1: warning: no structured comments found
   include/linux/iio/iio.h:597: warning: No description found for parameter 'trig_readonly'
   include/linux/iio/trigger.h:151: warning: No description found for parameter 'indio_dev'
   include/linux/iio/trigger.h:151: warning: No description found for parameter 'trig'
   include/linux/device.h:970: warning: No description found for parameter 'dma_ops'
   drivers/regulator/core.c:1467: warning: Excess function parameter 'ret' description in 'regulator_dev_lookup'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'open'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'preclose'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'postclose'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'lastclose'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'set_busid'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'irq_handler'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'irq_preinstall'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'irq_postinstall'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'irq_uninstall'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'debugfs_init'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'debugfs_cleanup'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_open_object'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_close_object'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'prime_handle_to_fd'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'prime_fd_to_handle'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_export'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_import'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_pin'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_unpin'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_res_obj'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_get_sg_table'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_import_sg_table'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_vmap'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_vunmap'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_prime_mmap'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'gem_vm_ops'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'major'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'minor'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'patchlevel'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'name'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'desc'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'date'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'driver_features'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'ioctls'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'num_ioctls'
   include/drm/drm_drv.h:438: warning: No description found for parameter 'fops'
   include/drm/drm_color_mgmt.h:1: warning: no structured comments found
   drivers/gpu/drm/drm_fb_cma_helper.c:557: warning: Excess function parameter 'num_crtc' description in 'drm_fbdev_cma_init'
   drivers/gpu/drm/drm_fb_cma_helper.c:558: warning: Excess function parameter 'num_crtc' description in 'drm_fbdev_cma_init'
   drivers/gpu/drm/i915/intel_lpe_audio.c:342: warning: No description found for parameter 'pipe'
   drivers/gpu/drm/i915/intel_lpe_audio.c:342: warning: No description found for parameter 'dp_output'
   drivers/gpu/drm/i915/intel_lpe_audio.c:342: warning: No description found for parameter 'link_rate'
   drivers/gpu/drm/i915/intel_lpe_audio.c:343: warning: No description found for parameter 'pipe'
   drivers/gpu/drm/i915/intel_lpe_audio.c:343: warning: No description found for parameter 'dp_output'
   drivers/gpu/drm/i915/intel_lpe_audio.c:343: warning: No description found for parameter 'link_rate'
   drivers/media/dvb-core/dvb_frontend.h:677: warning: No description found for parameter 'refcount'
>> include/uapi/linux/bpf.h:1: warning: no structured comments found
   Documentation/core-api/assoc_array.rst:13: WARNING: Enumerated list ends without a blank line; unexpected unindent.
   Documentation/doc-guide/sphinx.rst:110: ERROR: Unknown target name: "sphinx c domain".
   kernel/sched/fair.c:7616: WARNING: Inline emphasis start-string without end-string.
   kernel/time/timer.c:1200: ERROR: Unexpected indentation.
   kernel/time/timer.c:1202: ERROR: Unexpected indentation.
   kernel/time/timer.c:1203: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/wait.h:122: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/wait.h:125: ERROR: Unexpected indentation.
   include/linux/wait.h:127: WARNING: Block quote ends without a blank line; unexpected unindent.
   kernel/time/hrtimer.c:990: WARNING: Block quote ends without a blank line; unexpected unindent.
   kernel/signal.c:322: WARNING: Inline literal start-string without end-string.
   include/linux/iio/iio.h:219: ERROR: Unexpected indentation.
   include/linux/iio/iio.h:220: WARNING: Block quote ends without a blank line; unexpected unindent.
   include/linux/iio/iio.h:226: WARNING: Definition list ends without a blank line; unexpected unindent.
   drivers/iio/industrialio-core.c:639: ERROR: Unknown target name: "iio_val".
   drivers/iio/industrialio-core.c:646: ERROR: Unknown target name: "iio_val".
   drivers/message/fusion/mptbase.c:5051: WARNING: Definition list ends without a blank line; unexpected unindent.
   drivers/tty/serial/serial_core.c:1898: WARNING: Definition list ends without a blank line; unexpected unindent.
   include/linux/regulator/driver.h:271: ERROR: Unknown target name: "regulator_regmap_x_voltage".
   include/linux/spi/spi.h:369: ERROR: Unexpected indentation.
   drivers/usb/core/message.c:478: ERROR: Unexpected indentation.
   drivers/usb/core/message.c:479: WARNING: Block quote ends without a blank line; unexpected unindent.
   Documentation/driver-api/usb.rst:623: ERROR: Unknown target name: "usb_type".
   Documentation/driver-api/usb.rst:623: ERROR: Unknown target name: "usb_dir".
   Documentation/driver-api/usb.rst:623: ERROR: Unknown target name: "usb_recip".
   Documentation/driver-api/usb.rst:689: ERROR: Unknown target name: "usbdevfs_urb_type".
   sound/soc/soc-core.c:2670: ERROR: Unknown target name: "snd_soc_daifmt".
   sound/core/jack.c:312: ERROR: Unknown target name: "snd_jack_btn".
   WARNING: dvipng command 'dvipng' cannot be run (needed for math display), check the imgmath_dvipng setting

vim +1 include/uapi/linux/bpf.h

daedfb22 Alexei Starovoitov 2014-09-04 @1  /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
daedfb22 Alexei Starovoitov 2014-09-04  2   *
daedfb22 Alexei Starovoitov 2014-09-04  3   * This program is free software; you can redistribute it and/or
daedfb22 Alexei Starovoitov 2014-09-04  4   * modify it under the terms of version 2 of the GNU General Public
daedfb22 Alexei Starovoitov 2014-09-04  5   * License as published by the Free Software Foundation.
daedfb22 Alexei Starovoitov 2014-09-04  6   */
daedfb22 Alexei Starovoitov 2014-09-04  7  #ifndef _UAPI__LINUX_BPF_H__
daedfb22 Alexei Starovoitov 2014-09-04  8  #define _UAPI__LINUX_BPF_H__
daedfb22 Alexei Starovoitov 2014-09-04  9  

:::::: The code at line 1 was first introduced by commit
:::::: daedfb22451dd02b35c0549566cbb7cc06bdd53b net: filter: split filter.h and expose eBPF to user space

:::::: TO: Alexei Starovoitov <ast@plumgrid.com>
:::::: CC: David S. Miller <davem@davemloft.net>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6576 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-29 10:35   ` [kernel-hardening] " Djalal Harouni
@ 2017-03-31 21:15     ` Mickaël Salaün
  2017-04-18 22:54       ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-03-31 21:15 UTC (permalink / raw)
  To: Djalal Harouni
  Cc: linux-kernel, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, Linux API, LSM List, netdev,
	Andrew Morton, Tetsuo Handa

[-- Attachment #1.1: Type: text/plain, Size: 1095 bytes --]



On 29/03/2017 12:35, Djalal Harouni wrote:
> On Wed, Mar 29, 2017 at 1:46 AM, Mickaël Salaün <mic@digikod.net> wrote:

>> @@ -25,6 +30,9 @@ struct seccomp_filter;
>>  struct seccomp {
>>         int mode;
>>         struct seccomp_filter *filter;
>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>> +       struct landlock_events *landlock_events;
>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>>  };
> 
> Sorry if this was discussed before, but since this is mean to be a
> stackable LSM, I'm wondering if later you could move the events from
> seccomp, and go with a security_task_alloc() model [1] ?
> 
> Thanks!
> 
> [1] http://kernsec.org/pipermail/linux-security-module-archive/2017-March/000184.html
> 

Landlock use the seccomp syscall to attach a rule to a process and using
struct seccomp to store this rule make sense. There is currently no way
to store multiple task->security, which is needed for a stackable LSM
like Landlock, but we could move the events there if needed in the future.

 Mickaël


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
  2017-03-28 23:46 ` [PATCH net-next v6 07/11] landlock: Add ptrace restrictions Mickaël Salaün
@ 2017-04-10  6:48   ` " Djalal Harouni
  2017-04-11  7:19     ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Djalal Harouni @ 2017-04-10  6:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: linux-kernel, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, Linux API, LSM List, netdev

On Wed, Mar 29, 2017 at 1:46 AM, Mickaël Salaün <mic@digikod.net> wrote:
> A landlocked process has less privileges than a non-landlocked process
> and must then be subject to additional restrictions when manipulating
> processes. To be allowed to use ptrace(2) and related syscalls on a
> target process, a landlocked process must have a subset of the target
> process' rules.
>
> New in v6
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
>  security/landlock/Makefile       |   2 +-
>  security/landlock/hooks_ptrace.c | 126 +++++++++++++++++++++++++++++++++++++++
>  security/landlock/hooks_ptrace.h |  11 ++++
>  security/landlock/init.c         |   2 +
>  4 files changed, 140 insertions(+), 1 deletion(-)
>  create mode 100644 security/landlock/hooks_ptrace.c
>  create mode 100644 security/landlock/hooks_ptrace.h
>
[...]

> +/**
> + * landlock_ptrace_access_check - determine whether the current process may
> + *                               access another
> + *
> + * @child: the process to be accessed
> + * @mode: the mode of attachment
> + *
> + * If the current task has Landlock rules, then the child must have at least
> + * the same rules.  Else denied.
> + *
> + * Determine whether a process may access another, returning 0 if permission
> + * granted, -errno if denied.
> + */
> +static int landlock_ptrace_access_check(struct task_struct *child,
> +               unsigned int mode)
> +{
> +       if (!landlocked(current))
> +               return 0;
> +
> +       if (!landlocked(child))
> +               return -EPERM;
> +
> +       if (landlock_task_has_subset_events(current, child))
> +               return 0;
> +
> +       return -EPERM;
> +}
> +

Maybe you want to check the mode argument here if it is a
PTRACE_ATTACH which may translate to read/writes ? PTRACE_READ are
normally for reads only. Or also which creds were used if this was a
direct syscall or a filesystem call through procfs.

I'm bringing this, since you may want to make some room for landlock
ptrace events and what others may want to do with it. Also I'm
planning to send another v2 RFC for procfs separate instances [1], the
aim is to give LSMs a security_ptrace_access_check hook path when
dealing with /proc/<pids>/ [2]  . Right now LSMs don't really have a
security path there, and the implementation does not guarantee that.
With this Yama ptrace scope or other LSMs may take advantage of it and
check the 'PTRACE_MODE_READ_FSCRED' mode for filesystem accesses.
That's why I think it would be better if the default landlock ptrace
semantics are not that wide.

Thanks!

[1] https://lkml.org/lkml/2017/3/30/670
[2] http://lxr.free-electrons.com/source/fs/proc/base.c#L719

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
  2017-04-10  6:48   ` [kernel-hardening] " Djalal Harouni
@ 2017-04-11  7:19     ` Mickaël Salaün
  0 siblings, 0 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-11  7:19 UTC (permalink / raw)
  To: Djalal Harouni
  Cc: linux-kernel, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Kees Cook, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, Linux API, LSM List, netdev

[-- Attachment #1.1: Type: text/plain, Size: 4021 bytes --]


On 10/04/2017 08:48, Djalal Harouni wrote:
> On Wed, Mar 29, 2017 at 1:46 AM, Mickaël Salaün <mic@digikod.net> wrote:
>> A landlocked process has less privileges than a non-landlocked process
>> and must then be subject to additional restrictions when manipulating
>> processes. To be allowed to use ptrace(2) and related syscalls on a
>> target process, a landlocked process must have a subset of the target
>> process' rules.
>>
>> New in v6
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: James Morris <james.l.morris@oracle.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> ---
>>  security/landlock/Makefile       |   2 +-
>>  security/landlock/hooks_ptrace.c | 126 +++++++++++++++++++++++++++++++++++++++
>>  security/landlock/hooks_ptrace.h |  11 ++++
>>  security/landlock/init.c         |   2 +
>>  4 files changed, 140 insertions(+), 1 deletion(-)
>>  create mode 100644 security/landlock/hooks_ptrace.c
>>  create mode 100644 security/landlock/hooks_ptrace.h
>>
> [...]
> 
>> +/**
>> + * landlock_ptrace_access_check - determine whether the current process may
>> + *                               access another
>> + *
>> + * @child: the process to be accessed
>> + * @mode: the mode of attachment
>> + *
>> + * If the current task has Landlock rules, then the child must have at least
>> + * the same rules.  Else denied.
>> + *
>> + * Determine whether a process may access another, returning 0 if permission
>> + * granted, -errno if denied.
>> + */
>> +static int landlock_ptrace_access_check(struct task_struct *child,
>> +               unsigned int mode)
>> +{
>> +       if (!landlocked(current))
>> +               return 0;
>> +
>> +       if (!landlocked(child))
>> +               return -EPERM;
>> +
>> +       if (landlock_task_has_subset_events(current, child))
>> +               return 0;
>> +
>> +       return -EPERM;
>> +}
>> +
> 
> Maybe you want to check the mode argument here if it is a
> PTRACE_ATTACH which may translate to read/writes ? PTRACE_READ are
> normally for reads only. Or also which creds were used if this was a
> direct syscall or a filesystem call through procfs.

The idea is to mimic the behavior of UID/GID checks, namespaces and so
on. A hierarchy of Landlock rules has a similar semantic as namespaces,
at least for now with the FS event.

> 
> I'm bringing this, since you may want to make some room for landlock
> ptrace events and what others may want to do with it. Also I'm

I don't see any no problem to add a ptrace event in the future as long
as the composition with this default rule is a logical AND to allow a
ptrace action.

It would be possible to relax this default policy for rules with a
dedicated option flag, but the current behavior is a sane one from a
security point of view.

> planning to send another v2 RFC for procfs separate instances [1], the
> aim is to give LSMs a security_ptrace_access_check hook path when
> dealing with /proc/<pids>/ [2]  . Right now LSMs don't really have a
> security path there, and the implementation does not guarantee that.
> With this Yama ptrace scope or other LSMs may take advantage of it and
> check the 'PTRACE_MODE_READ_FSCRED' mode for filesystem accesses.

Interesting, feel free to CC me.

> That's why I think it would be better if the default landlock ptrace
> semantics are not that wide.
> 
> Thanks!
> 
> [1] https://lkml.org/lkml/2017/3/30/670
> [2] http://lxr.free-electrons.com/source/fs/proc/base.c#L719
> 

If a task is allowed to ptrace/read the memory of another task with
different privileges, the tracer could also access sensitive data not
allowed otherwise.

Do you have an use case where this constraint would be an issue?

 Mickaël


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
  2017-03-28 23:46 ` [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock Mickaël Salaün
@ 2017-04-16 21:57   ` Mickaël Salaün
  2017-04-18 21:58   ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-16 21:57 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexei Starovoitov, Andy Lutomirski, Arnaldo Carvalho de Melo,
	Casey Schaufler, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Kees Cook,
	Paul Moore, Sargun Dhillon, Serge E . Hallyn, Shuah Khan,
	Tejun Heo, Thomas Graf, Will Drewry, kernel-hardening, linux-api,
	linux-security-module, netdev

[-- Attachment #1.1: Type: text/plain, Size: 6593 bytes --]


On 29/03/2017 01:46, Mickaël Salaün wrote:
> Add a new type of eBPF program used by Landlock rules.
> 
> This new BPF program type will be registered with the Landlock LSM
> initialization.
> 
> Add an initial Landlock Kconfig.
> 
> Changes since v5:
> * rename file hooks.c to init.c
> * fix spelling
> 
> Changes since v4:
> * merge a minimal (not enabled) LSM code and Kconfig in this commit
> 
> Changes since v3:
> * split commit
> * revamp the landlock_context:
>   * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
>     cross-check action with the event type
>   * replace args array with dedicated fields to ease the addition of new
>     fields
> 
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
>  include/linux/landlock.h       |  23 ++++++++
>  include/uapi/linux/bpf.h       | 105 +++++++++++++++++++++++++++++++++++
>  security/Kconfig               |   1 +
>  security/Makefile              |   2 +
>  security/landlock/Kconfig      |  18 ++++++
>  security/landlock/Makefile     |   3 +
>  security/landlock/common.h     |  25 +++++++++
>  security/landlock/init.c       | 123 +++++++++++++++++++++++++++++++++++++++++
>  tools/include/uapi/linux/bpf.h | 105 +++++++++++++++++++++++++++++++++++
>  9 files changed, 405 insertions(+)
>  create mode 100644 include/linux/landlock.h
>  create mode 100644 security/landlock/Kconfig
>  create mode 100644 security/landlock/Makefile
>  create mode 100644 security/landlock/common.h
>  create mode 100644 security/landlock/init.c
>
[...]
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 0eb71ab9b4fd..619b1f8707cc 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -114,6 +114,7 @@ enum bpf_prog_type {
>  	BPF_PROG_TYPE_LWT_IN,
>  	BPF_PROG_TYPE_LWT_OUT,
>  	BPF_PROG_TYPE_LWT_XMIT,
> +	BPF_PROG_TYPE_LANDLOCK,
>  };
>  
>  enum bpf_attach_type {
> @@ -661,4 +662,108 @@ struct xdp_md {
>  	__u32 data_end;
>  };
>  
> +/**
> + * enum landlock_subtype_event - event occurring when an action is performed on
> + * a particular kernel object
> + *
> + * An event is a policy decision point which exposes the same context type
> + * (especially the same arg[0-9] field types) for each rule execution.
> + *
> + * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value
> + * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event
> + */
> +enum landlock_subtype_event {
> +	LANDLOCK_SUBTYPE_EVENT_UNSPEC,
> +	LANDLOCK_SUBTYPE_EVENT_FS,
> +};
> +#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS
[...]
> +/**
> + * DOC: landlock_action_fs
> + *
> + * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory
> + * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which
> + *   include mount actions)
> + * - %LANDLOCK_ACTION_FS_READ: read a file or a directory
> + * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory
> + * - %LANDLOCK_ACTION_FS_GET: open or receive a file
> + * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory
> + *
> + * Each of the following actions are specific to syscall multiplexers. They
> + * fill the syscall_cmd field from &struct landlock_context with their custom
> + * command.
> + *
> + * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command
> + * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command
> + * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command
> + */
> +#define LANDLOCK_ACTION_FS_EXEC			(1ULL << 0)
> +#define LANDLOCK_ACTION_FS_WRITE		(1ULL << 1)
> +#define LANDLOCK_ACTION_FS_READ			(1ULL << 2)
> +#define LANDLOCK_ACTION_FS_NEW			(1ULL << 3)
> +#define LANDLOCK_ACTION_FS_GET			(1ULL << 4)
> +#define LANDLOCK_ACTION_FS_REMOVE		(1ULL << 5)
> +#define LANDLOCK_ACTION_FS_IOCTL		(1ULL << 6)
> +#define LANDLOCK_ACTION_FS_LOCK			(1ULL << 7)
> +#define LANDLOCK_ACTION_FS_FCNTL		(1ULL << 8)
> +#define _LANDLOCK_ACTION_FS_NB			9
> +#define _LANDLOCK_ACTION_FS_MASK		((1ULL << _LANDLOCK_ACTION_FS_NB) - 1)
> +
> +
> +/**
> + * struct landlock_context - context accessible to a Landlock rule
> + *
> + * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*)
> + * @arch: indicates system call convention as an AUDIT_ARCH_* value
> + *        as defined in <linux/audit.h>
> + * @syscall_nr: the system call number called by the current process (may be
> + *              useful to debug: find out from which syscall this request came
> + *              from)
> + * @syscall_cmd: contains the command used by a multiplexer syscall (e.g.
> + *               ioctl, fcntl, flock)
> + * @event: event type (&enum landlock_subtype_event)
> + * @arg1: event's first optional argument
> + * @arg2: event's second optional argument
> + */
> +struct landlock_context {
> +	__u64 status;
> +	__u32 arch;
> +	__u32 syscall_nr;
> +	__u32 syscall_cmd;
> +	__u32 event;
> +	__u64 arg1;
> +	__u64 arg2;
> +};

I plan to simplify and make the FS event more generic for the IOCTL,
LOCK or FCNTL actions. The action flags for the
LANDLOCK_SUBTYPE_EVENT_FS event will remain the same but the syscall_cmd
field will be removed from struct landlock_context. Instead, one of
three dedicated events will be triggered in addition to one of this
three multiplexed actions.

The aim is to trigger the LANDLOCK_SUBTYPE_EVENT_FS for all file system
events (still including IOCTL/LOCK/FCNTL actions). This should avoid a
developer/user to forget such actions. However, when this kind of action
is triggered, a LANDLOCK_SUBTYPE_EVENT_FS_{IOCTL,LOCK,FCNTL} event will
follow. This enable to simplify the struct landlock_context while still
having it as generic as possible. The difference will be that the arg2
field for one of the LANDLOCK_SUBTYPE_EVENT_FS_{IOCTL,LOCK,FCNTL} events
will contain a custom IOCTL, LOCK or FCNTL command (currently in the
syscall_cmd field) instead of a LANDLOCK_ACTION_FS_* value. The same
logic could be used to tighten other actions in the future.

The HOOK_NEW_FS_CMD(...) from [04/11]:security/landlock/hooks_fs.c will
be replaced with dedicated calls.


I also plan to remove the arch and syscall_nr fields. This will make
struct landlock_context even more simple and arch-independent.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
  2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
  2017-03-29 13:48   ` kbuild test robot
@ 2017-04-18 21:48   ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 21:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> The goal of the program subtype is to be able to have different static
> fine-grained verifications for a unique program type.
>
> The struct bpf_verifier_ops gets a new optional function:
> is_valid_subtype(). This new verifier is called at the beginning of the
> eBPF program verification to check if the (optional) program subtype is
> valid.
>
> For now, only Landlock eBPF programs are using a program subtype (see
> next commit) but this could be used by other program types in the future.
>
> Changes since v5:
> * use a prog_subtype pointer and make it future-proof
> * add subtype test
> * constify bpf_load_program()'s subtype argument
> * cleanup subtype initialization
> * rebase
>
> Changes since v4:
> * replace the "status" field with "version" (more generic)
> * replace the "access" field with "ability" (less confusing)
>
> Changes since v3:
> * remove the "origin" field
> * add an "option" field
> * cleanup comments
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Link: https://lkml.kernel.org/r/20160827205559.GA43880@ast-mbp.thefacebook.com
> ---
> [...]
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index c35ebfe6d84d..3d07b10ade5e 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -843,6 +879,26 @@ static int bpf_prog_load(union bpf_attr *attr)
>         if (err < 0)
>                 goto free_prog;
>
> +       /* copy eBPF program subtype from user space */
> +       if (attr->prog_subtype) {
> +               __u32 size;
> +
> +               size = check_user_buf((void __user *)attr->prog_subtype,
> +                                     attr->prog_subtype_size,
> +                                     sizeof(prog->subtype));
> +               if (size < 0) {
> +                       err = size;
> +                       goto free_prog;
> +               }
> +               /* prog->subtype is __GFP_ZERO */
> +               if (copy_from_user(&prog->subtype,
> +                                  u64_to_user_ptr(attr->prog_subtype), size)
> +                                  != 0)

It might be worth adding a comment here about how the ToCToU of the
check-then-copy doesn't matter in this case, since it's just a
future-proofing of bits, etc.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
  2017-03-28 23:46 ` [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock Mickaël Salaün
  2017-04-16 21:57   ` Mickaël Salaün
@ 2017-04-18 21:58   ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 21:58 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> Add a new type of eBPF program used by Landlock rules.
>
> This new BPF program type will be registered with the Landlock LSM
> initialization.
>
> Add an initial Landlock Kconfig.
>
> Changes since v5:
> * rename file hooks.c to init.c
> * fix spelling
>
> Changes since v4:
> * merge a minimal (not enabled) LSM code and Kconfig in this commit
>
> Changes since v3:
> * split commit
> * revamp the landlock_context:
>   * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
>     cross-check action with the event type
>   * replace args array with dedicated fields to ease the addition of new
>     fields
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
> [...]
> +static inline bool bpf_landlock_is_valid_subtype(
> +               union bpf_prog_subtype *prog_subtype)
> +{
> +       if (WARN_ON(!prog_subtype))
> +               return false;
> +
> +       switch (prog_subtype->landlock_rule.event) {
> +       case LANDLOCK_SUBTYPE_EVENT_FS:
> +               break;
> +       case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
> +       default:
> +               return false;
> +       }
> +
> +       if (!prog_subtype->landlock_rule.version ||
> +                       prog_subtype->landlock_rule.version > LANDLOCK_VERSION)
> +               return false;
> +       if (!prog_subtype->landlock_rule.event ||
> +                       prog_subtype->landlock_rule.event > _LANDLOCK_SUBTYPE_EVENT_LAST)
> +               return false;
> +       if (prog_subtype->landlock_rule.ability & ~_LANDLOCK_SUBTYPE_ABILITY_MASK)
> +               return false;
> +       if (prog_subtype->landlock_rule.option & ~_LANDLOCK_SUBTYPE_OPTION_MASK)
> +               return false;
> +
> +       /* check ability flags */
> +       if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_WRITE &&
> +                       !capable(CAP_SYS_ADMIN))
> +               return false;
> +       if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_DEBUG &&
> +                       !capable(CAP_SYS_ADMIN))
> +               return false;
> +
> +       return true;
> +}

I would add more comments for the rule and ability tests just to help
people read this.

> +
> +static inline const struct bpf_func_proto *bpf_landlock_func_proto(
> +               enum bpf_func_id func_id, union bpf_prog_subtype *prog_subtype)
> +{
> +       bool event_fs = (prog_subtype->landlock_rule.event ==
> +                       LANDLOCK_SUBTYPE_EVENT_FS);
> +       bool ability_write = !!(prog_subtype->landlock_rule.ability &
> +                       LANDLOCK_SUBTYPE_ABILITY_WRITE);
> +       bool ability_debug = !!(prog_subtype->landlock_rule.ability &
> +                       LANDLOCK_SUBTYPE_ABILITY_DEBUG);
> +
> +       switch (func_id) {
> +       case BPF_FUNC_map_lookup_elem:
> +               return &bpf_map_lookup_elem_proto;
> +
> +       /* ability_write */
> +       case BPF_FUNC_map_delete_elem:
> +               if (ability_write)
> +                       return &bpf_map_delete_elem_proto;
> +               return NULL;
> +       case BPF_FUNC_map_update_elem:
> +               if (ability_write)
> +                       return &bpf_map_update_elem_proto;
> +               return NULL;
> +
> +       /* ability_debug */
> +       case BPF_FUNC_get_current_comm:
> +               if (ability_debug)
> +                       return &bpf_get_current_comm_proto;
> +               return NULL;
> +       case BPF_FUNC_get_current_pid_tgid:
> +               if (ability_debug)
> +                       return &bpf_get_current_pid_tgid_proto;
> +               return NULL;
> +       case BPF_FUNC_get_current_uid_gid:
> +               if (ability_debug)
> +                       return &bpf_get_current_uid_gid_proto;
> +               return NULL;
> +       case BPF_FUNC_trace_printk:
> +               if (ability_debug)
> +                       return bpf_get_trace_printk_proto();
> +               return NULL;
> +
> +       default:
> +               return NULL;
> +       }
> +}

I find this switch statement mixed with the "if (ability...)" kind of
hard to read and a bit fragile. I think it'd be better written as:

switch (func_id) {
case BPF_FUNC_map_lookup_elem:
   return ...
}

if (ability_write) {
    switch (func_id) {
        ...
    }
}

if (ability_debug) {
    switch (func_id) {
        ...
    }
}

return NULL;

Then it's self-documenting and it's harder to add a case without the
desired ability check...

> +static const struct bpf_verifier_ops bpf_landlock_ops = {
> +       .get_func_proto = bpf_landlock_func_proto,
> +       .is_valid_access = bpf_landlock_is_valid_access,
> +       .is_valid_subtype = bpf_landlock_is_valid_subtype,
> +};
> +
> +static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
> +       .ops = &bpf_landlock_ops,
> +       .type = BPF_PROG_TYPE_LANDLOCK,
> +};

Yay const and ro_after_init! :)

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-03-28 23:46 ` [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem Mickaël Salaün
  2017-03-29 15:18   ` kbuild test robot
@ 2017-04-18 22:17   ` Kees Cook
  2017-04-18 22:44     ` Mickaël Salaün
  1 sibling, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 22:17 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> Handle 33 filesystem-related LSM hooks for the Landlock filesystem
> event: LANDLOCK_SUBTYPE_EVENT_FS.
>
> A Landlock event wrap LSM hooks for similar kernel object types (e.g.
> struct file, struct path...). Multiple LSM hooks can trigger the same
> Landlock event.
>
> Landlock handle nine coarse-grained actions: read, write, execute, new,
> get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
> access control in a way that can be extended in the future.
>
> The Landlock LSM hook registration is done after other LSM to only run
> actions from user-space, via eBPF programs, if the access was granted by
> major (privileged) LSMs.
>
> Changes since v5:
> * split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
> * add more documentation
> * cosmetic fixes
>
> Changes since v4:
> * add LSM hook abstraction called Landlock event
>   * use the compiler type checking to verify hooks use by an event
>   * handle all filesystem related LSM hooks (e.g. file_permission,
>     mmap_file, sb_mount...)
> * register BPF programs for Landlock just after LSM hooks registration
> * move hooks registration after other LSMs
> * add failsafes to check if a hook is not used by the kernel
> * allow partial raw value access form the context (needed for programs
>   generated by LLVM)
>
> Changes since v3:
> * split commit
> * add hooks dealing with struct inode and struct path pointers:
>   inode_permission and inode_getattr
> * add abstraction over eBPF helper arguments thanks to wrapping structs
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
>  include/linux/lsm_hooks.h    |   5 +
>  security/landlock/Makefile   |   4 +-
>  security/landlock/hooks.c    | 115 +++++++++
>  security/landlock/hooks.h    | 177 ++++++++++++++
>  security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
>  security/landlock/hooks_fs.h |  19 ++
>  security/landlock/init.c     |  13 +
>  security/security.c          |   7 +-
>  8 files changed, 901 insertions(+), 2 deletions(-)
>  create mode 100644 security/landlock/hooks.c
>  create mode 100644 security/landlock/hooks.h
>  create mode 100644 security/landlock/hooks_fs.c
>  create mode 100644 security/landlock/hooks_fs.h
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index e29d4c62a3c8..884289166a0e 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1920,5 +1920,10 @@ void __init loadpin_add_hooks(void);
>  #else
>  static inline void loadpin_add_hooks(void) { };
>  #endif
> +#ifdef CONFIG_SECURITY_LANDLOCK
> +extern void __init landlock_add_hooks(void);
> +#else
> +static inline void __init landlock_add_hooks(void) { }
> +#endif /* CONFIG_SECURITY_LANDLOCK */
>
>  #endif /* ! __LINUX_LSM_HOOKS_H */
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index 7205f9a7a2ee..c0db504a6335 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -1,3 +1,5 @@
> +ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function

Why is this needed? If it can't be avoided, a comment should exist
here explaining why.

> [...]
> @@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>         .ops = &bpf_landlock_ops,
>         .type = BPF_PROG_TYPE_LANDLOCK,
>  };
> +
> +void __init landlock_add_hooks(void)
> +{
> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
> +       landlock_add_hooks_fs();
> +       security_add_hooks(NULL, 0, "landlock");
> +       bpf_register_prog_type(&bpf_landlock_type);

I'm confused by the separation of hook registration here. The call to
security_add_hooks is with count=0 is especially weird. Why isn't this
just a single call with security_add_hooks(landlock_hooks,
ARRAY_SIZE(landlock_hooks), "landlock")?

> +}
> diff --git a/security/security.c b/security/security.c
> index d0e07f269b2d..a3e9f4625991 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -64,10 +64,15 @@ int __init security_init(void)
>         loadpin_add_hooks();
>
>         /*
> -        * Load all the remaining security modules.
> +        * Load all remaining privileged security modules.
>          */
>         do_security_initcalls();
>
> +       /*
> +        * Load potentially-unprivileged security modules at the end.
> +        */
> +       landlock_add_hooks();

Oh, is this to make it last in the list? Is there a reason it has to be last?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-03-28 23:46 ` [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp() Mickaël Salaün
@ 2017-04-18 22:23   ` Kees Cook
  2017-04-18 22:47     ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 22:23 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> The semantic is unchanged. This will be useful for the Landlock
> integration with seccomp (next commit).
>
> 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>
> ---
>  include/linux/seccomp.h |  4 ++--
>  kernel/fork.c           |  2 +-
>  kernel/seccomp.c        | 18 +++++++++++++-----
>  3 files changed, 16 insertions(+), 8 deletions(-)
>
> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
> index ecc296c137cd..e25aee2cdfc0 100644
> --- a/include/linux/seccomp.h
> +++ b/include/linux/seccomp.h
> @@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s)
>  #endif /* CONFIG_SECCOMP */
>
>  #ifdef CONFIG_SECCOMP_FILTER
> -extern void put_seccomp_filter(struct task_struct *tsk);
> +extern void put_seccomp(struct task_struct *tsk);
>  extern void get_seccomp_filter(struct task_struct *tsk);
>  #else  /* CONFIG_SECCOMP_FILTER */
> -static inline void put_seccomp_filter(struct task_struct *tsk)
> +static inline void put_seccomp(struct task_struct *tsk)
>  {
>         return;
>  }
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 6c463c80e93d..a27d8e67ce33 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -363,7 +363,7 @@ void free_task(struct task_struct *tsk)
>  #endif
>         rt_mutex_debug_task_free(tsk);
>         ftrace_graph_exit_task(tsk);
> -       put_seccomp_filter(tsk);
> +       put_seccomp(tsk);
>         arch_release_task_struct(tsk);
>         if (tsk->flags & PF_KTHREAD)
>                 free_kthread_struct(tsk);
> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
> index 65f61077ad50..326f79e32127 100644
> --- a/kernel/seccomp.c
> +++ b/kernel/seccomp.c
> @@ -64,6 +64,8 @@ struct seccomp_filter {
>  /* Limit any path through the tree to 256KB worth of instructions. */
>  #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
>
> +static void put_seccomp_filter(struct seccomp_filter *filter);

Can this be reorganized easily to avoid a forward-declaration?

> +
>  /*
>   * Endianness is explicitly ignored and left for BPF program authors to manage
>   * as per the specific architecture.
> @@ -314,7 +316,7 @@ static inline void seccomp_sync_threads(void)
>                  * current's path will hold a reference.  (This also
>                  * allows a put before the assignment.)
>                  */
> -               put_seccomp_filter(thread);
> +               put_seccomp_filter(thread->seccomp.filter);
>                 smp_store_release(&thread->seccomp.filter,
>                                   caller->seccomp.filter);
>
> @@ -476,10 +478,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter)
>         }
>  }
>
> -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
> -void put_seccomp_filter(struct task_struct *tsk)
> +/* put_seccomp_filter - decrements the ref count of a filter */
> +static void put_seccomp_filter(struct seccomp_filter *filter)
>  {
> -       struct seccomp_filter *orig = tsk->seccomp.filter;
> +       struct seccomp_filter *orig = filter;
> +
>         /* Clean up single-reference branches iteratively. */
>         while (orig && atomic_dec_and_test(&orig->usage)) {
>                 struct seccomp_filter *freeme = orig;
> @@ -488,6 +491,11 @@ void put_seccomp_filter(struct task_struct *tsk)
>         }
>  }
>
> +void put_seccomp(struct task_struct *tsk)
> +{
> +       put_seccomp_filter(tsk->seccomp.filter);
> +}
> +
>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
>  {
>         memset(info, 0, sizeof(*info));
> @@ -914,7 +922,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
>         if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
>                 ret = -EFAULT;
>
> -       put_seccomp_filter(task);
> +       put_seccomp_filter(task->seccomp.filter);
>         return ret;

I don't like that the arguments to get_seccomp_filter() and
put_seccomp_filter() are now different. I think they should match for
readability.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-18 22:17   ` Kees Cook
@ 2017-04-18 22:44     ` Mickaël Salaün
  2017-04-18 23:16       ` Casey Schaufler
  2017-04-18 23:39       ` Kees Cook
  0 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-18 22:44 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 6054 bytes --]


On 19/04/2017 00:17, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> Handle 33 filesystem-related LSM hooks for the Landlock filesystem
>> event: LANDLOCK_SUBTYPE_EVENT_FS.
>>
>> A Landlock event wrap LSM hooks for similar kernel object types (e.g.
>> struct file, struct path...). Multiple LSM hooks can trigger the same
>> Landlock event.
>>
>> Landlock handle nine coarse-grained actions: read, write, execute, new,
>> get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
>> access control in a way that can be extended in the future.
>>
>> The Landlock LSM hook registration is done after other LSM to only run
>> actions from user-space, via eBPF programs, if the access was granted by
>> major (privileged) LSMs.
>>
>> Changes since v5:
>> * split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
>> * add more documentation
>> * cosmetic fixes
>>
>> Changes since v4:
>> * add LSM hook abstraction called Landlock event
>>   * use the compiler type checking to verify hooks use by an event
>>   * handle all filesystem related LSM hooks (e.g. file_permission,
>>     mmap_file, sb_mount...)
>> * register BPF programs for Landlock just after LSM hooks registration
>> * move hooks registration after other LSMs
>> * add failsafes to check if a hook is not used by the kernel
>> * allow partial raw value access form the context (needed for programs
>>   generated by LLVM)
>>
>> Changes since v3:
>> * split commit
>> * add hooks dealing with struct inode and struct path pointers:
>>   inode_permission and inode_getattr
>> * add abstraction over eBPF helper arguments thanks to wrapping structs
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: James Morris <james.l.morris@oracle.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> ---
>>  include/linux/lsm_hooks.h    |   5 +
>>  security/landlock/Makefile   |   4 +-
>>  security/landlock/hooks.c    | 115 +++++++++
>>  security/landlock/hooks.h    | 177 ++++++++++++++
>>  security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
>>  security/landlock/hooks_fs.h |  19 ++
>>  security/landlock/init.c     |  13 +
>>  security/security.c          |   7 +-
>>  8 files changed, 901 insertions(+), 2 deletions(-)
>>  create mode 100644 security/landlock/hooks.c
>>  create mode 100644 security/landlock/hooks.h
>>  create mode 100644 security/landlock/hooks_fs.c
>>  create mode 100644 security/landlock/hooks_fs.h
>>
>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>> index e29d4c62a3c8..884289166a0e 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -1920,5 +1920,10 @@ void __init loadpin_add_hooks(void);
>>  #else
>>  static inline void loadpin_add_hooks(void) { };
>>  #endif
>> +#ifdef CONFIG_SECURITY_LANDLOCK
>> +extern void __init landlock_add_hooks(void);
>> +#else
>> +static inline void __init landlock_add_hooks(void) { }
>> +#endif /* CONFIG_SECURITY_LANDLOCK */
>>
>>  #endif /* ! __LINUX_LSM_HOOKS_H */
>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>> index 7205f9a7a2ee..c0db504a6335 100644
>> --- a/security/landlock/Makefile
>> +++ b/security/landlock/Makefile
>> @@ -1,3 +1,5 @@
>> +ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
> 
> Why is this needed? If it can't be avoided, a comment should exist
> here explaining why.

This is useful to catch defined but unused hooks: error out if a
HOOK_NEW_FS(foo) is not used with a HOOK_INIT_FS(foo) in the struct
security_hook_list landlock_hooks.

> 
>> [...]
>> @@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>>         .ops = &bpf_landlock_ops,
>>         .type = BPF_PROG_TYPE_LANDLOCK,
>>  };
>> +
>> +void __init landlock_add_hooks(void)
>> +{
>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>> +       landlock_add_hooks_fs();
>> +       security_add_hooks(NULL, 0, "landlock");
>> +       bpf_register_prog_type(&bpf_landlock_type);
> 
> I'm confused by the separation of hook registration here. The call to
> security_add_hooks is with count=0 is especially weird. Why isn't this
> just a single call with security_add_hooks(landlock_hooks,
> ARRAY_SIZE(landlock_hooks), "landlock")?

Yes, this is ugly with the new security_add_hooks() with three arguments
but I wanted to split the hooks definition in multiple files.

The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
is not exported. Unfortunately, calling multiple security_add_hooks()
with the same LSM name would register multiple names for the same LSM…
Is it OK if I modify this function to not add duplicated entries?


> 
>> +}
>> diff --git a/security/security.c b/security/security.c
>> index d0e07f269b2d..a3e9f4625991 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -64,10 +64,15 @@ int __init security_init(void)
>>         loadpin_add_hooks();
>>
>>         /*
>> -        * Load all the remaining security modules.
>> +        * Load all remaining privileged security modules.
>>          */
>>         do_security_initcalls();
>>
>> +       /*
>> +        * Load potentially-unprivileged security modules at the end.
>> +        */
>> +       landlock_add_hooks();
> 
> Oh, is this to make it last in the list? Is there a reason it has to be last?

Right, this is the intend. I'm not sure it is the only way to register
hooks, though.

For an unprivileged access-control, we don't want to give the ability to
any process to do some checks, through an eBPF program, on kernel
objects (e.g. files) if they should not be accessible (because of a
following LSM hook check).

 Mickaël


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-04-18 22:23   ` Kees Cook
@ 2017-04-18 22:47     ` Mickaël Salaün
  2017-04-19 22:18       ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-18 22:47 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 4536 bytes --]


On 19/04/2017 00:23, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> The semantic is unchanged. This will be useful for the Landlock
>> integration with seccomp (next commit).
>>
>> 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>
>> ---
>>  include/linux/seccomp.h |  4 ++--
>>  kernel/fork.c           |  2 +-
>>  kernel/seccomp.c        | 18 +++++++++++++-----
>>  3 files changed, 16 insertions(+), 8 deletions(-)
>>
>> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
>> index ecc296c137cd..e25aee2cdfc0 100644
>> --- a/include/linux/seccomp.h
>> +++ b/include/linux/seccomp.h
>> @@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s)
>>  #endif /* CONFIG_SECCOMP */
>>
>>  #ifdef CONFIG_SECCOMP_FILTER
>> -extern void put_seccomp_filter(struct task_struct *tsk);
>> +extern void put_seccomp(struct task_struct *tsk);
>>  extern void get_seccomp_filter(struct task_struct *tsk);
>>  #else  /* CONFIG_SECCOMP_FILTER */
>> -static inline void put_seccomp_filter(struct task_struct *tsk)
>> +static inline void put_seccomp(struct task_struct *tsk)
>>  {
>>         return;
>>  }
>> diff --git a/kernel/fork.c b/kernel/fork.c
>> index 6c463c80e93d..a27d8e67ce33 100644
>> --- a/kernel/fork.c
>> +++ b/kernel/fork.c
>> @@ -363,7 +363,7 @@ void free_task(struct task_struct *tsk)
>>  #endif
>>         rt_mutex_debug_task_free(tsk);
>>         ftrace_graph_exit_task(tsk);
>> -       put_seccomp_filter(tsk);
>> +       put_seccomp(tsk);
>>         arch_release_task_struct(tsk);
>>         if (tsk->flags & PF_KTHREAD)
>>                 free_kthread_struct(tsk);
>> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
>> index 65f61077ad50..326f79e32127 100644
>> --- a/kernel/seccomp.c
>> +++ b/kernel/seccomp.c
>> @@ -64,6 +64,8 @@ struct seccomp_filter {
>>  /* Limit any path through the tree to 256KB worth of instructions. */
>>  #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
>>
>> +static void put_seccomp_filter(struct seccomp_filter *filter);
> 
> Can this be reorganized easily to avoid a forward-declaration?

I didn't want to move too much code but I will.

> 
>> +
>>  /*
>>   * Endianness is explicitly ignored and left for BPF program authors to manage
>>   * as per the specific architecture.
>> @@ -314,7 +316,7 @@ static inline void seccomp_sync_threads(void)
>>                  * current's path will hold a reference.  (This also
>>                  * allows a put before the assignment.)
>>                  */
>> -               put_seccomp_filter(thread);
>> +               put_seccomp_filter(thread->seccomp.filter);
>>                 smp_store_release(&thread->seccomp.filter,
>>                                   caller->seccomp.filter);
>>
>> @@ -476,10 +478,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter)
>>         }
>>  }
>>
>> -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
>> -void put_seccomp_filter(struct task_struct *tsk)
>> +/* put_seccomp_filter - decrements the ref count of a filter */
>> +static void put_seccomp_filter(struct seccomp_filter *filter)
>>  {
>> -       struct seccomp_filter *orig = tsk->seccomp.filter;
>> +       struct seccomp_filter *orig = filter;
>> +
>>         /* Clean up single-reference branches iteratively. */
>>         while (orig && atomic_dec_and_test(&orig->usage)) {
>>                 struct seccomp_filter *freeme = orig;
>> @@ -488,6 +491,11 @@ void put_seccomp_filter(struct task_struct *tsk)
>>         }
>>  }
>>
>> +void put_seccomp(struct task_struct *tsk)
>> +{
>> +       put_seccomp_filter(tsk->seccomp.filter);
>> +}
>> +
>>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
>>  {
>>         memset(info, 0, sizeof(*info));
>> @@ -914,7 +922,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
>>         if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
>>                 ret = -EFAULT;
>>
>> -       put_seccomp_filter(task);
>> +       put_seccomp_filter(task->seccomp.filter);
>>         return ret;
> 
> I don't like that the arguments to get_seccomp_filter() and
> put_seccomp_filter() are now different. I think they should match for
> readability.

OK, I can do that.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-28 23:46 ` [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy Mickaël Salaün
  2017-03-29 10:35   ` [kernel-hardening] " Djalal Harouni
@ 2017-04-18 22:53   ` Kees Cook
  2017-04-18 23:24     ` Mickaël Salaün
  1 sibling, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 22:53 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development, Andrew Morton

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> The seccomp(2) syscall can be used by a task to apply a Landlock rule to
> itself. As a seccomp filter, a Landlock rule is enforced for the current
> task and all its future children. A rule is immutable and a task can
> only add new restricting rules to itself, forming a chain of rules.
>
> A Landlock rule is tied to a Landlock event. If the use of a kernel
> object is allowed by the other Linux security mechanisms (e.g. DAC,
> capabilities, other LSM), then a Landlock event related to this kind of
> object is triggered. The chain of rules for this event is then
> evaluated. Each rule return a 32-bit value which can deny the use of a
> kernel object with a non-zero value. If every rules of the chain return
> zero, then the use of the object is allowed.
>
> Changes since v5:
> * remove struct landlock_node and use a similar inheritance mechanisme
>   as seccomp-bpf (requested by Andy Lutomirski)
> * rename SECCOMP_ADD_LANDLOCK_RULE to SECCOMP_APPEND_LANDLOCK_RULE
> * rename file manager.c to providers.c
> * add comments
> * typo and cosmetic fixes
>
> Changes since v4:
> * merge manager and seccomp patches
> * return -EFAULT in seccomp(2) when user_bpf_fd is null to easely check
>   if Landlock is supported
> * only allow a process with the global CAP_SYS_ADMIN to use Landlock
>   (will be lifted in the future)
> * add an early check to exit as soon as possible if the current process
>   does not have Landlock rules
>
> Changes since v3:
> * remove the hard link with seccomp (suggested by Andy Lutomirski and
>   Kees Cook):
>   * remove the cookie which could imply multiple evaluation of Landlock
>     rules
>   * remove the origin field in struct landlock_data
> * remove documentation fix (merged upstream)
> * rename the new seccomp command to SECCOMP_ADD_LANDLOCK_RULE
> * internal renaming
> * split commit
> * new design to be able to inherit on the fly the parent rules
>
> Changes since v2:
> * Landlock programs can now be run without seccomp filter but for any
>   syscall (from the process) or interruption
> * move Landlock related functions and structs into security/landlock/*
>   (to manage cgroups as well)
> * fix seccomp filter handling: run Landlock programs for each of their
>   legitimate seccomp filter
> * properly clean up all seccomp results
> * cosmetic changes to ease the understanding
> * fix some ifdef
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Cc: Will Drewry <wad@chromium.org>
> Link: https://lkml.kernel.org/r/c10a503d-5e35-7785-2f3d-25ed8dd63fab@digikod.net
> ---
>  include/linux/landlock.h      |  36 +++++++
>  include/linux/seccomp.h       |   8 ++
>  include/uapi/linux/seccomp.h  |   1 +
>  kernel/fork.c                 |  14 ++-
>  kernel/seccomp.c              |   8 ++
>  security/landlock/Makefile    |   2 +-
>  security/landlock/hooks.c     |  37 +++++++
>  security/landlock/hooks.h     |   5 +
>  security/landlock/init.c      |   3 +-
>  security/landlock/providers.c | 232 ++++++++++++++++++++++++++++++++++++++++++
>  10 files changed, 342 insertions(+), 4 deletions(-)
>  create mode 100644 security/landlock/providers.c
>
> diff --git a/include/linux/landlock.h b/include/linux/landlock.h
> index 53013dc374fe..c40ee78e86e0 100644
> --- a/include/linux/landlock.h
> +++ b/include/linux/landlock.h
> @@ -12,6 +12,9 @@
>  #define _LINUX_LANDLOCK_H
>  #ifdef CONFIG_SECURITY_LANDLOCK
>
> +#include <linux/bpf.h> /* _LANDLOCK_SUBTYPE_EVENT_LAST */
> +#include <linux/types.h> /* atomic_t */
> +
>  /*
>   * This is not intended for the UAPI headers. Each userland software should use
>   * a static minimal version for the required features as explained in the
> @@ -19,5 +22,38 @@
>   */
>  #define LANDLOCK_VERSION 1
>
> +struct landlock_rule {
> +       atomic_t usage;

This should be refcount_t. (And I should convert seccomp to use
refcount_t too!) :)

> +       struct landlock_rule *prev;
> +       struct bpf_prog *prog;
> +};
> +
> +/**
> + * struct landlock_events - Landlock event rules enforced on a thread
> + *
> + * This is used for low performance impact when forking a process. Instead of
> + * copying the full array and incrementing the usage of each entries, only
> + * create a pointer to &struct landlock_events and increments its usage. When
> + * appending a new rule, if &struct landlock_events is shared with other tasks,
> + * then duplicate it and append the rule to this new &struct landlock_events.
> + *
> + * @usage: reference count to manage the object lifetime. When a thread need to
> + *         add Landlock rules and if @usage is greater than 1, then the thread
> + *         must duplicate &struct landlock_events to not change the children's
> + *         rules as well.
> + * @rules: array of non-NULL &struct landlock_rule pointers
> + */
> +struct landlock_events {
> +       atomic_t usage;
> +       struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST];
> +};
> +
> +void put_landlock_events(struct landlock_events *events);
> +
> +#ifdef CONFIG_SECCOMP_FILTER

Isn't CONFIG_SECCOMP_FILTER already required for landlock?

> +int landlock_seccomp_append_prog(unsigned int flags,
> +               const char __user *user_bpf_fd);
> +#endif /* CONFIG_SECCOMP_FILTER */
> +
>  #endif /* CONFIG_SECURITY_LANDLOCK */
>  #endif /* _LINUX_LANDLOCK_H */
> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
> index e25aee2cdfc0..9a38de3c0e72 100644
> --- a/include/linux/seccomp.h
> +++ b/include/linux/seccomp.h
> @@ -10,6 +10,10 @@
>  #include <linux/thread_info.h>
>  #include <asm/seccomp.h>
>
> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
> +struct landlock_events;
> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */

Testing LANDLOCK should be sufficient, since it requires ..._FILTER.

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

Same.

>  };
>
>  #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
> diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
> index 0f238a43ff1e..74891cf60ca6 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_APPEND_LANDLOCK_RULE   2
>
>  /* Valid flags for SECCOMP_SET_MODE_FILTER */
>  #define SECCOMP_FILTER_FLAG_TSYNC      1
> diff --git a/kernel/fork.c b/kernel/fork.c
> index a27d8e67ce33..14c09486c565 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -47,6 +47,7 @@
>  #include <linux/security.h>
>  #include <linux/hugetlb.h>
>  #include <linux/seccomp.h>
> +#include <linux/landlock.h>
>  #include <linux/swap.h>
>  #include <linux/syscalls.h>
>  #include <linux/jiffies.h>
> @@ -528,7 +529,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
>          * the usage counts on the error path calling free_task.
>          */
>         tsk->seccomp.filter = NULL;
> -#endif
> +#ifdef CONFIG_SECURITY_LANDLOCK
> +       tsk->seccomp.landlock_events = NULL;
> +#endif /* CONFIG_SECURITY_LANDLOCK */
> +#endif /* CONFIG_SECCOMP */
>
>         setup_thread_stack(tsk, orig);
>         clear_user_return_notifier(tsk);
> @@ -1405,7 +1409,13 @@ 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;
> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
> +       p->seccomp.landlock_events = current->seccomp.landlock_events;
> +       if (p->seccomp.landlock_events)
> +               atomic_inc(&p->seccomp.landlock_events->usage);
> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */

Hrm. So, this needs config cleanup as above. Also, I'm going to need
some help understanding the usage tracking on landlock_events (which
should use a get/put rather than open-coding the _inc). I don't see
why individual assignments are needed here. The only thing that
matters is the usage bump. I would have expected no changes at all in
this code, actually. The filter and the events share the same usage
don't they?

>         /*
>          * Explicitly enable no_new_privs here in case it got set
> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
> index 326f79e32127..d122829e6da1 100644
> --- a/kernel/seccomp.c
> +++ b/kernel/seccomp.c
> @@ -34,6 +34,7 @@
>  #include <linux/security.h>
>  #include <linux/tracehook.h>
>  #include <linux/uaccess.h>
> +#include <linux/landlock.h>
>
>  /**
>   * struct seccomp_filter - container for seccomp BPF programs
> @@ -494,6 +495,9 @@ 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_events(tsk->seccomp.landlock_events);
> +#endif /* CONFIG_SECURITY_LANDLOCK */

put_landlock_events() should be defined in a header file to be a
static inline no-op if ..._LANDLOCK isn't defined. That way we can
avoid all the #ifdef stuff here. Similarly, I think all of these
should take just task_struct so that there isn't an exposed structure
name which lets you avoid the #ifdef too.

>  }
>
>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
> @@ -813,6 +817,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);
> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
> +       case SECCOMP_APPEND_LANDLOCK_RULE:
> +               return landlock_seccomp_append_prog(flags, uargs);
> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>         default:
>                 return -EINVAL;
>         }
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index c0db504a6335..da8ba8b5183e 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
>
>  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>
> -landlock-y := init.o hooks.o hooks_fs.o
> +landlock-y := init.o providers.o hooks.o hooks_fs.o
> diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
> index eaee8162ff70..4fa7d0b38d41 100644
> --- a/security/landlock/hooks.c
> +++ b/security/landlock/hooks.c
> @@ -95,6 +95,38 @@ bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
>         return true;
>  }
>
> +/**
> + * landlock_event_deny - run Landlock rules tied to an event
> + *
> + * @event_idx: event index in the rules array
> + * @ctx: non-NULL eBPF context
> + * @events: Landlock events pointer
> + *
> + * Return true if at least one rule deny the event.
> + */
> +static bool landlock_event_deny(u32 event_idx, const struct landlock_context *ctx,
> +               struct landlock_events *events)
> +{
> +       struct landlock_rule *rule;
> +
> +       if (!events)
> +               return false;
> +
> +       for (rule = events->rules[event_idx]; rule; rule = rule->prev) {
> +               u32 ret;
> +
> +               if (WARN_ON(!rule->prog))
> +                       continue;
> +               rcu_read_lock();
> +               ret = BPF_PROG_RUN(rule->prog, (void *)ctx);
> +               rcu_read_unlock();
> +               /* deny access if a program returns a value different than 0 */
> +               if (ret)
> +                       return true;
> +       }
> +       return false;
> +}
> +
>  int landlock_decide(enum landlock_subtype_event event,
>                 __u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook)
>  {
> @@ -111,5 +143,10 @@ int landlock_decide(enum landlock_subtype_event event,
>                 .arg2 = ctx_values[1],
>         };
>
> +#ifdef CONFIG_SECCOMP_FILTER
> +       deny = landlock_event_deny(event_idx, &ctx,
> +                       current->seccomp.landlock_events);
> +#endif /* CONFIG_SECCOMP_FILTER */

Isn't ..._FILTER required?

> +
>         return deny ? -EPERM : 0;
>  }
> diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
> index 2e180f6ed86b..dd0486a4c284 100644
> --- a/security/landlock/hooks.h
> +++ b/security/landlock/hooks.h
> @@ -12,6 +12,7 @@
>  #include <linux/bpf.h> /* enum bpf_access_type */
>  #include <linux/lsm_hooks.h>
>  #include <linux/sched.h> /* struct task_struct */
> +#include <linux/seccomp.h>
>
>  /* separators */
>  #define SEP_COMMA() ,
> @@ -163,7 +164,11 @@ WRAP_TYPE_RAW_C;
>
>  static inline bool landlocked(const struct task_struct *task)
>  {
> +#ifdef CONFIG_SECCOMP_FILTER
> +       return !!(task->seccomp.landlock_events);
> +#else
>         return false;
> +#endif /* CONFIG_SECCOMP_FILTER */
>  }
>
>  __init void landlock_register_hooks(struct security_hook_list *hooks, int count);
> diff --git a/security/landlock/init.c b/security/landlock/init.c
> index 1c2750e12dfa..ef8a3da69860 100644
> --- a/security/landlock/init.c
> +++ b/security/landlock/init.c
> @@ -135,7 +135,8 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>
>  void __init landlock_add_hooks(void)
>  {
> -       pr_info("landlock: Version %u", LANDLOCK_VERSION);
> +       pr_info("landlock: Version %u, ready to sandbox with %s\n",
> +                       LANDLOCK_VERSION, "seccomp");
>         landlock_add_hooks_fs();
>         security_add_hooks(NULL, 0, "landlock");
>         bpf_register_prog_type(&bpf_landlock_type);
> diff --git a/security/landlock/providers.c b/security/landlock/providers.c
> new file mode 100644
> index 000000000000..6d867a39c947
> --- /dev/null
> +++ b/security/landlock/providers.c
> @@ -0,0 +1,232 @@
> +/*
> + * Landlock LSM - seccomp provider
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <asm/page.h> /* PAGE_SIZE */
> +#include <linux/atomic.h> /* atomic_*(), smp_store_release() */
> +#include <linux/bpf.h> /* bpf_prog_put() */
> +#include <linux/filter.h> /* struct bpf_prog */
> +#include <linux/kernel.h> /* round_up() */
> +#include <linux/landlock.h>
> +#include <linux/sched.h> /* current_cred(), task_no_new_privs() */
> +#include <linux/security.h> /* security_capable_noaudit() */
> +#include <linux/slab.h> /* alloc(), kfree() */
> +#include <linux/types.h> /* atomic_t */
> +#include <linux/uaccess.h> /* copy_from_user() */
> +
> +#include "common.h"
> +
> +static void put_landlock_rule(struct landlock_rule *rule)
> +{
> +       struct landlock_rule *orig = rule;
> +
> +       /* clean up single-reference branches iteratively */
> +       while (orig && atomic_dec_and_test(&orig->usage)) {
> +               struct landlock_rule *freeme = orig;
> +
> +               bpf_prog_put(orig->prog);
> +               orig = orig->prev;
> +               kfree(freeme);
> +       }
> +}
> +
> +void put_landlock_events(struct landlock_events *events)
> +{
> +       if (events && atomic_dec_and_test(&events->usage)) {
> +               size_t i;
> +
> +               for (i = 0; i < ARRAY_SIZE(events->rules); i++)
> +                       /* XXX: Do we need to use lockless_dereference() here? */
> +                       put_landlock_rule(events->rules[i]);
> +               kfree(events);
> +       }
> +}
> +
> +static struct landlock_events *new_landlock_events(void)
> +{
> +       struct landlock_events *ret;
> +
> +       /* array filled with NULL values */
> +       ret = kzalloc(sizeof(*ret), GFP_KERNEL);
> +       if (!ret)
> +               return ERR_PTR(-ENOMEM);
> +       atomic_set(&ret->usage, 1);
> +       return ret;
> +}
> +
> +static void add_landlock_rule(struct landlock_events *events,
> +               struct landlock_rule *rule)
> +{
> +       /* subtype.landlock_rule.event > 0 for loaded programs */
> +       u32 event_idx = get_index(rule->prog->subtype.landlock_rule.event);
> +
> +       rule->prev = events->rules[event_idx];
> +       WARN_ON(atomic_read(&rule->usage));
> +       atomic_set(&rule->usage, 1);
> +       /* do not increment the previous rule usage */
> +       smp_store_release(&events->rules[event_idx], rule);
> +}
> +
> +/* limit Landlock events to 256KB */
> +#define LANDLOCK_EVENTS_MAX_PAGES (1 << 6)
> +
> +/**
> + * landlock_append_prog - attach a Landlock rule to @current_events
> + *
> + * @current_events: landlock_events pointer, must be locked (if needed) to
> + *                  prevent a concurrent put/free. This pointer must not be
> + *                  freed after the call.
> + * @prog: non-NULL Landlock rule to append to @current_events. @prog will be
> + *        owned by landlock_append_prog() and freed if an error happened.
> + *
> + * Return @current_events or a new pointer when OK. Return a pointer error
> + * otherwise.
> + */
> +static struct landlock_events *landlock_append_prog(
> +               struct landlock_events *current_events, struct bpf_prog *prog)
> +{
> +       struct landlock_events *new_events = current_events;
> +       unsigned long pages;
> +       struct landlock_rule *rule;
> +       u32 event_idx;
> +
> +       if (prog->type != BPF_PROG_TYPE_LANDLOCK) {
> +               new_events = ERR_PTR(-EINVAL);
> +               goto put_prog;
> +       }
> +
> +       /* validate memory size allocation */
> +       pages = prog->pages;
> +       if (current_events) {
> +               size_t i;
> +
> +               for (i = 0; i < ARRAY_SIZE(current_events->rules); i++) {
> +                       struct landlock_rule *walker_r;
> +
> +                       for (walker_r = current_events->rules[i]; walker_r;
> +                                       walker_r = walker_r->prev)
> +                               pages += walker_r->prog->pages;
> +               }
> +               /* count a struct landlock_events if we need to allocate one */
> +               if (atomic_read(&current_events->usage) != 1)
> +                       pages += round_up(sizeof(*current_events), PAGE_SIZE) /
> +                               PAGE_SIZE;
> +       }
> +       if (pages > LANDLOCK_EVENTS_MAX_PAGES) {
> +               new_events = ERR_PTR(-E2BIG);
> +               goto put_prog;
> +       }
> +
> +       rule = kzalloc(sizeof(*rule), GFP_KERNEL);
> +       if (!rule) {
> +               new_events = ERR_PTR(-ENOMEM);
> +               goto put_prog;
> +       }
> +       rule->prog = prog;
> +
> +       /* subtype.landlock_rule.event > 0 for loaded programs */
> +       event_idx = get_index(rule->prog->subtype.landlock_rule.event);
> +
> +       if (!new_events) {
> +               /*
> +                * If there is no Landlock events used by the current task,
> +                * then create a new one.
> +                */
> +               new_events = new_landlock_events();
> +               if (IS_ERR(new_events))
> +                       goto put_rule;

Shouldn't bpf_prog_put() get called in the face of a rule failure too?
Why separate exit paths?

> +       } else if (atomic_read(&current_events->usage) > 1) {
> +               /*
> +                * If the current task is not the sole user of its Landlock
> +                * events, then duplicate them.
> +                */
> +               size_t i;
> +
> +               new_events = new_landlock_events();
> +               if (IS_ERR(new_events))
> +                       goto put_rule;
> +               for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) {
> +                       new_events->rules[i] =
> +                               lockless_dereference(current_events->rules[i]);
> +                       if (new_events->rules[i])
> +                               atomic_inc(&new_events->rules[i]->usage);

I was going to ask: isn't the top-level usage counter sufficient to
track rule lifetime? But I think I see how things are tracked now:
each task_struct points to an array of rule list pointers. These
tables are duplicated when additions are made (which means each table
needs to be refcounted for the processes using it), and when a new
table is created, all the refcounters on the rules are bumped (to
track each table that references the rule), and when a new rule is
added, it's just prepended to the list for the new table to point at.

Does this mean that rules are processed in reverse?

> +               }
> +
> +               /*
> +                * Landlock events from the current task will not be freed here
> +                * because the usage is strictly greater than 1. It is only
> +                * prevented to be freed by another subject thanks to the
> +                * caller of landlock_append_prog() which should be locked if
> +                * needed.
> +                */
> +               put_landlock_events(current_events);
> +       }
> +       add_landlock_rule(new_events, rule);
> +       return new_events;
> +
> +put_prog:
> +       bpf_prog_put(prog);
> +       return new_events;
> +
> +put_rule:
> +       put_landlock_rule(rule);
> +       return new_events;
> +}
> +
> +/**
> + * landlock_seccomp_append_prog - attach a Landlock rule to the current process
> + *
> + * current->seccomp.landlock_events is lazily allocated. When a process fork,
> + * only a pointer is copied. When a new event is added by a process, if there
> + * is other references to this process' landlock_events, then a new allocation
> + * is made to contain an array pointing to Landlock rule lists. This design
> + * enable low-performance impact and is memory efficient while keeping the
> + * property of append-only rules.
> + *
> + * @flags: not used for now, but could be used for TSYNC
> + * @user_bpf_fd: file descriptor pointing to a loaded Landlock rule
> + */
> +#ifdef CONFIG_SECCOMP_FILTER
> +int landlock_seccomp_append_prog(unsigned int flags,
> +               const char __user *user_bpf_fd)
> +{
> +       struct landlock_events *new_events;
> +       struct bpf_prog *prog;
> +       int bpf_fd;
> +
> +       /* force no_new_privs to limit privilege escalation */
> +       if (!task_no_new_privs(current))
> +               return -EPERM;
> +       /* will be removed in the future to allow unprivileged tasks */
> +       if (!capable(CAP_SYS_ADMIN))
> +               return -EPERM;
> +       /* enable to check if Landlock is supported with early EFAULT */
> +       if (!user_bpf_fd)
> +               return -EFAULT;
> +       if (flags)
> +               return -EINVAL;
> +       if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(bpf_fd)))
> +               return -EFAULT;

I think this can just be get_user()?

> +       prog = bpf_prog_get(bpf_fd);
> +       if (IS_ERR(prog))
> +               return PTR_ERR(prog);
> +
> +       /*
> +        * We don't need to lock anything for the current process hierarchy,
> +        * everything is guarded by the atomic counters.
> +        */
> +       new_events = landlock_append_prog(current->seccomp.landlock_events,
> +                       prog);
> +       /* @prog is managed/freed by landlock_append_prog() */

Does kmemcheck notice this "leak"? (i.e. is further annotation needed?)

> +       if (IS_ERR(new_events))
> +               return PTR_ERR(new_events);
> +       current->seccomp.landlock_events = new_events;
> +       return 0;
> +}
> +#endif /* CONFIG_SECCOMP_FILTER */
> --
> 2.11.0
>



-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-31 21:15     ` Mickaël Salaün
@ 2017-04-18 22:54       ` Kees Cook
  0 siblings, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 22:54 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Djalal Harouni, linux-kernel, Alexei Starovoitov,
	Andy Lutomirski, Arnaldo Carvalho de Melo, Casey Schaufler,
	Daniel Borkmann, David Drysdale, David S . Miller,
	Eric W . Biederman, James Morris, Jann Horn, Jonathan Corbet,
	Matthew Garrett, Michael Kerrisk, Paul Moore, Sargun Dhillon,
	Serge E . Hallyn, Shuah Khan, Tejun Heo, Thomas Graf,
	Will Drewry, kernel-hardening, Linux API, LSM List,
	Network Development, Andrew Morton, Tetsuo Handa

On Fri, Mar 31, 2017 at 2:15 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
>
> On 29/03/2017 12:35, Djalal Harouni wrote:
>> On Wed, Mar 29, 2017 at 1:46 AM, Mickaël Salaün <mic@digikod.net> wrote:
>
>>> @@ -25,6 +30,9 @@ struct seccomp_filter;
>>>  struct seccomp {
>>>         int mode;
>>>         struct seccomp_filter *filter;
>>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>>> +       struct landlock_events *landlock_events;
>>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>>>  };
>>
>> Sorry if this was discussed before, but since this is mean to be a
>> stackable LSM, I'm wondering if later you could move the events from
>> seccomp, and go with a security_task_alloc() model [1] ?
>>
>> Thanks!
>>
>> [1] http://kernsec.org/pipermail/linux-security-module-archive/2017-March/000184.html
>>
>
> Landlock use the seccomp syscall to attach a rule to a process and using
> struct seccomp to store this rule make sense. There is currently no way
> to store multiple task->security, which is needed for a stackable LSM
> like Landlock, but we could move the events there if needed in the future.

It does stand out to me that the only thing landlock is using seccomp
for is its syscall... :P

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
  2017-03-28 23:46 ` [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example Mickaël Salaün
@ 2017-04-18 23:06   ` Kees Cook
  2017-04-18 23:35     ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:06 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> Add a basic sandbox tool to create a process isolated from some part of
> the system. This sandbox create a read-only environment. It is only
> allowed to write to a character device such as a TTY:
>
>   # :> X
>   # echo $?
>   0
>   # ./samples/bpf/landlock1 /bin/sh -i
>   Launching a new sandboxed process.
>   # :> Y
>   cannot create Y: Operation not permitted
>
> Changes since v5:
> * cosmetic fixes
> * rebase
>
> Changes since v4:
> * write Landlock rule in C and compiled it with LLVM
> * remove cgroup handling
> * remove path handling: only handle a read-only environment
> * remove errno return codes
>
> Changes since v3:
> * remove seccomp and origin field: completely free from seccomp programs
> * handle more FS-related hooks
> * handle inode hooks and directory traversal
> * add faked but consistent view thanks to ENOENT
> * add /lib64 in the example
> * fix spelling
> * rename some types and definitions (e.g. SECCOMP_ADD_LANDLOCK_RULE)
>
> Changes since v2:
> * use BPF_PROG_ATTACH for cgroup handling
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
>  samples/bpf/Makefile         |   4 ++
>  samples/bpf/bpf_load.c       |  31 +++++++++++--
>  samples/bpf/landlock1_kern.c |  46 +++++++++++++++++++
>  samples/bpf/landlock1_user.c | 102 +++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 179 insertions(+), 4 deletions(-)
>  create mode 100644 samples/bpf/landlock1_kern.c
>  create mode 100644 samples/bpf/landlock1_user.c
>
> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> index d42b495b0992..4743674a3fa3 100644
> --- a/samples/bpf/Makefile
> +++ b/samples/bpf/Makefile
> @@ -36,6 +36,7 @@ hostprogs-y += lwt_len_hist
>  hostprogs-y += xdp_tx_iptunnel
>  hostprogs-y += test_map_in_map
>  hostprogs-y += per_socket_stats_example
> +hostprogs-y += landlock1
>
>  # Libbpf dependencies
>  LIBBPF := ../../tools/lib/bpf/bpf.o
> @@ -76,6 +77,7 @@ lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
>  xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
>  test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
>  per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o
> +landlock1-objs := bpf_load.o $(LIBBPF) landlock1_user.o
>
>  # Tell kbuild to always build the programs
>  always := $(hostprogs-y)
> @@ -111,6 +113,7 @@ always += lwt_len_hist_kern.o
>  always += xdp_tx_iptunnel_kern.o
>  always += test_map_in_map_kern.o
>  always += cookie_uid_helper_example.o
> +always += landlock1_kern.o
>
>  HOSTCFLAGS += -I$(objtree)/usr/include
>  HOSTCFLAGS += -I$(srctree)/tools/lib/
> @@ -146,6 +149,7 @@ HOSTLOADLIBES_tc_l2_redirect += -l elf
>  HOSTLOADLIBES_lwt_len_hist += -l elf
>  HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
>  HOSTLOADLIBES_test_map_in_map += -lelf
> +HOSTLOADLIBES_landlock1 += -lelf
>
>  # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
>  #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
> index 4a3460d7c01f..3713e5e2e998 100644
> --- a/samples/bpf/bpf_load.c
> +++ b/samples/bpf/bpf_load.c
> @@ -29,6 +29,8 @@
>
>  static char license[128];
>  static int kern_version;
> +static union bpf_prog_subtype subtype = {};
> +static bool has_subtype;
>  static bool processed_sec[128];
>  char bpf_log_buf[BPF_LOG_BUF_SIZE];
>  int map_fd[MAX_MAPS];
> @@ -68,6 +70,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>         bool is_perf_event = strncmp(event, "perf_event", 10) == 0;
>         bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0;
>         bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0;
> +       bool is_landlock = strncmp(event, "landlock", 8) == 0;
>         size_t insns_cnt = size / sizeof(struct bpf_insn);
>         enum bpf_prog_type prog_type;
>         char buf[256];
> @@ -94,6 +97,13 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>                 prog_type = BPF_PROG_TYPE_CGROUP_SKB;
>         } else if (is_cgroup_sk) {
>                 prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
> +       } else if (is_landlock) {
> +               prog_type = BPF_PROG_TYPE_LANDLOCK;
> +               if (!has_subtype) {
> +                       printf("No subtype\n");
> +                       return -1;
> +               }
> +               st = &subtype;
>         } else {
>                 printf("Unknown event '%s'\n", event);
>                 return -1;
> @@ -108,7 +118,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>
>         prog_fd[prog_cnt++] = fd;
>
> -       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
> +       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
> +           is_landlock)
>                 return 0;
>
>         if (is_socket) {
> @@ -294,6 +305,7 @@ int load_bpf_file(char *path)
>         kern_version = 0;
>         memset(license, 0, sizeof(license));
>         memset(processed_sec, 0, sizeof(processed_sec));
> +       has_subtype = false;
>
>         if (elf_version(EV_CURRENT) == EV_NONE)
>                 return 1;
> @@ -339,6 +351,16 @@ int load_bpf_file(char *path)
>                         processed_sec[i] = true;
>                         if (load_maps(data->d_buf, data->d_size))
>                                 return 1;
> +               } else if (strcmp(shname, "subtype") == 0) {
> +                       processed_sec[i] = true;
> +                       if (data->d_size != sizeof(union bpf_prog_subtype)) {
> +                               printf("invalid size of subtype section %zd\n",
> +                                      data->d_size);
> +                               return 1;
> +                       }
> +                       memcpy(&subtype, data->d_buf,
> +                              sizeof(union bpf_prog_subtype));
> +                       has_subtype = true;
>                 } else if (shdr.sh_type == SHT_SYMTAB) {
>                         symbols = data;
>                 }
> @@ -376,14 +398,14 @@ int load_bpf_file(char *path)
>                             memcmp(shname_prog, "xdp", 3) == 0 ||
>                             memcmp(shname_prog, "perf_event", 10) == 0 ||
>                             memcmp(shname_prog, "socket", 6) == 0 ||
> -                           memcmp(shname_prog, "cgroup/", 7) == 0)
> +                           memcmp(shname_prog, "cgroup/", 7) == 0 ||
> +                           memcmp(shname_prog, "landlock", 8) == 0)
>                                 load_and_attach(shname_prog, insns, data_prog->d_size);
>                 }
>         }
>
>         /* load programs that don't use maps */
>         for (i = 1; i < ehdr.e_shnum; i++) {
> -
>                 if (processed_sec[i])
>                         continue;
>
> @@ -396,7 +418,8 @@ int load_bpf_file(char *path)
>                     memcmp(shname, "xdp", 3) == 0 ||
>                     memcmp(shname, "perf_event", 10) == 0 ||
>                     memcmp(shname, "socket", 6) == 0 ||
> -                   memcmp(shname, "cgroup/", 7) == 0)
> +                   memcmp(shname, "cgroup/", 7) == 0 ||
> +                   memcmp(shname, "landlock", 8) == 0)
>                         load_and_attach(shname, data->d_buf, data->d_size);
>         }
>
> diff --git a/samples/bpf/landlock1_kern.c b/samples/bpf/landlock1_kern.c
> new file mode 100644
> index 000000000000..b8a9b0ca84c9
> --- /dev/null
> +++ b/samples/bpf/landlock1_kern.c
> @@ -0,0 +1,46 @@
> +/*
> + * Landlock rule - partial read-only filesystem
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#define KBUILD_MODNAME "foo"
> +#include <uapi/linux/bpf.h>
> +#include <uapi/linux/stat.h> /* S_ISCHR() */
> +#include "bpf_helpers.h"
> +
> +SEC("landlock1")
> +static int landlock_fs_prog1(struct landlock_context *ctx)

Since this is in samples, I think this needs a lot more comments to
describe each pieces, how it fits together, etc. This is where people
are going to come to learn how to use landlock, so making it as clear
as possible is important. This is especially true for each step of the
rule logic. (e.g. some will wonder why is S_ISCHR excluded, etc.)

> +{
> +       char fmt_error[] = "landlock1: error: get_mode:%lld\n";
> +       char fmt_name[] = "landlock1: syscall:%d\n";
> +       long long ret;
> +
> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
> +               return 0;
> +       ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
> +       if (ret < 0) {
> +               bpf_trace_printk(fmt_error, sizeof(fmt_error), ret);
> +               return 1;
> +       }
> +       if (S_ISCHR(ret))
> +               return 0;
> +       bpf_trace_printk(fmt_name, sizeof(fmt_name), ctx->syscall_nr);
> +       return 1;
> +}
> +
> +SEC("subtype")
> +static union bpf_prog_subtype _subtype = {

Can this be const?

> +       .landlock_rule = {
> +               .version = 1,
> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +               .ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
> +       }
> +};
> +
> +SEC("license")
> +static const char _license[] = "GPL";
> diff --git a/samples/bpf/landlock1_user.c b/samples/bpf/landlock1_user.c
> new file mode 100644
> index 000000000000..6f79eb0ee6db
> --- /dev/null
> +++ b/samples/bpf/landlock1_user.c
> @@ -0,0 +1,102 @@
> +/*
> + * Landlock sandbox - partial read-only filesystem
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#include "bpf_load.h"
> +#include "libbpf.h"
> +
> +#define _GNU_SOURCE
> +#include <errno.h>
> +#include <fcntl.h> /* open() */
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include <linux/prctl.h>
> +#include <linux/seccomp.h>
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/prctl.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +
> +#ifndef seccomp
> +static int seccomp(unsigned int op, unsigned int flags, void *args)
> +{
> +       errno = 0;
> +       return syscall(__NR_seccomp, op, flags, args);
> +}
> +#endif
> +
> +#define ARRAY_SIZE(a)  (sizeof(a) / sizeof(a[0]))
> +#define MAX_ERRNO      4095

Is MAX_ERRNO actually needed?

> +
> +
> +struct landlock_rule {
> +       enum landlock_subtype_event event;
> +       struct bpf_insn *bpf;
> +       size_t size;
> +};
> +
> +static int apply_sandbox(int prog_fd)
> +{
> +       int ret = 0;
> +
> +       /* set up the test sandbox */
> +       if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
> +               perror("prctl(no_new_priv)");
> +               return 1;
> +       }
> +       if (seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog_fd)) {
> +               perror("seccomp(set_hook)");
> +               ret = 1;
> +       }
> +       close(prog_fd);
> +
> +       return ret;
> +}
> +
> +int main(int argc, char * const argv[], char * const *envp)
> +{
> +       char filename[256];
> +       char *cmd_path;
> +       char * const *cmd_argv;
> +
> +       if (argc < 2) {
> +               fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]);
> +               fprintf(stderr, "Launch a command in a read-only environment "
> +                               "(except for character devices).\n");
> +               fprintf(stderr, "Display debug with: "
> +                               "cat /sys/kernel/debug/tracing/trace_pipe &\n");
> +               return 1;
> +       }
> +
> +       snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
> +       if (load_bpf_file(filename)) {
> +               printf("%s", bpf_log_buf);
> +               return 1;
> +       }
> +       if (!prog_fd[0]) {
> +               if (errno) {
> +                       printf("load_bpf_file: %s\n", strerror(errno));
> +               } else {
> +                       printf("load_bpf_file: Error\n");
> +               }
> +               return 1;
> +       }
> +
> +       if (apply_sandbox(prog_fd[0]))
> +               return 1;
> +       cmd_path = argv[1];
> +       cmd_argv = argv + 1;
> +       fprintf(stderr, "Launching a new sandboxed process.\n");
> +       execve(cmd_path, cmd_argv, envp);
> +       perror("execve");
> +       return 1;
> +}

I like this example. It's a very powerful rule to for a program to
enforce on itself. :)

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-18 22:44     ` Mickaël Salaün
@ 2017-04-18 23:16       ` Casey Schaufler
  2017-04-18 23:40         ` Kees Cook
  2017-04-18 23:39       ` Kees Cook
  1 sibling, 1 reply; 50+ messages in thread
From: Casey Schaufler @ 2017-04-18 23:16 UTC (permalink / raw)
  To: Mickaël Salaün, Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Paul Moore,
	Sargun Dhillon, Serge E . Hallyn, Shuah Khan, Tejun Heo,
	Thomas Graf, Will Drewry, kernel-hardening, Linux API,
	linux-security-module, Network Development, Casey Schaufler

On 4/18/2017 3:44 PM, Mickaël Salaün wrote:
> On 19/04/2017 00:17, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> Handle 33 filesystem-related LSM hooks for the Landlock filesystem
>>> event: LANDLOCK_SUBTYPE_EVENT_FS.
>>>
>>> A Landlock event wrap LSM hooks for similar kernel object types (e.g.
>>> struct file, struct path...). Multiple LSM hooks can trigger the same
>>> Landlock event.
>>>
>>> Landlock handle nine coarse-grained actions: read, write, execute, new,
>>> get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
>>> access control in a way that can be extended in the future.
>>>
>>> The Landlock LSM hook registration is done after other LSM to only run
>>> actions from user-space, via eBPF programs, if the access was granted by
>>> major (privileged) LSMs.
>>>
>>> Changes since v5:
>>> * split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
>>> * add more documentation
>>> * cosmetic fixes
>>>
>>> Changes since v4:
>>> * add LSM hook abstraction called Landlock event
>>>   * use the compiler type checking to verify hooks use by an event
>>>   * handle all filesystem related LSM hooks (e.g. file_permission,
>>>     mmap_file, sb_mount...)
>>> * register BPF programs for Landlock just after LSM hooks registration
>>> * move hooks registration after other LSMs
>>> * add failsafes to check if a hook is not used by the kernel
>>> * allow partial raw value access form the context (needed for programs
>>>   generated by LLVM)
>>>
>>> Changes since v3:
>>> * split commit
>>> * add hooks dealing with struct inode and struct path pointers:
>>>   inode_permission and inode_getattr
>>> * add abstraction over eBPF helper arguments thanks to wrapping structs
>>>
>>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>> Cc: Alexei Starovoitov <ast@kernel.org>
>>> Cc: Andy Lutomirski <luto@amacapital.net>
>>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>>> Cc: David S. Miller <davem@davemloft.net>
>>> Cc: James Morris <james.l.morris@oracle.com>
>>> Cc: Kees Cook <keescook@chromium.org>
>>> Cc: Serge E. Hallyn <serge@hallyn.com>
>>> ---
>>>  include/linux/lsm_hooks.h    |   5 +
>>>  security/landlock/Makefile   |   4 +-
>>>  security/landlock/hooks.c    | 115 +++++++++
>>>  security/landlock/hooks.h    | 177 ++++++++++++++
>>>  security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
>>>  security/landlock/hooks_fs.h |  19 ++
>>>  security/landlock/init.c     |  13 +
>>>  security/security.c          |   7 +-
>>>  8 files changed, 901 insertions(+), 2 deletions(-)
>>>  create mode 100644 security/landlock/hooks.c
>>>  create mode 100644 security/landlock/hooks.h
>>>  create mode 100644 security/landlock/hooks_fs.c
>>>  create mode 100644 security/landlock/hooks_fs.h
>>>
>>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>>> index e29d4c62a3c8..884289166a0e 100644
>>> --- a/include/linux/lsm_hooks.h
>>> +++ b/include/linux/lsm_hooks.h
>>> @@ -1920,5 +1920,10 @@ void __init loadpin_add_hooks(void);
>>>  #else
>>>  static inline void loadpin_add_hooks(void) { };
>>>  #endif
>>> +#ifdef CONFIG_SECURITY_LANDLOCK
>>> +extern void __init landlock_add_hooks(void);
>>> +#else
>>> +static inline void __init landlock_add_hooks(void) { }
>>> +#endif /* CONFIG_SECURITY_LANDLOCK */
>>>
>>>  #endif /* ! __LINUX_LSM_HOOKS_H */
>>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>>> index 7205f9a7a2ee..c0db504a6335 100644
>>> --- a/security/landlock/Makefile
>>> +++ b/security/landlock/Makefile
>>> @@ -1,3 +1,5 @@
>>> +ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
>> Why is this needed? If it can't be avoided, a comment should exist
>> here explaining why.
> This is useful to catch defined but unused hooks: error out if a
> HOOK_NEW_FS(foo) is not used with a HOOK_INIT_FS(foo) in the struct
> security_hook_list landlock_hooks.
>
>>> [...]
>>> @@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>>>         .ops = &bpf_landlock_ops,
>>>         .type = BPF_PROG_TYPE_LANDLOCK,
>>>  };
>>> +
>>> +void __init landlock_add_hooks(void)
>>> +{
>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>> +       landlock_add_hooks_fs();
>>> +       security_add_hooks(NULL, 0, "landlock");
>>> +       bpf_register_prog_type(&bpf_landlock_type);
>> I'm confused by the separation of hook registration here. The call to
>> security_add_hooks is with count=0 is especially weird. Why isn't this
>> just a single call with security_add_hooks(landlock_hooks,
>> ARRAY_SIZE(landlock_hooks), "landlock")?
> Yes, this is ugly with the new security_add_hooks() with three arguments
> but I wanted to split the hooks definition in multiple files.

Why? I'll buy a good argument, but there are dangers in
allowing multiple calls to security_add_hooks(). 

>
> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
> is not exported. Unfortunately, calling multiple security_add_hooks()
> with the same LSM name would register multiple names for the same LSM…
> Is it OK if I modify this function to not add duplicated entries?

It may seem absurd, but it's conceivable that a module might
have two hooks it wants called. My example is a module that
counts the number of times SELinux denies a process access to
things (which needs to be called before and after SELinux in
order to detect denials) and takes "appropriate action" if
too many denials occur. It would be weird, wonky and hackish,
but that never stopped anybody before.

>
>
>>> +}
>>> diff --git a/security/security.c b/security/security.c
>>> index d0e07f269b2d..a3e9f4625991 100644
>>> --- a/security/security.c
>>> +++ b/security/security.c
>>> @@ -64,10 +64,15 @@ int __init security_init(void)
>>>         loadpin_add_hooks();
>>>
>>>         /*
>>> -        * Load all the remaining security modules.
>>> +        * Load all remaining privileged security modules.
>>>          */
>>>         do_security_initcalls();
>>>
>>> +       /*
>>> +        * Load potentially-unprivileged security modules at the end.
>>> +        */
>>> +       landlock_add_hooks();
>> Oh, is this to make it last in the list? Is there a reason it has to be last?
> Right, this is the intend. I'm not sure it is the only way to register
> hooks, though.
>
> For an unprivileged access-control, we don't want to give the ability to
> any process to do some checks, through an eBPF program, on kernel
> objects (e.g. files) if they should not be accessible (because of a
> following LSM hook check).
>
>  Mickaël
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
  2017-03-28 23:46 ` [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock Mickaël Salaün
@ 2017-04-18 23:16   ` Kees Cook
  2017-04-18 23:53     ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> Test basic context access, ptrace protection and filesystem event with
> multiple cases.
>
> Changes since v5:
> * add subtype test
> * add ptrace tests
> * split and rename files
> * cleanup and rebase
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: James Morris <james.l.morris@oracle.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Cc: Shuah Khan <shuah@kernel.org>
> Cc: Will Drewry <wad@chromium.org>
> ---
>  tools/testing/selftests/Makefile                   |   1 +
>  tools/testing/selftests/bpf/test_verifier.c        |  64 +++++
>  tools/testing/selftests/landlock/.gitignore        |   4 +
>  tools/testing/selftests/landlock/Makefile          |  47 ++++
>  tools/testing/selftests/landlock/rules/Makefile    |  52 ++++
>  tools/testing/selftests/landlock/rules/README.rst  |   1 +
>  .../testing/selftests/landlock/rules/bpf_helpers.h |   1 +
>  .../testing/selftests/landlock/rules/fs_no_open.c  |  31 +++
>  .../selftests/landlock/rules/fs_read_only.c        |  31 +++
>  tools/testing/selftests/landlock/test.h            |  35 +++
>  tools/testing/selftests/landlock/test_base.c       |  31 +++
>  tools/testing/selftests/landlock/test_fs.c         | 305 +++++++++++++++++++++
>  tools/testing/selftests/landlock/test_ptrace.c     | 161 +++++++++++
>  13 files changed, 764 insertions(+)
>  create mode 100644 tools/testing/selftests/landlock/.gitignore
>  create mode 100644 tools/testing/selftests/landlock/Makefile
>  create mode 100644 tools/testing/selftests/landlock/rules/Makefile
>  create mode 120000 tools/testing/selftests/landlock/rules/README.rst
>  create mode 120000 tools/testing/selftests/landlock/rules/bpf_helpers.h
>  create mode 100644 tools/testing/selftests/landlock/rules/fs_no_open.c
>  create mode 100644 tools/testing/selftests/landlock/rules/fs_read_only.c
>  create mode 100644 tools/testing/selftests/landlock/test.h
>  create mode 100644 tools/testing/selftests/landlock/test_base.c
>  create mode 100644 tools/testing/selftests/landlock/test_fs.c
>  create mode 100644 tools/testing/selftests/landlock/test_ptrace.c
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index d8593f1251ec..b584ad456428 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -12,6 +12,7 @@ TARGETS += gpio
>  TARGETS += intel_pstate
>  TARGETS += ipc
>  TARGETS += kcmp
> +TARGETS += landlock
>  TARGETS += lib
>  TARGETS += membarrier
>  TARGETS += memfd
> diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
> index daa87dd7c80e..77255b14871e 100644
> --- a/tools/testing/selftests/bpf/test_verifier.c
> +++ b/tools/testing/selftests/bpf/test_verifier.c
> @@ -4536,6 +4536,70 @@ static struct bpf_test tests[] = {
>                 .result = REJECT,
>                 .has_prog_subtype = true,
>         },
> +       {
> +               "missing subtype",
> +               .insns = {
> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
> +                       BPF_EXIT_INSN(),
> +               },
> +               .errstr = "",
> +               .result = REJECT,
> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
> +       },
> +       {
> +               "landlock/fs: always accept",
> +               .insns = {
> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
> +                       BPF_EXIT_INSN(),
> +               },
> +               .result = ACCEPT,
> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
> +               .has_prog_subtype = true,
> +               .prog_subtype = {
> +                       .landlock_rule = {
> +                               .version = 1,
> +                               .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +                       }
> +               },
> +       },
> +       {
> +               "landlock/fs: read context",
> +               .insns = {
> +                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, status)),
> +                       /* test operations on raw values */
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, arch)),
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, syscall_nr)),
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, syscall_cmd)),
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, event)),
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, arg1)),
> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
> +                               offsetof(struct landlock_context, arg2)),
> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
> +                       BPF_EXIT_INSN(),
> +               },
> +               .result = ACCEPT,
> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
> +               .has_prog_subtype = true,
> +               .prog_subtype = {
> +                       .landlock_rule = {
> +                               .version = 1,
> +                               .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +                       }
> +               },
> +       },
>  };
>
>  static int probe_filter_length(const struct bpf_insn *fp)
> diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
> new file mode 100644
> index 000000000000..25b9cd834c3c
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/.gitignore
> @@ -0,0 +1,4 @@
> +/test_base
> +/test_fs
> +/test_ptrace
> +/tmp_*
> diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
> new file mode 100644
> index 000000000000..9a52c82d64fa
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/Makefile
> @@ -0,0 +1,47 @@
> +LIBDIR := ../../../lib
> +BPFOBJ := $(LIBDIR)/bpf/bpf.o
> +LOADOBJ := ../../../../samples/bpf/bpf_load.o

Is the selftest tarball creation tool okay with this? IIRC, it should
be fine since it'll be a built object already, but it's a random
thought I had while looking at this.

> +
> +CFLAGS += -Wl,-no-as-needed -Wall -O2 -I../../../include/uapi -I$(LIBDIR)
> +LDFLAGS += -lelf
> +
> +test_src = $(wildcard test_*.c)
> +rule_src = $(wildcard rules/*.c)
> +
> +test_objs := $(test_src:.c=)
> +rule_objs := $(rule_src:.c=.o)
> +
> +TEST_PROGS := $(test_objs)
> +
> +.PHONY: all clean clean_tmp force
> +
> +all: $(test_objs) $(rule_objs)
> +
> +# force a rebuild of BPFOBJ when its dependencies are updated
> +force:
> +
> +$(BPFOBJ): force
> +       $(MAKE) -C $(dir $(BPFOBJ))
> +
> +$(LOADOBJ):
> +       $(MAKE) -C $(dir $(LOADOBJ))
> +
> +# minimize builds
> +rules/modules.order: $(rule_src)
> +       $(MAKE) -C rules
> +       @touch $@
> +
> +$(rule_objs): rules/modules.order
> +       @
> +
> +$(test_objs): $(BPFOBJ) $(LOADOBJ)
> +
> +include ../lib.mk
> +
> +clean_tmp:
> +       $(RM) -r tmp_*
> +
> +clean: clean_tmp
> +       $(MAKE) -C rules clean
> +       $(RM) $(test_objs)
> +
> diff --git a/tools/testing/selftests/landlock/rules/Makefile b/tools/testing/selftests/landlock/rules/Makefile
> new file mode 100644
> index 000000000000..8d6ff960ff7c
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/rules/Makefile
> @@ -0,0 +1,52 @@
> +# kbuild trick to avoid linker error. Can be omitted if a module is built.
> +obj- := dummy.o
> +
> +# Tell kbuild to always build the programs
> +always := fs_read_only.o
> +always += fs_no_open.o
> +
> +EXTRA_CFLAGS = -Wall -Wextra
> +
> +# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
> +#  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> +LLC ?= llc
> +CLANG ?= clang
> +
> +# Verify LLVM compiler tools are available and bpf target is supported by llc
> +.PHONY: all clean verify_cmds verify_target_bpf $(CLANG) $(LLC)
> +
> +# Trick to allow make to be run from this directory
> +all:
> +       $(MAKE) -C ../../../../../ $(CURDIR)/
> +
> +clean:
> +       $(MAKE) -C ../../../../../ M=$(CURDIR) clean

Is this really needed? Others don't have it, I think.

> +verify_cmds: $(CLANG) $(LLC)
> +       @for TOOL in $^ ; do \
> +               if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
> +                       echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
> +                       exit 1; \
> +               else true; fi; \
> +       done
> +
> +verify_target_bpf: verify_cmds
> +       @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
> +               echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
> +               echo "   NOTICE: LLVM version >= 3.7.1 required" ;\
> +               exit 2; \
> +       else true; fi
> +
> +%_kern.c: verify_target_bpf
> +
> +# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
> +# But, there is no easy way to fix it, so just exclude it since it is
> +# useless for BPF samples.
> +$(obj)/%.o: $(src)/%.c
> +       $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
> +               -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
> +               -Wno-compare-distinct-pointer-types \
> +               -Wno-gnu-variable-sized-type-not-at-end \
> +               -Wno-tautological-compare \
> +               -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@

Is clang required for the samples and the selftests? That needs to be
avoided... there needs to be a way to show people how to build a
landlock rule without requiring clang.

> +
> diff --git a/tools/testing/selftests/landlock/rules/README.rst b/tools/testing/selftests/landlock/rules/README.rst
> new file mode 120000
> index 000000000000..605f48aa6f72
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/rules/README.rst
> @@ -0,0 +1 @@
> +../../../../../samples/bpf/README.rst
> \ No newline at end of file
> diff --git a/tools/testing/selftests/landlock/rules/bpf_helpers.h b/tools/testing/selftests/landlock/rules/bpf_helpers.h
> new file mode 120000
> index 000000000000..0aa1a521b39a
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/rules/bpf_helpers.h
> @@ -0,0 +1 @@
> +../../../../../samples/bpf/bpf_helpers.h
> \ No newline at end of file
> diff --git a/tools/testing/selftests/landlock/rules/fs_no_open.c b/tools/testing/selftests/landlock/rules/fs_no_open.c
> new file mode 100644
> index 000000000000..c6ea305e58a7
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/rules/fs_no_open.c
> @@ -0,0 +1,31 @@
> +/*
> + * Landlock rule - no-open filesystem
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <uapi/linux/bpf.h>
> +#include "bpf_helpers.h"
> +
> +SEC("landlock1")
> +static int landlock_fs_prog1(struct landlock_context *ctx)
> +{
> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_GET))
> +               return 0;
> +       return 1;
> +}
> +
> +SEC("subtype")
> +static union bpf_prog_subtype _subtype = {
> +       .landlock_rule = {
> +               .version = 1,
> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +       }
> +};
> +
> +SEC("license")
> +static const char _license[] = "GPL";
> diff --git a/tools/testing/selftests/landlock/rules/fs_read_only.c b/tools/testing/selftests/landlock/rules/fs_read_only.c
> new file mode 100644
> index 000000000000..212dda7c0c27
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/rules/fs_read_only.c
> @@ -0,0 +1,31 @@
> +/*
> + * Landlock rule - read-only filesystem
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <uapi/linux/bpf.h>
> +#include "bpf_helpers.h"
> +
> +SEC("landlock1")
> +static int landlock_fs_prog1(struct landlock_context *ctx)
> +{
> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
> +               return 0;
> +       return 1;
> +}
> +
> +SEC("subtype")
> +static union bpf_prog_subtype _subtype = {
> +       .landlock_rule = {
> +               .version = 1,
> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +       }
> +};
> +
> +SEC("license")
> +static const char _license[] = "GPL";
> diff --git a/tools/testing/selftests/landlock/test.h b/tools/testing/selftests/landlock/test.h
> new file mode 100644
> index 000000000000..7a194815391b
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/test.h
> @@ -0,0 +1,35 @@
> +/*
> + * Landlock helpers
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <errno.h>
> +#include <sys/prctl.h>
> +#include <sys/syscall.h>
> +
> +#include "../seccomp/test_harness.h"
> +#include "../../../../samples/bpf/bpf_load.h"
> +
> +#ifndef SECCOMP_APPEND_LANDLOCK_RULE
> +#define SECCOMP_APPEND_LANDLOCK_RULE   2
> +#endif
> +
> +#ifndef seccomp
> +static int seccomp(unsigned int op, unsigned int flags, void *args)
> +{
> +       errno = 0;
> +       return syscall(__NR_seccomp, op, flags, args);
> +}
> +#endif
> +
> +#define ASSERT_STEP(cond) \
> +       { \
> +               step--; \
> +               if (!(cond)) \
> +                       _exit(step); \
> +       }

Can you explain this in more detail? I'm assuming there is a problem
with writing to the TH_LOG_STREAM fd or something?

> diff --git a/tools/testing/selftests/landlock/test_base.c b/tools/testing/selftests/landlock/test_base.c
> new file mode 100644
> index 000000000000..bdf056edee03
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/test_base.c
> @@ -0,0 +1,31 @@
> +/*
> + * Landlock tests - base
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#define _GNU_SOURCE
> +#include <errno.h>
> +
> +#include "test.h"
> +
> +TEST(seccomp_landlock)
> +{
> +       int ret;
> +
> +       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
> +       ASSERT_EQ(0, ret) {
> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
> +       }
> +       ret = seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, NULL);
> +       EXPECT_EQ(-1, ret);
> +       EXPECT_EQ(EFAULT, errno) {
> +               TH_LOG("Kernel does not support CONFIG_SECURITY_LANDLOCK");
> +       }
> +}
> +
> +TEST_HARNESS_MAIN
> diff --git a/tools/testing/selftests/landlock/test_fs.c b/tools/testing/selftests/landlock/test_fs.c
> new file mode 100644
> index 000000000000..e69eda433716
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/test_fs.c
> @@ -0,0 +1,305 @@
> +/*
> + * Landlock tests - filesystem
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#define _GNU_SOURCE
> +#include <errno.h>
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include <linux/seccomp.h>
> +#include <stddef.h>
> +#include <string.h>
> +#include <sys/prctl.h>
> +#include <sys/syscall.h>
> +
> +#include <fcntl.h> /* open() */
> +#include <sys/mount.h>
> +#include <sys/stat.h> /* mkdir() */
> +#include <sys/mman.h> /* mmap() */
> +
> +#include "test.h"
> +
> +#define TMP_PREFIX "tmp_"
> +
> +struct layout1 {
> +       int file_ro;
> +       int file_rw;
> +       int file_wo;
> +};
> +
> +static void setup_layout1(struct __test_metadata *_metadata,
> +               struct layout1 *l1)
> +{
> +       int fd;
> +       char buf[] = "fs_read_only";
> +
> +       l1->file_ro = -1;
> +       l1->file_rw = -1;
> +       l1->file_wo = -1;
> +
> +       fd = open(TMP_PREFIX "file_created",
> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
> +       ASSERT_GE(fd, 0);
> +       ASSERT_EQ(sizeof(buf), write(fd, buf, sizeof(buf)));
> +       ASSERT_EQ(0, close(fd));
> +
> +       fd = mkdir(TMP_PREFIX "dir_created", 0600);
> +       ASSERT_GE(fd, 0);
> +       ASSERT_EQ(0, close(fd));
> +
> +       l1->file_ro = open(TMP_PREFIX "file_ro",
> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
> +       ASSERT_LE(0, l1->file_ro);
> +       ASSERT_EQ(sizeof(buf), write(l1->file_ro, buf, sizeof(buf)));
> +       ASSERT_EQ(0, close(l1->file_ro));
> +       l1->file_ro = open(TMP_PREFIX "file_ro",
> +                       O_RDONLY | O_CLOEXEC, 0600);
> +       ASSERT_LE(0, l1->file_ro);
> +
> +       l1->file_rw = open(TMP_PREFIX "file_rw",
> +                       O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
> +       ASSERT_LE(0, l1->file_rw);
> +       ASSERT_EQ(sizeof(buf), write(l1->file_rw, buf, sizeof(buf)));
> +       ASSERT_EQ(0, lseek(l1->file_rw, 0, SEEK_SET));
> +
> +       l1->file_wo = open(TMP_PREFIX "file_wo",
> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
> +       ASSERT_LE(0, l1->file_wo);
> +       ASSERT_EQ(sizeof(buf), write(l1->file_wo, buf, sizeof(buf)));
> +       ASSERT_EQ(0, lseek(l1->file_wo, 0, SEEK_SET));
> +}
> +
> +static void cleanup_layout1(void)
> +{
> +       unlink(TMP_PREFIX "file_created");
> +       unlink(TMP_PREFIX "file_ro");
> +       unlink(TMP_PREFIX "file_rw");
> +       unlink(TMP_PREFIX "file_wo");
> +       unlink(TMP_PREFIX "should_not_exist");
> +       rmdir(TMP_PREFIX "dir_created");
> +}
> +
> +FIXTURE(fs_read_only) {
> +       struct layout1 l1;
> +       int prog;
> +};
> +
> +FIXTURE_SETUP(fs_read_only)
> +{
> +       cleanup_layout1();
> +       setup_layout1(_metadata, &self->l1);
> +
> +       ASSERT_EQ(0, load_bpf_file("rules/fs_read_only.o")) {
> +               TH_LOG("%s", bpf_log_buf);
> +       }
> +       self->prog = prog_fd[0];
> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
> +       }
> +}
> +
> +FIXTURE_TEARDOWN(fs_read_only)
> +{
> +       EXPECT_EQ(0, close(self->prog));
> +       /* cleanup_layout1() would be denied here */
> +}
> +
> +TEST_F(fs_read_only, load_prog) {}
> +
> +TEST_F(fs_read_only, read_only_file)
> +{
> +       int fd;
> +       int step = 0;
> +       char buf_write[] = "should not be written";
> +       char buf_read[2];
> +
> +       ASSERT_EQ(-1, write(self->l1.file_ro, buf_write, sizeof(buf_write)));
> +       ASSERT_EQ(EBADF, errno);
> +
> +       ASSERT_EQ(-1, read(self->l1.file_wo, buf_read, sizeof(buf_read)));
> +       ASSERT_EQ(EBADF, errno);
> +
> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
> +               TH_LOG("Failed to apply rule fs_read_only: %s",
> +                               strerror(errno));
> +       }
> +
> +       fd = open(".", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
> +       ASSERT_STEP(fd == -1);
> +       ASSERT_STEP(errno != EOPNOTSUPP)
> +       ASSERT_STEP(errno == EPERM);
> +
> +       fd = open(TMP_PREFIX "file_created",
> +                       O_RDONLY | O_CLOEXEC);
> +       ASSERT_STEP(fd >= 0);
> +       ASSERT_STEP(!close(fd));
> +
> +       fd = open(TMP_PREFIX "file_created",
> +                       O_RDWR | O_CLOEXEC);
> +       ASSERT_STEP(fd == -1);
> +       ASSERT_STEP(errno == EPERM);
> +
> +       fd = open(TMP_PREFIX "file_created",
> +                       O_WRONLY | O_CLOEXEC);
> +       ASSERT_STEP(fd == -1);
> +       ASSERT_STEP(errno == EPERM);
> +
> +       fd = open(TMP_PREFIX "should_not_exist",
> +                       O_CREAT | O_EXCL | O_CLOEXEC, 0600);
> +       ASSERT_STEP(fd == -1);
> +       ASSERT_STEP(errno == EPERM);
> +
> +       ASSERT_STEP(-1 ==
> +                       write(self->l1.file_ro, buf_write, sizeof(buf_write)));
> +       ASSERT_STEP(errno == EBADF);
> +       ASSERT_STEP(sizeof(buf_read) ==
> +                       read(self->l1.file_ro, buf_read, sizeof(buf_read)));
> +
> +       ASSERT_STEP(-1 ==
> +                       write(self->l1.file_rw, buf_write, sizeof(buf_write)));
> +       ASSERT_STEP(errno == EPERM);
> +       ASSERT_STEP(sizeof(buf_read) ==
> +                       read(self->l1.file_rw, buf_read, sizeof(buf_read)));
> +
> +       ASSERT_STEP(-1 == write(self->l1.file_wo, buf_write, sizeof(buf_write)));
> +       ASSERT_STEP(errno == EPERM);
> +       ASSERT_STEP(-1 == read(self->l1.file_wo, buf_read, sizeof(buf_read)));
> +       ASSERT_STEP(errno == EBADF);
> +
> +       ASSERT_STEP(-1 == unlink(TMP_PREFIX "file_created"));
> +       ASSERT_STEP(errno == EPERM);
> +       ASSERT_STEP(-1 == rmdir(TMP_PREFIX "dir_created"));
> +       ASSERT_STEP(errno == EPERM);
> +
> +       ASSERT_STEP(0 == close(self->l1.file_ro));
> +       ASSERT_STEP(0 == close(self->l1.file_rw));
> +       ASSERT_STEP(0 == close(self->l1.file_wo));
> +}
> +
> +TEST_F(fs_read_only, read_only_mount)
> +{
> +       int step = 0;
> +
> +       ASSERT_EQ(0, mount(".", TMP_PREFIX "dir_created",
> +                               NULL, MS_BIND, NULL));
> +       ASSERT_EQ(0, umount2(TMP_PREFIX "dir_created", MNT_FORCE));
> +
> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
> +               TH_LOG("Failed to apply rule fs_read_only: %s",
> +                               strerror(errno));
> +       }
> +
> +       ASSERT_STEP(-1 == mount(".", TMP_PREFIX "dir_created",
> +                               NULL, MS_BIND, NULL));
> +       ASSERT_STEP(errno == EPERM);
> +       ASSERT_STEP(-1 == umount("/"));
> +       ASSERT_STEP(errno == EPERM);
> +}
> +
> +TEST_F(fs_read_only, read_only_mem)
> +{
> +       int step = 0;
> +       void *addr;
> +
> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
> +                       MAP_SHARED, self->l1.file_rw, 0);
> +       ASSERT_NE(NULL, addr);
> +       ASSERT_EQ(0, munmap(addr, 1));
> +
> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
> +               TH_LOG("Failed to apply rule fs_read_only: %s",
> +                               strerror(errno));
> +       }
> +
> +       addr = mmap(NULL, 1, PROT_READ, MAP_SHARED,
> +                       self->l1.file_rw, 0);
> +       ASSERT_STEP(addr != NULL);
> +       ASSERT_STEP(-1 == mprotect(addr, 1, PROT_WRITE));
> +       ASSERT_STEP(errno == EPERM);
> +       ASSERT_STEP(0 == munmap(addr, 1));
> +
> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED,
> +                       self->l1.file_rw, 0);
> +       ASSERT_STEP(addr != NULL);
> +       ASSERT_STEP(errno == EPERM);
> +
> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE,
> +                       self->l1.file_rw, 0);
> +       ASSERT_STEP(addr != NULL);
> +       ASSERT_STEP(0 == munmap(addr, 1));
> +}
> +
> +FIXTURE(fs_no_open) {
> +       struct layout1 l1;
> +       int prog;
> +};
> +
> +FIXTURE_SETUP(fs_no_open)
> +{
> +       cleanup_layout1();
> +       setup_layout1(_metadata, &self->l1);
> +
> +       ASSERT_EQ(0, load_bpf_file("rules/fs_no_open.o")) {
> +               TH_LOG("%s", bpf_log_buf);
> +       }
> +       self->prog = prog_fd[0];
> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
> +       }
> +}
> +
> +FIXTURE_TEARDOWN(fs_no_open)
> +{
> +       EXPECT_EQ(0, close(self->prog));
> +       cleanup_layout1();
> +}
> +
> +static void landlocked_deny_open(struct __test_metadata *_metadata,
> +               struct layout1 *l1)
> +{
> +       int fd;
> +       void *addr;
> +
> +       fd = open(".", O_DIRECTORY | O_CLOEXEC);
> +       ASSERT_EQ(-1, fd);
> +       ASSERT_EQ(EPERM, errno);
> +
> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
> +                       MAP_SHARED, l1->file_rw, 0);
> +       ASSERT_NE(NULL, addr);
> +       ASSERT_EQ(0, munmap(addr, 1));
> +}
> +
> +TEST_F(fs_no_open, deny_open_for_hierarchy) {
> +       int fd;
> +       int status;
> +       pid_t child;
> +
> +       fd = open(".", O_DIRECTORY | O_CLOEXEC);
> +       ASSERT_LE(0, fd);
> +       ASSERT_EQ(0, close(fd));
> +
> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
> +               TH_LOG("Failed to apply rule fs_no_open: %s", strerror(errno));
> +       }
> +
> +       landlocked_deny_open(_metadata, &self->l1);
> +
> +       child = fork();
> +       ASSERT_LE(0, child);
> +       if (!child) {
> +               landlocked_deny_open(_metadata, &self->l1);
> +               _exit(1);
> +       }
> +       ASSERT_EQ(child, waitpid(child, &status, 0));
> +       ASSERT_TRUE(WIFEXITED(status));
> +       _exit(WEXITSTATUS(status));
> +}
> +
> +TEST_HARNESS_MAIN
> diff --git a/tools/testing/selftests/landlock/test_ptrace.c b/tools/testing/selftests/landlock/test_ptrace.c
> new file mode 100644
> index 000000000000..0c940a7fd3d0
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/test_ptrace.c
> @@ -0,0 +1,161 @@
> +/*
> + * Landlock tests - ptrace
> + *
> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + */
> +
> +#define _GNU_SOURCE
> +#include <signal.h> /* raise */
> +#include <sys/ptrace.h>
> +#include <sys/types.h> /* waitpid */
> +#include <sys/wait.h> /* waitpid */
> +#include <unistd.h> /* fork, pipe */
> +
> +#include "test.h"
> +
> +static void apply_null_sandbox(struct __test_metadata *_metadata)
> +{
> +       const struct bpf_insn prog_accept[] = {
> +               BPF_MOV32_IMM(BPF_REG_0, 0),
> +               BPF_EXIT_INSN(),
> +       };
> +       const union bpf_prog_subtype subtype = {
> +               .landlock_rule = {
> +                       .version = 1,
> +                       .event = LANDLOCK_SUBTYPE_EVENT_FS,
> +               }
> +       };
> +       int prog;
> +       char log[256] = "";
> +
> +       prog = bpf_load_program(BPF_PROG_TYPE_LANDLOCK,
> +                       (const struct bpf_insn *)&prog_accept,
> +                       sizeof(prog_accept) / sizeof(struct bpf_insn), "GPL",
> +                       0, log, sizeof(log), &subtype);
> +       ASSERT_NE(-1, prog) {
> +               TH_LOG("Failed to load minimal rule: %s\n%s",
> +                               strerror(errno), log);
> +       }
> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
> +       }
> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog)) {
> +               TH_LOG("Failed to apply minimal rule: %s", strerror(errno));
> +       }
> +       EXPECT_EQ(0, close(prog));
> +}
> +
> +/* PTRACE_TRACEME and PTRACE_ATTACH without Landlock rules effect */
> +static void check_ptrace(struct __test_metadata *_metadata,
> +               int sandbox_both, int sandbox_parent, int sandbox_child,
> +               int expect_ptrace)
> +{
> +       pid_t child;
> +       int status;
> +       int pipefd[2];
> +
> +       ASSERT_EQ(0, pipe(pipefd));
> +       if (sandbox_both)
> +               apply_null_sandbox(_metadata);
> +
> +       child = fork();
> +       ASSERT_LE(0, child);
> +       if (child == 0) {
> +               char buf;
> +
> +               EXPECT_EQ(0, close(pipefd[1]));
> +               if (sandbox_child)
> +                       apply_null_sandbox(_metadata);
> +
> +               /* test traceme */
> +               ASSERT_EQ(expect_ptrace, ptrace(PTRACE_TRACEME));
> +               if (expect_ptrace) {
> +                       ASSERT_EQ(EPERM, errno);
> +               } else {
> +                       ASSERT_EQ(0, raise(SIGSTOP));
> +               }
> +
> +               /* sync */
> +               ASSERT_EQ(1, read(pipefd[0], &buf, 1)) {
> +                       TH_LOG("Failed to read() sync from parent");
> +               }
> +               ASSERT_EQ('.', buf);
> +               _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
> +       }
> +
> +       EXPECT_EQ(0, close(pipefd[0]));
> +       if (sandbox_parent)
> +               apply_null_sandbox(_metadata);
> +
> +       /* test traceme */
> +       if (!expect_ptrace) {
> +               ASSERT_EQ(child, waitpid(child, &status, 0));
> +               ASSERT_EQ(1, WIFSTOPPED(status));
> +               ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
> +       }
> +       /* test attach */
> +       ASSERT_EQ(expect_ptrace, ptrace(PTRACE_ATTACH, child, NULL, 0));
> +       if (expect_ptrace) {
> +               ASSERT_EQ(EPERM, errno);
> +       } else {
> +               ASSERT_EQ(child, waitpid(child, &status, 0));
> +               ASSERT_EQ(1, WIFSTOPPED(status));
> +               ASSERT_EQ(0, ptrace(PTRACE_CONT, child, NULL, 0));
> +       }
> +
> +       /* sync */
> +       ASSERT_EQ(1, write(pipefd[1], ".", 1)) {
> +               TH_LOG("Failed to write() sync to child");
> +       }
> +       ASSERT_EQ(child, waitpid(child, &status, 0));
> +       if (WIFSIGNALED(status) || WEXITSTATUS(status))
> +               _metadata->passed = 0;
> +}
> +
> +TEST(ptrace_allow_without_sandbox)
> +{
> +       /* no sandbox */
> +       check_ptrace(_metadata, 0, 0, 0, 0);
> +}
> +
> +TEST(ptrace_allow_with_one_sandbox)
> +{
> +       /* child sandbox */
> +       check_ptrace(_metadata, 0, 0, 1, 0);
> +}
> +
> +TEST(ptrace_allow_with_nested_sandbox)
> +{
> +       /* inherited and child sandbox */
> +       check_ptrace(_metadata, 1, 0, 1, 0);
> +}
> +
> +TEST(ptrace_deny_with_parent_sandbox)
> +{
> +       /* parent sandbox */
> +       check_ptrace(_metadata, 0, 1, 0, -1);
> +}
> +
> +TEST(ptrace_deny_with_nested_and_parent_sandbox)
> +{
> +       /* inherited and parent sandbox */
> +       check_ptrace(_metadata, 1, 1, 0, -1);
> +}
> +
> +TEST(ptrace_deny_with_forked_sandbox)
> +{
> +       /* inherited, parent and child sandbox */
> +       check_ptrace(_metadata, 1, 1, 1, -1);
> +}
> +
> +TEST(ptrace_deny_with_sibling_sandbox)
> +{
> +       /* parent and child sandbox */
> +       check_ptrace(_metadata, 0, 1, 1, -1);
> +}
> +
> +TEST_HARNESS_MAIN
> --
> 2.11.0
>

Awesome. I love to see all these tests, with both positive and
negative checks. Nice!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-04-18 22:53   ` Kees Cook
@ 2017-04-18 23:24     ` Mickaël Salaün
  2017-04-18 23:48       ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-18 23:24 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development, Andrew Morton

[-- Attachment #1.1: Type: text/plain, Size: 26816 bytes --]



On 19/04/2017 00:53, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> The seccomp(2) syscall can be used by a task to apply a Landlock rule to
>> itself. As a seccomp filter, a Landlock rule is enforced for the current
>> task and all its future children. A rule is immutable and a task can
>> only add new restricting rules to itself, forming a chain of rules.
>>
>> A Landlock rule is tied to a Landlock event. If the use of a kernel
>> object is allowed by the other Linux security mechanisms (e.g. DAC,
>> capabilities, other LSM), then a Landlock event related to this kind of
>> object is triggered. The chain of rules for this event is then
>> evaluated. Each rule return a 32-bit value which can deny the use of a
>> kernel object with a non-zero value. If every rules of the chain return
>> zero, then the use of the object is allowed.
>>
>> Changes since v5:
>> * remove struct landlock_node and use a similar inheritance mechanisme
>>   as seccomp-bpf (requested by Andy Lutomirski)
>> * rename SECCOMP_ADD_LANDLOCK_RULE to SECCOMP_APPEND_LANDLOCK_RULE
>> * rename file manager.c to providers.c
>> * add comments
>> * typo and cosmetic fixes
>>
>> Changes since v4:
>> * merge manager and seccomp patches
>> * return -EFAULT in seccomp(2) when user_bpf_fd is null to easely check
>>   if Landlock is supported
>> * only allow a process with the global CAP_SYS_ADMIN to use Landlock
>>   (will be lifted in the future)
>> * add an early check to exit as soon as possible if the current process
>>   does not have Landlock rules
>>
>> Changes since v3:
>> * remove the hard link with seccomp (suggested by Andy Lutomirski and
>>   Kees Cook):
>>   * remove the cookie which could imply multiple evaluation of Landlock
>>     rules
>>   * remove the origin field in struct landlock_data
>> * remove documentation fix (merged upstream)
>> * rename the new seccomp command to SECCOMP_ADD_LANDLOCK_RULE
>> * internal renaming
>> * split commit
>> * new design to be able to inherit on the fly the parent rules
>>
>> Changes since v2:
>> * Landlock programs can now be run without seccomp filter but for any
>>   syscall (from the process) or interruption
>> * move Landlock related functions and structs into security/landlock/*
>>   (to manage cgroups as well)
>> * fix seccomp filter handling: run Landlock programs for each of their
>>   legitimate seccomp filter
>> * properly clean up all seccomp results
>> * cosmetic changes to ease the understanding
>> * fix some ifdef
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Andrew Morton <akpm@linux-foundation.org>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: James Morris <james.l.morris@oracle.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> Cc: Will Drewry <wad@chromium.org>
>> Link: https://lkml.kernel.org/r/c10a503d-5e35-7785-2f3d-25ed8dd63fab@digikod.net
>> ---
>>  include/linux/landlock.h      |  36 +++++++
>>  include/linux/seccomp.h       |   8 ++
>>  include/uapi/linux/seccomp.h  |   1 +
>>  kernel/fork.c                 |  14 ++-
>>  kernel/seccomp.c              |   8 ++
>>  security/landlock/Makefile    |   2 +-
>>  security/landlock/hooks.c     |  37 +++++++
>>  security/landlock/hooks.h     |   5 +
>>  security/landlock/init.c      |   3 +-
>>  security/landlock/providers.c | 232 ++++++++++++++++++++++++++++++++++++++++++
>>  10 files changed, 342 insertions(+), 4 deletions(-)
>>  create mode 100644 security/landlock/providers.c
>>
>> diff --git a/include/linux/landlock.h b/include/linux/landlock.h
>> index 53013dc374fe..c40ee78e86e0 100644
>> --- a/include/linux/landlock.h
>> +++ b/include/linux/landlock.h
>> @@ -12,6 +12,9 @@
>>  #define _LINUX_LANDLOCK_H
>>  #ifdef CONFIG_SECURITY_LANDLOCK
>>
>> +#include <linux/bpf.h> /* _LANDLOCK_SUBTYPE_EVENT_LAST */
>> +#include <linux/types.h> /* atomic_t */
>> +
>>  /*
>>   * This is not intended for the UAPI headers. Each userland software should use
>>   * a static minimal version for the required features as explained in the
>> @@ -19,5 +22,38 @@
>>   */
>>  #define LANDLOCK_VERSION 1
>>
>> +struct landlock_rule {
>> +       atomic_t usage;
> 
> This should be refcount_t. (And I should convert seccomp to use
> refcount_t too!) :)

OK

> 
>> +       struct landlock_rule *prev;
>> +       struct bpf_prog *prog;
>> +};
>> +
>> +/**
>> + * struct landlock_events - Landlock event rules enforced on a thread
>> + *
>> + * This is used for low performance impact when forking a process. Instead of
>> + * copying the full array and incrementing the usage of each entries, only
>> + * create a pointer to &struct landlock_events and increments its usage. When
>> + * appending a new rule, if &struct landlock_events is shared with other tasks,
>> + * then duplicate it and append the rule to this new &struct landlock_events.
>> + *
>> + * @usage: reference count to manage the object lifetime. When a thread need to
>> + *         add Landlock rules and if @usage is greater than 1, then the thread
>> + *         must duplicate &struct landlock_events to not change the children's
>> + *         rules as well.
>> + * @rules: array of non-NULL &struct landlock_rule pointers
>> + */
>> +struct landlock_events {
>> +       atomic_t usage;
>> +       struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST];
>> +};
>> +
>> +void put_landlock_events(struct landlock_events *events);
>> +
>> +#ifdef CONFIG_SECCOMP_FILTER
> 
> Isn't CONFIG_SECCOMP_FILTER already required for landlock?

Yes it is, but Landlock could only/also be used through cgroups in the
future. :)

> 
>> +int landlock_seccomp_append_prog(unsigned int flags,
>> +               const char __user *user_bpf_fd);
>> +#endif /* CONFIG_SECCOMP_FILTER */
>> +
>>  #endif /* CONFIG_SECURITY_LANDLOCK */
>>  #endif /* _LINUX_LANDLOCK_H */
>> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
>> index e25aee2cdfc0..9a38de3c0e72 100644
>> --- a/include/linux/seccomp.h
>> +++ b/include/linux/seccomp.h
>> @@ -10,6 +10,10 @@
>>  #include <linux/thread_info.h>
>>  #include <asm/seccomp.h>
>>
>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>> +struct landlock_events;
>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
> 
> Testing LANDLOCK should be sufficient, since it requires ..._FILTER.
> 
>> +
>>  struct seccomp_filter;
>>  /**
>>   * struct seccomp - the state of a seccomp'ed process
>> @@ -18,6 +22,7 @@ struct seccomp_filter;
>>   *         system calls available to a process.
>>   * @filter: must always point to a valid seccomp-filter or NULL as it is
>>   *          accessed without locking during system call entry.
>> + * @landlock_events: contains an array of Landlock rules.
>>   *
>>   *          @filter must only be accessed from the context of current as there
>>   *          is no read locking.
>> @@ -25,6 +30,9 @@ struct seccomp_filter;
>>  struct seccomp {
>>         int mode;
>>         struct seccomp_filter *filter;
>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>> +       struct landlock_events *landlock_events;
>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
> 
> Same.
> 
>>  };
>>
>>  #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
>> diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
>> index 0f238a43ff1e..74891cf60ca6 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_APPEND_LANDLOCK_RULE   2
>>
>>  /* Valid flags for SECCOMP_SET_MODE_FILTER */
>>  #define SECCOMP_FILTER_FLAG_TSYNC      1
>> diff --git a/kernel/fork.c b/kernel/fork.c
>> index a27d8e67ce33..14c09486c565 100644
>> --- a/kernel/fork.c
>> +++ b/kernel/fork.c
>> @@ -47,6 +47,7 @@
>>  #include <linux/security.h>
>>  #include <linux/hugetlb.h>
>>  #include <linux/seccomp.h>
>> +#include <linux/landlock.h>
>>  #include <linux/swap.h>
>>  #include <linux/syscalls.h>
>>  #include <linux/jiffies.h>
>> @@ -528,7 +529,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
>>          * the usage counts on the error path calling free_task.
>>          */
>>         tsk->seccomp.filter = NULL;
>> -#endif
>> +#ifdef CONFIG_SECURITY_LANDLOCK
>> +       tsk->seccomp.landlock_events = NULL;
>> +#endif /* CONFIG_SECURITY_LANDLOCK */
>> +#endif /* CONFIG_SECCOMP */
>>
>>         setup_thread_stack(tsk, orig);
>>         clear_user_return_notifier(tsk);
>> @@ -1405,7 +1409,13 @@ 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;
>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>> +       p->seccomp.landlock_events = current->seccomp.landlock_events;
>> +       if (p->seccomp.landlock_events)
>> +               atomic_inc(&p->seccomp.landlock_events->usage);
>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
> 
> Hrm. So, this needs config cleanup as above. Also, I'm going to need
> some help understanding the usage tracking on landlock_events (which
> should use a get/put rather than open-coding the _inc). I don't see
> why individual assignments are needed here. The only thing that
> matters is the usage bump. I would have expected no changes at all in
> this code, actually. The filter and the events share the same usage
> don't they?

Right, I can move the struct landlock_event into the struct
seccomp_filter. This should make the code cleaner.


> 
>>         /*
>>          * Explicitly enable no_new_privs here in case it got set
>> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
>> index 326f79e32127..d122829e6da1 100644
>> --- a/kernel/seccomp.c
>> +++ b/kernel/seccomp.c
>> @@ -34,6 +34,7 @@
>>  #include <linux/security.h>
>>  #include <linux/tracehook.h>
>>  #include <linux/uaccess.h>
>> +#include <linux/landlock.h>
>>
>>  /**
>>   * struct seccomp_filter - container for seccomp BPF programs
>> @@ -494,6 +495,9 @@ 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_events(tsk->seccomp.landlock_events);
>> +#endif /* CONFIG_SECURITY_LANDLOCK */
> 
> put_landlock_events() should be defined in a header file to be a
> static inline no-op if ..._LANDLOCK isn't defined. That way we can
> avoid all the #ifdef stuff here. Similarly, I think all of these
> should take just task_struct so that there isn't an exposed structure
> name which lets you avoid the #ifdef too.

Right

> 
>>  }
>>
>>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
>> @@ -813,6 +817,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);
>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>> +       case SECCOMP_APPEND_LANDLOCK_RULE:
>> +               return landlock_seccomp_append_prog(flags, uargs);
>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>>         default:
>>                 return -EINVAL;
>>         }
>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>> index c0db504a6335..da8ba8b5183e 100644
>> --- a/security/landlock/Makefile
>> +++ b/security/landlock/Makefile
>> @@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
>>
>>  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>>
>> -landlock-y := init.o hooks.o hooks_fs.o
>> +landlock-y := init.o providers.o hooks.o hooks_fs.o
>> diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c
>> index eaee8162ff70..4fa7d0b38d41 100644
>> --- a/security/landlock/hooks.c
>> +++ b/security/landlock/hooks.c
>> @@ -95,6 +95,38 @@ bool landlock_is_valid_access(int off, int size, enum bpf_access_type type,
>>         return true;
>>  }
>>
>> +/**
>> + * landlock_event_deny - run Landlock rules tied to an event
>> + *
>> + * @event_idx: event index in the rules array
>> + * @ctx: non-NULL eBPF context
>> + * @events: Landlock events pointer
>> + *
>> + * Return true if at least one rule deny the event.
>> + */
>> +static bool landlock_event_deny(u32 event_idx, const struct landlock_context *ctx,
>> +               struct landlock_events *events)
>> +{
>> +       struct landlock_rule *rule;
>> +
>> +       if (!events)
>> +               return false;
>> +
>> +       for (rule = events->rules[event_idx]; rule; rule = rule->prev) {
>> +               u32 ret;
>> +
>> +               if (WARN_ON(!rule->prog))
>> +                       continue;
>> +               rcu_read_lock();
>> +               ret = BPF_PROG_RUN(rule->prog, (void *)ctx);
>> +               rcu_read_unlock();
>> +               /* deny access if a program returns a value different than 0 */
>> +               if (ret)
>> +                       return true;
>> +       }
>> +       return false;
>> +}
>> +
>>  int landlock_decide(enum landlock_subtype_event event,
>>                 __u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook)
>>  {
>> @@ -111,5 +143,10 @@ int landlock_decide(enum landlock_subtype_event event,
>>                 .arg2 = ctx_values[1],
>>         };
>>
>> +#ifdef CONFIG_SECCOMP_FILTER
>> +       deny = landlock_event_deny(event_idx, &ctx,
>> +                       current->seccomp.landlock_events);
>> +#endif /* CONFIG_SECCOMP_FILTER */
> 
> Isn't ..._FILTER required?

Same as above, this is required for now but could change in the near
future. :)

> 
>> +
>>         return deny ? -EPERM : 0;
>>  }
>> diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h
>> index 2e180f6ed86b..dd0486a4c284 100644
>> --- a/security/landlock/hooks.h
>> +++ b/security/landlock/hooks.h
>> @@ -12,6 +12,7 @@
>>  #include <linux/bpf.h> /* enum bpf_access_type */
>>  #include <linux/lsm_hooks.h>
>>  #include <linux/sched.h> /* struct task_struct */
>> +#include <linux/seccomp.h>
>>
>>  /* separators */
>>  #define SEP_COMMA() ,
>> @@ -163,7 +164,11 @@ WRAP_TYPE_RAW_C;
>>
>>  static inline bool landlocked(const struct task_struct *task)
>>  {
>> +#ifdef CONFIG_SECCOMP_FILTER
>> +       return !!(task->seccomp.landlock_events);
>> +#else
>>         return false;
>> +#endif /* CONFIG_SECCOMP_FILTER */
>>  }
>>
>>  __init void landlock_register_hooks(struct security_hook_list *hooks, int count);
>> diff --git a/security/landlock/init.c b/security/landlock/init.c
>> index 1c2750e12dfa..ef8a3da69860 100644
>> --- a/security/landlock/init.c
>> +++ b/security/landlock/init.c
>> @@ -135,7 +135,8 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>>
>>  void __init landlock_add_hooks(void)
>>  {
>> -       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>> +       pr_info("landlock: Version %u, ready to sandbox with %s\n",
>> +                       LANDLOCK_VERSION, "seccomp");
>>         landlock_add_hooks_fs();
>>         security_add_hooks(NULL, 0, "landlock");
>>         bpf_register_prog_type(&bpf_landlock_type);
>> diff --git a/security/landlock/providers.c b/security/landlock/providers.c
>> new file mode 100644
>> index 000000000000..6d867a39c947
>> --- /dev/null
>> +++ b/security/landlock/providers.c
>> @@ -0,0 +1,232 @@
>> +/*
>> + * Landlock LSM - seccomp provider
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <asm/page.h> /* PAGE_SIZE */
>> +#include <linux/atomic.h> /* atomic_*(), smp_store_release() */
>> +#include <linux/bpf.h> /* bpf_prog_put() */
>> +#include <linux/filter.h> /* struct bpf_prog */
>> +#include <linux/kernel.h> /* round_up() */
>> +#include <linux/landlock.h>
>> +#include <linux/sched.h> /* current_cred(), task_no_new_privs() */
>> +#include <linux/security.h> /* security_capable_noaudit() */
>> +#include <linux/slab.h> /* alloc(), kfree() */
>> +#include <linux/types.h> /* atomic_t */
>> +#include <linux/uaccess.h> /* copy_from_user() */
>> +
>> +#include "common.h"
>> +
>> +static void put_landlock_rule(struct landlock_rule *rule)
>> +{
>> +       struct landlock_rule *orig = rule;
>> +
>> +       /* clean up single-reference branches iteratively */
>> +       while (orig && atomic_dec_and_test(&orig->usage)) {
>> +               struct landlock_rule *freeme = orig;
>> +
>> +               bpf_prog_put(orig->prog);
>> +               orig = orig->prev;
>> +               kfree(freeme);
>> +       }
>> +}
>> +
>> +void put_landlock_events(struct landlock_events *events)
>> +{
>> +       if (events && atomic_dec_and_test(&events->usage)) {
>> +               size_t i;
>> +
>> +               for (i = 0; i < ARRAY_SIZE(events->rules); i++)
>> +                       /* XXX: Do we need to use lockless_dereference() here? */
>> +                       put_landlock_rule(events->rules[i]);
>> +               kfree(events);
>> +       }
>> +}
>> +
>> +static struct landlock_events *new_landlock_events(void)
>> +{
>> +       struct landlock_events *ret;
>> +
>> +       /* array filled with NULL values */
>> +       ret = kzalloc(sizeof(*ret), GFP_KERNEL);
>> +       if (!ret)
>> +               return ERR_PTR(-ENOMEM);
>> +       atomic_set(&ret->usage, 1);
>> +       return ret;
>> +}
>> +
>> +static void add_landlock_rule(struct landlock_events *events,
>> +               struct landlock_rule *rule)
>> +{
>> +       /* subtype.landlock_rule.event > 0 for loaded programs */
>> +       u32 event_idx = get_index(rule->prog->subtype.landlock_rule.event);
>> +
>> +       rule->prev = events->rules[event_idx];
>> +       WARN_ON(atomic_read(&rule->usage));
>> +       atomic_set(&rule->usage, 1);
>> +       /* do not increment the previous rule usage */
>> +       smp_store_release(&events->rules[event_idx], rule);
>> +}
>> +
>> +/* limit Landlock events to 256KB */
>> +#define LANDLOCK_EVENTS_MAX_PAGES (1 << 6)
>> +
>> +/**
>> + * landlock_append_prog - attach a Landlock rule to @current_events
>> + *
>> + * @current_events: landlock_events pointer, must be locked (if needed) to
>> + *                  prevent a concurrent put/free. This pointer must not be
>> + *                  freed after the call.
>> + * @prog: non-NULL Landlock rule to append to @current_events. @prog will be
>> + *        owned by landlock_append_prog() and freed if an error happened.
>> + *
>> + * Return @current_events or a new pointer when OK. Return a pointer error
>> + * otherwise.
>> + */
>> +static struct landlock_events *landlock_append_prog(
>> +               struct landlock_events *current_events, struct bpf_prog *prog)
>> +{
>> +       struct landlock_events *new_events = current_events;
>> +       unsigned long pages;
>> +       struct landlock_rule *rule;
>> +       u32 event_idx;
>> +
>> +       if (prog->type != BPF_PROG_TYPE_LANDLOCK) {
>> +               new_events = ERR_PTR(-EINVAL);
>> +               goto put_prog;
>> +       }
>> +
>> +       /* validate memory size allocation */
>> +       pages = prog->pages;
>> +       if (current_events) {
>> +               size_t i;
>> +
>> +               for (i = 0; i < ARRAY_SIZE(current_events->rules); i++) {
>> +                       struct landlock_rule *walker_r;
>> +
>> +                       for (walker_r = current_events->rules[i]; walker_r;
>> +                                       walker_r = walker_r->prev)
>> +                               pages += walker_r->prog->pages;
>> +               }
>> +               /* count a struct landlock_events if we need to allocate one */
>> +               if (atomic_read(&current_events->usage) != 1)
>> +                       pages += round_up(sizeof(*current_events), PAGE_SIZE) /
>> +                               PAGE_SIZE;
>> +       }
>> +       if (pages > LANDLOCK_EVENTS_MAX_PAGES) {
>> +               new_events = ERR_PTR(-E2BIG);
>> +               goto put_prog;
>> +       }
>> +
>> +       rule = kzalloc(sizeof(*rule), GFP_KERNEL);
>> +       if (!rule) {
>> +               new_events = ERR_PTR(-ENOMEM);
>> +               goto put_prog;
>> +       }
>> +       rule->prog = prog;
>> +
>> +       /* subtype.landlock_rule.event > 0 for loaded programs */
>> +       event_idx = get_index(rule->prog->subtype.landlock_rule.event);
>> +
>> +       if (!new_events) {
>> +               /*
>> +                * If there is no Landlock events used by the current task,
>> +                * then create a new one.
>> +                */
>> +               new_events = new_landlock_events();
>> +               if (IS_ERR(new_events))
>> +                       goto put_rule;
> 
> Shouldn't bpf_prog_put() get called in the face of a rule failure too?
> Why separate exit paths?

You're right but put_landlock_rule() call bpf_prog_put() by itself.


> 
>> +       } else if (atomic_read(&current_events->usage) > 1) {
>> +               /*
>> +                * If the current task is not the sole user of its Landlock
>> +                * events, then duplicate them.
>> +                */
>> +               size_t i;
>> +
>> +               new_events = new_landlock_events();
>> +               if (IS_ERR(new_events))
>> +                       goto put_rule;
>> +               for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) {
>> +                       new_events->rules[i] =
>> +                               lockless_dereference(current_events->rules[i]);
>> +                       if (new_events->rules[i])
>> +                               atomic_inc(&new_events->rules[i]->usage);
> 
> I was going to ask: isn't the top-level usage counter sufficient to
> track rule lifetime? But I think I see how things are tracked now:
> each task_struct points to an array of rule list pointers. These
> tables are duplicated when additions are made (which means each table
> needs to be refcounted for the processes using it), and when a new
> table is created, all the refcounters on the rules are bumped (to
> track each table that references the rule), and when a new rule is
> added, it's just prepended to the list for the new table to point at.

That's right.

> 
> Does this mean that rules are processed in reverse?

Yes, the rules are processed from the newest to the oldest, as
seccomp-bpf does with filters.

> 
>> +               }
>> +
>> +               /*
>> +                * Landlock events from the current task will not be freed here
>> +                * because the usage is strictly greater than 1. It is only
>> +                * prevented to be freed by another subject thanks to the
>> +                * caller of landlock_append_prog() which should be locked if
>> +                * needed.
>> +                */
>> +               put_landlock_events(current_events);
>> +       }
>> +       add_landlock_rule(new_events, rule);
>> +       return new_events;
>> +
>> +put_prog:
>> +       bpf_prog_put(prog);
>> +       return new_events;
>> +
>> +put_rule:
>> +       put_landlock_rule(rule);
>> +       return new_events;
>> +}
>> +
>> +/**
>> + * landlock_seccomp_append_prog - attach a Landlock rule to the current process
>> + *
>> + * current->seccomp.landlock_events is lazily allocated. When a process fork,
>> + * only a pointer is copied. When a new event is added by a process, if there
>> + * is other references to this process' landlock_events, then a new allocation
>> + * is made to contain an array pointing to Landlock rule lists. This design
>> + * enable low-performance impact and is memory efficient while keeping the
>> + * property of append-only rules.
>> + *
>> + * @flags: not used for now, but could be used for TSYNC
>> + * @user_bpf_fd: file descriptor pointing to a loaded Landlock rule
>> + */
>> +#ifdef CONFIG_SECCOMP_FILTER
>> +int landlock_seccomp_append_prog(unsigned int flags,
>> +               const char __user *user_bpf_fd)
>> +{
>> +       struct landlock_events *new_events;
>> +       struct bpf_prog *prog;
>> +       int bpf_fd;
>> +
>> +       /* force no_new_privs to limit privilege escalation */
>> +       if (!task_no_new_privs(current))
>> +               return -EPERM;
>> +       /* will be removed in the future to allow unprivileged tasks */
>> +       if (!capable(CAP_SYS_ADMIN))
>> +               return -EPERM;
>> +       /* enable to check if Landlock is supported with early EFAULT */
>> +       if (!user_bpf_fd)
>> +               return -EFAULT;
>> +       if (flags)
>> +               return -EINVAL;
>> +       if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(bpf_fd)))
>> +               return -EFAULT;
> 
> I think this can just be get_user()?

Yes, I didn't know about that.

> 
>> +       prog = bpf_prog_get(bpf_fd);
>> +       if (IS_ERR(prog))
>> +               return PTR_ERR(prog);
>> +
>> +       /*
>> +        * We don't need to lock anything for the current process hierarchy,
>> +        * everything is guarded by the atomic counters.
>> +        */
>> +       new_events = landlock_append_prog(current->seccomp.landlock_events,
>> +                       prog);
>> +       /* @prog is managed/freed by landlock_append_prog() */
> 
> Does kmemcheck notice this "leak"? (i.e. is further annotation needed?)

I didn't enable kmemcheck, I will take a look at it.

> 
>> +       if (IS_ERR(new_events))
>> +               return PTR_ERR(new_events);
>> +       current->seccomp.landlock_events = new_events;
>> +       return 0;
>> +}
>> +#endif /* CONFIG_SECCOMP_FILTER */
>> --
>> 2.11.0
>>
> 
> 
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
  2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
                   ` (10 preceding siblings ...)
  2017-03-28 23:46 ` [PATCH net-next v6 11/11] landlock: Add user and kernel documentation " Mickaël Salaün
@ 2017-04-18 23:26 ` Kees Cook
  2017-04-19  0:12   ` Mickaël Salaün
  11 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:26 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> This sixth series add some changes to the previous one [1], including a simpler
> rule inheritance hierarchy (similar to seccomp-bpf), a ptrace scope protection,
> some file renaming (better feature identification per file), a future-proof
> eBPF subtype and miscellaneous cosmetic fixes.

Sorry for the delay in review! I finally had a chunk of time I could
devote to this. I really like how it's heading. I still wonder about
its overlap with seccomp (it's really only using the syscall now...),
but that's just a detail. Getting the abstraction away from direct LSM
hooks looks good.

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

I understand why you've taken a progressive approach here, but I think
there are two fundamental areas where people will use Landlock: path
evaluation and network address evaluation. I think it's worth
expanding this series to include those two confinement examples, since
that can help people understand what the "general" case will look
like.

As I mentioned in one of the patch review emails, I think there needs
to be a clearer explanation of how usage counting works vs the
"events" (which I think of as "rule tables" not events -- maybe it
needs a new name?) and "rule" lists. I think I understand it, but I
spent a lot of time trying to get there. More comments would help.

Finally, another thing I'm curious about is how to deal with the
thread-sync issue. Seccomp uses its TSYNC thing, and I'd expect we'd
want something similar for landlock... but that looks really hairy as
far as locking goes. Perhaps it's already solved by using the same
locking seccomp uses, in which case I'm less inclined to kick landlock
out of seccomp.c. :)

Looks like it's coming along nicely! Thanks for continuing to work on this!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
  2017-04-18 23:06   ` Kees Cook
@ 2017-04-18 23:35     ` Mickaël Salaün
  0 siblings, 0 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-18 23:35 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 14194 bytes --]


On 19/04/2017 01:06, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> Add a basic sandbox tool to create a process isolated from some part of
>> the system. This sandbox create a read-only environment. It is only
>> allowed to write to a character device such as a TTY:
>>
>>   # :> X
>>   # echo $?
>>   0
>>   # ./samples/bpf/landlock1 /bin/sh -i
>>   Launching a new sandboxed process.
>>   # :> Y
>>   cannot create Y: Operation not permitted
>>
>> Changes since v5:
>> * cosmetic fixes
>> * rebase
>>
>> Changes since v4:
>> * write Landlock rule in C and compiled it with LLVM
>> * remove cgroup handling
>> * remove path handling: only handle a read-only environment
>> * remove errno return codes
>>
>> Changes since v3:
>> * remove seccomp and origin field: completely free from seccomp programs
>> * handle more FS-related hooks
>> * handle inode hooks and directory traversal
>> * add faked but consistent view thanks to ENOENT
>> * add /lib64 in the example
>> * fix spelling
>> * rename some types and definitions (e.g. SECCOMP_ADD_LANDLOCK_RULE)
>>
>> Changes since v2:
>> * use BPF_PROG_ATTACH for cgroup handling
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: James Morris <james.l.morris@oracle.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> ---
>>  samples/bpf/Makefile         |   4 ++
>>  samples/bpf/bpf_load.c       |  31 +++++++++++--
>>  samples/bpf/landlock1_kern.c |  46 +++++++++++++++++++
>>  samples/bpf/landlock1_user.c | 102 +++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 179 insertions(+), 4 deletions(-)
>>  create mode 100644 samples/bpf/landlock1_kern.c
>>  create mode 100644 samples/bpf/landlock1_user.c
>>
>> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
>> index d42b495b0992..4743674a3fa3 100644
>> --- a/samples/bpf/Makefile
>> +++ b/samples/bpf/Makefile
>> @@ -36,6 +36,7 @@ hostprogs-y += lwt_len_hist
>>  hostprogs-y += xdp_tx_iptunnel
>>  hostprogs-y += test_map_in_map
>>  hostprogs-y += per_socket_stats_example
>> +hostprogs-y += landlock1
>>
>>  # Libbpf dependencies
>>  LIBBPF := ../../tools/lib/bpf/bpf.o
>> @@ -76,6 +77,7 @@ lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
>>  xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
>>  test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
>>  per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o
>> +landlock1-objs := bpf_load.o $(LIBBPF) landlock1_user.o
>>
>>  # Tell kbuild to always build the programs
>>  always := $(hostprogs-y)
>> @@ -111,6 +113,7 @@ always += lwt_len_hist_kern.o
>>  always += xdp_tx_iptunnel_kern.o
>>  always += test_map_in_map_kern.o
>>  always += cookie_uid_helper_example.o
>> +always += landlock1_kern.o
>>
>>  HOSTCFLAGS += -I$(objtree)/usr/include
>>  HOSTCFLAGS += -I$(srctree)/tools/lib/
>> @@ -146,6 +149,7 @@ HOSTLOADLIBES_tc_l2_redirect += -l elf
>>  HOSTLOADLIBES_lwt_len_hist += -l elf
>>  HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
>>  HOSTLOADLIBES_test_map_in_map += -lelf
>> +HOSTLOADLIBES_landlock1 += -lelf
>>
>>  # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
>>  #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
>> diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
>> index 4a3460d7c01f..3713e5e2e998 100644
>> --- a/samples/bpf/bpf_load.c
>> +++ b/samples/bpf/bpf_load.c
>> @@ -29,6 +29,8 @@
>>
>>  static char license[128];
>>  static int kern_version;
>> +static union bpf_prog_subtype subtype = {};
>> +static bool has_subtype;
>>  static bool processed_sec[128];
>>  char bpf_log_buf[BPF_LOG_BUF_SIZE];
>>  int map_fd[MAX_MAPS];
>> @@ -68,6 +70,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>>         bool is_perf_event = strncmp(event, "perf_event", 10) == 0;
>>         bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0;
>>         bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0;
>> +       bool is_landlock = strncmp(event, "landlock", 8) == 0;
>>         size_t insns_cnt = size / sizeof(struct bpf_insn);
>>         enum bpf_prog_type prog_type;
>>         char buf[256];
>> @@ -94,6 +97,13 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>>                 prog_type = BPF_PROG_TYPE_CGROUP_SKB;
>>         } else if (is_cgroup_sk) {
>>                 prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
>> +       } else if (is_landlock) {
>> +               prog_type = BPF_PROG_TYPE_LANDLOCK;
>> +               if (!has_subtype) {
>> +                       printf("No subtype\n");
>> +                       return -1;
>> +               }
>> +               st = &subtype;
>>         } else {
>>                 printf("Unknown event '%s'\n", event);
>>                 return -1;
>> @@ -108,7 +118,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>>
>>         prog_fd[prog_cnt++] = fd;
>>
>> -       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
>> +       if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
>> +           is_landlock)
>>                 return 0;
>>
>>         if (is_socket) {
>> @@ -294,6 +305,7 @@ int load_bpf_file(char *path)
>>         kern_version = 0;
>>         memset(license, 0, sizeof(license));
>>         memset(processed_sec, 0, sizeof(processed_sec));
>> +       has_subtype = false;
>>
>>         if (elf_version(EV_CURRENT) == EV_NONE)
>>                 return 1;
>> @@ -339,6 +351,16 @@ int load_bpf_file(char *path)
>>                         processed_sec[i] = true;
>>                         if (load_maps(data->d_buf, data->d_size))
>>                                 return 1;
>> +               } else if (strcmp(shname, "subtype") == 0) {
>> +                       processed_sec[i] = true;
>> +                       if (data->d_size != sizeof(union bpf_prog_subtype)) {
>> +                               printf("invalid size of subtype section %zd\n",
>> +                                      data->d_size);
>> +                               return 1;
>> +                       }
>> +                       memcpy(&subtype, data->d_buf,
>> +                              sizeof(union bpf_prog_subtype));
>> +                       has_subtype = true;
>>                 } else if (shdr.sh_type == SHT_SYMTAB) {
>>                         symbols = data;
>>                 }
>> @@ -376,14 +398,14 @@ int load_bpf_file(char *path)
>>                             memcmp(shname_prog, "xdp", 3) == 0 ||
>>                             memcmp(shname_prog, "perf_event", 10) == 0 ||
>>                             memcmp(shname_prog, "socket", 6) == 0 ||
>> -                           memcmp(shname_prog, "cgroup/", 7) == 0)
>> +                           memcmp(shname_prog, "cgroup/", 7) == 0 ||
>> +                           memcmp(shname_prog, "landlock", 8) == 0)
>>                                 load_and_attach(shname_prog, insns, data_prog->d_size);
>>                 }
>>         }
>>
>>         /* load programs that don't use maps */
>>         for (i = 1; i < ehdr.e_shnum; i++) {
>> -
>>                 if (processed_sec[i])
>>                         continue;
>>
>> @@ -396,7 +418,8 @@ int load_bpf_file(char *path)
>>                     memcmp(shname, "xdp", 3) == 0 ||
>>                     memcmp(shname, "perf_event", 10) == 0 ||
>>                     memcmp(shname, "socket", 6) == 0 ||
>> -                   memcmp(shname, "cgroup/", 7) == 0)
>> +                   memcmp(shname, "cgroup/", 7) == 0 ||
>> +                   memcmp(shname, "landlock", 8) == 0)
>>                         load_and_attach(shname, data->d_buf, data->d_size);
>>         }
>>
>> diff --git a/samples/bpf/landlock1_kern.c b/samples/bpf/landlock1_kern.c
>> new file mode 100644
>> index 000000000000..b8a9b0ca84c9
>> --- /dev/null
>> +++ b/samples/bpf/landlock1_kern.c
>> @@ -0,0 +1,46 @@
>> +/*
>> + * Landlock rule - partial read-only filesystem
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define KBUILD_MODNAME "foo"
>> +#include <uapi/linux/bpf.h>
>> +#include <uapi/linux/stat.h> /* S_ISCHR() */
>> +#include "bpf_helpers.h"
>> +
>> +SEC("landlock1")
>> +static int landlock_fs_prog1(struct landlock_context *ctx)
> 
> Since this is in samples, I think this needs a lot more comments to
> describe each pieces, how it fits together, etc. This is where people
> are going to come to learn how to use landlock, so making it as clear
> as possible is important. This is especially true for each step of the
> rule logic. (e.g. some will wonder why is S_ISCHR excluded, etc.)

Right, I already extended a bit this example with a S_ISFIFO() and some
comments. There is also the Documentation/.../user.rst which should help
understand how it works.


> 
>> +{
>> +       char fmt_error[] = "landlock1: error: get_mode:%lld\n";
>> +       char fmt_name[] = "landlock1: syscall:%d\n";
>> +       long long ret;
>> +
>> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
>> +               return 0;
>> +       ret = bpf_handle_fs_get_mode((void *)ctx->arg1);
>> +       if (ret < 0) {
>> +               bpf_trace_printk(fmt_error, sizeof(fmt_error), ret);
>> +               return 1;
>> +       }
>> +       if (S_ISCHR(ret))
>> +               return 0;
>> +       bpf_trace_printk(fmt_name, sizeof(fmt_name), ctx->syscall_nr);
>> +       return 1;
>> +}
>> +
>> +SEC("subtype")
>> +static union bpf_prog_subtype _subtype = {
> 
> Can this be const?

Yes

> 
>> +       .landlock_rule = {
>> +               .version = 1,
>> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +               .ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG,
>> +       }
>> +};
>> +
>> +SEC("license")
>> +static const char _license[] = "GPL";
>> diff --git a/samples/bpf/landlock1_user.c b/samples/bpf/landlock1_user.c
>> new file mode 100644
>> index 000000000000..6f79eb0ee6db
>> --- /dev/null
>> +++ b/samples/bpf/landlock1_user.c
>> @@ -0,0 +1,102 @@
>> +/*
>> + * Landlock sandbox - partial read-only filesystem
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include "bpf_load.h"
>> +#include "libbpf.h"
>> +
>> +#define _GNU_SOURCE
>> +#include <errno.h>
>> +#include <fcntl.h> /* open() */
>> +#include <linux/bpf.h>
>> +#include <linux/filter.h>
>> +#include <linux/prctl.h>
>> +#include <linux/seccomp.h>
>> +#include <stddef.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <sys/prctl.h>
>> +#include <sys/syscall.h>
>> +#include <unistd.h>
>> +
>> +#ifndef seccomp
>> +static int seccomp(unsigned int op, unsigned int flags, void *args)
>> +{
>> +       errno = 0;
>> +       return syscall(__NR_seccomp, op, flags, args);
>> +}
>> +#endif
>> +
>> +#define ARRAY_SIZE(a)  (sizeof(a) / sizeof(a[0]))
>> +#define MAX_ERRNO      4095
> 
> Is MAX_ERRNO actually needed?

Not anymore.

> 
>> +
>> +
>> +struct landlock_rule {
>> +       enum landlock_subtype_event event;
>> +       struct bpf_insn *bpf;
>> +       size_t size;
>> +};
>> +
>> +static int apply_sandbox(int prog_fd)
>> +{
>> +       int ret = 0;
>> +
>> +       /* set up the test sandbox */
>> +       if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
>> +               perror("prctl(no_new_priv)");
>> +               return 1;
>> +       }
>> +       if (seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog_fd)) {
>> +               perror("seccomp(set_hook)");
>> +               ret = 1;
>> +       }
>> +       close(prog_fd);
>> +
>> +       return ret;
>> +}
>> +
>> +int main(int argc, char * const argv[], char * const *envp)
>> +{
>> +       char filename[256];
>> +       char *cmd_path;
>> +       char * const *cmd_argv;
>> +
>> +       if (argc < 2) {
>> +               fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]);
>> +               fprintf(stderr, "Launch a command in a read-only environment "
>> +                               "(except for character devices).\n");
>> +               fprintf(stderr, "Display debug with: "
>> +                               "cat /sys/kernel/debug/tracing/trace_pipe &\n");
>> +               return 1;
>> +       }
>> +
>> +       snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
>> +       if (load_bpf_file(filename)) {
>> +               printf("%s", bpf_log_buf);
>> +               return 1;
>> +       }
>> +       if (!prog_fd[0]) {
>> +               if (errno) {
>> +                       printf("load_bpf_file: %s\n", strerror(errno));
>> +               } else {
>> +                       printf("load_bpf_file: Error\n");
>> +               }
>> +               return 1;
>> +       }
>> +
>> +       if (apply_sandbox(prog_fd[0]))
>> +               return 1;
>> +       cmd_path = argv[1];
>> +       cmd_argv = argv + 1;
>> +       fprintf(stderr, "Launching a new sandboxed process.\n");
>> +       execve(cmd_path, cmd_argv, envp);
>> +       perror("execve");
>> +       return 1;
>> +}
> 
> I like this example. It's a very powerful rule to for a program to
> enforce on itself. :)
> 
> -Kees
> 

Thanks!


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-18 22:44     ` Mickaël Salaün
  2017-04-18 23:16       ` Casey Schaufler
@ 2017-04-18 23:39       ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:39 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Apr 18, 2017 at 3:44 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
> On 19/04/2017 00:17, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> Handle 33 filesystem-related LSM hooks for the Landlock filesystem
>>> event: LANDLOCK_SUBTYPE_EVENT_FS.
>>>
>>> A Landlock event wrap LSM hooks for similar kernel object types (e.g.
>>> struct file, struct path...). Multiple LSM hooks can trigger the same
>>> Landlock event.
>>>
>>> Landlock handle nine coarse-grained actions: read, write, execute, new,
>>> get, remove, ioctl, lock and fcntl. Each of them abstract LSM hook
>>> access control in a way that can be extended in the future.
>>>
>>> The Landlock LSM hook registration is done after other LSM to only run
>>> actions from user-space, via eBPF programs, if the access was granted by
>>> major (privileged) LSMs.
>>>
>>> Changes since v5:
>>> * split hooks.[ch] into hooks.[ch] and hooks_fs.[ch]
>>> * add more documentation
>>> * cosmetic fixes
>>>
>>> Changes since v4:
>>> * add LSM hook abstraction called Landlock event
>>>   * use the compiler type checking to verify hooks use by an event
>>>   * handle all filesystem related LSM hooks (e.g. file_permission,
>>>     mmap_file, sb_mount...)
>>> * register BPF programs for Landlock just after LSM hooks registration
>>> * move hooks registration after other LSMs
>>> * add failsafes to check if a hook is not used by the kernel
>>> * allow partial raw value access form the context (needed for programs
>>>   generated by LLVM)
>>>
>>> Changes since v3:
>>> * split commit
>>> * add hooks dealing with struct inode and struct path pointers:
>>>   inode_permission and inode_getattr
>>> * add abstraction over eBPF helper arguments thanks to wrapping structs
>>>
>>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>> Cc: Alexei Starovoitov <ast@kernel.org>
>>> Cc: Andy Lutomirski <luto@amacapital.net>
>>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>>> Cc: David S. Miller <davem@davemloft.net>
>>> Cc: James Morris <james.l.morris@oracle.com>
>>> Cc: Kees Cook <keescook@chromium.org>
>>> Cc: Serge E. Hallyn <serge@hallyn.com>
>>> ---
>>>  include/linux/lsm_hooks.h    |   5 +
>>>  security/landlock/Makefile   |   4 +-
>>>  security/landlock/hooks.c    | 115 +++++++++
>>>  security/landlock/hooks.h    | 177 ++++++++++++++
>>>  security/landlock/hooks_fs.c | 563 +++++++++++++++++++++++++++++++++++++++++++
>>>  security/landlock/hooks_fs.h |  19 ++
>>>  security/landlock/init.c     |  13 +
>>>  security/security.c          |   7 +-
>>>  8 files changed, 901 insertions(+), 2 deletions(-)
>>>  create mode 100644 security/landlock/hooks.c
>>>  create mode 100644 security/landlock/hooks.h
>>>  create mode 100644 security/landlock/hooks_fs.c
>>>  create mode 100644 security/landlock/hooks_fs.h
>>>
>>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>>> index e29d4c62a3c8..884289166a0e 100644
>>> --- a/include/linux/lsm_hooks.h
>>> +++ b/include/linux/lsm_hooks.h
>>> @@ -1920,5 +1920,10 @@ void __init loadpin_add_hooks(void);
>>>  #else
>>>  static inline void loadpin_add_hooks(void) { };
>>>  #endif
>>> +#ifdef CONFIG_SECURITY_LANDLOCK
>>> +extern void __init landlock_add_hooks(void);
>>> +#else
>>> +static inline void __init landlock_add_hooks(void) { }
>>> +#endif /* CONFIG_SECURITY_LANDLOCK */
>>>
>>>  #endif /* ! __LINUX_LSM_HOOKS_H */
>>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>>> index 7205f9a7a2ee..c0db504a6335 100644
>>> --- a/security/landlock/Makefile
>>> +++ b/security/landlock/Makefile
>>> @@ -1,3 +1,5 @@
>>> +ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function
>>
>> Why is this needed? If it can't be avoided, a comment should exist
>> here explaining why.
>
> This is useful to catch defined but unused hooks: error out if a
> HOOK_NEW_FS(foo) is not used with a HOOK_INIT_FS(foo) in the struct
> security_hook_list landlock_hooks.

Gotcha. Please convert into a comment for the next revision. :)

>
>>
>>> [...]
>>> @@ -127,3 +132,11 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = {
>>>         .ops = &bpf_landlock_ops,
>>>         .type = BPF_PROG_TYPE_LANDLOCK,
>>>  };
>>> +
>>> +void __init landlock_add_hooks(void)
>>> +{
>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>> +       landlock_add_hooks_fs();
>>> +       security_add_hooks(NULL, 0, "landlock");
>>> +       bpf_register_prog_type(&bpf_landlock_type);
>>
>> I'm confused by the separation of hook registration here. The call to
>> security_add_hooks is with count=0 is especially weird. Why isn't this
>> just a single call with security_add_hooks(landlock_hooks,
>> ARRAY_SIZE(landlock_hooks), "landlock")?
>
> Yes, this is ugly with the new security_add_hooks() with three arguments
> but I wanted to split the hooks definition in multiple files.
>
> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
> is not exported. Unfortunately, calling multiple security_add_hooks()
> with the same LSM name would register multiple names for the same LSM…
> Is it OK if I modify this function to not add duplicated entries?

I'm not sure which would be more sane: a single table (constructed
from header-defined function declarations), or changes to this core
area.

>>> diff --git a/security/security.c b/security/security.c
>>> index d0e07f269b2d..a3e9f4625991 100644
>>> --- a/security/security.c
>>> +++ b/security/security.c
>>> @@ -64,10 +64,15 @@ int __init security_init(void)
>>>         loadpin_add_hooks();
>>>
>>>         /*
>>> -        * Load all the remaining security modules.
>>> +        * Load all remaining privileged security modules.
>>>          */
>>>         do_security_initcalls();
>>>
>>> +       /*
>>> +        * Load potentially-unprivileged security modules at the end.
>>> +        */
>>> +       landlock_add_hooks();
>>
>> Oh, is this to make it last in the list? Is there a reason it has to be last?
>
> Right, this is the intend. I'm not sure it is the only way to register
> hooks, though.
>
> For an unprivileged access-control, we don't want to give the ability to
> any process to do some checks, through an eBPF program, on kernel
> objects (e.g. files) if they should not be accessible (because of a
> following LSM hook check).

Ah right, yes. I thought some of that was already going to be handled
by getting handles to things, but yeah, best to run last. (If you can
expand the comment above the call to landlock_add_hooks() that would
be great.)

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-18 23:16       ` Casey Schaufler
@ 2017-04-18 23:40         ` Kees Cook
  2017-04-19 22:03           ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:40 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Mickaël Salaün, LKML, Alexei Starovoitov,
	Andy Lutomirski, Arnaldo Carvalho de Melo, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Apr 18, 2017 at 4:16 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> On 4/18/2017 3:44 PM, Mickaël Salaün wrote:
>> On 19/04/2017 00:17, Kees Cook wrote:
>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>> +void __init landlock_add_hooks(void)
>>>> +{
>>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>>> +       landlock_add_hooks_fs();
>>>> +       security_add_hooks(NULL, 0, "landlock");
>>>> +       bpf_register_prog_type(&bpf_landlock_type);
>>> I'm confused by the separation of hook registration here. The call to
>>> security_add_hooks is with count=0 is especially weird. Why isn't this
>>> just a single call with security_add_hooks(landlock_hooks,
>>> ARRAY_SIZE(landlock_hooks), "landlock")?
>> Yes, this is ugly with the new security_add_hooks() with three arguments
>> but I wanted to split the hooks definition in multiple files.
>
> Why? I'll buy a good argument, but there are dangers in
> allowing multiple calls to security_add_hooks().
>
>>
>> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
>> is not exported. Unfortunately, calling multiple security_add_hooks()
>> with the same LSM name would register multiple names for the same LSM…
>> Is it OK if I modify this function to not add duplicated entries?
>
> It may seem absurd, but it's conceivable that a module might
> have two hooks it wants called. My example is a module that
> counts the number of times SELinux denies a process access to
> things (which needs to be called before and after SELinux in
> order to detect denials) and takes "appropriate action" if
> too many denials occur. It would be weird, wonky and hackish,
> but that never stopped anybody before.

If ends up being sane and clear, I'm fine with allowing multiple calls.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-04-18 23:24     ` Mickaël Salaün
@ 2017-04-18 23:48       ` Kees Cook
  0 siblings, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development, Andrew Morton

On Tue, Apr 18, 2017 at 4:24 PM, Mickaël Salaün <mic@digikod.net> wrote:
> On 19/04/2017 00:53, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> +#ifdef CONFIG_SECCOMP_FILTER
>>
>> Isn't CONFIG_SECCOMP_FILTER already required for landlock?
>
> Yes it is, but Landlock could only/also be used through cgroups in the
> future. :)

Hm, okay. I still feel like the configs could be more sensible. :)

>>> @@ -1405,7 +1409,13 @@ 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;
>>> +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK)
>>> +       p->seccomp.landlock_events = current->seccomp.landlock_events;
>>> +       if (p->seccomp.landlock_events)
>>> +               atomic_inc(&p->seccomp.landlock_events->usage);
>>> +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */
>>
>> Hrm. So, this needs config cleanup as above. Also, I'm going to need
>> some help understanding the usage tracking on landlock_events (which
>> should use a get/put rather than open-coding the _inc). I don't see
>> why individual assignments are needed here. The only thing that
>> matters is the usage bump. I would have expected no changes at all in
>> this code, actually. The filter and the events share the same usage
>> don't they?
>
> Right, I can move the struct landlock_event into the struct
> seccomp_filter. This should make the code cleaner.

No, that wasn't my point. I meant that since landlock_events is
already in ->seccomp, it's already copied by p->seccomp =
current->seccomp. The only thing you need is a
get_seccomp_landlock(current) call before the copy:

get_seccomp_filter(current);
get_seccomp_landlock(current);
p->seccomp = current->seccomp;

done! :)

And get_seccomp_landlock() can do a check for landlock_events existing, etc etc.

>>> +       if (!new_events) {
>>> +               /*
>>> +                * If there is no Landlock events used by the current task,
>>> +                * then create a new one.
>>> +                */
>>> +               new_events = new_landlock_events();
>>> +               if (IS_ERR(new_events))
>>> +                       goto put_rule;
>>
>> Shouldn't bpf_prog_put() get called in the face of a rule failure too?
>> Why separate exit paths?
>
> You're right but put_landlock_rule() call bpf_prog_put() by itself.

Ah! Missed that, thanks!

>>> +       } else if (atomic_read(&current_events->usage) > 1) {
>>> +               /*
>>> +                * If the current task is not the sole user of its Landlock
>>> +                * events, then duplicate them.
>>> +                */
>>> +               size_t i;
>>> +
>>> +               new_events = new_landlock_events();
>>> +               if (IS_ERR(new_events))
>>> +                       goto put_rule;
>>> +               for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) {
>>> +                       new_events->rules[i] =
>>> +                               lockless_dereference(current_events->rules[i]);
>>> +                       if (new_events->rules[i])
>>> +                               atomic_inc(&new_events->rules[i]->usage);
>>
>> I was going to ask: isn't the top-level usage counter sufficient to
>> track rule lifetime? But I think I see how things are tracked now:
>> each task_struct points to an array of rule list pointers. These
>> tables are duplicated when additions are made (which means each table
>> needs to be refcounted for the processes using it), and when a new
>> table is created, all the refcounters on the rules are bumped (to
>> track each table that references the rule), and when a new rule is
>> added, it's just prepended to the list for the new table to point at.
>
> That's right.

Okay, excellent. This should end up in a comment somewhere so when I
forget I can go read it again. ;)

>> Does this mean that rules are processed in reverse?
>
> Yes, the rules are processed from the newest to the oldest, as
> seccomp-bpf does with filters.

Cool. If not already mentioned, that should end up in the docs somewhere.

>>> +       if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(bpf_fd)))
>>> +               return -EFAULT;
>>
>> I think this can just be get_user()?
>
> Yes, I didn't know about that.

No worries. It's nice for small things. :)

>>> +       prog = bpf_prog_get(bpf_fd);
>>> +       if (IS_ERR(prog))
>>> +               return PTR_ERR(prog);
>>> +
>>> +       /*
>>> +        * We don't need to lock anything for the current process hierarchy,
>>> +        * everything is guarded by the atomic counters.
>>> +        */
>>> +       new_events = landlock_append_prog(current->seccomp.landlock_events,
>>> +                       prog);
>>> +       /* @prog is managed/freed by landlock_append_prog() */
>>
>> Does kmemcheck notice this "leak"? (i.e. is further annotation needed?)
>
> I didn't enable kmemcheck, I will take a look at it.

Yeah, I'd turn on at least these while you're testing:

CONFIG_PROVE_LOCKING=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_KMEMLEAK=y

I'm sure people will suggest more, too. :)

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
  2017-04-18 23:16   ` Kees Cook
@ 2017-04-18 23:53     ` Mickaël Salaün
  2017-04-18 23:59       ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-18 23:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 34260 bytes --]


On 19/04/2017 01:16, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> Test basic context access, ptrace protection and filesystem event with
>> multiple cases.
>>
>> Changes since v5:
>> * add subtype test
>> * add ptrace tests
>> * split and rename files
>> * cleanup and rebase
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: James Morris <james.l.morris@oracle.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> Cc: Shuah Khan <shuah@kernel.org>
>> Cc: Will Drewry <wad@chromium.org>
>> ---
>>  tools/testing/selftests/Makefile                   |   1 +
>>  tools/testing/selftests/bpf/test_verifier.c        |  64 +++++
>>  tools/testing/selftests/landlock/.gitignore        |   4 +
>>  tools/testing/selftests/landlock/Makefile          |  47 ++++
>>  tools/testing/selftests/landlock/rules/Makefile    |  52 ++++
>>  tools/testing/selftests/landlock/rules/README.rst  |   1 +
>>  .../testing/selftests/landlock/rules/bpf_helpers.h |   1 +
>>  .../testing/selftests/landlock/rules/fs_no_open.c  |  31 +++
>>  .../selftests/landlock/rules/fs_read_only.c        |  31 +++
>>  tools/testing/selftests/landlock/test.h            |  35 +++
>>  tools/testing/selftests/landlock/test_base.c       |  31 +++
>>  tools/testing/selftests/landlock/test_fs.c         | 305 +++++++++++++++++++++
>>  tools/testing/selftests/landlock/test_ptrace.c     | 161 +++++++++++
>>  13 files changed, 764 insertions(+)
>>  create mode 100644 tools/testing/selftests/landlock/.gitignore
>>  create mode 100644 tools/testing/selftests/landlock/Makefile
>>  create mode 100644 tools/testing/selftests/landlock/rules/Makefile
>>  create mode 120000 tools/testing/selftests/landlock/rules/README.rst
>>  create mode 120000 tools/testing/selftests/landlock/rules/bpf_helpers.h
>>  create mode 100644 tools/testing/selftests/landlock/rules/fs_no_open.c
>>  create mode 100644 tools/testing/selftests/landlock/rules/fs_read_only.c
>>  create mode 100644 tools/testing/selftests/landlock/test.h
>>  create mode 100644 tools/testing/selftests/landlock/test_base.c
>>  create mode 100644 tools/testing/selftests/landlock/test_fs.c
>>  create mode 100644 tools/testing/selftests/landlock/test_ptrace.c
>>
>> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
>> index d8593f1251ec..b584ad456428 100644
>> --- a/tools/testing/selftests/Makefile
>> +++ b/tools/testing/selftests/Makefile
>> @@ -12,6 +12,7 @@ TARGETS += gpio
>>  TARGETS += intel_pstate
>>  TARGETS += ipc
>>  TARGETS += kcmp
>> +TARGETS += landlock
>>  TARGETS += lib
>>  TARGETS += membarrier
>>  TARGETS += memfd
>> diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
>> index daa87dd7c80e..77255b14871e 100644
>> --- a/tools/testing/selftests/bpf/test_verifier.c
>> +++ b/tools/testing/selftests/bpf/test_verifier.c
>> @@ -4536,6 +4536,70 @@ static struct bpf_test tests[] = {
>>                 .result = REJECT,
>>                 .has_prog_subtype = true,
>>         },
>> +       {
>> +               "missing subtype",
>> +               .insns = {
>> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
>> +                       BPF_EXIT_INSN(),
>> +               },
>> +               .errstr = "",
>> +               .result = REJECT,
>> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
>> +       },
>> +       {
>> +               "landlock/fs: always accept",
>> +               .insns = {
>> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
>> +                       BPF_EXIT_INSN(),
>> +               },
>> +               .result = ACCEPT,
>> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
>> +               .has_prog_subtype = true,
>> +               .prog_subtype = {
>> +                       .landlock_rule = {
>> +                               .version = 1,
>> +                               .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +                       }
>> +               },
>> +       },
>> +       {
>> +               "landlock/fs: read context",
>> +               .insns = {
>> +                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
>> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, status)),
>> +                       /* test operations on raw values */
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, arch)),
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, syscall_nr)),
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, syscall_cmd)),
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, event)),
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, arg1)),
>> +                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
>> +                               offsetof(struct landlock_context, arg2)),
>> +                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
>> +                       BPF_MOV32_IMM(BPF_REG_0, 0),
>> +                       BPF_EXIT_INSN(),
>> +               },
>> +               .result = ACCEPT,
>> +               .prog_type = BPF_PROG_TYPE_LANDLOCK,
>> +               .has_prog_subtype = true,
>> +               .prog_subtype = {
>> +                       .landlock_rule = {
>> +                               .version = 1,
>> +                               .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +                       }
>> +               },
>> +       },
>>  };
>>
>>  static int probe_filter_length(const struct bpf_insn *fp)
>> diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
>> new file mode 100644
>> index 000000000000..25b9cd834c3c
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/.gitignore
>> @@ -0,0 +1,4 @@
>> +/test_base
>> +/test_fs
>> +/test_ptrace
>> +/tmp_*
>> diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
>> new file mode 100644
>> index 000000000000..9a52c82d64fa
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/Makefile
>> @@ -0,0 +1,47 @@
>> +LIBDIR := ../../../lib
>> +BPFOBJ := $(LIBDIR)/bpf/bpf.o
>> +LOADOBJ := ../../../../samples/bpf/bpf_load.o
> 
> Is the selftest tarball creation tool okay with this? IIRC, it should
> be fine since it'll be a built object already, but it's a random
> thought I had while looking at this.

Hum, I'll check since it's the same for BPF tests.

> 
>> +
>> +CFLAGS += -Wl,-no-as-needed -Wall -O2 -I../../../include/uapi -I$(LIBDIR)
>> +LDFLAGS += -lelf
>> +
>> +test_src = $(wildcard test_*.c)
>> +rule_src = $(wildcard rules/*.c)
>> +
>> +test_objs := $(test_src:.c=)
>> +rule_objs := $(rule_src:.c=.o)
>> +
>> +TEST_PROGS := $(test_objs)
>> +
>> +.PHONY: all clean clean_tmp force
>> +
>> +all: $(test_objs) $(rule_objs)
>> +
>> +# force a rebuild of BPFOBJ when its dependencies are updated
>> +force:
>> +
>> +$(BPFOBJ): force
>> +       $(MAKE) -C $(dir $(BPFOBJ))
>> +
>> +$(LOADOBJ):
>> +       $(MAKE) -C $(dir $(LOADOBJ))
>> +
>> +# minimize builds
>> +rules/modules.order: $(rule_src)
>> +       $(MAKE) -C rules
>> +       @touch $@
>> +
>> +$(rule_objs): rules/modules.order
>> +       @
>> +
>> +$(test_objs): $(BPFOBJ) $(LOADOBJ)
>> +
>> +include ../lib.mk
>> +
>> +clean_tmp:
>> +       $(RM) -r tmp_*
>> +
>> +clean: clean_tmp
>> +       $(MAKE) -C rules clean
>> +       $(RM) $(test_objs)
>> +
>> diff --git a/tools/testing/selftests/landlock/rules/Makefile b/tools/testing/selftests/landlock/rules/Makefile
>> new file mode 100644
>> index 000000000000..8d6ff960ff7c
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/rules/Makefile
>> @@ -0,0 +1,52 @@
>> +# kbuild trick to avoid linker error. Can be omitted if a module is built.
>> +obj- := dummy.o
>> +
>> +# Tell kbuild to always build the programs
>> +always := fs_read_only.o
>> +always += fs_no_open.o
>> +
>> +EXTRA_CFLAGS = -Wall -Wextra
>> +
>> +# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
>> +#  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
>> +LLC ?= llc
>> +CLANG ?= clang
>> +
>> +# Verify LLVM compiler tools are available and bpf target is supported by llc
>> +.PHONY: all clean verify_cmds verify_target_bpf $(CLANG) $(LLC)
>> +
>> +# Trick to allow make to be run from this directory
>> +all:
>> +       $(MAKE) -C ../../../../../ $(CURDIR)/
>> +
>> +clean:
>> +       $(MAKE) -C ../../../../../ M=$(CURDIR) clean
> 
> Is this really needed? Others don't have it, I think.

This is copied from the BPF tests and yes it's needed.

> 
>> +verify_cmds: $(CLANG) $(LLC)
>> +       @for TOOL in $^ ; do \
>> +               if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
>> +                       echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
>> +                       exit 1; \
>> +               else true; fi; \
>> +       done
>> +
>> +verify_target_bpf: verify_cmds
>> +       @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
>> +               echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
>> +               echo "   NOTICE: LLVM version >= 3.7.1 required" ;\
>> +               exit 2; \
>> +       else true; fi
>> +
>> +%_kern.c: verify_target_bpf
>> +
>> +# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
>> +# But, there is no easy way to fix it, so just exclude it since it is
>> +# useless for BPF samples.
>> +$(obj)/%.o: $(src)/%.c
>> +       $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
>> +               -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
>> +               -Wno-compare-distinct-pointer-types \
>> +               -Wno-gnu-variable-sized-type-not-at-end \
>> +               -Wno-tautological-compare \
>> +               -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
> 
> Is clang required for the samples and the selftests? That needs to be
> avoided... there needs to be a way to show people how to build a
> landlock rule without requiring clang.

I can rewrite this tests without requiring clang but it is already
required for BPF tests…

> 
>> +
>> diff --git a/tools/testing/selftests/landlock/rules/README.rst b/tools/testing/selftests/landlock/rules/README.rst
>> new file mode 120000
>> index 000000000000..605f48aa6f72
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/rules/README.rst
>> @@ -0,0 +1 @@
>> +../../../../../samples/bpf/README.rst
>> \ No newline at end of file
>> diff --git a/tools/testing/selftests/landlock/rules/bpf_helpers.h b/tools/testing/selftests/landlock/rules/bpf_helpers.h
>> new file mode 120000
>> index 000000000000..0aa1a521b39a
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/rules/bpf_helpers.h
>> @@ -0,0 +1 @@
>> +../../../../../samples/bpf/bpf_helpers.h
>> \ No newline at end of file
>> diff --git a/tools/testing/selftests/landlock/rules/fs_no_open.c b/tools/testing/selftests/landlock/rules/fs_no_open.c
>> new file mode 100644
>> index 000000000000..c6ea305e58a7
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/rules/fs_no_open.c
>> @@ -0,0 +1,31 @@
>> +/*
>> + * Landlock rule - no-open filesystem
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <uapi/linux/bpf.h>
>> +#include "bpf_helpers.h"
>> +
>> +SEC("landlock1")
>> +static int landlock_fs_prog1(struct landlock_context *ctx)
>> +{
>> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_GET))
>> +               return 0;
>> +       return 1;
>> +}
>> +
>> +SEC("subtype")
>> +static union bpf_prog_subtype _subtype = {
>> +       .landlock_rule = {
>> +               .version = 1,
>> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +       }
>> +};
>> +
>> +SEC("license")
>> +static const char _license[] = "GPL";
>> diff --git a/tools/testing/selftests/landlock/rules/fs_read_only.c b/tools/testing/selftests/landlock/rules/fs_read_only.c
>> new file mode 100644
>> index 000000000000..212dda7c0c27
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/rules/fs_read_only.c
>> @@ -0,0 +1,31 @@
>> +/*
>> + * Landlock rule - read-only filesystem
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <uapi/linux/bpf.h>
>> +#include "bpf_helpers.h"
>> +
>> +SEC("landlock1")
>> +static int landlock_fs_prog1(struct landlock_context *ctx)
>> +{
>> +       if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE))
>> +               return 0;
>> +       return 1;
>> +}
>> +
>> +SEC("subtype")
>> +static union bpf_prog_subtype _subtype = {
>> +       .landlock_rule = {
>> +               .version = 1,
>> +               .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +       }
>> +};
>> +
>> +SEC("license")
>> +static const char _license[] = "GPL";
>> diff --git a/tools/testing/selftests/landlock/test.h b/tools/testing/selftests/landlock/test.h
>> new file mode 100644
>> index 000000000000..7a194815391b
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/test.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * Landlock helpers
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <errno.h>
>> +#include <sys/prctl.h>
>> +#include <sys/syscall.h>
>> +
>> +#include "../seccomp/test_harness.h"
>> +#include "../../../../samples/bpf/bpf_load.h"
>> +
>> +#ifndef SECCOMP_APPEND_LANDLOCK_RULE
>> +#define SECCOMP_APPEND_LANDLOCK_RULE   2
>> +#endif
>> +
>> +#ifndef seccomp
>> +static int seccomp(unsigned int op, unsigned int flags, void *args)
>> +{
>> +       errno = 0;
>> +       return syscall(__NR_seccomp, op, flags, args);
>> +}
>> +#endif
>> +
>> +#define ASSERT_STEP(cond) \
>> +       { \
>> +               step--; \
>> +               if (!(cond)) \
>> +                       _exit(step); \
>> +       }
> 
> Can you explain this in more detail? I'm assuming there is a problem
> with writing to the TH_LOG_STREAM fd or something?

It's a trick to use the test framework without requiring to be allowed
to write to an FD (i.e. log stream), but only to exit a code. I use this
to test a Landlock rule which forbid access to any FS objects (including
open FD). This could be used for seccomp too.

> 
>> diff --git a/tools/testing/selftests/landlock/test_base.c b/tools/testing/selftests/landlock/test_base.c
>> new file mode 100644
>> index 000000000000..bdf056edee03
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/test_base.c
>> @@ -0,0 +1,31 @@
>> +/*
>> + * Landlock tests - base
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#include <errno.h>
>> +
>> +#include "test.h"
>> +
>> +TEST(seccomp_landlock)
>> +{
>> +       int ret;
>> +
>> +       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
>> +       ASSERT_EQ(0, ret) {
>> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
>> +       }
>> +       ret = seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, NULL);
>> +       EXPECT_EQ(-1, ret);
>> +       EXPECT_EQ(EFAULT, errno) {
>> +               TH_LOG("Kernel does not support CONFIG_SECURITY_LANDLOCK");
>> +       }
>> +}
>> +
>> +TEST_HARNESS_MAIN
>> diff --git a/tools/testing/selftests/landlock/test_fs.c b/tools/testing/selftests/landlock/test_fs.c
>> new file mode 100644
>> index 000000000000..e69eda433716
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/test_fs.c
>> @@ -0,0 +1,305 @@
>> +/*
>> + * Landlock tests - filesystem
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#include <errno.h>
>> +#include <linux/bpf.h>
>> +#include <linux/filter.h>
>> +#include <linux/seccomp.h>
>> +#include <stddef.h>
>> +#include <string.h>
>> +#include <sys/prctl.h>
>> +#include <sys/syscall.h>
>> +
>> +#include <fcntl.h> /* open() */
>> +#include <sys/mount.h>
>> +#include <sys/stat.h> /* mkdir() */
>> +#include <sys/mman.h> /* mmap() */
>> +
>> +#include "test.h"
>> +
>> +#define TMP_PREFIX "tmp_"
>> +
>> +struct layout1 {
>> +       int file_ro;
>> +       int file_rw;
>> +       int file_wo;
>> +};
>> +
>> +static void setup_layout1(struct __test_metadata *_metadata,
>> +               struct layout1 *l1)
>> +{
>> +       int fd;
>> +       char buf[] = "fs_read_only";
>> +
>> +       l1->file_ro = -1;
>> +       l1->file_rw = -1;
>> +       l1->file_wo = -1;
>> +
>> +       fd = open(TMP_PREFIX "file_created",
>> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
>> +       ASSERT_GE(fd, 0);
>> +       ASSERT_EQ(sizeof(buf), write(fd, buf, sizeof(buf)));
>> +       ASSERT_EQ(0, close(fd));
>> +
>> +       fd = mkdir(TMP_PREFIX "dir_created", 0600);
>> +       ASSERT_GE(fd, 0);
>> +       ASSERT_EQ(0, close(fd));
>> +
>> +       l1->file_ro = open(TMP_PREFIX "file_ro",
>> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
>> +       ASSERT_LE(0, l1->file_ro);
>> +       ASSERT_EQ(sizeof(buf), write(l1->file_ro, buf, sizeof(buf)));
>> +       ASSERT_EQ(0, close(l1->file_ro));
>> +       l1->file_ro = open(TMP_PREFIX "file_ro",
>> +                       O_RDONLY | O_CLOEXEC, 0600);
>> +       ASSERT_LE(0, l1->file_ro);
>> +
>> +       l1->file_rw = open(TMP_PREFIX "file_rw",
>> +                       O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
>> +       ASSERT_LE(0, l1->file_rw);
>> +       ASSERT_EQ(sizeof(buf), write(l1->file_rw, buf, sizeof(buf)));
>> +       ASSERT_EQ(0, lseek(l1->file_rw, 0, SEEK_SET));
>> +
>> +       l1->file_wo = open(TMP_PREFIX "file_wo",
>> +                       O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
>> +       ASSERT_LE(0, l1->file_wo);
>> +       ASSERT_EQ(sizeof(buf), write(l1->file_wo, buf, sizeof(buf)));
>> +       ASSERT_EQ(0, lseek(l1->file_wo, 0, SEEK_SET));
>> +}
>> +
>> +static void cleanup_layout1(void)
>> +{
>> +       unlink(TMP_PREFIX "file_created");
>> +       unlink(TMP_PREFIX "file_ro");
>> +       unlink(TMP_PREFIX "file_rw");
>> +       unlink(TMP_PREFIX "file_wo");
>> +       unlink(TMP_PREFIX "should_not_exist");
>> +       rmdir(TMP_PREFIX "dir_created");
>> +}
>> +
>> +FIXTURE(fs_read_only) {
>> +       struct layout1 l1;
>> +       int prog;
>> +};
>> +
>> +FIXTURE_SETUP(fs_read_only)
>> +{
>> +       cleanup_layout1();
>> +       setup_layout1(_metadata, &self->l1);
>> +
>> +       ASSERT_EQ(0, load_bpf_file("rules/fs_read_only.o")) {
>> +               TH_LOG("%s", bpf_log_buf);
>> +       }
>> +       self->prog = prog_fd[0];
>> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
>> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
>> +       }
>> +}
>> +
>> +FIXTURE_TEARDOWN(fs_read_only)
>> +{
>> +       EXPECT_EQ(0, close(self->prog));
>> +       /* cleanup_layout1() would be denied here */
>> +}
>> +
>> +TEST_F(fs_read_only, load_prog) {}
>> +
>> +TEST_F(fs_read_only, read_only_file)
>> +{
>> +       int fd;
>> +       int step = 0;
>> +       char buf_write[] = "should not be written";
>> +       char buf_read[2];
>> +
>> +       ASSERT_EQ(-1, write(self->l1.file_ro, buf_write, sizeof(buf_write)));
>> +       ASSERT_EQ(EBADF, errno);
>> +
>> +       ASSERT_EQ(-1, read(self->l1.file_wo, buf_read, sizeof(buf_read)));
>> +       ASSERT_EQ(EBADF, errno);
>> +
>> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
>> +               TH_LOG("Failed to apply rule fs_read_only: %s",
>> +                               strerror(errno));
>> +       }
>> +
>> +       fd = open(".", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
>> +       ASSERT_STEP(fd == -1);
>> +       ASSERT_STEP(errno != EOPNOTSUPP)
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       fd = open(TMP_PREFIX "file_created",
>> +                       O_RDONLY | O_CLOEXEC);
>> +       ASSERT_STEP(fd >= 0);
>> +       ASSERT_STEP(!close(fd));
>> +
>> +       fd = open(TMP_PREFIX "file_created",
>> +                       O_RDWR | O_CLOEXEC);
>> +       ASSERT_STEP(fd == -1);
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       fd = open(TMP_PREFIX "file_created",
>> +                       O_WRONLY | O_CLOEXEC);
>> +       ASSERT_STEP(fd == -1);
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       fd = open(TMP_PREFIX "should_not_exist",
>> +                       O_CREAT | O_EXCL | O_CLOEXEC, 0600);
>> +       ASSERT_STEP(fd == -1);
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       ASSERT_STEP(-1 ==
>> +                       write(self->l1.file_ro, buf_write, sizeof(buf_write)));
>> +       ASSERT_STEP(errno == EBADF);
>> +       ASSERT_STEP(sizeof(buf_read) ==
>> +                       read(self->l1.file_ro, buf_read, sizeof(buf_read)));
>> +
>> +       ASSERT_STEP(-1 ==
>> +                       write(self->l1.file_rw, buf_write, sizeof(buf_write)));
>> +       ASSERT_STEP(errno == EPERM);
>> +       ASSERT_STEP(sizeof(buf_read) ==
>> +                       read(self->l1.file_rw, buf_read, sizeof(buf_read)));
>> +
>> +       ASSERT_STEP(-1 == write(self->l1.file_wo, buf_write, sizeof(buf_write)));
>> +       ASSERT_STEP(errno == EPERM);
>> +       ASSERT_STEP(-1 == read(self->l1.file_wo, buf_read, sizeof(buf_read)));
>> +       ASSERT_STEP(errno == EBADF);
>> +
>> +       ASSERT_STEP(-1 == unlink(TMP_PREFIX "file_created"));
>> +       ASSERT_STEP(errno == EPERM);
>> +       ASSERT_STEP(-1 == rmdir(TMP_PREFIX "dir_created"));
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       ASSERT_STEP(0 == close(self->l1.file_ro));
>> +       ASSERT_STEP(0 == close(self->l1.file_rw));
>> +       ASSERT_STEP(0 == close(self->l1.file_wo));
>> +}
>> +
>> +TEST_F(fs_read_only, read_only_mount)
>> +{
>> +       int step = 0;
>> +
>> +       ASSERT_EQ(0, mount(".", TMP_PREFIX "dir_created",
>> +                               NULL, MS_BIND, NULL));
>> +       ASSERT_EQ(0, umount2(TMP_PREFIX "dir_created", MNT_FORCE));
>> +
>> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
>> +               TH_LOG("Failed to apply rule fs_read_only: %s",
>> +                               strerror(errno));
>> +       }
>> +
>> +       ASSERT_STEP(-1 == mount(".", TMP_PREFIX "dir_created",
>> +                               NULL, MS_BIND, NULL));
>> +       ASSERT_STEP(errno == EPERM);
>> +       ASSERT_STEP(-1 == umount("/"));
>> +       ASSERT_STEP(errno == EPERM);
>> +}
>> +
>> +TEST_F(fs_read_only, read_only_mem)
>> +{
>> +       int step = 0;
>> +       void *addr;
>> +
>> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
>> +                       MAP_SHARED, self->l1.file_rw, 0);
>> +       ASSERT_NE(NULL, addr);
>> +       ASSERT_EQ(0, munmap(addr, 1));
>> +
>> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
>> +               TH_LOG("Failed to apply rule fs_read_only: %s",
>> +                               strerror(errno));
>> +       }
>> +
>> +       addr = mmap(NULL, 1, PROT_READ, MAP_SHARED,
>> +                       self->l1.file_rw, 0);
>> +       ASSERT_STEP(addr != NULL);
>> +       ASSERT_STEP(-1 == mprotect(addr, 1, PROT_WRITE));
>> +       ASSERT_STEP(errno == EPERM);
>> +       ASSERT_STEP(0 == munmap(addr, 1));
>> +
>> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED,
>> +                       self->l1.file_rw, 0);
>> +       ASSERT_STEP(addr != NULL);
>> +       ASSERT_STEP(errno == EPERM);
>> +
>> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE,
>> +                       self->l1.file_rw, 0);
>> +       ASSERT_STEP(addr != NULL);
>> +       ASSERT_STEP(0 == munmap(addr, 1));
>> +}
>> +
>> +FIXTURE(fs_no_open) {
>> +       struct layout1 l1;
>> +       int prog;
>> +};
>> +
>> +FIXTURE_SETUP(fs_no_open)
>> +{
>> +       cleanup_layout1();
>> +       setup_layout1(_metadata, &self->l1);
>> +
>> +       ASSERT_EQ(0, load_bpf_file("rules/fs_no_open.o")) {
>> +               TH_LOG("%s", bpf_log_buf);
>> +       }
>> +       self->prog = prog_fd[0];
>> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
>> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
>> +       }
>> +}
>> +
>> +FIXTURE_TEARDOWN(fs_no_open)
>> +{
>> +       EXPECT_EQ(0, close(self->prog));
>> +       cleanup_layout1();
>> +}
>> +
>> +static void landlocked_deny_open(struct __test_metadata *_metadata,
>> +               struct layout1 *l1)
>> +{
>> +       int fd;
>> +       void *addr;
>> +
>> +       fd = open(".", O_DIRECTORY | O_CLOEXEC);
>> +       ASSERT_EQ(-1, fd);
>> +       ASSERT_EQ(EPERM, errno);
>> +
>> +       addr = mmap(NULL, 1, PROT_READ | PROT_WRITE,
>> +                       MAP_SHARED, l1->file_rw, 0);
>> +       ASSERT_NE(NULL, addr);
>> +       ASSERT_EQ(0, munmap(addr, 1));
>> +}
>> +
>> +TEST_F(fs_no_open, deny_open_for_hierarchy) {
>> +       int fd;
>> +       int status;
>> +       pid_t child;
>> +
>> +       fd = open(".", O_DIRECTORY | O_CLOEXEC);
>> +       ASSERT_LE(0, fd);
>> +       ASSERT_EQ(0, close(fd));
>> +
>> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &self->prog)) {
>> +               TH_LOG("Failed to apply rule fs_no_open: %s", strerror(errno));
>> +       }
>> +
>> +       landlocked_deny_open(_metadata, &self->l1);
>> +
>> +       child = fork();
>> +       ASSERT_LE(0, child);
>> +       if (!child) {
>> +               landlocked_deny_open(_metadata, &self->l1);
>> +               _exit(1);
>> +       }
>> +       ASSERT_EQ(child, waitpid(child, &status, 0));
>> +       ASSERT_TRUE(WIFEXITED(status));
>> +       _exit(WEXITSTATUS(status));
>> +}
>> +
>> +TEST_HARNESS_MAIN
>> diff --git a/tools/testing/selftests/landlock/test_ptrace.c b/tools/testing/selftests/landlock/test_ptrace.c
>> new file mode 100644
>> index 000000000000..0c940a7fd3d0
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/test_ptrace.c
>> @@ -0,0 +1,161 @@
>> +/*
>> + * Landlock tests - ptrace
>> + *
>> + * Copyright © 2017 Mickaël Salaün <mic@digikod.net>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#include <signal.h> /* raise */
>> +#include <sys/ptrace.h>
>> +#include <sys/types.h> /* waitpid */
>> +#include <sys/wait.h> /* waitpid */
>> +#include <unistd.h> /* fork, pipe */
>> +
>> +#include "test.h"
>> +
>> +static void apply_null_sandbox(struct __test_metadata *_metadata)
>> +{
>> +       const struct bpf_insn prog_accept[] = {
>> +               BPF_MOV32_IMM(BPF_REG_0, 0),
>> +               BPF_EXIT_INSN(),
>> +       };
>> +       const union bpf_prog_subtype subtype = {
>> +               .landlock_rule = {
>> +                       .version = 1,
>> +                       .event = LANDLOCK_SUBTYPE_EVENT_FS,
>> +               }
>> +       };
>> +       int prog;
>> +       char log[256] = "";
>> +
>> +       prog = bpf_load_program(BPF_PROG_TYPE_LANDLOCK,
>> +                       (const struct bpf_insn *)&prog_accept,
>> +                       sizeof(prog_accept) / sizeof(struct bpf_insn), "GPL",
>> +                       0, log, sizeof(log), &subtype);
>> +       ASSERT_NE(-1, prog) {
>> +               TH_LOG("Failed to load minimal rule: %s\n%s",
>> +                               strerror(errno), log);
>> +       }
>> +       ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0)) {
>> +               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS");
>> +       }
>> +       ASSERT_EQ(0, seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog)) {
>> +               TH_LOG("Failed to apply minimal rule: %s", strerror(errno));
>> +       }
>> +       EXPECT_EQ(0, close(prog));
>> +}
>> +
>> +/* PTRACE_TRACEME and PTRACE_ATTACH without Landlock rules effect */
>> +static void check_ptrace(struct __test_metadata *_metadata,
>> +               int sandbox_both, int sandbox_parent, int sandbox_child,
>> +               int expect_ptrace)
>> +{
>> +       pid_t child;
>> +       int status;
>> +       int pipefd[2];
>> +
>> +       ASSERT_EQ(0, pipe(pipefd));
>> +       if (sandbox_both)
>> +               apply_null_sandbox(_metadata);
>> +
>> +       child = fork();
>> +       ASSERT_LE(0, child);
>> +       if (child == 0) {
>> +               char buf;
>> +
>> +               EXPECT_EQ(0, close(pipefd[1]));
>> +               if (sandbox_child)
>> +                       apply_null_sandbox(_metadata);
>> +
>> +               /* test traceme */
>> +               ASSERT_EQ(expect_ptrace, ptrace(PTRACE_TRACEME));
>> +               if (expect_ptrace) {
>> +                       ASSERT_EQ(EPERM, errno);
>> +               } else {
>> +                       ASSERT_EQ(0, raise(SIGSTOP));
>> +               }
>> +
>> +               /* sync */
>> +               ASSERT_EQ(1, read(pipefd[0], &buf, 1)) {
>> +                       TH_LOG("Failed to read() sync from parent");
>> +               }
>> +               ASSERT_EQ('.', buf);
>> +               _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
>> +       }
>> +
>> +       EXPECT_EQ(0, close(pipefd[0]));
>> +       if (sandbox_parent)
>> +               apply_null_sandbox(_metadata);
>> +
>> +       /* test traceme */
>> +       if (!expect_ptrace) {
>> +               ASSERT_EQ(child, waitpid(child, &status, 0));
>> +               ASSERT_EQ(1, WIFSTOPPED(status));
>> +               ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
>> +       }
>> +       /* test attach */
>> +       ASSERT_EQ(expect_ptrace, ptrace(PTRACE_ATTACH, child, NULL, 0));
>> +       if (expect_ptrace) {
>> +               ASSERT_EQ(EPERM, errno);
>> +       } else {
>> +               ASSERT_EQ(child, waitpid(child, &status, 0));
>> +               ASSERT_EQ(1, WIFSTOPPED(status));
>> +               ASSERT_EQ(0, ptrace(PTRACE_CONT, child, NULL, 0));
>> +       }
>> +
>> +       /* sync */
>> +       ASSERT_EQ(1, write(pipefd[1], ".", 1)) {
>> +               TH_LOG("Failed to write() sync to child");
>> +       }
>> +       ASSERT_EQ(child, waitpid(child, &status, 0));
>> +       if (WIFSIGNALED(status) || WEXITSTATUS(status))
>> +               _metadata->passed = 0;
>> +}
>> +
>> +TEST(ptrace_allow_without_sandbox)
>> +{
>> +       /* no sandbox */
>> +       check_ptrace(_metadata, 0, 0, 0, 0);
>> +}
>> +
>> +TEST(ptrace_allow_with_one_sandbox)
>> +{
>> +       /* child sandbox */
>> +       check_ptrace(_metadata, 0, 0, 1, 0);
>> +}
>> +
>> +TEST(ptrace_allow_with_nested_sandbox)
>> +{
>> +       /* inherited and child sandbox */
>> +       check_ptrace(_metadata, 1, 0, 1, 0);
>> +}
>> +
>> +TEST(ptrace_deny_with_parent_sandbox)
>> +{
>> +       /* parent sandbox */
>> +       check_ptrace(_metadata, 0, 1, 0, -1);
>> +}
>> +
>> +TEST(ptrace_deny_with_nested_and_parent_sandbox)
>> +{
>> +       /* inherited and parent sandbox */
>> +       check_ptrace(_metadata, 1, 1, 0, -1);
>> +}
>> +
>> +TEST(ptrace_deny_with_forked_sandbox)
>> +{
>> +       /* inherited, parent and child sandbox */
>> +       check_ptrace(_metadata, 1, 1, 1, -1);
>> +}
>> +
>> +TEST(ptrace_deny_with_sibling_sandbox)
>> +{
>> +       /* parent and child sandbox */
>> +       check_ptrace(_metadata, 0, 1, 1, -1);
>> +}
>> +
>> +TEST_HARNESS_MAIN
>> --
>> 2.11.0
>>
> 
> Awesome. I love to see all these tests, with both positive and
> negative checks. Nice!
> 
> -Kees
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
  2017-04-18 23:53     ` Mickaël Salaün
@ 2017-04-18 23:59       ` Kees Cook
  0 siblings, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-18 23:59 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Apr 18, 2017 at 4:53 PM, Mickaël Salaün <mic@digikod.net> wrote:
> On 19/04/2017 01:16, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/landlock/Makefile
>>> @@ -0,0 +1,47 @@
>>> +LIBDIR := ../../../lib
>>> +BPFOBJ := $(LIBDIR)/bpf/bpf.o
>>> +LOADOBJ := ../../../../samples/bpf/bpf_load.o
>>
>> Is the selftest tarball creation tool okay with this? IIRC, it should
>> be fine since it'll be a built object already, but it's a random
>> thought I had while looking at this.
>
> Hum, I'll check since it's the same for BPF tests.

Okay, cool.

>>> +# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
>>> +# But, there is no easy way to fix it, so just exclude it since it is
>>> +# useless for BPF samples.
>>> +$(obj)/%.o: $(src)/%.c
>>> +       $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
>>> +               -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
>>> +               -Wno-compare-distinct-pointer-types \
>>> +               -Wno-gnu-variable-sized-type-not-at-end \
>>> +               -Wno-tautological-compare \
>>> +               -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
>>
>> Is clang required for the samples and the selftests? That needs to be
>> avoided... there needs to be a way to show people how to build a
>> landlock rule without requiring clang.
>
> I can rewrite this tests without requiring clang but it is already
> required for BPF tests…

So, I guess it's not a big deal for selftests (but it'd be nice, even
for BPF), but I think at least the samples/ should have examples on
how to do it "by hand", etc. Not everyone will build stuff with clang,
and it'd be good to make landlock as available as possible.

>>> +#define ASSERT_STEP(cond) \
>>> +       { \
>>> +               step--; \
>>> +               if (!(cond)) \
>>> +                       _exit(step); \
>>> +       }
>>
>> Can you explain this in more detail? I'm assuming there is a problem
>> with writing to the TH_LOG_STREAM fd or something?
>
> It's a trick to use the test framework without requiring to be allowed
> to write to an FD (i.e. log stream), but only to exit a code. I use this
> to test a Landlock rule which forbid access to any FS objects (including
> open FD). This could be used for seccomp too.

Okay. For seccomp, we just allow the fd. :P I'm not opposed to it; it
just makes some debugging harder without text details, etc.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-03-28 23:46 ` [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism Mickaël Salaün
@ 2017-04-19  0:02   ` Kees Cook
  2017-04-19 21:51     ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-19  0:02 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
> This is useful to return an information about the error without being
> able to write to TH_LOG_STREAM.
>
> Helpers from test_harness.h may be useful outside of the seccomp
> directory.
>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Shuah Khan <shuah@kernel.org>
> Cc: Will Drewry <wad@chromium.org>
> ---
>  tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
> index a786c69c7584..77e407663e06 100644
> --- a/tools/testing/selftests/seccomp/test_harness.h
> +++ b/tools/testing/selftests/seccomp/test_harness.h
> @@ -397,7 +397,7 @@ struct __test_metadata {
>         const char *name;
>         void (*fn)(struct __test_metadata *);
>         int termsig;
> -       int passed;
> +       __s8 passed;

Why the reduction here? int is signed too?

>         int trigger; /* extra handler after the evaluation */
>         struct __test_metadata *prev, *next;
>  };
> @@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
>                                         "instead of by signal (code: %d)\n",
>                                         t->name,
>                                         WEXITSTATUS(status));
> +                       } else if (t->passed < 0) {
> +                               fprintf(TH_LOG_STREAM,
> +                                       "%s: Failed at step #%d\n",
> +                                       t->name,
> +                                       t->passed * -1);
> +                               t->passed = 0;
>                         }

Instead of creating an overloaded mechanism here, perhaps have an
option reporting mechanism that can be enabled. Like adding to
__test_metadata "bool no_stream; int test_number;" and adding
test_number++ to each ASSERT/EXCEPT call, and doing something like:

if (t->no_stream) {
                              fprintf(TH_LOG_STREAM,
                                      "%s: Failed at step #%d\n",
                                      t->name,
                                       t->test_number);
}

It'd be a cleaner approach, maybe?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
  2017-04-18 23:26 ` [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Kees Cook
@ 2017-04-19  0:12   ` Mickaël Salaün
  0 siblings, 0 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-19  0:12 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 3031 bytes --]


On 19/04/2017 01:26, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> This sixth series add some changes to the previous one [1], including a simpler
>> rule inheritance hierarchy (similar to seccomp-bpf), a ptrace scope protection,
>> some file renaming (better feature identification per file), a future-proof
>> eBPF subtype and miscellaneous cosmetic fixes.
> 
> Sorry for the delay in review! I finally had a chunk of time I could
> devote to this. I really like how it's heading. I still wonder about
> its overlap with seccomp (it's really only using the syscall now...),
> but that's just a detail. Getting the abstraction away from direct LSM
> hooks looks good.
> 
>> There is as yet no way to allow a process to access only a subset of the
>> filesystem where the subset is specified via a path or a file descriptor.  This
>> feature is intentionally left out so as to minimize the amount of code of this
>> patch series but will come in a following series.  However, it is possible to
>> check the file type, as done in the following example.
> 
> I understand why you've taken a progressive approach here, but I think
> there are two fundamental areas where people will use Landlock: path
> evaluation and network address evaluation. I think it's worth
> expanding this series to include those two confinement examples, since
> that can help people understand what the "general" case will look
> like.

I agree that it would be more useful to add a path/FS evaluation,
however we agreed at LPC that it would be another patch series:
https://lkml.kernel.org/r/5828776A.1010104@digikod.net
It brings more complexity and a new kind of BPF map, which should be
reviewed in a separate series.

> 
> As I mentioned in one of the patch review emails, I think there needs
> to be a clearer explanation of how usage counting works vs the
> "events" (which I think of as "rule tables" not events -- maybe it
> needs a new name?) and "rule" lists. I think I understand it, but I
> spent a lot of time trying to get there. More comments would help.

I though about different names and the previous one was PDP (for Policy
Decision Point) which is used in the literature, but "event" seems
easier to understand and close enough with "hook". Rule tables doesn't
seems to express what is the meaning to register an event/hook.

I'll add more comments to explain how it works.

> 
> Finally, another thing I'm curious about is how to deal with the
> thread-sync issue. Seccomp uses its TSYNC thing, and I'd expect we'd
> want something similar for landlock... but that looks really hairy as
> far as locking goes. Perhaps it's already solved by using the same
> locking seccomp uses, in which case I'm less inclined to kick landlock
> out of seccomp.c. :)

I didn't work on it but it should be really similar to seccomp-bpf.

> 
> Looks like it's coming along nicely! Thanks for continuing to work on this!
> 
> -Kees
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-04-19  0:02   ` Kees Cook
@ 2017-04-19 21:51     ` Mickaël Salaün
  2017-04-19 22:02       ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-19 21:51 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 3006 bytes --]


On 19/04/2017 02:02, Kees Cook wrote:
> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>> This is useful to return an information about the error without being
>> able to write to TH_LOG_STREAM.
>>
>> Helpers from test_harness.h may be useful outside of the seccomp
>> directory.
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Shuah Khan <shuah@kernel.org>
>> Cc: Will Drewry <wad@chromium.org>
>> ---
>>  tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
>> index a786c69c7584..77e407663e06 100644
>> --- a/tools/testing/selftests/seccomp/test_harness.h
>> +++ b/tools/testing/selftests/seccomp/test_harness.h
>> @@ -397,7 +397,7 @@ struct __test_metadata {
>>         const char *name;
>>         void (*fn)(struct __test_metadata *);
>>         int termsig;
>> -       int passed;
>> +       __s8 passed;
> 
> Why the reduction here? int is signed too?

Because the return code of a process is capped to 8 bits and I use a
negative value to not mess with the current interpretation of 0 (error)
and 1 (OK) for the "passed" variable.

> 
>>         int trigger; /* extra handler after the evaluation */
>>         struct __test_metadata *prev, *next;
>>  };
>> @@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
>>                                         "instead of by signal (code: %d)\n",
>>                                         t->name,
>>                                         WEXITSTATUS(status));
>> +                       } else if (t->passed < 0) {
>> +                               fprintf(TH_LOG_STREAM,
>> +                                       "%s: Failed at step #%d\n",
>> +                                       t->name,
>> +                                       t->passed * -1);
>> +                               t->passed = 0;
>>                         }
> 
> Instead of creating an overloaded mechanism here, perhaps have an
> option reporting mechanism that can be enabled. Like adding to
> __test_metadata "bool no_stream; int test_number;" and adding
> test_number++ to each ASSERT/EXCEPT call, and doing something like:
> 
> if (t->no_stream) {
>                               fprintf(TH_LOG_STREAM,
>                                       "%s: Failed at step #%d\n",
>                                       t->name,
>                                        t->test_number);
> }
> 
> It'd be a cleaner approach, maybe?

Good idea, we will then be able to use 255 steps!

Do you want me to send this as a separate patch?

Can we move test_harness.h outside of the seccomp directory to be
available to other subsystems as well?


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-04-19 21:51     ` Mickaël Salaün
@ 2017-04-19 22:02       ` Kees Cook
  2017-04-19 22:05         ` Mickaël Salaün
  0 siblings, 1 reply; 50+ messages in thread
From: Kees Cook @ 2017-04-19 22:02 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Wed, Apr 19, 2017 at 2:51 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
> On 19/04/2017 02:02, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> This is useful to return an information about the error without being
>>> able to write to TH_LOG_STREAM.
>>>
>>> Helpers from test_harness.h may be useful outside of the seccomp
>>> directory.
>>>
>>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>> Cc: Andy Lutomirski <luto@amacapital.net>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>> Cc: Kees Cook <keescook@chromium.org>
>>> Cc: Shuah Khan <shuah@kernel.org>
>>> Cc: Will Drewry <wad@chromium.org>
>>> ---
>>>  tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
>>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
>>> index a786c69c7584..77e407663e06 100644
>>> --- a/tools/testing/selftests/seccomp/test_harness.h
>>> +++ b/tools/testing/selftests/seccomp/test_harness.h
>>> @@ -397,7 +397,7 @@ struct __test_metadata {
>>>         const char *name;
>>>         void (*fn)(struct __test_metadata *);
>>>         int termsig;
>>> -       int passed;
>>> +       __s8 passed;
>>
>> Why the reduction here? int is signed too?
>
> Because the return code of a process is capped to 8 bits and I use a
> negative value to not mess with the current interpretation of 0 (error)
> and 1 (OK) for the "passed" variable.
>
>>
>>>         int trigger; /* extra handler after the evaluation */
>>>         struct __test_metadata *prev, *next;
>>>  };
>>> @@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
>>>                                         "instead of by signal (code: %d)\n",
>>>                                         t->name,
>>>                                         WEXITSTATUS(status));
>>> +                       } else if (t->passed < 0) {
>>> +                               fprintf(TH_LOG_STREAM,
>>> +                                       "%s: Failed at step #%d\n",
>>> +                                       t->name,
>>> +                                       t->passed * -1);
>>> +                               t->passed = 0;
>>>                         }
>>
>> Instead of creating an overloaded mechanism here, perhaps have an
>> option reporting mechanism that can be enabled. Like adding to
>> __test_metadata "bool no_stream; int test_number;" and adding
>> test_number++ to each ASSERT/EXCEPT call, and doing something like:
>>
>> if (t->no_stream) {
>>                               fprintf(TH_LOG_STREAM,
>>                                       "%s: Failed at step #%d\n",
>>                                       t->name,
>>                                        t->test_number);
>> }
>>
>> It'd be a cleaner approach, maybe?
>
> Good idea, we will then be able to use 255 steps!
>
> Do you want me to send this as a separate patch?
>
> Can we move test_harness.h outside of the seccomp directory to be
> available to other subsystems as well?

Yeah, I would do two patches, and send them out separately (to shuah
with lkml and me in cc at least), one to move test_hardness.h into
some include/ directory, and then to add the new logic for streamless
reporting.

Thanks!

-Kees


-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-18 23:40         ` Kees Cook
@ 2017-04-19 22:03           ` Mickaël Salaün
  2017-04-19 23:58             ` [kernel-hardening] " Casey Schaufler
  2017-04-20  1:48             ` Kees Cook
  0 siblings, 2 replies; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-19 22:03 UTC (permalink / raw)
  To: Kees Cook, Casey Schaufler
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Paul Moore,
	Sargun Dhillon, Serge E . Hallyn, Shuah Khan, Tejun Heo,
	Thomas Graf, Will Drewry, kernel-hardening, Linux API,
	linux-security-module, Network Development

[-- Attachment #1.1: Type: text/plain, Size: 2384 bytes --]


On 19/04/2017 01:40, Kees Cook wrote:
> On Tue, Apr 18, 2017 at 4:16 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> On 4/18/2017 3:44 PM, Mickaël Salaün wrote:
>>> On 19/04/2017 00:17, Kees Cook wrote:
>>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>>> +void __init landlock_add_hooks(void)
>>>>> +{
>>>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>>>> +       landlock_add_hooks_fs();
>>>>> +       security_add_hooks(NULL, 0, "landlock");
>>>>> +       bpf_register_prog_type(&bpf_landlock_type);
>>>> I'm confused by the separation of hook registration here. The call to
>>>> security_add_hooks is with count=0 is especially weird. Why isn't this
>>>> just a single call with security_add_hooks(landlock_hooks,
>>>> ARRAY_SIZE(landlock_hooks), "landlock")?
>>> Yes, this is ugly with the new security_add_hooks() with three arguments
>>> but I wanted to split the hooks definition in multiple files.
>>
>> Why? I'll buy a good argument, but there are dangers in
>> allowing multiple calls to security_add_hooks().

I prefer to have one file per hook "family" (e.g. filesystem, network,
ptrace…). This reduce the mess with all the included files (needed for
LSM hook argument types) and make the files easier to read, understand
and maintain.

>>
>>>
>>> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
>>> is not exported. Unfortunately, calling multiple security_add_hooks()
>>> with the same LSM name would register multiple names for the same LSM…
>>> Is it OK if I modify this function to not add duplicated entries?
>>
>> It may seem absurd, but it's conceivable that a module might
>> have two hooks it wants called. My example is a module that
>> counts the number of times SELinux denies a process access to
>> things (which needs to be called before and after SELinux in
>> order to detect denials) and takes "appropriate action" if
>> too many denials occur. It would be weird, wonky and hackish,
>> but that never stopped anybody before.

Right, but now, with the new lsm_append(), module names are concatenated
("%s,%s") in the lsm_names variable. It would be nice to not pollute
this string with multiple time the same module name.

> 
> If ends up being sane and clear, I'm fine with allowing multiple calls.
> 
> -Kees
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-04-19 22:02       ` Kees Cook
@ 2017-04-19 22:05         ` Mickaël Salaün
  2017-04-20  1:50           ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-19 22:05 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 3597 bytes --]



On 20/04/2017 00:02, Kees Cook wrote:
> On Wed, Apr 19, 2017 at 2:51 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>
>> On 19/04/2017 02:02, Kees Cook wrote:
>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>> This is useful to return an information about the error without being
>>>> able to write to TH_LOG_STREAM.
>>>>
>>>> Helpers from test_harness.h may be useful outside of the seccomp
>>>> directory.
>>>>
>>>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>>> Cc: Andy Lutomirski <luto@amacapital.net>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Cc: Kees Cook <keescook@chromium.org>
>>>> Cc: Shuah Khan <shuah@kernel.org>
>>>> Cc: Will Drewry <wad@chromium.org>
>>>> ---
>>>>  tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
>>>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
>>>> index a786c69c7584..77e407663e06 100644
>>>> --- a/tools/testing/selftests/seccomp/test_harness.h
>>>> +++ b/tools/testing/selftests/seccomp/test_harness.h
>>>> @@ -397,7 +397,7 @@ struct __test_metadata {
>>>>         const char *name;
>>>>         void (*fn)(struct __test_metadata *);
>>>>         int termsig;
>>>> -       int passed;
>>>> +       __s8 passed;
>>>
>>> Why the reduction here? int is signed too?
>>
>> Because the return code of a process is capped to 8 bits and I use a
>> negative value to not mess with the current interpretation of 0 (error)
>> and 1 (OK) for the "passed" variable.
>>
>>>
>>>>         int trigger; /* extra handler after the evaluation */
>>>>         struct __test_metadata *prev, *next;
>>>>  };
>>>> @@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
>>>>                                         "instead of by signal (code: %d)\n",
>>>>                                         t->name,
>>>>                                         WEXITSTATUS(status));
>>>> +                       } else if (t->passed < 0) {
>>>> +                               fprintf(TH_LOG_STREAM,
>>>> +                                       "%s: Failed at step #%d\n",
>>>> +                                       t->name,
>>>> +                                       t->passed * -1);
>>>> +                               t->passed = 0;
>>>>                         }
>>>
>>> Instead of creating an overloaded mechanism here, perhaps have an
>>> option reporting mechanism that can be enabled. Like adding to
>>> __test_metadata "bool no_stream; int test_number;" and adding
>>> test_number++ to each ASSERT/EXCEPT call, and doing something like:
>>>
>>> if (t->no_stream) {
>>>                               fprintf(TH_LOG_STREAM,
>>>                                       "%s: Failed at step #%d\n",
>>>                                       t->name,
>>>                                        t->test_number);
>>> }
>>>
>>> It'd be a cleaner approach, maybe?
>>
>> Good idea, we will then be able to use 255 steps!
>>
>> Do you want me to send this as a separate patch?
>>
>> Can we move test_harness.h outside of the seccomp directory to be
>> available to other subsystems as well?
> 
> Yeah, I would do two patches, and send them out separately (to shuah
> with lkml and me in cc at least), one to move test_hardness.h into
> some include/ directory, and then to add the new logic for streamless
> reporting.
> 
> Thanks!
> 
> -Kees
> 
> 

Good, in which place and name would it fit better?


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-04-18 22:47     ` Mickaël Salaün
@ 2017-04-19 22:18       ` Mickaël Salaün
  2017-04-20  1:54         ` Kees Cook
  0 siblings, 1 reply; 50+ messages in thread
From: Mickaël Salaün @ 2017-04-19 22:18 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

[-- Attachment #1.1: Type: text/plain, Size: 4752 bytes --]


On 19/04/2017 00:47, Mickaël Salaün wrote:
> 
> On 19/04/2017 00:23, Kees Cook wrote:
>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>> The semantic is unchanged. This will be useful for the Landlock
>>> integration with seccomp (next commit).
>>>
>>> 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>
>>> ---
>>>  include/linux/seccomp.h |  4 ++--
>>>  kernel/fork.c           |  2 +-
>>>  kernel/seccomp.c        | 18 +++++++++++++-----
>>>  3 files changed, 16 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
>>> index ecc296c137cd..e25aee2cdfc0 100644
>>> --- a/include/linux/seccomp.h
>>> +++ b/include/linux/seccomp.h
>>> @@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s)
>>>  #endif /* CONFIG_SECCOMP */
>>>
>>>  #ifdef CONFIG_SECCOMP_FILTER
>>> -extern void put_seccomp_filter(struct task_struct *tsk);
>>> +extern void put_seccomp(struct task_struct *tsk);
>>>  extern void get_seccomp_filter(struct task_struct *tsk);
>>>  #else  /* CONFIG_SECCOMP_FILTER */
>>> -static inline void put_seccomp_filter(struct task_struct *tsk)
>>> +static inline void put_seccomp(struct task_struct *tsk)
>>>  {
>>>         return;
>>>  }
>>> diff --git a/kernel/fork.c b/kernel/fork.c
>>> index 6c463c80e93d..a27d8e67ce33 100644
>>> --- a/kernel/fork.c
>>> +++ b/kernel/fork.c
>>> @@ -363,7 +363,7 @@ void free_task(struct task_struct *tsk)
>>>  #endif
>>>         rt_mutex_debug_task_free(tsk);
>>>         ftrace_graph_exit_task(tsk);
>>> -       put_seccomp_filter(tsk);
>>> +       put_seccomp(tsk);
>>>         arch_release_task_struct(tsk);
>>>         if (tsk->flags & PF_KTHREAD)
>>>                 free_kthread_struct(tsk);
>>> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
>>> index 65f61077ad50..326f79e32127 100644
>>> --- a/kernel/seccomp.c
>>> +++ b/kernel/seccomp.c
>>> @@ -64,6 +64,8 @@ struct seccomp_filter {
>>>  /* Limit any path through the tree to 256KB worth of instructions. */
>>>  #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
>>>
>>> +static void put_seccomp_filter(struct seccomp_filter *filter);
>>
>> Can this be reorganized easily to avoid a forward-declaration?
> 
> I didn't want to move too much code but I will.
> 
>>
>>> +
>>>  /*
>>>   * Endianness is explicitly ignored and left for BPF program authors to manage
>>>   * as per the specific architecture.
>>> @@ -314,7 +316,7 @@ static inline void seccomp_sync_threads(void)
>>>                  * current's path will hold a reference.  (This also
>>>                  * allows a put before the assignment.)
>>>                  */
>>> -               put_seccomp_filter(thread);
>>> +               put_seccomp_filter(thread->seccomp.filter);
>>>                 smp_store_release(&thread->seccomp.filter,
>>>                                   caller->seccomp.filter);
>>>
>>> @@ -476,10 +478,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter)
>>>         }
>>>  }
>>>
>>> -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
>>> -void put_seccomp_filter(struct task_struct *tsk)
>>> +/* put_seccomp_filter - decrements the ref count of a filter */
>>> +static void put_seccomp_filter(struct seccomp_filter *filter)
>>>  {
>>> -       struct seccomp_filter *orig = tsk->seccomp.filter;
>>> +       struct seccomp_filter *orig = filter;
>>> +
>>>         /* Clean up single-reference branches iteratively. */
>>>         while (orig && atomic_dec_and_test(&orig->usage)) {
>>>                 struct seccomp_filter *freeme = orig;
>>> @@ -488,6 +491,11 @@ void put_seccomp_filter(struct task_struct *tsk)
>>>         }
>>>  }
>>>
>>> +void put_seccomp(struct task_struct *tsk)
>>> +{
>>> +       put_seccomp_filter(tsk->seccomp.filter);
>>> +}
>>> +
>>>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
>>>  {
>>>         memset(info, 0, sizeof(*info));
>>> @@ -914,7 +922,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
>>>         if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
>>>                 ret = -EFAULT;
>>>
>>> -       put_seccomp_filter(task);
>>> +       put_seccomp_filter(task->seccomp.filter);
>>>         return ret;
>>
>> I don't like that the arguments to get_seccomp_filter() and
>> put_seccomp_filter() are now different. I think they should match for
>> readability.
> 
> OK, I can do that.
> 

Kees, can I send this as a separate patch?


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [kernel-hardening] Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-19 22:03           ` Mickaël Salaün
@ 2017-04-19 23:58             ` " Casey Schaufler
  2017-04-20  1:48             ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Casey Schaufler @ 2017-04-19 23:58 UTC (permalink / raw)
  To: Mickaël Salaün, Kees Cook
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Paul Moore,
	Sargun Dhillon, Serge E . Hallyn, Shuah Khan, Tejun Heo,
	Thomas Graf, Will Drewry, kernel-hardening, Linux API,
	linux-security-module, Network Development

On 4/19/2017 3:03 PM, Mickaël Salaün wrote:
> On 19/04/2017 01:40, Kees Cook wrote:
>> On Tue, Apr 18, 2017 at 4:16 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>>> On 4/18/2017 3:44 PM, Mickaël Salaün wrote:
>>>> On 19/04/2017 00:17, Kees Cook wrote:
>>>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>>>> +void __init landlock_add_hooks(void)
>>>>>> +{
>>>>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>>>>> +       landlock_add_hooks_fs();
>>>>>> +       security_add_hooks(NULL, 0, "landlock");
>>>>>> +       bpf_register_prog_type(&bpf_landlock_type);
>>>>> I'm confused by the separation of hook registration here. The call to
>>>>> security_add_hooks is with count=0 is especially weird. Why isn't this
>>>>> just a single call with security_add_hooks(landlock_hooks,
>>>>> ARRAY_SIZE(landlock_hooks), "landlock")?
>>>> Yes, this is ugly with the new security_add_hooks() with three arguments
>>>> but I wanted to split the hooks definition in multiple files.
>>> Why? I'll buy a good argument, but there are dangers in
>>> allowing multiple calls to security_add_hooks().
> I prefer to have one file per hook "family" (e.g. filesystem, network,
> ptrace…). This reduce the mess with all the included files (needed for
> LSM hook argument types) and make the files easier to read, understand
> and maintain.

Yeah, there's that tradeoff and it really is a matter
of taste I suppose.

>>>> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
>>>> is not exported. Unfortunately, calling multiple security_add_hooks()
>>>> with the same LSM name would register multiple names for the same LSM…
>>>> Is it OK if I modify this function to not add duplicated entries?
>>> It may seem absurd, but it's conceivable that a module might
>>> have two hooks it wants called. My example is a module that
>>> counts the number of times SELinux denies a process access to
>>> things (which needs to be called before and after SELinux in
>>> order to detect denials) and takes "appropriate action" if
>>> too many denials occur. It would be weird, wonky and hackish,
>>> but that never stopped anybody before.
> Right, but now, with the new lsm_append(), module names are concatenated
> ("%s,%s") in the lsm_names variable. It would be nice to not pollute
> this string with multiple time the same module name.

All it would take is a check that the module name
isn't already on the list. It's a trivial change.

>> If ends up being sane and clear, I'm fine with allowing multiple calls.
>>
>> -Kees
>>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-04-19 22:03           ` Mickaël Salaün
  2017-04-19 23:58             ` [kernel-hardening] " Casey Schaufler
@ 2017-04-20  1:48             ` Kees Cook
  1 sibling, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-20  1:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Casey Schaufler, LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Daniel Borkmann, David Drysdale,
	David S . Miller, Eric W . Biederman, James Morris, Jann Horn,
	Jonathan Corbet, Matthew Garrett, Michael Kerrisk, Paul Moore,
	Sargun Dhillon, Serge E . Hallyn, Shuah Khan, Tejun Heo,
	Thomas Graf, Will Drewry, kernel-hardening, Linux API,
	linux-security-module, Network Development

On Wed, Apr 19, 2017 at 3:03 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
> On 19/04/2017 01:40, Kees Cook wrote:
>> On Tue, Apr 18, 2017 at 4:16 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>>> On 4/18/2017 3:44 PM, Mickaël Salaün wrote:
>>>> On 19/04/2017 00:17, Kees Cook wrote:
>>>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>>>> +void __init landlock_add_hooks(void)
>>>>>> +{
>>>>>> +       pr_info("landlock: Version %u", LANDLOCK_VERSION);
>>>>>> +       landlock_add_hooks_fs();
>>>>>> +       security_add_hooks(NULL, 0, "landlock");
>>>>>> +       bpf_register_prog_type(&bpf_landlock_type);
>>>>> I'm confused by the separation of hook registration here. The call to
>>>>> security_add_hooks is with count=0 is especially weird. Why isn't this
>>>>> just a single call with security_add_hooks(landlock_hooks,
>>>>> ARRAY_SIZE(landlock_hooks), "landlock")?
>>>> Yes, this is ugly with the new security_add_hooks() with three arguments
>>>> but I wanted to split the hooks definition in multiple files.
>>>
>>> Why? I'll buy a good argument, but there are dangers in
>>> allowing multiple calls to security_add_hooks().
>
> I prefer to have one file per hook "family" (e.g. filesystem, network,
> ptrace…). This reduce the mess with all the included files (needed for
> LSM hook argument types) and make the files easier to read, understand
> and maintain.
>
>>>
>>>>
>>>> The current security_add_hooks() use lsm_append(lsm, &lsm_names) which
>>>> is not exported. Unfortunately, calling multiple security_add_hooks()
>>>> with the same LSM name would register multiple names for the same LSM…
>>>> Is it OK if I modify this function to not add duplicated entries?
>>>
>>> It may seem absurd, but it's conceivable that a module might
>>> have two hooks it wants called. My example is a module that
>>> counts the number of times SELinux denies a process access to
>>> things (which needs to be called before and after SELinux in
>>> order to detect denials) and takes "appropriate action" if
>>> too many denials occur. It would be weird, wonky and hackish,
>>> but that never stopped anybody before.
>
> Right, but now, with the new lsm_append(), module names are concatenated
> ("%s,%s") in the lsm_names variable. It would be nice to not pollute
> this string with multiple time the same module name.

Perhaps security_add_hooks could be modified to accept a NULL lsm to
skip the lsm_append() call, so it could do:

security_add_hooks(hooks1, count1, NULL);
security_add_hooks(hooks2, count2, NULL);
security_add_hooks(NULL, 0, "landlock");

Or, as Casey suggests, disregard adding the name when it already exists:

security_add_hooks(hooks1, count1, "landlock");
security_add_hooks(hooks2, count2, "landlock");

Yeah, I think I prefer this...

-Kees

>
>>
>> If ends up being sane and clear, I'm fine with allowing multiple calls.
>>
>> -Kees
>>
>



-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-04-19 22:05         ` Mickaël Salaün
@ 2017-04-20  1:50           ` Kees Cook
  0 siblings, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-20  1:50 UTC (permalink / raw)
  To: Mickaël Salaün, Shuah Khan
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Tejun Heo, Thomas Graf, Will Drewry, kernel-hardening, Linux API,
	linux-security-module, Network Development

On Wed, Apr 19, 2017 at 3:05 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
>
> On 20/04/2017 00:02, Kees Cook wrote:
>> On Wed, Apr 19, 2017 at 2:51 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>
>>> On 19/04/2017 02:02, Kees Cook wrote:
>>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>>> This is useful to return an information about the error without being
>>>>> able to write to TH_LOG_STREAM.
>>>>>
>>>>> Helpers from test_harness.h may be useful outside of the seccomp
>>>>> directory.
>>>>>
>>>>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>>>> Cc: Andy Lutomirski <luto@amacapital.net>
>>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>>> Cc: Kees Cook <keescook@chromium.org>
>>>>> Cc: Shuah Khan <shuah@kernel.org>
>>>>> Cc: Will Drewry <wad@chromium.org>
>>>>> ---
>>>>>  tools/testing/selftests/seccomp/test_harness.h | 8 +++++++-
>>>>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h
>>>>> index a786c69c7584..77e407663e06 100644
>>>>> --- a/tools/testing/selftests/seccomp/test_harness.h
>>>>> +++ b/tools/testing/selftests/seccomp/test_harness.h
>>>>> @@ -397,7 +397,7 @@ struct __test_metadata {
>>>>>         const char *name;
>>>>>         void (*fn)(struct __test_metadata *);
>>>>>         int termsig;
>>>>> -       int passed;
>>>>> +       __s8 passed;
>>>>
>>>> Why the reduction here? int is signed too?
>>>
>>> Because the return code of a process is capped to 8 bits and I use a
>>> negative value to not mess with the current interpretation of 0 (error)
>>> and 1 (OK) for the "passed" variable.
>>>
>>>>
>>>>>         int trigger; /* extra handler after the evaluation */
>>>>>         struct __test_metadata *prev, *next;
>>>>>  };
>>>>> @@ -476,6 +476,12 @@ void __run_test(struct __test_metadata *t)
>>>>>                                         "instead of by signal (code: %d)\n",
>>>>>                                         t->name,
>>>>>                                         WEXITSTATUS(status));
>>>>> +                       } else if (t->passed < 0) {
>>>>> +                               fprintf(TH_LOG_STREAM,
>>>>> +                                       "%s: Failed at step #%d\n",
>>>>> +                                       t->name,
>>>>> +                                       t->passed * -1);
>>>>> +                               t->passed = 0;
>>>>>                         }
>>>>
>>>> Instead of creating an overloaded mechanism here, perhaps have an
>>>> option reporting mechanism that can be enabled. Like adding to
>>>> __test_metadata "bool no_stream; int test_number;" and adding
>>>> test_number++ to each ASSERT/EXCEPT call, and doing something like:
>>>>
>>>> if (t->no_stream) {
>>>>                               fprintf(TH_LOG_STREAM,
>>>>                                       "%s: Failed at step #%d\n",
>>>>                                       t->name,
>>>>                                        t->test_number);
>>>> }
>>>>
>>>> It'd be a cleaner approach, maybe?
>>>
>>> Good idea, we will then be able to use 255 steps!
>>>
>>> Do you want me to send this as a separate patch?
>>>
>>> Can we move test_harness.h outside of the seccomp directory to be
>>> available to other subsystems as well?
>>
>> Yeah, I would do two patches, and send them out separately (to shuah
>> with lkml and me in cc at least), one to move test_hardness.h into
>> some include/ directory, and then to add the new logic for streamless
>> reporting.
>>
>> Thanks!
>>
>
> Good, in which place and name would it fit better?

I've added Shuah to CC. Shuah, where should a common header file for
selftests live? Should a new "include" directory be added?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-04-19 22:18       ` Mickaël Salaün
@ 2017-04-20  1:54         ` Kees Cook
  0 siblings, 0 replies; 50+ messages in thread
From: Kees Cook @ 2017-04-20  1:54 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: LKML, Alexei Starovoitov, Andy Lutomirski,
	Arnaldo Carvalho de Melo, Casey Schaufler, Daniel Borkmann,
	David Drysdale, David S . Miller, Eric W . Biederman,
	James Morris, Jann Horn, Jonathan Corbet, Matthew Garrett,
	Michael Kerrisk, Paul Moore, Sargun Dhillon, Serge E . Hallyn,
	Shuah Khan, Tejun Heo, Thomas Graf, Will Drewry,
	kernel-hardening, Linux API, linux-security-module,
	Network Development

On Wed, Apr 19, 2017 at 3:18 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
> On 19/04/2017 00:47, Mickaël Salaün wrote:
>>
>> On 19/04/2017 00:23, Kees Cook wrote:
>>> On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün <mic@digikod.net> wrote:
>>>> The semantic is unchanged. This will be useful for the Landlock
>>>> integration with seccomp (next commit).
>>>>
>>>> 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>
>>>> ---
>>>>  include/linux/seccomp.h |  4 ++--
>>>>  kernel/fork.c           |  2 +-
>>>>  kernel/seccomp.c        | 18 +++++++++++++-----
>>>>  3 files changed, 16 insertions(+), 8 deletions(-)
>>>>
>>>> diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
>>>> index ecc296c137cd..e25aee2cdfc0 100644
>>>> --- a/include/linux/seccomp.h
>>>> +++ b/include/linux/seccomp.h
>>>> @@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s)
>>>>  #endif /* CONFIG_SECCOMP */
>>>>
>>>>  #ifdef CONFIG_SECCOMP_FILTER
>>>> -extern void put_seccomp_filter(struct task_struct *tsk);
>>>> +extern void put_seccomp(struct task_struct *tsk);
>>>>  extern void get_seccomp_filter(struct task_struct *tsk);
>>>>  #else  /* CONFIG_SECCOMP_FILTER */
>>>> -static inline void put_seccomp_filter(struct task_struct *tsk)
>>>> +static inline void put_seccomp(struct task_struct *tsk)
>>>>  {
>>>>         return;
>>>>  }
>>>> diff --git a/kernel/fork.c b/kernel/fork.c
>>>> index 6c463c80e93d..a27d8e67ce33 100644
>>>> --- a/kernel/fork.c
>>>> +++ b/kernel/fork.c
>>>> @@ -363,7 +363,7 @@ void free_task(struct task_struct *tsk)
>>>>  #endif
>>>>         rt_mutex_debug_task_free(tsk);
>>>>         ftrace_graph_exit_task(tsk);
>>>> -       put_seccomp_filter(tsk);
>>>> +       put_seccomp(tsk);
>>>>         arch_release_task_struct(tsk);
>>>>         if (tsk->flags & PF_KTHREAD)
>>>>                 free_kthread_struct(tsk);
>>>> diff --git a/kernel/seccomp.c b/kernel/seccomp.c
>>>> index 65f61077ad50..326f79e32127 100644
>>>> --- a/kernel/seccomp.c
>>>> +++ b/kernel/seccomp.c
>>>> @@ -64,6 +64,8 @@ struct seccomp_filter {
>>>>  /* Limit any path through the tree to 256KB worth of instructions. */
>>>>  #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
>>>>
>>>> +static void put_seccomp_filter(struct seccomp_filter *filter);
>>>
>>> Can this be reorganized easily to avoid a forward-declaration?
>>
>> I didn't want to move too much code but I will.
>>
>>>
>>>> +
>>>>  /*
>>>>   * Endianness is explicitly ignored and left for BPF program authors to manage
>>>>   * as per the specific architecture.
>>>> @@ -314,7 +316,7 @@ static inline void seccomp_sync_threads(void)
>>>>                  * current's path will hold a reference.  (This also
>>>>                  * allows a put before the assignment.)
>>>>                  */
>>>> -               put_seccomp_filter(thread);
>>>> +               put_seccomp_filter(thread->seccomp.filter);
>>>>                 smp_store_release(&thread->seccomp.filter,
>>>>                                   caller->seccomp.filter);
>>>>
>>>> @@ -476,10 +478,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter)
>>>>         }
>>>>  }
>>>>
>>>> -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
>>>> -void put_seccomp_filter(struct task_struct *tsk)
>>>> +/* put_seccomp_filter - decrements the ref count of a filter */
>>>> +static void put_seccomp_filter(struct seccomp_filter *filter)
>>>>  {
>>>> -       struct seccomp_filter *orig = tsk->seccomp.filter;
>>>> +       struct seccomp_filter *orig = filter;
>>>> +
>>>>         /* Clean up single-reference branches iteratively. */
>>>>         while (orig && atomic_dec_and_test(&orig->usage)) {
>>>>                 struct seccomp_filter *freeme = orig;
>>>> @@ -488,6 +491,11 @@ void put_seccomp_filter(struct task_struct *tsk)
>>>>         }
>>>>  }
>>>>
>>>> +void put_seccomp(struct task_struct *tsk)
>>>> +{
>>>> +       put_seccomp_filter(tsk->seccomp.filter);
>>>> +}
>>>> +
>>>>  static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
>>>>  {
>>>>         memset(info, 0, sizeof(*info));
>>>> @@ -914,7 +922,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
>>>>         if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
>>>>                 ret = -EFAULT;
>>>>
>>>> -       put_seccomp_filter(task);
>>>> +       put_seccomp_filter(task->seccomp.filter);
>>>>         return ret;
>>>
>>> I don't like that the arguments to get_seccomp_filter() and
>>> put_seccomp_filter() are now different. I think they should match for
>>> readability.
>>
>> OK, I can do that.
>>
>
> Kees, can I send this as a separate patch?

Sure! Though I still think the argument to get/put_seccomp_filter()
should be task_struct.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, back to index

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-28 23:46 [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Mickaël Salaün
2017-03-28 23:46 ` [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier Mickaël Salaün
2017-03-29 13:48   ` kbuild test robot
2017-04-18 21:48   ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock Mickaël Salaün
2017-04-16 21:57   ` Mickaël Salaün
2017-04-18 21:58   ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 03/11] bpf: Define handle_fs and add a new helper bpf_handle_fs_get_mode() Mickaël Salaün
2017-03-28 23:46 ` [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem Mickaël Salaün
2017-03-29 15:18   ` kbuild test robot
2017-04-18 22:17   ` Kees Cook
2017-04-18 22:44     ` Mickaël Salaün
2017-04-18 23:16       ` Casey Schaufler
2017-04-18 23:40         ` Kees Cook
2017-04-19 22:03           ` Mickaël Salaün
2017-04-19 23:58             ` [kernel-hardening] " Casey Schaufler
2017-04-20  1:48             ` Kees Cook
2017-04-18 23:39       ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp() Mickaël Salaün
2017-04-18 22:23   ` Kees Cook
2017-04-18 22:47     ` Mickaël Salaün
2017-04-19 22:18       ` Mickaël Salaün
2017-04-20  1:54         ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy Mickaël Salaün
2017-03-29 10:35   ` [kernel-hardening] " Djalal Harouni
2017-03-31 21:15     ` Mickaël Salaün
2017-04-18 22:54       ` Kees Cook
2017-04-18 22:53   ` Kees Cook
2017-04-18 23:24     ` Mickaël Salaün
2017-04-18 23:48       ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 07/11] landlock: Add ptrace restrictions Mickaël Salaün
2017-04-10  6:48   ` [kernel-hardening] " Djalal Harouni
2017-04-11  7:19     ` Mickaël Salaün
2017-03-28 23:46 ` [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example Mickaël Salaün
2017-04-18 23:06   ` Kees Cook
2017-04-18 23:35     ` Mickaël Salaün
2017-03-28 23:46 ` [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism Mickaël Salaün
2017-04-19  0:02   ` Kees Cook
2017-04-19 21:51     ` Mickaël Salaün
2017-04-19 22:02       ` Kees Cook
2017-04-19 22:05         ` Mickaël Salaün
2017-04-20  1:50           ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock Mickaël Salaün
2017-04-18 23:16   ` Kees Cook
2017-04-18 23:53     ` Mickaël Salaün
2017-04-18 23:59       ` Kees Cook
2017-03-28 23:46 ` [PATCH net-next v6 11/11] landlock: Add user and kernel documentation " Mickaël Salaün
2017-03-29 15:58   ` kbuild test robot
2017-04-18 23:26 ` [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing Kees Cook
2017-04-19  0:12   ` Mickaël Salaün

LKML Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/lkml/0 lkml/git/0.git
	git clone --mirror https://lore.kernel.org/lkml/1 lkml/git/1.git
	git clone --mirror https://lore.kernel.org/lkml/2 lkml/git/2.git
	git clone --mirror https://lore.kernel.org/lkml/3 lkml/git/3.git
	git clone --mirror https://lore.kernel.org/lkml/4 lkml/git/4.git
	git clone --mirror https://lore.kernel.org/lkml/5 lkml/git/5.git
	git clone --mirror https://lore.kernel.org/lkml/6 lkml/git/6.git
	git clone --mirror https://lore.kernel.org/lkml/7 lkml/git/7.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 lkml lkml/ https://lore.kernel.org/lkml \
		linux-kernel@vger.kernel.org linux-kernel@archiver.kernel.org
	public-inbox-index lkml


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-kernel


AGPL code for this site: git clone https://public-inbox.org/ public-inbox