Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+811 -46
+62 -16
Documentation/userspace-api/landlock.rst
··· 8 8 ===================================== 9 9 10 10 :Author: Mickaël Salaün 11 - :Date: October 2023 11 + :Date: April 2024 12 12 13 13 The goal of Landlock is to enable to restrict ambient rights (e.g. global 14 14 filesystem or network access) for a set of processes. Because Landlock ··· 76 76 LANDLOCK_ACCESS_FS_MAKE_BLOCK | 77 77 LANDLOCK_ACCESS_FS_MAKE_SYM | 78 78 LANDLOCK_ACCESS_FS_REFER | 79 - LANDLOCK_ACCESS_FS_TRUNCATE, 79 + LANDLOCK_ACCESS_FS_TRUNCATE | 80 + LANDLOCK_ACCESS_FS_IOCTL_DEV, 80 81 .handled_access_net = 81 82 LANDLOCK_ACCESS_NET_BIND_TCP | 82 83 LANDLOCK_ACCESS_NET_CONNECT_TCP, ··· 86 85 Because we may not know on which kernel version an application will be 87 86 executed, it is safer to follow a best-effort security approach. Indeed, we 88 87 should try to protect users as much as possible whatever the kernel they are 89 - using. To avoid binary enforcement (i.e. either all security features or 90 - none), we can leverage a dedicated Landlock command to get the current version 91 - of the Landlock ABI and adapt the handled accesses. Let's check if we should 92 - remove access rights which are only supported in higher versions of the ABI. 88 + using. 89 + 90 + To be compatible with older Linux versions, we detect the available Landlock ABI 91 + version, and only use the available subset of access rights: 93 92 94 93 .. code-block:: c 95 94 ··· 115 114 ruleset_attr.handled_access_net &= 116 115 ~(LANDLOCK_ACCESS_NET_BIND_TCP | 117 116 LANDLOCK_ACCESS_NET_CONNECT_TCP); 117 + __attribute__((fallthrough)); 118 + case 4: 119 + /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ 120 + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; 118 121 } 119 122 120 123 This enables to create an inclusive ruleset that will contain our rules. ··· 230 225 without relying on the destination directory access rights (except those that 231 226 are required for this operation, see ``LANDLOCK_ACCESS_FS_REFER`` 232 227 documentation). 228 + 233 229 Having self-sufficient hierarchies also helps to tighten the required access 234 230 rights to the minimal set of data. This also helps avoid sinkhole directories, 235 231 i.e. directories where data can be linked to but not linked from. However, ··· 324 318 system call, this can also be done through :manpage:`open(2)` with the flags 325 319 ``O_RDONLY | O_TRUNC``. 326 320 327 - When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE`` 328 - right is associated with the newly created file descriptor and will be used for 329 - subsequent truncation attempts using :manpage:`ftruncate(2)`. The behavior is 330 - similar to opening a file for reading or writing, where permissions are checked 331 - during :manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and 321 + The truncate right is associated with the opened file (see below). 322 + 323 + Rights associated with file descriptors 324 + --------------------------------------- 325 + 326 + When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE`` and 327 + ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` rights is associated with the newly created 328 + file descriptor and will be used for subsequent truncation and ioctl attempts 329 + using :manpage:`ftruncate(2)` and :manpage:`ioctl(2)`. The behavior is similar 330 + to opening a file for reading or writing, where permissions are checked during 331 + :manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and 332 332 :manpage:`write(2)` calls. 333 333 334 - As a consequence, it is possible to have multiple open file descriptors for the 335 - same file, where one grants the right to truncate the file and the other does 336 - not. It is also possible to pass such file descriptors between processes, 337 - keeping their Landlock properties, even when these processes do not have an 338 - enforced Landlock ruleset. 334 + As a consequence, it is possible that a process has multiple open file 335 + descriptors referring to the same file, but Landlock enforces different things 336 + when operating with these file descriptors. This can happen when a Landlock 337 + ruleset gets enforced and the process keeps file descriptors which were opened 338 + both before and after the enforcement. It is also possible to pass such file 339 + descriptors between processes, keeping their Landlock properties, even when some 340 + of the involved processes do not have an enforced Landlock ruleset. 339 341 340 342 Compatibility 341 343 ============= ··· 472 458 Kernel memory allocated to create rulesets is accounted and can be restricted 473 459 by the Documentation/admin-guide/cgroup-v1/memory.rst. 474 460 461 + IOCTL support 462 + ------------- 463 + 464 + The ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right restricts the use of 465 + :manpage:`ioctl(2)`, but it only applies to *newly opened* device files. This 466 + means specifically that pre-existing file descriptors like stdin, stdout and 467 + stderr are unaffected. 468 + 469 + Users should be aware that TTY devices have traditionally permitted to control 470 + other processes on the same TTY through the ``TIOCSTI`` and ``TIOCLINUX`` IOCTL 471 + commands. Both of these require ``CAP_SYS_ADMIN`` on modern Linux systems, but 472 + the behavior is configurable for ``TIOCSTI``. 473 + 474 + On older systems, it is therefore recommended to close inherited TTY file 475 + descriptors, or to reopen them from ``/proc/self/fd/*`` without the 476 + ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right, if possible. 477 + 478 + Landlock's IOCTL support is coarse-grained at the moment, but may become more 479 + fine-grained in the future. Until then, users are advised to establish the 480 + guarantees that they need through the file hierarchy, by only allowing the 481 + ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right on files where it is really required. 482 + 475 483 Previous limitations 476 484 ==================== 477 485 ··· 530 494 bind and connect actions to only a set of allowed ports thanks to the new 531 495 ``LANDLOCK_ACCESS_NET_BIND_TCP`` and ``LANDLOCK_ACCESS_NET_CONNECT_TCP`` 532 496 access rights. 497 + 498 + IOCTL (ABI < 5) 499 + --------------- 500 + 501 + IOCTL operations could not be denied before the fifth Landlock ABI, so 502 + :manpage:`ioctl(2)` is always allowed when using a kernel that only supports an 503 + earlier ABI. 504 + 505 + Starting with the Landlock ABI version 5, it is possible to restrict the use of 506 + :manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right. 533 507 534 508 .. _kernel_support: 535 509
+2
MAINTAINERS
··· 12374 12374 12375 12375 LANDLOCK SECURITY MODULE 12376 12376 M: Mickaël Salaün <mic@digikod.net> 12377 + R: Günther Noack <gnoack@google.com> 12377 12378 L: linux-security-module@vger.kernel.org 12378 12379 S: Supported 12379 12380 W: https://landlock.io 12380 12381 T: git https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git 12381 12382 F: Documentation/security/landlock.rst 12382 12383 F: Documentation/userspace-api/landlock.rst 12384 + F: fs/ioctl.c 12383 12385 F: include/uapi/linux/landlock.h 12384 12386 F: samples/landlock/ 12385 12387 F: security/landlock/
+3
fs/ioctl.c
··· 796 796 * 797 797 * When you add any new common ioctls to the switches above and below, 798 798 * please ensure they have compatible arguments in compat mode. 799 + * 800 + * The LSM mailing list should also be notified of any command additions or 801 + * changes, as specific LSMs may be affected. 799 802 */ 800 803 static int do_vfs_ioctl(struct file *filp, unsigned int fd, 801 804 unsigned int cmd, unsigned long arg)
+30 -8
include/uapi/linux/landlock.h
··· 128 128 * files and directories. Files or directories opened before the sandboxing 129 129 * are not subject to these restrictions. 130 130 * 131 - * A file can only receive these access rights: 131 + * The following access rights apply only to files: 132 132 * 133 133 * - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file. 134 134 * - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. Note that ··· 138 138 * - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access. 139 139 * - %LANDLOCK_ACCESS_FS_TRUNCATE: Truncate a file with :manpage:`truncate(2)`, 140 140 * :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with 141 - * ``O_TRUNC``. Whether an opened file can be truncated with 142 - * :manpage:`ftruncate(2)` is determined during :manpage:`open(2)`, in the 143 - * same way as read and write permissions are checked during 144 - * :manpage:`open(2)` using %LANDLOCK_ACCESS_FS_READ_FILE and 145 - * %LANDLOCK_ACCESS_FS_WRITE_FILE. This access right is available since the 146 - * third version of the Landlock ABI. 141 + * ``O_TRUNC``. This access right is available since the third version of the 142 + * Landlock ABI. 143 + * 144 + * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used 145 + * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as 146 + * read and write permissions are checked during :manpage:`open(2)` using 147 + * %LANDLOCK_ACCESS_FS_READ_FILE and %LANDLOCK_ACCESS_FS_WRITE_FILE. 147 148 * 148 149 * A directory can receive access rights related to files or directories. The 149 150 * following access right is applied to the directory itself, and the ··· 199 198 * If multiple requirements are not met, the ``EACCES`` error code takes 200 199 * precedence over ``EXDEV``. 201 200 * 201 + * The following access right applies both to files and directories: 202 + * 203 + * - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened 204 + * character or block device. 205 + * 206 + * This access right applies to all `ioctl(2)` commands implemented by device 207 + * drivers. However, the following common IOCTL commands continue to be 208 + * invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right: 209 + * 210 + * * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``), 211 + * * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``), 212 + * * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``, 213 + * ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``) 214 + * * Some IOCTL commands which do not make sense when used with devices, but 215 + * whose implementations are safe and return the right error codes 216 + * (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``) 217 + * 218 + * This access right is available since the fifth version of the Landlock 219 + * ABI. 220 + * 202 221 * .. warning:: 203 222 * 204 223 * It is currently not possible to restrict some file-related actions 205 224 * accessible through these syscall families: :manpage:`chdir(2)`, 206 225 * :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`, 207 226 * :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`, 208 - * :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`. 227 + * :manpage:`fcntl(2)`, :manpage:`access(2)`. 209 228 * Future Landlock evolutions will enable to restrict them. 210 229 */ 211 230 /* clang-format off */ ··· 244 223 #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) 245 224 #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) 246 225 #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) 226 + #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) 247 227 /* clang-format on */ 248 228 249 229 /**
+13 -5
samples/landlock/sandboxer.c
··· 81 81 LANDLOCK_ACCESS_FS_EXECUTE | \ 82 82 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 83 83 LANDLOCK_ACCESS_FS_READ_FILE | \ 84 - LANDLOCK_ACCESS_FS_TRUNCATE) 84 + LANDLOCK_ACCESS_FS_TRUNCATE | \ 85 + LANDLOCK_ACCESS_FS_IOCTL_DEV) 85 86 86 87 /* clang-format on */ 87 88 ··· 154 153 const __u64 allowed_access) 155 154 { 156 155 int ret = 1; 157 - char *env_port_name, *strport; 156 + char *env_port_name, *env_port_name_next, *strport; 158 157 struct landlock_net_port_attr net_port = { 159 158 .allowed_access = allowed_access, 160 159 .port = 0, ··· 166 165 env_port_name = strdup(env_port_name); 167 166 unsetenv(env_var); 168 167 169 - while ((strport = strsep(&env_port_name, ENV_DELIMITER))) { 168 + env_port_name_next = env_port_name; 169 + while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) { 170 170 net_port.port = atoi(strport); 171 171 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, 172 172 &net_port, 0)) { ··· 203 201 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 204 202 LANDLOCK_ACCESS_FS_MAKE_SYM | \ 205 203 LANDLOCK_ACCESS_FS_REFER | \ 206 - LANDLOCK_ACCESS_FS_TRUNCATE) 204 + LANDLOCK_ACCESS_FS_TRUNCATE | \ 205 + LANDLOCK_ACCESS_FS_IOCTL_DEV) 207 206 208 207 /* clang-format on */ 209 208 210 - #define LANDLOCK_ABI_LAST 4 209 + #define LANDLOCK_ABI_LAST 5 211 210 212 211 int main(const int argc, char *const argv[], char *const *const envp) 213 212 { ··· 322 319 ruleset_attr.handled_access_net &= 323 320 ~(LANDLOCK_ACCESS_NET_BIND_TCP | 324 321 LANDLOCK_ACCESS_NET_CONNECT_TCP); 322 + __attribute__((fallthrough)); 323 + case 4: 324 + /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ 325 + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; 326 + 325 327 fprintf(stderr, 326 328 "Hint: You should update the running kernel " 327 329 "to leverage Landlock features "
+222 -3
security/landlock/fs.c
··· 5 5 * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 6 * Copyright © 2018-2020 ANSSI 7 7 * Copyright © 2021-2022 Microsoft Corporation 8 + * Copyright © 2022 Günther Noack <gnoack3000@gmail.com> 9 + * Copyright © 2023-2024 Google LLC 8 10 */ 9 11 12 + #include <asm/ioctls.h> 10 13 #include <kunit/test.h> 11 14 #include <linux/atomic.h> 12 15 #include <linux/bitops.h> ··· 17 14 #include <linux/compiler_types.h> 18 15 #include <linux/dcache.h> 19 16 #include <linux/err.h> 17 + #include <linux/falloc.h> 20 18 #include <linux/fs.h> 21 19 #include <linux/init.h> 22 20 #include <linux/kernel.h> ··· 33 29 #include <linux/types.h> 34 30 #include <linux/wait_bit.h> 35 31 #include <linux/workqueue.h> 32 + #include <uapi/linux/fiemap.h> 36 33 #include <uapi/linux/landlock.h> 37 34 38 35 #include "common.h" ··· 88 83 static const struct landlock_object_underops landlock_fs_underops = { 89 84 .release = release_inode 90 85 }; 86 + 87 + /* IOCTL helpers */ 88 + 89 + /** 90 + * is_masked_device_ioctl - Determine whether an IOCTL command is always 91 + * permitted with Landlock for device files. These commands can not be 92 + * restricted on device files by enforcing a Landlock policy. 93 + * 94 + * @cmd: The IOCTL command that is supposed to be run. 95 + * 96 + * By default, any IOCTL on a device file requires the 97 + * LANDLOCK_ACCESS_FS_IOCTL_DEV right. However, we blanket-permit some 98 + * commands, if: 99 + * 100 + * 1. The command is implemented in fs/ioctl.c's do_vfs_ioctl(), 101 + * not in f_ops->unlocked_ioctl() or f_ops->compat_ioctl(). 102 + * 103 + * 2. The command is harmless when invoked on devices. 104 + * 105 + * We also permit commands that do not make sense for devices, but where the 106 + * do_vfs_ioctl() implementation returns a more conventional error code. 107 + * 108 + * Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl() 109 + * should be considered for inclusion here. 110 + * 111 + * Returns: true if the IOCTL @cmd can not be restricted with Landlock for 112 + * device files. 113 + */ 114 + static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd) 115 + { 116 + switch (cmd) { 117 + /* 118 + * FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC manipulate the FD's 119 + * close-on-exec and the file's buffered-IO and async flags. These 120 + * operations are also available through fcntl(2), and are 121 + * unconditionally permitted in Landlock. 122 + */ 123 + case FIOCLEX: 124 + case FIONCLEX: 125 + case FIONBIO: 126 + case FIOASYNC: 127 + /* 128 + * FIOQSIZE queries the size of a regular file, directory, or link. 129 + * 130 + * We still permit it, because it always returns -ENOTTY for 131 + * other file types. 132 + */ 133 + case FIOQSIZE: 134 + /* 135 + * FIFREEZE and FITHAW freeze and thaw the file system which the 136 + * given file belongs to. Requires CAP_SYS_ADMIN. 137 + * 138 + * These commands operate on the file system's superblock rather 139 + * than on the file itself. The same operations can also be 140 + * done through any other file or directory on the same file 141 + * system, so it is safe to permit these. 142 + */ 143 + case FIFREEZE: 144 + case FITHAW: 145 + /* 146 + * FS_IOC_FIEMAP queries information about the allocation of 147 + * blocks within a file. 148 + * 149 + * This IOCTL command only makes sense for regular files and is 150 + * not implemented by devices. It is harmless to permit. 151 + */ 152 + case FS_IOC_FIEMAP: 153 + /* 154 + * FIGETBSZ queries the file system's block size for a file or 155 + * directory. 156 + * 157 + * This command operates on the file system's superblock rather 158 + * than on the file itself. The same operation can also be done 159 + * through any other file or directory on the same file system, 160 + * so it is safe to permit it. 161 + */ 162 + case FIGETBSZ: 163 + /* 164 + * FICLONE, FICLONERANGE and FIDEDUPERANGE make files share 165 + * their underlying storage ("reflink") between source and 166 + * destination FDs, on file systems which support that. 167 + * 168 + * These IOCTL commands only apply to regular files 169 + * and are harmless to permit for device files. 170 + */ 171 + case FICLONE: 172 + case FICLONERANGE: 173 + case FIDEDUPERANGE: 174 + /* 175 + * FS_IOC_GETFSUUID and FS_IOC_GETFSSYSFSPATH both operate on 176 + * the file system superblock, not on the specific file, so 177 + * these operations are available through any other file on the 178 + * same file system as well. 179 + */ 180 + case FS_IOC_GETFSUUID: 181 + case FS_IOC_GETFSSYSFSPATH: 182 + return true; 183 + 184 + /* 185 + * FIONREAD, FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and 186 + * FS_IOC_FSSETXATTR are forwarded to device implementations. 187 + */ 188 + 189 + /* 190 + * file_ioctl() commands (FIBMAP, FS_IOC_RESVSP, FS_IOC_RESVSP64, 191 + * FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64 and FS_IOC_ZERO_RANGE) are 192 + * forwarded to device implementations, so not permitted. 193 + */ 194 + 195 + /* Other commands are guarded by the access right. */ 196 + default: 197 + return false; 198 + } 199 + } 200 + 201 + /* 202 + * is_masked_device_ioctl_compat - same as the helper above, but checking the 203 + * "compat" IOCTL commands. 204 + * 205 + * The IOCTL commands with special handling in compat-mode should behave the 206 + * same as their non-compat counterparts. 207 + */ 208 + static __attribute_const__ bool 209 + is_masked_device_ioctl_compat(const unsigned int cmd) 210 + { 211 + switch (cmd) { 212 + /* FICLONE is permitted, same as in the non-compat variant. */ 213 + case FICLONE: 214 + return true; 215 + 216 + #if defined(CONFIG_X86_64) 217 + /* 218 + * FS_IOC_RESVSP_32, FS_IOC_RESVSP64_32, FS_IOC_UNRESVSP_32, 219 + * FS_IOC_UNRESVSP64_32, FS_IOC_ZERO_RANGE_32: not blanket-permitted, 220 + * for consistency with their non-compat variants. 221 + */ 222 + case FS_IOC_RESVSP_32: 223 + case FS_IOC_RESVSP64_32: 224 + case FS_IOC_UNRESVSP_32: 225 + case FS_IOC_UNRESVSP64_32: 226 + case FS_IOC_ZERO_RANGE_32: 227 + #endif 228 + 229 + /* 230 + * FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS are forwarded to their device 231 + * implementations. 232 + */ 233 + case FS_IOC32_GETFLAGS: 234 + case FS_IOC32_SETFLAGS: 235 + return false; 236 + default: 237 + return is_masked_device_ioctl(cmd); 238 + } 239 + } 91 240 92 241 /* Ruleset management */ 93 242 ··· 307 148 LANDLOCK_ACCESS_FS_EXECUTE | \ 308 149 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 309 150 LANDLOCK_ACCESS_FS_READ_FILE | \ 310 - LANDLOCK_ACCESS_FS_TRUNCATE) 151 + LANDLOCK_ACCESS_FS_TRUNCATE | \ 152 + LANDLOCK_ACCESS_FS_IOCTL_DEV) 311 153 /* clang-format on */ 312 154 313 155 /* ··· 1492 1332 return 0; 1493 1333 } 1494 1334 1335 + static bool is_device(const struct file *const file) 1336 + { 1337 + const struct inode *inode = file_inode(file); 1338 + 1339 + return S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode); 1340 + } 1341 + 1495 1342 static int hook_file_open(struct file *const file) 1496 1343 { 1497 1344 layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 1498 - access_mask_t open_access_request, full_access_request, allowed_access; 1499 - const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; 1345 + access_mask_t open_access_request, full_access_request, allowed_access, 1346 + optional_access; 1500 1347 const struct landlock_ruleset *const dom = 1501 1348 get_fs_domain(landlock_cred(file->f_cred)->domain); 1502 1349 ··· 1521 1354 * We look up more access than what we immediately need for open(), so 1522 1355 * that we can later authorize operations on opened files. 1523 1356 */ 1357 + optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; 1358 + if (is_device(file)) 1359 + optional_access |= LANDLOCK_ACCESS_FS_IOCTL_DEV; 1360 + 1524 1361 full_access_request = open_access_request | optional_access; 1525 1362 1526 1363 if (is_access_to_paths_allowed( ··· 1581 1410 return -EACCES; 1582 1411 } 1583 1412 1413 + static int hook_file_ioctl(struct file *file, unsigned int cmd, 1414 + unsigned long arg) 1415 + { 1416 + access_mask_t allowed_access = landlock_file(file)->allowed_access; 1417 + 1418 + /* 1419 + * It is the access rights at the time of opening the file which 1420 + * determine whether IOCTL can be used on the opened file later. 1421 + * 1422 + * The access right is attached to the opened file in hook_file_open(). 1423 + */ 1424 + if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) 1425 + return 0; 1426 + 1427 + if (!is_device(file)) 1428 + return 0; 1429 + 1430 + if (is_masked_device_ioctl(cmd)) 1431 + return 0; 1432 + 1433 + return -EACCES; 1434 + } 1435 + 1436 + static int hook_file_ioctl_compat(struct file *file, unsigned int cmd, 1437 + unsigned long arg) 1438 + { 1439 + access_mask_t allowed_access = landlock_file(file)->allowed_access; 1440 + 1441 + /* 1442 + * It is the access rights at the time of opening the file which 1443 + * determine whether IOCTL can be used on the opened file later. 1444 + * 1445 + * The access right is attached to the opened file in hook_file_open(). 1446 + */ 1447 + if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV) 1448 + return 0; 1449 + 1450 + if (!is_device(file)) 1451 + return 0; 1452 + 1453 + if (is_masked_device_ioctl_compat(cmd)) 1454 + return 0; 1455 + 1456 + return -EACCES; 1457 + } 1458 + 1584 1459 static struct security_hook_list landlock_hooks[] __ro_after_init = { 1585 1460 LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), 1586 1461 ··· 1649 1432 LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), 1650 1433 LSM_HOOK_INIT(file_open, hook_file_open), 1651 1434 LSM_HOOK_INIT(file_truncate, hook_file_truncate), 1435 + LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), 1436 + LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), 1652 1437 }; 1653 1438 1654 1439 __init void landlock_add_fs_hooks(void)
+1 -1
security/landlock/limits.h
··· 18 18 #define LANDLOCK_MAX_NUM_LAYERS 16 19 19 #define LANDLOCK_MAX_NUM_RULES U32_MAX 20 20 21 - #define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE 21 + #define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV 22 22 #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) 23 23 #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) 24 24 #define LANDLOCK_SHIFT_ACCESS_FS 0
+1 -1
security/landlock/syscalls.c
··· 149 149 .write = fop_dummy_write, 150 150 }; 151 151 152 - #define LANDLOCK_ABI_VERSION 4 152 + #define LANDLOCK_ABI_VERSION 5 153 153 154 154 /** 155 155 * sys_landlock_create_ruleset - Create a new ruleset
+1 -1
tools/testing/selftests/landlock/base_test.c
··· 75 75 const struct landlock_ruleset_attr ruleset_attr = { 76 76 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 77 77 }; 78 - ASSERT_EQ(4, landlock_create_ruleset(NULL, 0, 78 + ASSERT_EQ(5, landlock_create_ruleset(NULL, 0, 79 79 LANDLOCK_CREATE_RULESET_VERSION)); 80 80 81 81 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
+476 -11
tools/testing/selftests/landlock/fs_test.c
··· 8 8 */ 9 9 10 10 #define _GNU_SOURCE 11 + #include <asm/termbits.h> 11 12 #include <fcntl.h> 12 13 #include <libgen.h> 14 + #include <linux/fiemap.h> 13 15 #include <linux/landlock.h> 14 16 #include <linux/magic.h> 15 17 #include <sched.h> 18 + #include <stddef.h> 16 19 #include <stdio.h> 17 20 #include <string.h> 18 21 #include <sys/capability.h> 22 + #include <sys/ioctl.h> 19 23 #include <sys/mount.h> 20 24 #include <sys/prctl.h> 21 25 #include <sys/sendfile.h> 26 + #include <sys/socket.h> 22 27 #include <sys/stat.h> 23 28 #include <sys/sysmacros.h> 29 + #include <sys/un.h> 24 30 #include <sys/vfs.h> 25 31 #include <unistd.h> 32 + 33 + /* 34 + * Intentionally included last to work around header conflict. 35 + * See https://sourceware.org/glibc/wiki/Synchronizing_Headers. 36 + */ 37 + #include <linux/fs.h> 26 38 27 39 #include "common.h" 28 40 ··· 548 536 LANDLOCK_ACCESS_FS_EXECUTE | \ 549 537 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 550 538 LANDLOCK_ACCESS_FS_READ_FILE | \ 551 - LANDLOCK_ACCESS_FS_TRUNCATE) 539 + LANDLOCK_ACCESS_FS_TRUNCATE | \ 540 + LANDLOCK_ACCESS_FS_IOCTL_DEV) 552 541 553 - #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE 542 + #define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV 554 543 555 544 #define ACCESS_ALL ( \ 556 545 ACCESS_FILE | \ ··· 756 743 } 757 744 758 745 for (i = 0; rules[i].path; i++) { 746 + if (!rules[i].access) 747 + continue; 748 + 759 749 add_path_beneath(_metadata, ruleset_fd, rules[i].access, 760 750 rules[i].path); 761 751 } ··· 3467 3451 LANDLOCK_ACCESS_FS_WRITE_FILE; 3468 3452 int ruleset_fd; 3469 3453 3470 - /* Enable Landlock. */ 3454 + /* Enables Landlock. */ 3471 3455 ruleset_fd = create_ruleset(_metadata, handled, rules); 3472 3456 3473 3457 ASSERT_LE(0, ruleset_fd); ··· 3550 3534 LANDLOCK_ACCESS_FS_TRUNCATE; 3551 3535 int ruleset_fd; 3552 3536 3553 - /* Enable Landlock. */ 3537 + /* Enables Landlock. */ 3554 3538 ruleset_fd = create_ruleset(_metadata, handled, rules); 3555 3539 3556 3540 ASSERT_LE(0, ruleset_fd); ··· 3776 3760 }; 3777 3761 int fd, ruleset_fd; 3778 3762 3779 - /* Enable Landlock. */ 3763 + /* Enables Landlock. */ 3780 3764 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3781 3765 ASSERT_LE(0, ruleset_fd); 3782 3766 enforce_ruleset(_metadata, ruleset_fd); ··· 3853 3837 ASSERT_EQ(0, close(socket_fds[1])); 3854 3838 } 3855 3839 3856 - TEST(memfd_ftruncate) 3840 + /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */ 3841 + static int test_fs_ioc_getflags_ioctl(int fd) 3857 3842 { 3858 - int fd; 3843 + uint32_t flags; 3859 3844 3860 - fd = memfd_create("name", MFD_CLOEXEC); 3845 + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) 3846 + return errno; 3847 + return 0; 3848 + } 3849 + 3850 + TEST(memfd_ftruncate_and_ioctl) 3851 + { 3852 + const struct landlock_ruleset_attr attr = { 3853 + .handled_access_fs = ACCESS_ALL, 3854 + }; 3855 + int ruleset_fd, fd, i; 3856 + 3857 + /* 3858 + * We exercise the same test both with and without Landlock enabled, to 3859 + * ensure that it behaves the same in both cases. 3860 + */ 3861 + for (i = 0; i < 2; i++) { 3862 + /* Creates a new memfd. */ 3863 + fd = memfd_create("name", MFD_CLOEXEC); 3864 + ASSERT_LE(0, fd); 3865 + 3866 + /* 3867 + * Checks that operations associated with the opened file 3868 + * (ftruncate, ioctl) are permitted on file descriptors that are 3869 + * created in ways other than open(2). 3870 + */ 3871 + EXPECT_EQ(0, test_ftruncate(fd)); 3872 + EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd)); 3873 + 3874 + ASSERT_EQ(0, close(fd)); 3875 + 3876 + /* Enables Landlock. */ 3877 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 3878 + ASSERT_LE(0, ruleset_fd); 3879 + enforce_ruleset(_metadata, ruleset_fd); 3880 + ASSERT_EQ(0, close(ruleset_fd)); 3881 + } 3882 + } 3883 + 3884 + static int test_fionread_ioctl(int fd) 3885 + { 3886 + size_t sz = 0; 3887 + 3888 + if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES) 3889 + return errno; 3890 + return 0; 3891 + } 3892 + 3893 + TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl) 3894 + { 3895 + const struct landlock_ruleset_attr attr = { 3896 + .handled_access_fs = ACCESS_ALL, 3897 + }; 3898 + int ruleset_fd, fd; 3899 + 3900 + /* 3901 + * Checks that for files opened with O_PATH, both ioctl(2) and 3902 + * ftruncate(2) yield EBADF, as it is documented in open(2) for the 3903 + * O_PATH flag. 3904 + */ 3905 + fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3906 + ASSERT_LE(0, fd); 3907 + 3908 + EXPECT_EQ(EBADF, test_ftruncate(fd)); 3909 + EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3910 + 3911 + ASSERT_EQ(0, close(fd)); 3912 + 3913 + /* Enables Landlock. */ 3914 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 3915 + ASSERT_LE(0, ruleset_fd); 3916 + enforce_ruleset(_metadata, ruleset_fd); 3917 + ASSERT_EQ(0, close(ruleset_fd)); 3918 + 3919 + /* 3920 + * Checks that after enabling Landlock, 3921 + * - the file can still be opened with O_PATH 3922 + * - both ioctl and truncate still yield EBADF (not EACCES). 3923 + */ 3924 + fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3925 + ASSERT_LE(0, fd); 3926 + 3927 + EXPECT_EQ(EBADF, test_ftruncate(fd)); 3928 + EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3929 + 3930 + ASSERT_EQ(0, close(fd)); 3931 + } 3932 + 3933 + /* 3934 + * ioctl_error - generically call the given ioctl with a pointer to a 3935 + * sufficiently large zeroed-out memory region. 3936 + * 3937 + * Returns the IOCTLs error, or 0. 3938 + */ 3939 + static int ioctl_error(struct __test_metadata *const _metadata, int fd, 3940 + unsigned int cmd) 3941 + { 3942 + char buf[128]; /* sufficiently large */ 3943 + int res, stdinbak_fd; 3944 + 3945 + /* 3946 + * Depending on the IOCTL command, parts of the zeroed-out buffer might 3947 + * be interpreted as file descriptor numbers. We do not want to 3948 + * accidentally operate on file descriptor 0 (stdin), so we temporarily 3949 + * move stdin to a different FD and close FD 0 for the IOCTL call. 3950 + */ 3951 + stdinbak_fd = dup(0); 3952 + ASSERT_LT(0, stdinbak_fd); 3953 + ASSERT_EQ(0, close(0)); 3954 + 3955 + /* Invokes the IOCTL with a zeroed-out buffer. */ 3956 + bzero(&buf, sizeof(buf)); 3957 + res = ioctl(fd, cmd, &buf); 3958 + 3959 + /* Restores the old FD 0 and closes the backup FD. */ 3960 + ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 3961 + ASSERT_EQ(0, close(stdinbak_fd)); 3962 + 3963 + if (res < 0) 3964 + return errno; 3965 + 3966 + return 0; 3967 + } 3968 + 3969 + /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 3970 + struct space_resv { 3971 + __s16 l_type; 3972 + __s16 l_whence; 3973 + __s64 l_start; 3974 + __s64 l_len; /* len == 0 means until end of file */ 3975 + __s32 l_sysid; 3976 + __u32 l_pid; 3977 + __s32 l_pad[4]; /* reserved area */ 3978 + }; 3979 + 3980 + #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 3981 + #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 3982 + #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 3983 + #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 3984 + #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 3985 + 3986 + /* 3987 + * Tests a series of blanket-permitted and denied IOCTLs. 3988 + */ 3989 + TEST_F_FORK(layout1, blanket_permitted_ioctls) 3990 + { 3991 + const struct landlock_ruleset_attr attr = { 3992 + .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 3993 + }; 3994 + int ruleset_fd, fd; 3995 + 3996 + /* Enables Landlock. */ 3997 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 3998 + ASSERT_LE(0, ruleset_fd); 3999 + enforce_ruleset(_metadata, ruleset_fd); 4000 + ASSERT_EQ(0, close(ruleset_fd)); 4001 + 4002 + fd = open("/dev/null", O_RDWR | O_CLOEXEC); 3861 4003 ASSERT_LE(0, fd); 3862 4004 3863 4005 /* 3864 - * Checks that ftruncate is permitted on file descriptors that are 3865 - * created in ways other than open(2). 4006 + * Checks permitted commands. 4007 + * These ones may return errors, but should not be blocked by Landlock. 3866 4008 */ 3867 - EXPECT_EQ(0, test_ftruncate(fd)); 4009 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4010 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4011 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4012 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4013 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4014 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4015 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4016 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4017 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4018 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4019 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4020 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4021 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4022 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4023 + 4024 + /* 4025 + * Checks blocked commands. 4026 + * A call to a blocked IOCTL command always returns EACCES. 4027 + */ 4028 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4029 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4030 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4031 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4032 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4033 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4034 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4035 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4036 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4037 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4038 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4039 + 4040 + /* Default case is also blocked. */ 4041 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee)); 3868 4042 3869 4043 ASSERT_EQ(0, close(fd)); 4044 + } 4045 + 4046 + /* 4047 + * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right, 4048 + * because they are not character or block devices. 4049 + */ 4050 + TEST_F_FORK(layout1, named_pipe_ioctl) 4051 + { 4052 + pid_t child_pid; 4053 + int fd, ruleset_fd; 4054 + const char *const path = file1_s1d1; 4055 + const struct landlock_ruleset_attr attr = { 4056 + .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4057 + }; 4058 + 4059 + ASSERT_EQ(0, unlink(path)); 4060 + ASSERT_EQ(0, mkfifo(path, 0600)); 4061 + 4062 + /* Enables Landlock. */ 4063 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4064 + ASSERT_LE(0, ruleset_fd); 4065 + enforce_ruleset(_metadata, ruleset_fd); 4066 + ASSERT_EQ(0, close(ruleset_fd)); 4067 + 4068 + /* The child process opens the pipe for writing. */ 4069 + child_pid = fork(); 4070 + ASSERT_NE(-1, child_pid); 4071 + if (child_pid == 0) { 4072 + fd = open(path, O_WRONLY); 4073 + close(fd); 4074 + exit(0); 4075 + } 4076 + 4077 + fd = open(path, O_RDONLY); 4078 + ASSERT_LE(0, fd); 4079 + 4080 + /* FIONREAD is implemented by pipefifo_fops. */ 4081 + EXPECT_EQ(0, test_fionread_ioctl(fd)); 4082 + 4083 + ASSERT_EQ(0, close(fd)); 4084 + ASSERT_EQ(0, unlink(path)); 4085 + 4086 + ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0)); 4087 + } 4088 + 4089 + /* For named UNIX domain sockets, no IOCTL restrictions apply. */ 4090 + TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) 4091 + { 4092 + const char *const path = file1_s1d1; 4093 + int srv_fd, cli_fd, ruleset_fd; 4094 + socklen_t size; 4095 + struct sockaddr_un srv_un, cli_un; 4096 + const struct landlock_ruleset_attr attr = { 4097 + .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4098 + }; 4099 + 4100 + /* Sets up a server */ 4101 + srv_un.sun_family = AF_UNIX; 4102 + strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path)); 4103 + 4104 + ASSERT_EQ(0, unlink(path)); 4105 + srv_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4106 + ASSERT_LE(0, srv_fd); 4107 + 4108 + size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path); 4109 + ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size)); 4110 + ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */)); 4111 + 4112 + /* Enables Landlock. */ 4113 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4114 + ASSERT_LE(0, ruleset_fd); 4115 + enforce_ruleset(_metadata, ruleset_fd); 4116 + ASSERT_EQ(0, close(ruleset_fd)); 4117 + 4118 + /* Sets up a client connection to it */ 4119 + cli_un.sun_family = AF_UNIX; 4120 + cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4121 + ASSERT_LE(0, cli_fd); 4122 + 4123 + size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path); 4124 + ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size)); 4125 + 4126 + bzero(&cli_un, sizeof(cli_un)); 4127 + cli_un.sun_family = AF_UNIX; 4128 + strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path)); 4129 + size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path); 4130 + 4131 + ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size)); 4132 + 4133 + /* FIONREAD and other IOCTLs should not be forbidden. */ 4134 + EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4135 + 4136 + ASSERT_EQ(0, close(cli_fd)); 4137 + } 4138 + 4139 + /* clang-format off */ 4140 + FIXTURE(ioctl) {}; 4141 + 4142 + FIXTURE_SETUP(ioctl) {}; 4143 + 4144 + FIXTURE_TEARDOWN(ioctl) {}; 4145 + /* clang-format on */ 4146 + 4147 + FIXTURE_VARIANT(ioctl) 4148 + { 4149 + const __u64 handled; 4150 + const __u64 allowed; 4151 + const mode_t open_mode; 4152 + /* 4153 + * FIONREAD is used as a characteristic device-specific IOCTL command. 4154 + * It is implemented in fs/ioctl.c for regular files, 4155 + * but we do not blanket-permit it for devices. 4156 + */ 4157 + const int expected_fionread_result; 4158 + }; 4159 + 4160 + /* clang-format off */ 4161 + FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4162 + /* clang-format on */ 4163 + .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4164 + .allowed = 0, 4165 + .open_mode = O_RDWR, 4166 + .expected_fionread_result = EACCES, 4167 + }; 4168 + 4169 + /* clang-format off */ 4170 + FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4171 + /* clang-format on */ 4172 + .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4173 + .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4174 + .open_mode = O_RDWR, 4175 + .expected_fionread_result = 0, 4176 + }; 4177 + 4178 + /* clang-format off */ 4179 + FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4180 + /* clang-format on */ 4181 + .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4182 + .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4183 + .open_mode = O_RDWR, 4184 + .expected_fionread_result = 0, 4185 + }; 4186 + 4187 + TEST_F_FORK(ioctl, handle_dir_access_file) 4188 + { 4189 + const int flag = 0; 4190 + const struct rule rules[] = { 4191 + { 4192 + .path = "/dev", 4193 + .access = variant->allowed, 4194 + }, 4195 + {}, 4196 + }; 4197 + int file_fd, ruleset_fd; 4198 + 4199 + /* Enables Landlock. */ 4200 + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4201 + ASSERT_LE(0, ruleset_fd); 4202 + enforce_ruleset(_metadata, ruleset_fd); 4203 + ASSERT_EQ(0, close(ruleset_fd)); 4204 + 4205 + file_fd = open("/dev/zero", variant->open_mode); 4206 + ASSERT_LE(0, file_fd); 4207 + 4208 + /* Checks that IOCTL commands return the expected errors. */ 4209 + EXPECT_EQ(variant->expected_fionread_result, 4210 + test_fionread_ioctl(file_fd)); 4211 + 4212 + /* Checks that unrestrictable commands are unrestricted. */ 4213 + EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4214 + EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4215 + EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4216 + EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4217 + EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4218 + 4219 + ASSERT_EQ(0, close(file_fd)); 4220 + } 4221 + 4222 + TEST_F_FORK(ioctl, handle_dir_access_dir) 4223 + { 4224 + const int flag = 0; 4225 + const struct rule rules[] = { 4226 + { 4227 + .path = "/dev", 4228 + .access = variant->allowed, 4229 + }, 4230 + {}, 4231 + }; 4232 + int dir_fd, ruleset_fd; 4233 + 4234 + /* Enables Landlock. */ 4235 + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4236 + ASSERT_LE(0, ruleset_fd); 4237 + enforce_ruleset(_metadata, ruleset_fd); 4238 + ASSERT_EQ(0, close(ruleset_fd)); 4239 + 4240 + /* 4241 + * Ignore variant->open_mode for this test, as we intend to open a 4242 + * directory. If the directory can not be opened, the variant is 4243 + * infeasible to test with an opened directory. 4244 + */ 4245 + dir_fd = open("/dev", O_RDONLY); 4246 + if (dir_fd < 0) 4247 + return; 4248 + 4249 + /* 4250 + * Checks that IOCTL commands return the expected errors. 4251 + * We do not use the expected values from the fixture here. 4252 + * 4253 + * When using IOCTL on a directory, no Landlock restrictions apply. 4254 + */ 4255 + EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4256 + 4257 + /* Checks that unrestrictable commands are unrestricted. */ 4258 + EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4259 + EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4260 + EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4261 + EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4262 + EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4263 + 4264 + ASSERT_EQ(0, close(dir_fd)); 4265 + } 4266 + 4267 + TEST_F_FORK(ioctl, handle_file_access_file) 4268 + { 4269 + const int flag = 0; 4270 + const struct rule rules[] = { 4271 + { 4272 + .path = "/dev/zero", 4273 + .access = variant->allowed, 4274 + }, 4275 + {}, 4276 + }; 4277 + int file_fd, ruleset_fd; 4278 + 4279 + /* Enables Landlock. */ 4280 + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4281 + ASSERT_LE(0, ruleset_fd); 4282 + enforce_ruleset(_metadata, ruleset_fd); 4283 + ASSERT_EQ(0, close(ruleset_fd)); 4284 + 4285 + file_fd = open("/dev/zero", variant->open_mode); 4286 + ASSERT_LE(0, file_fd) 4287 + { 4288 + TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4289 + } 4290 + 4291 + /* Checks that IOCTL commands return the expected errors. */ 4292 + EXPECT_EQ(variant->expected_fionread_result, 4293 + test_fionread_ioctl(file_fd)); 4294 + 4295 + /* Checks that unrestrictable commands are unrestricted. */ 4296 + EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4297 + EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4298 + EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4299 + EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4300 + EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4301 + 4302 + ASSERT_EQ(0, close(file_fd)); 3870 4303 } 3871 4304 3872 4305 /* clang-format off */