Commit 2fc0e789 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'landlock-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux

Pull landlock updates from Mickaël Salaün:
 "This brings ioctl control to Landlock, contributed by Günther Noack.
  This also adds him as a Landlock reviewer, and fixes an issue in the
  sample"

* tag 'landlock-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  MAINTAINERS: Add Günther Noack as Landlock reviewer
  fs/ioctl: Add a comment to keep the logic in sync with LSM policies
  MAINTAINERS: Notify Landlock maintainers about changes to fs/ioctl.c
  landlock: Document IOCTL support
  samples/landlock: Add support for LANDLOCK_ACCESS_FS_IOCTL_DEV
  selftests/landlock: Exhaustive test for the IOCTL allow-list
  selftests/landlock: Check IOCTL restrictions for named UNIX domain sockets
  selftests/landlock: Test IOCTLs on named pipes
  selftests/landlock: Test ioctl(2) and ftruncate(2) with open(O_PATH)
  selftests/landlock: Test IOCTL with memfds
  selftests/landlock: Test IOCTL support
  landlock: Add IOCTL access right for character and block devices
  samples/landlock: Fix incorrect free in populate_ruleset_net
parents 89721e30 5bf9e57e
...@@ -8,7 +8,7 @@ Landlock: unprivileged access control ...@@ -8,7 +8,7 @@ Landlock: unprivileged access control
===================================== =====================================
:Author: Mickaël Salaün :Author: Mickaël Salaün
:Date: October 2023 :Date: April 2024
The goal of Landlock is to enable to restrict ambient rights (e.g. global The goal of Landlock is to enable to restrict ambient rights (e.g. global
filesystem or network access) for a set of processes. Because Landlock filesystem or network access) for a set of processes. Because Landlock
...@@ -76,7 +76,8 @@ to be explicit about the denied-by-default access rights. ...@@ -76,7 +76,8 @@ to be explicit about the denied-by-default access rights.
LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_BLOCK |
LANDLOCK_ACCESS_FS_MAKE_SYM | LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REFER |
LANDLOCK_ACCESS_FS_TRUNCATE, LANDLOCK_ACCESS_FS_TRUNCATE |
LANDLOCK_ACCESS_FS_IOCTL_DEV,
.handled_access_net = .handled_access_net =
LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP, LANDLOCK_ACCESS_NET_CONNECT_TCP,
...@@ -85,10 +86,10 @@ to be explicit about the denied-by-default access rights. ...@@ -85,10 +86,10 @@ to be explicit about the denied-by-default access rights.
Because we may not know on which kernel version an application will be Because we may not know on which kernel version an application will be
executed, it is safer to follow a best-effort security approach. Indeed, we executed, it is safer to follow a best-effort security approach. Indeed, we
should try to protect users as much as possible whatever the kernel they are should try to protect users as much as possible whatever the kernel they are
using. To avoid binary enforcement (i.e. either all security features or using.
none), we can leverage a dedicated Landlock command to get the current version
of the Landlock ABI and adapt the handled accesses. Let's check if we should To be compatible with older Linux versions, we detect the available Landlock ABI
remove access rights which are only supported in higher versions of the ABI. version, and only use the available subset of access rights:
.. code-block:: c .. code-block:: c
...@@ -114,6 +115,10 @@ remove access rights which are only supported in higher versions of the ABI. ...@@ -114,6 +115,10 @@ remove access rights which are only supported in higher versions of the ABI.
ruleset_attr.handled_access_net &= ruleset_attr.handled_access_net &=
~(LANDLOCK_ACCESS_NET_BIND_TCP | ~(LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP); LANDLOCK_ACCESS_NET_CONNECT_TCP);
__attribute__((fallthrough));
case 4:
/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
} }
This enables to create an inclusive ruleset that will contain our rules. This enables to create an inclusive ruleset that will contain our rules.
...@@ -225,6 +230,7 @@ access rights per directory enables to change the location of such directory ...@@ -225,6 +230,7 @@ access rights per directory enables to change the location of such directory
without relying on the destination directory access rights (except those that without relying on the destination directory access rights (except those that
are required for this operation, see ``LANDLOCK_ACCESS_FS_REFER`` are required for this operation, see ``LANDLOCK_ACCESS_FS_REFER``
documentation). documentation).
Having self-sufficient hierarchies also helps to tighten the required access Having self-sufficient hierarchies also helps to tighten the required access
rights to the minimal set of data. This also helps avoid sinkhole directories, rights to the minimal set of data. This also helps avoid sinkhole directories,
i.e. directories where data can be linked to but not linked from. However, i.e. directories where data can be linked to but not linked from. However,
...@@ -318,18 +324,26 @@ It should also be noted that truncating files does not require the ...@@ -318,18 +324,26 @@ It should also be noted that truncating files does not require the
system call, this can also be done through :manpage:`open(2)` with the flags system call, this can also be done through :manpage:`open(2)` with the flags
``O_RDONLY | O_TRUNC``. ``O_RDONLY | O_TRUNC``.
When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE`` The truncate right is associated with the opened file (see below).
right is associated with the newly created file descriptor and will be used for
subsequent truncation attempts using :manpage:`ftruncate(2)`. The behavior is Rights associated with file descriptors
similar to opening a file for reading or writing, where permissions are checked ---------------------------------------
during :manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and
When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE`` and
``LANDLOCK_ACCESS_FS_IOCTL_DEV`` rights is associated with the newly created
file descriptor and will be used for subsequent truncation and ioctl attempts
using :manpage:`ftruncate(2)` and :manpage:`ioctl(2)`. The behavior is similar
to opening a file for reading or writing, where permissions are checked during
:manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and
:manpage:`write(2)` calls. :manpage:`write(2)` calls.
As a consequence, it is possible to have multiple open file descriptors for the As a consequence, it is possible that a process has multiple open file
same file, where one grants the right to truncate the file and the other does descriptors referring to the same file, but Landlock enforces different things
not. It is also possible to pass such file descriptors between processes, when operating with these file descriptors. This can happen when a Landlock
keeping their Landlock properties, even when these processes do not have an ruleset gets enforced and the process keeps file descriptors which were opened
enforced Landlock ruleset. both before and after the enforcement. It is also possible to pass such file
descriptors between processes, keeping their Landlock properties, even when some
of the involved processes do not have an enforced Landlock ruleset.
Compatibility Compatibility
============= =============
...@@ -458,6 +472,28 @@ Memory usage ...@@ -458,6 +472,28 @@ Memory usage
Kernel memory allocated to create rulesets is accounted and can be restricted Kernel memory allocated to create rulesets is accounted and can be restricted
by the Documentation/admin-guide/cgroup-v1/memory.rst. by the Documentation/admin-guide/cgroup-v1/memory.rst.
IOCTL support
-------------
The ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right restricts the use of
:manpage:`ioctl(2)`, but it only applies to *newly opened* device files. This
means specifically that pre-existing file descriptors like stdin, stdout and
stderr are unaffected.
Users should be aware that TTY devices have traditionally permitted to control
other processes on the same TTY through the ``TIOCSTI`` and ``TIOCLINUX`` IOCTL
commands. Both of these require ``CAP_SYS_ADMIN`` on modern Linux systems, but
the behavior is configurable for ``TIOCSTI``.
On older systems, it is therefore recommended to close inherited TTY file
descriptors, or to reopen them from ``/proc/self/fd/*`` without the
``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right, if possible.
Landlock's IOCTL support is coarse-grained at the moment, but may become more
fine-grained in the future. Until then, users are advised to establish the
guarantees that they need through the file hierarchy, by only allowing the
``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right on files where it is really required.
Previous limitations Previous limitations
==================== ====================
...@@ -495,6 +531,16 @@ bind and connect actions to only a set of allowed ports thanks to the new ...@@ -495,6 +531,16 @@ bind and connect actions to only a set of allowed ports thanks to the new
``LANDLOCK_ACCESS_NET_BIND_TCP`` and ``LANDLOCK_ACCESS_NET_CONNECT_TCP`` ``LANDLOCK_ACCESS_NET_BIND_TCP`` and ``LANDLOCK_ACCESS_NET_CONNECT_TCP``
access rights. access rights.
IOCTL (ABI < 5)
---------------
IOCTL operations could not be denied before the fifth Landlock ABI, so
:manpage:`ioctl(2)` is always allowed when using a kernel that only supports an
earlier ABI.
Starting with the Landlock ABI version 5, it is possible to restrict the use of
:manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
.. _kernel_support: .. _kernel_support:
Kernel support Kernel support
......
...@@ -12374,12 +12374,14 @@ F: net/l3mdev ...@@ -12374,12 +12374,14 @@ F: net/l3mdev
LANDLOCK SECURITY MODULE LANDLOCK SECURITY MODULE
M: Mickaël Salaün <mic@digikod.net> M: Mickaël Salaün <mic@digikod.net>
R: Günther Noack <gnoack@google.com>
L: linux-security-module@vger.kernel.org L: linux-security-module@vger.kernel.org
S: Supported S: Supported
W: https://landlock.io W: https://landlock.io
T: git https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git T: git https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
F: Documentation/security/landlock.rst F: Documentation/security/landlock.rst
F: Documentation/userspace-api/landlock.rst F: Documentation/userspace-api/landlock.rst
F: fs/ioctl.c
F: include/uapi/linux/landlock.h F: include/uapi/linux/landlock.h
F: samples/landlock/ F: samples/landlock/
F: security/landlock/ F: security/landlock/
......
...@@ -796,6 +796,9 @@ static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp) ...@@ -796,6 +796,9 @@ static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp)
* *
* When you add any new common ioctls to the switches above and below, * When you add any new common ioctls to the switches above and below,
* please ensure they have compatible arguments in compat mode. * please ensure they have compatible arguments in compat mode.
*
* The LSM mailing list should also be notified of any command additions or
* changes, as specific LSMs may be affected.
*/ */
static int do_vfs_ioctl(struct file *filp, unsigned int fd, static int do_vfs_ioctl(struct file *filp, unsigned int fd,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
......
...@@ -128,7 +128,7 @@ struct landlock_net_port_attr { ...@@ -128,7 +128,7 @@ struct landlock_net_port_attr {
* files and directories. Files or directories opened before the sandboxing * files and directories. Files or directories opened before the sandboxing
* are not subject to these restrictions. * are not subject to these restrictions.
* *
* A file can only receive these access rights: * The following access rights apply only to files:
* *
* - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file. * - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file.
* - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. Note that * - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. Note that
...@@ -138,12 +138,13 @@ struct landlock_net_port_attr { ...@@ -138,12 +138,13 @@ struct landlock_net_port_attr {
* - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access. * - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access.
* - %LANDLOCK_ACCESS_FS_TRUNCATE: Truncate a file with :manpage:`truncate(2)`, * - %LANDLOCK_ACCESS_FS_TRUNCATE: Truncate a file with :manpage:`truncate(2)`,
* :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with * :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with
* ``O_TRUNC``. Whether an opened file can be truncated with * ``O_TRUNC``. This access right is available since the third version of the
* :manpage:`ftruncate(2)` is determined during :manpage:`open(2)`, in the * Landlock ABI.
* same way as read and write permissions are checked during *
* :manpage:`open(2)` using %LANDLOCK_ACCESS_FS_READ_FILE and * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used
* %LANDLOCK_ACCESS_FS_WRITE_FILE. This access right is available since the * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as
* third version of the Landlock ABI. * read and write permissions are checked during :manpage:`open(2)` using
* %LANDLOCK_ACCESS_FS_READ_FILE and %LANDLOCK_ACCESS_FS_WRITE_FILE.
* *
* A directory can receive access rights related to files or directories. The * A directory can receive access rights related to files or directories. The
* following access right is applied to the directory itself, and the * following access right is applied to the directory itself, and the
...@@ -198,13 +199,33 @@ struct landlock_net_port_attr { ...@@ -198,13 +199,33 @@ struct landlock_net_port_attr {
* If multiple requirements are not met, the ``EACCES`` error code takes * If multiple requirements are not met, the ``EACCES`` error code takes
* precedence over ``EXDEV``. * precedence over ``EXDEV``.
* *
* The following access right applies both to files and directories:
*
* - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened
* character or block device.
*
* This access right applies to all `ioctl(2)` commands implemented by device
* drivers. However, the following common IOCTL commands continue to be
* invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right:
*
* * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``),
* * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``),
* * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``,
* ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``)
* * Some IOCTL commands which do not make sense when used with devices, but
* whose implementations are safe and return the right error codes
* (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``)
*
* This access right is available since the fifth version of the Landlock
* ABI.
*
* .. warning:: * .. warning::
* *
* It is currently not possible to restrict some file-related actions * It is currently not possible to restrict some file-related actions
* accessible through these syscall families: :manpage:`chdir(2)`, * accessible through these syscall families: :manpage:`chdir(2)`,
* :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`, * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
* :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`, * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
* :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`. * :manpage:`fcntl(2)`, :manpage:`access(2)`.
* Future Landlock evolutions will enable to restrict them. * Future Landlock evolutions will enable to restrict them.
*/ */
/* clang-format off */ /* clang-format off */
...@@ -223,6 +244,7 @@ struct landlock_net_port_attr { ...@@ -223,6 +244,7 @@ struct landlock_net_port_attr {
#define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
#define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
/* clang-format on */ /* clang-format on */
/** /**
......
...@@ -81,7 +81,8 @@ static int parse_path(char *env_path, const char ***const path_list) ...@@ -81,7 +81,8 @@ static int parse_path(char *env_path, const char ***const path_list)
LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \
LANDLOCK_ACCESS_FS_TRUNCATE) LANDLOCK_ACCESS_FS_TRUNCATE | \
LANDLOCK_ACCESS_FS_IOCTL_DEV)
/* clang-format on */ /* clang-format on */
...@@ -153,7 +154,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, ...@@ -153,7 +154,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
const __u64 allowed_access) const __u64 allowed_access)
{ {
int ret = 1; int ret = 1;
char *env_port_name, *strport; char *env_port_name, *env_port_name_next, *strport;
struct landlock_net_port_attr net_port = { struct landlock_net_port_attr net_port = {
.allowed_access = allowed_access, .allowed_access = allowed_access,
.port = 0, .port = 0,
...@@ -165,7 +166,8 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, ...@@ -165,7 +166,8 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
env_port_name = strdup(env_port_name); env_port_name = strdup(env_port_name);
unsetenv(env_var); unsetenv(env_var);
while ((strport = strsep(&env_port_name, ENV_DELIMITER))) { env_port_name_next = env_port_name;
while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) {
net_port.port = atoi(strport); net_port.port = atoi(strport);
if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
&net_port, 0)) { &net_port, 0)) {
...@@ -201,11 +203,12 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, ...@@ -201,11 +203,12 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
LANDLOCK_ACCESS_FS_MAKE_SYM | \ LANDLOCK_ACCESS_FS_MAKE_SYM | \
LANDLOCK_ACCESS_FS_REFER | \ LANDLOCK_ACCESS_FS_REFER | \
LANDLOCK_ACCESS_FS_TRUNCATE) LANDLOCK_ACCESS_FS_TRUNCATE | \
LANDLOCK_ACCESS_FS_IOCTL_DEV)
/* clang-format on */ /* clang-format on */
#define LANDLOCK_ABI_LAST 4 #define LANDLOCK_ABI_LAST 5
int main(const int argc, char *const argv[], char *const *const envp) int main(const int argc, char *const argv[], char *const *const envp)
{ {
...@@ -319,6 +322,11 @@ int main(const int argc, char *const argv[], char *const *const envp) ...@@ -319,6 +322,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
ruleset_attr.handled_access_net &= ruleset_attr.handled_access_net &=
~(LANDLOCK_ACCESS_NET_BIND_TCP | ~(LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP); LANDLOCK_ACCESS_NET_CONNECT_TCP);
__attribute__((fallthrough));
case 4:
/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
fprintf(stderr, fprintf(stderr,
"Hint: You should update the running kernel " "Hint: You should update the running kernel "
"to leverage Landlock features " "to leverage Landlock features "
......
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
* Copyright © 2018-2020 ANSSI * Copyright © 2018-2020 ANSSI
* Copyright © 2021-2022 Microsoft Corporation * Copyright © 2021-2022 Microsoft Corporation
* Copyright © 2022 Günther Noack <gnoack3000@gmail.com>
* Copyright © 2023-2024 Google LLC
*/ */
#include <asm/ioctls.h>
#include <kunit/test.h> #include <kunit/test.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -14,6 +17,7 @@ ...@@ -14,6 +17,7 @@
#include <linux/compiler_types.h> #include <linux/compiler_types.h>
#include <linux/dcache.h> #include <linux/dcache.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/falloc.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -29,6 +33,7 @@ ...@@ -29,6 +33,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/wait_bit.h> #include <linux/wait_bit.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <uapi/linux/fiemap.h>
#include <uapi/linux/landlock.h> #include <uapi/linux/landlock.h>
#include "common.h" #include "common.h"
...@@ -84,6 +89,160 @@ static const struct landlock_object_underops landlock_fs_underops = { ...@@ -84,6 +89,160 @@ static const struct landlock_object_underops landlock_fs_underops = {
.release = release_inode .release = release_inode
}; };
/* IOCTL helpers */
/**
* is_masked_device_ioctl - Determine whether an IOCTL command is always
* permitted with Landlock for device files. These commands can not be
* restricted on device files by enforcing a Landlock policy.
*
* @cmd: The IOCTL command that is supposed to be run.
*
* By default, any IOCTL on a device file requires the
* LANDLOCK_ACCESS_FS_IOCTL_DEV right. However, we blanket-permit some
* commands, if:
*
* 1. The command is implemented in fs/ioctl.c's do_vfs_ioctl(),
* not in f_ops->unlocked_ioctl() or f_ops->compat_ioctl().
*
* 2. The command is harmless when invoked on devices.
*
* We also permit commands that do not make sense for devices, but where the
* do_vfs_ioctl() implementation returns a more conventional error code.
*
* Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl()
* should be considered for inclusion here.
*
* Returns: true if the IOCTL @cmd can not be restricted with Landlock for
* device files.
*/
static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd)
{
switch (cmd) {
/*
* FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC manipulate the FD's
* close-on-exec and the file's buffered-IO and async flags. These
* operations are also available through fcntl(2), and are
* unconditionally permitted in Landlock.
*/
case FIOCLEX:
case FIONCLEX:
case FIONBIO:
case FIOASYNC:
/*
* FIOQSIZE queries the size of a regular file, directory, or link.
*
* We still permit it, because it always returns -ENOTTY for
* other file types.
*/
case FIOQSIZE:
/*
* FIFREEZE and FITHAW freeze and thaw the file system which the
* given file belongs to. Requires CAP_SYS_ADMIN.
*
* These commands operate on the file system's superblock rather
* than on the file itself. The same operations can also be
* done through any other file or directory on the same file
* system, so it is safe to permit these.
*/
case FIFREEZE:
case FITHAW:
/*
* FS_IOC_FIEMAP queries information about the allocation of
* blocks within a file.
*
* This IOCTL command only makes sense for regular files and is
* not implemented by devices. It is harmless to permit.
*/
case FS_IOC_FIEMAP:
/*
* FIGETBSZ queries the file system's block size for a file or
* directory.
*
* This command operates on the file system's superblock rather
* than on the file itself. The same operation can also be done
* through any other file or directory on the same file system,
* so it is safe to permit it.
*/
case FIGETBSZ:
/*
* FICLONE, FICLONERANGE and FIDEDUPERANGE make files share
* their underlying storage ("reflink") between source and
* destination FDs, on file systems which support that.
*
* These IOCTL commands only apply to regular files
* and are harmless to permit for device files.
*/
case FICLONE:
case FICLONERANGE:
case FIDEDUPERANGE:
/*
* FS_IOC_GETFSUUID and FS_IOC_GETFSSYSFSPATH both operate on
* the file system superblock, not on the specific file, so
* these operations are available through any other file on the
* same file system as well.
*/
case FS_IOC_GETFSUUID:
case FS_IOC_GETFSSYSFSPATH:
return true;
/*
* FIONREAD, FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and
* FS_IOC_FSSETXATTR are forwarded to device implementations.
*/
/*
* file_ioctl() commands (FIBMAP, FS_IOC_RESVSP, FS_IOC_RESVSP64,
* FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64 and FS_IOC_ZERO_RANGE) are
* forwarded to device implementations, so not permitted.
*/
/* Other commands are guarded by the access right. */
default:
return false;
}
}
/*
* is_masked_device_ioctl_compat - same as the helper above, but checking the
* "compat" IOCTL commands.
*
* The IOCTL commands with special handling in compat-mode should behave the
* same as their non-compat counterparts.
*/
static __attribute_const__ bool
is_masked_device_ioctl_compat(const unsigned int cmd)
{
switch (cmd) {
/* FICLONE is permitted, same as in the non-compat variant. */
case FICLONE:
return true;
#if defined(CONFIG_X86_64)
/*
* FS_IOC_RESVSP_32, FS_IOC_RESVSP64_32, FS_IOC_UNRESVSP_32,
* FS_IOC_UNRESVSP64_32, FS_IOC_ZERO_RANGE_32: not blanket-permitted,
* for consistency with their non-compat variants.
*/
case FS_IOC_RESVSP_32:
case FS_IOC_RESVSP64_32:
case FS_IOC_UNRESVSP_32:
case FS_IOC_UNRESVSP64_32:
case FS_IOC_ZERO_RANGE_32:
#endif
/*
* FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS are forwarded to their device
* implementations.
*/
case FS_IOC32_GETFLAGS:
case FS_IOC32_SETFLAGS:
return false;
default:
return is_masked_device_ioctl(cmd);
}
}
/* Ruleset management */ /* Ruleset management */
static struct landlock_object *get_inode_object(struct inode *const inode) static struct landlock_object *get_inode_object(struct inode *const inode)
...@@ -148,7 +307,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode) ...@@ -148,7 +307,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \
LANDLOCK_ACCESS_FS_TRUNCATE) LANDLOCK_ACCESS_FS_TRUNCATE | \
LANDLOCK_ACCESS_FS_IOCTL_DEV)
/* clang-format on */ /* clang-format on */
/* /*
...@@ -1332,11 +1492,18 @@ static int hook_file_alloc_security(struct file *const file) ...@@ -1332,11 +1492,18 @@ static int hook_file_alloc_security(struct file *const file)
return 0; return 0;
} }
static bool is_device(const struct file *const file)
{
const struct inode *inode = file_inode(file);
return S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode);
}
static int hook_file_open(struct file *const file) static int hook_file_open(struct file *const file)
{ {
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
access_mask_t open_access_request, full_access_request, allowed_access; access_mask_t open_access_request, full_access_request, allowed_access,
const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; optional_access;
const struct landlock_ruleset *const dom = const struct landlock_ruleset *const dom =
get_fs_domain(landlock_cred(file->f_cred)->domain); get_fs_domain(landlock_cred(file->f_cred)->domain);
...@@ -1354,6 +1521,10 @@ static int hook_file_open(struct file *const file) ...@@ -1354,6 +1521,10 @@ static int hook_file_open(struct file *const file)
* We look up more access than what we immediately need for open(), so * We look up more access than what we immediately need for open(), so
* that we can later authorize operations on opened files. * that we can later authorize operations on opened files.
*/ */
optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
if (is_device(file))
optional_access |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
full_access_request = open_access_request | optional_access; full_access_request = open_access_request | optional_access;
if (is_access_to_paths_allowed( if (is_access_to_paths_allowed(
...@@ -1410,6 +1581,52 @@ static int hook_file_truncate(struct file *const file) ...@@ -1410,6 +1581,52 @@ static int hook_file_truncate(struct file *const file)
return -EACCES; return -EACCES;
} }
static int hook_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
access_mask_t allowed_access = landlock_file(file)->allowed_access;
/*
* It is the access rights at the time of opening the file which
* determine whether IOCTL can be used on the opened file later.
*
* The access right is attached to the opened file in hook_file_open().
*/
if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV)
return 0;
if (!is_device(file))
return 0;
if (is_masked_device_ioctl(cmd))
return 0;
return -EACCES;
}
static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg)
{
access_mask_t allowed_access = landlock_file(file)->allowed_access;
/*
* It is the access rights at the time of opening the file which
* determine whether IOCTL can be used on the opened file later.
*
* The access right is attached to the opened file in hook_file_open().
*/
if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV)
return 0;
if (!is_device(file))
return 0;
if (is_masked_device_ioctl_compat(cmd))
return 0;
return -EACCES;
}
static struct security_hook_list landlock_hooks[] __ro_after_init = { static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), LSM_HOOK_INIT(inode_free_security, hook_inode_free_security),
...@@ -1432,6 +1649,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { ...@@ -1432,6 +1649,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security),
LSM_HOOK_INIT(file_open, hook_file_open), LSM_HOOK_INIT(file_open, hook_file_open),
LSM_HOOK_INIT(file_truncate, hook_file_truncate), LSM_HOOK_INIT(file_truncate, hook_file_truncate),
LSM_HOOK_INIT(file_ioctl, hook_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat),
}; };
__init void landlock_add_fs_hooks(void) __init void landlock_add_fs_hooks(void)
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_LAYERS 16
#define LANDLOCK_MAX_NUM_RULES U32_MAX #define LANDLOCK_MAX_NUM_RULES U32_MAX
#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE #define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
#define LANDLOCK_SHIFT_ACCESS_FS 0 #define LANDLOCK_SHIFT_ACCESS_FS 0
......
...@@ -149,7 +149,7 @@ static const struct file_operations ruleset_fops = { ...@@ -149,7 +149,7 @@ static const struct file_operations ruleset_fops = {
.write = fop_dummy_write, .write = fop_dummy_write,
}; };
#define LANDLOCK_ABI_VERSION 4 #define LANDLOCK_ABI_VERSION 5
/** /**
* sys_landlock_create_ruleset - Create a new ruleset * sys_landlock_create_ruleset - Create a new ruleset
......
...@@ -75,7 +75,7 @@ TEST(abi_version) ...@@ -75,7 +75,7 @@ TEST(abi_version)
const struct landlock_ruleset_attr ruleset_attr = { const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
}; };
ASSERT_EQ(4, landlock_create_ruleset(NULL, 0, ASSERT_EQ(5, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION)); LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment