All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
@ 2017-03-28 23:46 ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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] 221+ messages in thread

* [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
@ 2017-03-28 23:46 ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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] 221+ messages in thread

* [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
@ 2017-03-28 23:46 ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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 at digikod.net
[2] https://lkml.kernel.org/r/5828776A.1010104 at digikod.net
[3] https://lkml.kernel.org/r/1477390454-12553-1-git-send-email-daniel at zonque.org
[4] https://lkml.kernel.org/r/20160829114542.GA20836 at ircssh.c.rugged-nimbus-611.internal
[5] https://lkml.kernel.org/r/20161221231506.19800-1-mic at 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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 00/11] Landlock LSM: Toward unprivileged sandboxing
@ 2017-03-28 23:46 ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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] 221+ messages in thread

* [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  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

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-WFhQfpSGs3bR7s880joybQ@public.gmane.org>
Cc: Alexei Starovoitov <ast-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Arnaldo Carvalho de Melo <acme-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Daniel Borkmann <daniel-FeC+5ew28dpmcu3hnIyYJQ@public.gmane.org>
Cc: David S. Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
Link: https://lkml.kernel.org/r/20160827205559.GA43880-+o4/htvd0TDFYCXBM6kdu7fOX0fSgVTm@public.gmane.org
---
 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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 at 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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 01/11] bpf: Add eBPF program subtype and is_valid_subtype() verifier
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 02/11] bpf, landlock: Define an eBPF program type for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ 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 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ 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   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ 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   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info@ http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [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   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 04/11] landlock: Add LSM hooks related to filesystem
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 05/11] seccomp: Split put_seccomp_filter() with put_seccomp()
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 06/11] seccomp, landlock: Handle Landlock events per process hierarchy
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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 at 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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 07/11] landlock: Add ptrace restrictions
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 08/11] bpf: Add a Landlock sandbox example
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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@step #%d\n",
+					t->name,
+					t->passed * -1);
+				t->passed = 0;
 			}
 		} else if (WIFSIGNALED(status)) {
 			t->passed = 0;
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 09/11] seccomp: Enhance test_harness with an assert step mechanism
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 10/11] bpf,landlock: Add tests for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
  2017-03-28 23:46 ` Mickaël Salaün
  (?)
  (?)
@ 2017-03-28 23:46   ` Mickaël Salaün
  -1 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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

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 related	[flat|nested] 221+ messages in thread

* [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ messages in thread
From: Mickaël Salaün @ 2017-03-28 23:46 UTC (permalink / raw)
  To: linux-security-module

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

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [kernel-hardening] [PATCH net-next v6 11/11] landlock: Add user and kernel documentation for Landlock
@ 2017-03-28 23:46   ` Mickaël Salaün
  0 siblings, 0 replies; 221+ 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 related	[flat|nested] 221+ 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: " Mickaël Salaün
  (?)
  (?)
@ 2017-03-29 10:35     ` Djalal Harouni
  -1 siblings, 0 replies; 221+ 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] 221+ messages in thread

* Re: [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
@ 2017-03-29 10:35     ` Djalal Harouni
  0 siblings, 0 replies; 221+ 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

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] 221+ messages in thread

* Re: [PATCH net-next v6 06/11] seccomp,landlock: Handle Landlock events per process hierarchy
@ 2017-03-29 10:35     ` Djalal Harouni
  0 siblings, 0 replies; 221+ 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

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