130 lines
5.2 KiB
ReStructuredText
130 lines
5.2 KiB
ReStructuredText
.. SPDX-License-Identifier: GPL-2.0
|
|
.. Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
|
|
.. Copyright © 2019-2020 ANSSI
|
|
|
|
==================================
|
|
Landlock LSM: kernel documentation
|
|
==================================
|
|
|
|
:Author: Mickaël Salaün
|
|
:Date: December 2022
|
|
|
|
Landlock's goal is to create scoped access-control (i.e. sandboxing). To
|
|
harden a whole system, this feature should be available to any process,
|
|
including unprivileged ones. Because such process may be compromised or
|
|
backdoored (i.e. untrusted), Landlock's features must be safe to use from the
|
|
kernel and other processes point of view. Landlock's interface must therefore
|
|
expose a minimal attack surface.
|
|
|
|
Landlock is designed to be usable by unprivileged processes while following the
|
|
system security policy enforced by other access control mechanisms (e.g. DAC,
|
|
LSM). Indeed, a Landlock rule shall not interfere with other access-controls
|
|
enforced on the system, only add more restrictions.
|
|
|
|
Any user can enforce Landlock rulesets on their processes. They are merged and
|
|
evaluated according to the inherited ones in a way that ensures that only more
|
|
constraints can be added.
|
|
|
|
User space documentation can be found here:
|
|
Documentation/userspace-api/landlock.rst.
|
|
|
|
Guiding principles for safe access controls
|
|
===========================================
|
|
|
|
* A Landlock rule shall be focused on access control on kernel objects instead
|
|
of syscall filtering (i.e. syscall arguments), which is the purpose of
|
|
seccomp-bpf.
|
|
* To avoid multiple kinds of side-channel attacks (e.g. leak of security
|
|
policies, CPU-based attacks), Landlock rules shall not be able to
|
|
programmatically communicate with user space.
|
|
* Kernel access check shall not slow down access request from unsandboxed
|
|
processes.
|
|
* Computation related to Landlock operations (e.g. enforcing a ruleset) shall
|
|
only impact the processes requesting them.
|
|
* Resources (e.g. file descriptors) directly obtained from the kernel by a
|
|
sandboxed process shall retain their scoped accesses (at the time of resource
|
|
acquisition) whatever process use them.
|
|
Cf. `File descriptor access rights`_.
|
|
|
|
Design choices
|
|
==============
|
|
|
|
Inode access rights
|
|
-------------------
|
|
|
|
All access rights are tied to an inode and what can be accessed through it.
|
|
Reading the content of a directory does not imply to be allowed to read the
|
|
content of a listed inode. Indeed, a file name is local to its parent
|
|
directory, and an inode can be referenced by multiple file names thanks to
|
|
(hard) links. Being able to unlink a file only has a direct impact on the
|
|
directory, not the unlinked inode. This is the reason why
|
|
``LANDLOCK_ACCESS_FS_REMOVE_FILE`` or ``LANDLOCK_ACCESS_FS_REFER`` are not
|
|
allowed to be tied to files but only to directories.
|
|
|
|
File descriptor access rights
|
|
-----------------------------
|
|
|
|
Access rights are checked and tied to file descriptors at open time. The
|
|
underlying principle is that equivalent sequences of operations should lead to
|
|
the same results, when they are executed under the same Landlock domain.
|
|
|
|
Taking the ``LANDLOCK_ACCESS_FS_TRUNCATE`` right as an example, it may be
|
|
allowed to open a file for writing without being allowed to
|
|
:manpage:`ftruncate` the resulting file descriptor if the related file
|
|
hierarchy doesn't grant such access right. The following sequences of
|
|
operations have the same semantic and should then have the same result:
|
|
|
|
* ``truncate(path);``
|
|
* ``int fd = open(path, O_WRONLY); ftruncate(fd); close(fd);``
|
|
|
|
Similarly to file access modes (e.g. ``O_RDWR``), Landlock access rights
|
|
attached to file descriptors are retained even if they are passed between
|
|
processes (e.g. through a Unix domain socket). Such access rights will then be
|
|
enforced even if the receiving process is not sandboxed by Landlock. Indeed,
|
|
this is required to keep a consistent access control over the whole system, and
|
|
this avoids unattended bypasses through file descriptor passing (i.e. confused
|
|
deputy attack).
|
|
|
|
Tests
|
|
=====
|
|
|
|
Userspace tests for backward compatibility, ptrace restrictions and filesystem
|
|
support can be found here: `tools/testing/selftests/landlock/`_.
|
|
|
|
Kernel structures
|
|
=================
|
|
|
|
Object
|
|
------
|
|
|
|
.. kernel-doc:: security/landlock/object.h
|
|
:identifiers:
|
|
|
|
Filesystem
|
|
----------
|
|
|
|
.. kernel-doc:: security/landlock/fs.h
|
|
:identifiers:
|
|
|
|
Ruleset and domain
|
|
------------------
|
|
|
|
A domain is a read-only ruleset tied to a set of subjects (i.e. tasks'
|
|
credentials). Each time a ruleset is enforced on a task, the current domain is
|
|
duplicated and the ruleset is imported as a new layer of rules in the new
|
|
domain. Indeed, once in a domain, each rule is tied to a layer level. To
|
|
grant access to an object, at least one rule of each layer must allow the
|
|
requested action on the object. A task can then only transit to a new domain
|
|
that is the intersection of the constraints from the current domain and those
|
|
of a ruleset provided by the task.
|
|
|
|
The definition of a subject is implicit for a task sandboxing itself, which
|
|
makes the reasoning much easier and helps avoid pitfalls.
|
|
|
|
.. kernel-doc:: security/landlock/ruleset.h
|
|
:identifiers:
|
|
|
|
.. Links
|
|
.. _tools/testing/selftests/landlock/:
|
|
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/landlock/
|