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

Merge tag 'landlock_v34' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull Landlock LSM from James Morris:
"Add Landlock, a new LSM from Mickaël Salaün.

Briefly, Landlock provides for unprivileged application sandboxing.

From Mickaël's cover letter:
"The goal of Landlock is to enable to restrict ambient rights (e.g.
global filesystem access) for a set of processes. Because Landlock
is a stackable LSM [1], it makes possible to create safe security
sandboxes as new security layers in addition to the existing
system-wide access-controls. This kind of sandbox is expected to
help mitigate the security impact of bugs or unexpected/malicious
behaviors in user-space applications. Landlock empowers 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 restrict the
use of kernel objects like file hierarchies, according to the
kernel semantic. Landlock also takes inspiration from other OS
sandbox mechanisms: XNU Sandbox, FreeBSD Capsicum or OpenBSD
Pledge/Unveil.

In this current form, Landlock misses some access-control features.
This enables to minimize this patch series and ease review. This
series still addresses multiple use cases, especially with the
combined use of seccomp-bpf: applications with built-in sandboxing,
init systems, security sandbox tools and security-oriented APIs [2]"

The cover letter and v34 posting is here:

https://lore.kernel.org/linux-security-module/20210422154123.13086-1-mic@digikod.net/

See also:

https://landlock.io/

This code has had extensive design discussion and review over several
years"

Link: https://lore.kernel.org/lkml/50db058a-7dde-441b-a7f9-f6837fe8b69f@schaufler-ca.com/ [1]
Link: https://lore.kernel.org/lkml/f646e1c7-33cf-333f-070c-0a40ad0468cd@digikod.net/ [2]

* tag 'landlock_v34' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
landlock: Enable user space to infer supported features
landlock: Add user and kernel documentation
samples/landlock: Add a sandbox manager example
selftests/landlock: Add user space tests
landlock: Add syscall implementations
arch: Wire up Landlock syscalls
fs,security: Add sb_delete hook
landlock: Support filesystem access-control
LSM: Infrastructure management of the superblock
landlock: Add ptrace restrictions
landlock: Set up the security framework and manage credentials
landlock: Add ruleset and domain management
landlock: Add object management

+6987 -77
+1
Documentation/security/index.rst
··· 16 16 siphash 17 17 tpm/index 18 18 digsig 19 + landlock
+85
Documentation/security/landlock.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + .. Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 3 + .. Copyright © 2019-2020 ANSSI 4 + 5 + ================================== 6 + Landlock LSM: kernel documentation 7 + ================================== 8 + 9 + :Author: Mickaël Salaün 10 + :Date: March 2021 11 + 12 + Landlock's goal is to create scoped access-control (i.e. sandboxing). To 13 + harden a whole system, this feature should be available to any process, 14 + including unprivileged ones. Because such process may be compromised or 15 + backdoored (i.e. untrusted), Landlock's features must be safe to use from the 16 + kernel and other processes point of view. Landlock's interface must therefore 17 + expose a minimal attack surface. 18 + 19 + Landlock is designed to be usable by unprivileged processes while following the 20 + system security policy enforced by other access control mechanisms (e.g. DAC, 21 + LSM). Indeed, a Landlock rule shall not interfere with other access-controls 22 + enforced on the system, only add more restrictions. 23 + 24 + Any user can enforce Landlock rulesets on their processes. They are merged and 25 + evaluated according to the inherited ones in a way that ensures that only more 26 + constraints can be added. 27 + 28 + User space documentation can be found here: :doc:`/userspace-api/landlock`. 29 + 30 + Guiding principles for safe access controls 31 + =========================================== 32 + 33 + * A Landlock rule shall be focused on access control on kernel objects instead 34 + of syscall filtering (i.e. syscall arguments), which is the purpose of 35 + seccomp-bpf. 36 + * To avoid multiple kinds of side-channel attacks (e.g. leak of security 37 + policies, CPU-based attacks), Landlock rules shall not be able to 38 + programmatically communicate with user space. 39 + * Kernel access check shall not slow down access request from unsandboxed 40 + processes. 41 + * Computation related to Landlock operations (e.g. enforcing a ruleset) shall 42 + only impact the processes requesting them. 43 + 44 + Tests 45 + ===== 46 + 47 + Userspace tests for backward compatibility, ptrace restrictions and filesystem 48 + support can be found here: `tools/testing/selftests/landlock/`_. 49 + 50 + Kernel structures 51 + ================= 52 + 53 + Object 54 + ------ 55 + 56 + .. kernel-doc:: security/landlock/object.h 57 + :identifiers: 58 + 59 + Filesystem 60 + ---------- 61 + 62 + .. kernel-doc:: security/landlock/fs.h 63 + :identifiers: 64 + 65 + Ruleset and domain 66 + ------------------ 67 + 68 + A domain is a read-only ruleset tied to a set of subjects (i.e. tasks' 69 + credentials). Each time a ruleset is enforced on a task, the current domain is 70 + duplicated and the ruleset is imported as a new layer of rules in the new 71 + domain. Indeed, once in a domain, each rule is tied to a layer level. To 72 + grant access to an object, at least one rule of each layer must allow the 73 + requested action on the object. A task can then only transit to a new domain 74 + that is the intersection of the constraints from the current domain and those 75 + of a ruleset provided by the task. 76 + 77 + The definition of a subject is implicit for a task sandboxing itself, which 78 + makes the reasoning much easier and helps avoid pitfalls. 79 + 80 + .. kernel-doc:: security/landlock/ruleset.h 81 + :identifiers: 82 + 83 + .. Links 84 + .. _tools/testing/selftests/landlock/: 85 + https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/landlock/
+1
Documentation/userspace-api/index.rst
··· 18 18 19 19 no_new_privs 20 20 seccomp_filter 21 + landlock 21 22 unshare 22 23 spec_ctrl 23 24 accelerators/ocxl
+311
Documentation/userspace-api/landlock.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + .. Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 3 + .. Copyright © 2019-2020 ANSSI 4 + .. Copyright © 2021 Microsoft Corporation 5 + 6 + ===================================== 7 + Landlock: unprivileged access control 8 + ===================================== 9 + 10 + :Author: Mickaël Salaün 11 + :Date: March 2021 12 + 13 + The goal of Landlock is to enable to restrict ambient rights (e.g. global 14 + filesystem access) for a set of processes. Because Landlock is a stackable 15 + LSM, it makes possible to create safe security sandboxes as new security layers 16 + in addition to the existing system-wide access-controls. This kind of sandbox 17 + is expected to help mitigate the security impact of bugs or 18 + unexpected/malicious behaviors in user space applications. Landlock empowers 19 + any process, including unprivileged ones, to securely restrict themselves. 20 + 21 + Landlock rules 22 + ============== 23 + 24 + A Landlock rule describes an action on an object. An object is currently a 25 + file hierarchy, and the related filesystem actions are defined with `access 26 + rights`_. A set of rules is aggregated in a ruleset, which can then restrict 27 + the thread enforcing it, and its future children. 28 + 29 + Defining and enforcing a security policy 30 + ---------------------------------------- 31 + 32 + We first need to create the ruleset that will contain our rules. For this 33 + example, the ruleset will contain rules that only allow read actions, but write 34 + actions will be denied. The ruleset then needs to handle both of these kind of 35 + actions. 36 + 37 + .. code-block:: c 38 + 39 + int ruleset_fd; 40 + struct landlock_ruleset_attr ruleset_attr = { 41 + .handled_access_fs = 42 + LANDLOCK_ACCESS_FS_EXECUTE | 43 + LANDLOCK_ACCESS_FS_WRITE_FILE | 44 + LANDLOCK_ACCESS_FS_READ_FILE | 45 + LANDLOCK_ACCESS_FS_READ_DIR | 46 + LANDLOCK_ACCESS_FS_REMOVE_DIR | 47 + LANDLOCK_ACCESS_FS_REMOVE_FILE | 48 + LANDLOCK_ACCESS_FS_MAKE_CHAR | 49 + LANDLOCK_ACCESS_FS_MAKE_DIR | 50 + LANDLOCK_ACCESS_FS_MAKE_REG | 51 + LANDLOCK_ACCESS_FS_MAKE_SOCK | 52 + LANDLOCK_ACCESS_FS_MAKE_FIFO | 53 + LANDLOCK_ACCESS_FS_MAKE_BLOCK | 54 + LANDLOCK_ACCESS_FS_MAKE_SYM, 55 + }; 56 + 57 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 58 + if (ruleset_fd < 0) { 59 + perror("Failed to create a ruleset"); 60 + return 1; 61 + } 62 + 63 + We can now add a new rule to this ruleset thanks to the returned file 64 + descriptor referring to this ruleset. The rule will only allow reading the 65 + file hierarchy ``/usr``. Without another rule, write actions would then be 66 + denied by the ruleset. To add ``/usr`` to the ruleset, we open it with the 67 + ``O_PATH`` flag and fill the &struct landlock_path_beneath_attr with this file 68 + descriptor. 69 + 70 + .. code-block:: c 71 + 72 + int err; 73 + struct landlock_path_beneath_attr path_beneath = { 74 + .allowed_access = 75 + LANDLOCK_ACCESS_FS_EXECUTE | 76 + LANDLOCK_ACCESS_FS_READ_FILE | 77 + LANDLOCK_ACCESS_FS_READ_DIR, 78 + }; 79 + 80 + path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC); 81 + if (path_beneath.parent_fd < 0) { 82 + perror("Failed to open file"); 83 + close(ruleset_fd); 84 + return 1; 85 + } 86 + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 87 + &path_beneath, 0); 88 + close(path_beneath.parent_fd); 89 + if (err) { 90 + perror("Failed to update ruleset"); 91 + close(ruleset_fd); 92 + return 1; 93 + } 94 + 95 + We now have a ruleset with one rule allowing read access to ``/usr`` while 96 + denying all other handled accesses for the filesystem. The next step is to 97 + restrict the current thread from gaining more privileges (e.g. thanks to a SUID 98 + binary). 99 + 100 + .. code-block:: c 101 + 102 + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 103 + perror("Failed to restrict privileges"); 104 + close(ruleset_fd); 105 + return 1; 106 + } 107 + 108 + The current thread is now ready to sandbox itself with the ruleset. 109 + 110 + .. code-block:: c 111 + 112 + if (landlock_restrict_self(ruleset_fd, 0)) { 113 + perror("Failed to enforce ruleset"); 114 + close(ruleset_fd); 115 + return 1; 116 + } 117 + close(ruleset_fd); 118 + 119 + If the `landlock_restrict_self` system call succeeds, the current thread is now 120 + restricted and this policy will be enforced on all its subsequently created 121 + children as well. Once a thread is landlocked, there is no way to remove its 122 + security policy; only adding more restrictions is allowed. These threads are 123 + now in a new Landlock domain, merge of their parent one (if any) with the new 124 + ruleset. 125 + 126 + Full working code can be found in `samples/landlock/sandboxer.c`_. 127 + 128 + Layers of file path access rights 129 + --------------------------------- 130 + 131 + Each time a thread enforces a ruleset on itself, it updates its Landlock domain 132 + with a new layer of policy. Indeed, this complementary policy is stacked with 133 + the potentially other rulesets already restricting this thread. A sandboxed 134 + thread can then safely add more constraints to itself with a new enforced 135 + ruleset. 136 + 137 + One policy layer grants access to a file path if at least one of its rules 138 + encountered on the path grants the access. A sandboxed thread can only access 139 + a file path if all its enforced policy layers grant the access as well as all 140 + the other system access controls (e.g. filesystem DAC, other LSM policies, 141 + etc.). 142 + 143 + Bind mounts and OverlayFS 144 + ------------------------- 145 + 146 + Landlock enables to restrict access to file hierarchies, which means that these 147 + access rights can be propagated with bind mounts (cf. 148 + :doc:`/filesystems/sharedsubtree`) but not with :doc:`/filesystems/overlayfs`. 149 + 150 + A bind mount mirrors a source file hierarchy to a destination. The destination 151 + hierarchy is then composed of the exact same files, on which Landlock rules can 152 + be tied, either via the source or the destination path. These rules restrict 153 + access when they are encountered on a path, which means that they can restrict 154 + access to multiple file hierarchies at the same time, whether these hierarchies 155 + are the result of bind mounts or not. 156 + 157 + An OverlayFS mount point consists of upper and lower layers. These layers are 158 + combined in a merge directory, result of the mount point. This merge hierarchy 159 + may include files from the upper and lower layers, but modifications performed 160 + on the merge hierarchy only reflects on the upper layer. From a Landlock 161 + policy point of view, each OverlayFS layers and merge hierarchies are 162 + standalone and contains their own set of files and directories, which is 163 + different from bind mounts. A policy restricting an OverlayFS layer will not 164 + restrict the resulted merged hierarchy, and vice versa. Landlock users should 165 + then only think about file hierarchies they want to allow access to, regardless 166 + of the underlying filesystem. 167 + 168 + Inheritance 169 + ----------- 170 + 171 + Every new thread resulting from a :manpage:`clone(2)` inherits Landlock domain 172 + restrictions from its parent. This is similar to the seccomp inheritance (cf. 173 + :doc:`/userspace-api/seccomp_filter`) or any other LSM dealing with task's 174 + :manpage:`credentials(7)`. For instance, one process's thread may apply 175 + Landlock rules to itself, but they will not be automatically applied to other 176 + sibling threads (unlike POSIX thread credential changes, cf. 177 + :manpage:`nptl(7)`). 178 + 179 + When a thread sandboxes itself, we have the guarantee that the related security 180 + policy will stay enforced on all this thread's descendants. This allows 181 + creating standalone and modular security policies per application, which will 182 + automatically be composed between themselves according to their runtime parent 183 + policies. 184 + 185 + Ptrace restrictions 186 + ------------------- 187 + 188 + A sandboxed process has less privileges than a non-sandboxed process and must 189 + then be subject to additional restrictions when manipulating another process. 190 + To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target 191 + process, a sandboxed process should have a subset of the target process rules, 192 + which means the tracee must be in a sub-domain of the tracer. 193 + 194 + Kernel interface 195 + ================ 196 + 197 + Access rights 198 + ------------- 199 + 200 + .. kernel-doc:: include/uapi/linux/landlock.h 201 + :identifiers: fs_access 202 + 203 + Creating a new ruleset 204 + ---------------------- 205 + 206 + .. kernel-doc:: security/landlock/syscalls.c 207 + :identifiers: sys_landlock_create_ruleset 208 + 209 + .. kernel-doc:: include/uapi/linux/landlock.h 210 + :identifiers: landlock_ruleset_attr 211 + 212 + Extending a ruleset 213 + ------------------- 214 + 215 + .. kernel-doc:: security/landlock/syscalls.c 216 + :identifiers: sys_landlock_add_rule 217 + 218 + .. kernel-doc:: include/uapi/linux/landlock.h 219 + :identifiers: landlock_rule_type landlock_path_beneath_attr 220 + 221 + Enforcing a ruleset 222 + ------------------- 223 + 224 + .. kernel-doc:: security/landlock/syscalls.c 225 + :identifiers: sys_landlock_restrict_self 226 + 227 + Current limitations 228 + =================== 229 + 230 + File renaming and linking 231 + ------------------------- 232 + 233 + Because Landlock targets unprivileged access controls, it is needed to properly 234 + handle composition of rules. Such property also implies rules nesting. 235 + Properly handling multiple layers of ruleset, each one of them able to restrict 236 + access to files, also implies to inherit the ruleset restrictions from a parent 237 + to its hierarchy. Because files are identified and restricted by their 238 + hierarchy, moving or linking a file from one directory to another implies to 239 + propagate the hierarchy constraints. To protect against privilege escalations 240 + through renaming or linking, and for the sake of simplicity, Landlock currently 241 + limits linking and renaming to the same directory. Future Landlock evolutions 242 + will enable more flexibility for renaming and linking, with dedicated ruleset 243 + flags. 244 + 245 + Filesystem topology modification 246 + -------------------------------- 247 + 248 + As for file renaming and linking, a sandboxed thread cannot modify its 249 + filesystem topology, whether via :manpage:`mount(2)` or 250 + :manpage:`pivot_root(2)`. However, :manpage:`chroot(2)` calls are not denied. 251 + 252 + Special filesystems 253 + ------------------- 254 + 255 + Access to regular files and directories can be restricted by Landlock, 256 + according to the handled accesses of a ruleset. However, files that do not 257 + come from a user-visible filesystem (e.g. pipe, socket), but can still be 258 + accessed through ``/proc/<pid>/fd/*``, cannot currently be explicitly 259 + restricted. Likewise, some special kernel filesystems such as nsfs, which can 260 + be accessed through ``/proc/<pid>/ns/*``, cannot currently be explicitly 261 + restricted. However, thanks to the `ptrace restrictions`_, access to such 262 + sensitive ``/proc`` files are automatically restricted according to domain 263 + hierarchies. Future Landlock evolutions could still enable to explicitly 264 + restrict such paths with dedicated ruleset flags. 265 + 266 + Ruleset layers 267 + -------------- 268 + 269 + There is a limit of 64 layers of stacked rulesets. This can be an issue for a 270 + task willing to enforce a new ruleset in complement to its 64 inherited 271 + rulesets. Once this limit is reached, sys_landlock_restrict_self() returns 272 + E2BIG. It is then strongly suggested to carefully build rulesets once in the 273 + life of a thread, especially for applications able to launch other applications 274 + that may also want to sandbox themselves (e.g. shells, container managers, 275 + etc.). 276 + 277 + Memory usage 278 + ------------ 279 + 280 + Kernel memory allocated to create rulesets is accounted and can be restricted 281 + by the :doc:`/admin-guide/cgroup-v1/memory`. 282 + 283 + Questions and answers 284 + ===================== 285 + 286 + What about user space sandbox managers? 287 + --------------------------------------- 288 + 289 + Using user space process to enforce restrictions on kernel resources can lead 290 + to race conditions or inconsistent evaluations (i.e. `Incorrect mirroring of 291 + the OS code and state 292 + <https://www.ndss-symposium.org/ndss2003/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools/>`_). 293 + 294 + What about namespaces and containers? 295 + ------------------------------------- 296 + 297 + Namespaces can help create sandboxes but they are not designed for 298 + access-control and then miss useful features for such use case (e.g. no 299 + fine-grained restrictions). Moreover, their complexity can lead to security 300 + issues, especially when untrusted processes can manipulate them (cf. 301 + `Controlling access to user namespaces <https://lwn.net/Articles/673597/>`_). 302 + 303 + Additional documentation 304 + ======================== 305 + 306 + * :doc:`/security/landlock` 307 + * https://landlock.io 308 + 309 + .. Links 310 + .. _samples/landlock/sandboxer.c: 311 + https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/samples/landlock/sandboxer.c
+15
MAINTAINERS
··· 10191 10191 F: net/ipv4/tcp_bpf.c 10192 10192 F: net/ipv4/udp_bpf.c 10193 10193 10194 + LANDLOCK SECURITY MODULE 10195 + M: Mickaël Salaün <mic@digikod.net> 10196 + L: linux-security-module@vger.kernel.org 10197 + S: Supported 10198 + W: https://landlock.io 10199 + T: git https://github.com/landlock-lsm/linux.git 10200 + F: Documentation/security/landlock.rst 10201 + F: Documentation/userspace-api/landlock.rst 10202 + F: include/uapi/linux/landlock.h 10203 + F: samples/landlock/ 10204 + F: security/landlock/ 10205 + F: tools/testing/selftests/landlock/ 10206 + K: landlock 10207 + K: LANDLOCK 10208 + 10194 10209 LANTIQ / INTEL Ethernet drivers 10195 10210 M: Hauke Mehrtens <hauke@hauke-m.de> 10196 10211 L: netdev@vger.kernel.org
+7
arch/Kconfig
··· 1068 1068 config ARCH_NO_PREEMPT 1069 1069 bool 1070 1070 1071 + config ARCH_EPHEMERAL_INODES 1072 + def_bool n 1073 + help 1074 + An arch should select this symbol if it doesn't keep track of inode 1075 + instances on its own, but instead relies on something else (e.g. the 1076 + host kernel for an UML kernel). 1077 + 1071 1078 config ARCH_SUPPORTS_RT 1072 1079 bool 1073 1080
+3
arch/alpha/kernel/syscalls/syscall.tbl
··· 483 483 551 common epoll_pwait2 sys_epoll_pwait2 484 484 552 common mount_setattr sys_mount_setattr 485 485 553 common quotactl_path sys_quotactl_path 486 + 554 common landlock_create_ruleset sys_landlock_create_ruleset 487 + 555 common landlock_add_rule sys_landlock_add_rule 488 + 556 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/arm/tools/syscall.tbl
··· 457 457 441 common epoll_pwait2 sys_epoll_pwait2 458 458 442 common mount_setattr sys_mount_setattr 459 459 443 common quotactl_path sys_quotactl_path 460 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 461 + 445 common landlock_add_rule sys_landlock_add_rule 462 + 446 common landlock_restrict_self sys_landlock_restrict_self
+1 -1
arch/arm64/include/asm/unistd.h
··· 38 38 #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) 39 39 #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) 40 40 41 - #define __NR_compat_syscalls 444 41 + #define __NR_compat_syscalls 447 42 42 #endif 43 43 44 44 #define __ARCH_WANT_SYS_CLONE
+6
arch/arm64/include/asm/unistd32.h
··· 895 895 __SYSCALL(__NR_mount_setattr, sys_mount_setattr) 896 896 #define __NR_quotactl_path 443 897 897 __SYSCALL(__NR_quotactl_path, sys_quotactl_path) 898 + #define __NR_landlock_create_ruleset 444 899 + __SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset) 900 + #define __NR_landlock_add_rule 445 901 + __SYSCALL(__NR_landlock_add_rule, sys_landlock_add_rule) 902 + #define __NR_landlock_restrict_self 446 903 + __SYSCALL(__NR_landlock_restrict_self, sys_landlock_restrict_self) 898 904 899 905 /* 900 906 * Please add new compat syscalls above this comment and update
+3
arch/ia64/kernel/syscalls/syscall.tbl
··· 364 364 441 common epoll_pwait2 sys_epoll_pwait2 365 365 442 common mount_setattr sys_mount_setattr 366 366 443 common quotactl_path sys_quotactl_path 367 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 368 + 445 common landlock_add_rule sys_landlock_add_rule 369 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/m68k/kernel/syscalls/syscall.tbl
··· 443 443 441 common epoll_pwait2 sys_epoll_pwait2 444 444 442 common mount_setattr sys_mount_setattr 445 445 443 common quotactl_path sys_quotactl_path 446 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 447 + 445 common landlock_add_rule sys_landlock_add_rule 448 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/microblaze/kernel/syscalls/syscall.tbl
··· 449 449 441 common epoll_pwait2 sys_epoll_pwait2 450 450 442 common mount_setattr sys_mount_setattr 451 451 443 common quotactl_path sys_quotactl_path 452 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 453 + 445 common landlock_add_rule sys_landlock_add_rule 454 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/mips/kernel/syscalls/syscall_n32.tbl
··· 382 382 441 n32 epoll_pwait2 compat_sys_epoll_pwait2 383 383 442 n32 mount_setattr sys_mount_setattr 384 384 443 n32 quotactl_path sys_quotactl_path 385 + 444 n32 landlock_create_ruleset sys_landlock_create_ruleset 386 + 445 n32 landlock_add_rule sys_landlock_add_rule 387 + 446 n32 landlock_restrict_self sys_landlock_restrict_self
+3
arch/mips/kernel/syscalls/syscall_n64.tbl
··· 358 358 441 n64 epoll_pwait2 sys_epoll_pwait2 359 359 442 n64 mount_setattr sys_mount_setattr 360 360 443 n64 quotactl_path sys_quotactl_path 361 + 444 n64 landlock_create_ruleset sys_landlock_create_ruleset 362 + 445 n64 landlock_add_rule sys_landlock_add_rule 363 + 446 n64 landlock_restrict_self sys_landlock_restrict_self
+3
arch/mips/kernel/syscalls/syscall_o32.tbl
··· 431 431 441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 432 432 442 o32 mount_setattr sys_mount_setattr 433 433 443 o32 quotactl_path sys_quotactl_path 434 + 444 o32 landlock_create_ruleset sys_landlock_create_ruleset 435 + 445 o32 landlock_add_rule sys_landlock_add_rule 436 + 446 o32 landlock_restrict_self sys_landlock_restrict_self
+3
arch/parisc/kernel/syscalls/syscall.tbl
··· 441 441 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 442 442 common mount_setattr sys_mount_setattr 443 443 443 common quotactl_path sys_quotactl_path 444 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 + 445 common landlock_add_rule sys_landlock_add_rule 446 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/powerpc/kernel/syscalls/syscall.tbl
··· 523 523 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 524 524 442 common mount_setattr sys_mount_setattr 525 525 443 common quotactl_path sys_quotactl_path 526 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 527 + 445 common landlock_add_rule sys_landlock_add_rule 528 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/s390/kernel/syscalls/syscall.tbl
··· 446 446 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 447 447 442 common mount_setattr sys_mount_setattr sys_mount_setattr 448 448 443 common quotactl_path sys_quotactl_path sys_quotactl_path 449 + 444 common landlock_create_ruleset sys_landlock_create_ruleset sys_landlock_create_ruleset 450 + 445 common landlock_add_rule sys_landlock_add_rule sys_landlock_add_rule 451 + 446 common landlock_restrict_self sys_landlock_restrict_self sys_landlock_restrict_self
+3
arch/sh/kernel/syscalls/syscall.tbl
··· 446 446 441 common epoll_pwait2 sys_epoll_pwait2 447 447 442 common mount_setattr sys_mount_setattr 448 448 443 common quotactl_path sys_quotactl_path 449 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 450 + 445 common landlock_add_rule sys_landlock_add_rule 451 + 446 common landlock_restrict_self sys_landlock_restrict_self
+3
arch/sparc/kernel/syscalls/syscall.tbl
··· 489 489 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 490 490 442 common mount_setattr sys_mount_setattr 491 491 443 common quotactl_path sys_quotactl_path 492 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 493 + 445 common landlock_add_rule sys_landlock_add_rule 494 + 446 common landlock_restrict_self sys_landlock_restrict_self
+1
arch/um/Kconfig
··· 5 5 config UML 6 6 bool 7 7 default y 8 + select ARCH_EPHEMERAL_INODES 8 9 select ARCH_HAS_KCOV 9 10 select ARCH_NO_PREEMPT 10 11 select HAVE_ARCH_AUDITSYSCALL
+3
arch/x86/entry/syscalls/syscall_32.tbl
··· 448 448 441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 449 449 442 i386 mount_setattr sys_mount_setattr 450 450 443 i386 quotactl_path sys_quotactl_path 451 + 444 i386 landlock_create_ruleset sys_landlock_create_ruleset 452 + 445 i386 landlock_add_rule sys_landlock_add_rule 453 + 446 i386 landlock_restrict_self sys_landlock_restrict_self
+3
arch/x86/entry/syscalls/syscall_64.tbl
··· 365 365 441 common epoll_pwait2 sys_epoll_pwait2 366 366 442 common mount_setattr sys_mount_setattr 367 367 443 common quotactl_path sys_quotactl_path 368 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 369 + 445 common landlock_add_rule sys_landlock_add_rule 370 + 446 common landlock_restrict_self sys_landlock_restrict_self 368 371 369 372 # 370 373 # Due to a historical design error, certain syscalls are numbered differently
+3
arch/xtensa/kernel/syscalls/syscall.tbl
··· 414 414 441 common epoll_pwait2 sys_epoll_pwait2 415 415 442 common mount_setattr sys_mount_setattr 416 416 443 common quotactl_path sys_quotactl_path 417 + 444 common landlock_create_ruleset sys_landlock_create_ruleset 418 + 445 common landlock_add_rule sys_landlock_add_rule 419 + 446 common landlock_restrict_self sys_landlock_restrict_self
+1
fs/super.c
··· 454 454 evict_inodes(sb); 455 455 /* only nonzero refcount inodes can have marks */ 456 456 fsnotify_sb_delete(sb); 457 + security_sb_delete(sb); 457 458 458 459 if (sb->s_dio_done_wq) { 459 460 destroy_workqueue(sb->s_dio_done_wq);
+1
include/linux/lsm_hook_defs.h
··· 59 59 LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc, 60 60 struct fs_parameter *param) 61 61 LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb) 62 + LSM_HOOK(void, LSM_RET_VOID, sb_delete, struct super_block *sb) 62 63 LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb) 63 64 LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts) 64 65 LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
+4
include/linux/lsm_hooks.h
··· 108 108 * allocated. 109 109 * @sb contains the super_block structure to be modified. 110 110 * Return 0 if operation was successful. 111 + * @sb_delete: 112 + * Release objects tied to a superblock (e.g. inodes). 113 + * @sb contains the super_block structure being released. 111 114 * @sb_free_security: 112 115 * Deallocate and clear the sb->s_security field. 113 116 * @sb contains the super_block structure to be modified. ··· 1588 1585 int lbs_cred; 1589 1586 int lbs_file; 1590 1587 int lbs_inode; 1588 + int lbs_superblock; 1591 1589 int lbs_ipc; 1592 1590 int lbs_msg_msg; 1593 1591 int lbs_task;
+4
include/linux/security.h
··· 291 291 int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc); 292 292 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param); 293 293 int security_sb_alloc(struct super_block *sb); 294 + void security_sb_delete(struct super_block *sb); 294 295 void security_sb_free(struct super_block *sb); 295 296 void security_free_mnt_opts(void **mnt_opts); 296 297 int security_sb_eat_lsm_opts(char *options, void **mnt_opts); ··· 633 632 { 634 633 return 0; 635 634 } 635 + 636 + static inline void security_sb_delete(struct super_block *sb) 637 + { } 636 638 637 639 static inline void security_sb_free(struct super_block *sb) 638 640 { }
+7
include/linux/syscalls.h
··· 69 69 struct clone_args; 70 70 struct open_how; 71 71 struct mount_attr; 72 + struct landlock_ruleset_attr; 73 + enum landlock_rule_type; 72 74 73 75 #include <linux/types.h> 74 76 #include <linux/aio_abi.h> ··· 1045 1043 siginfo_t __user *info, 1046 1044 unsigned int flags); 1047 1045 asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags); 1046 + asmlinkage long sys_landlock_create_ruleset(const struct landlock_ruleset_attr __user *attr, 1047 + size_t size, __u32 flags); 1048 + asmlinkage long sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type, 1049 + const void __user *rule_attr, __u32 flags); 1050 + asmlinkage long sys_landlock_restrict_self(int ruleset_fd, __u32 flags); 1048 1051 1049 1052 /* 1050 1053 * Architecture-specific system calls
+8 -1
include/uapi/asm-generic/unistd.h
··· 866 866 #define __NR_quotactl_path 443 867 867 __SYSCALL(__NR_quotactl_path, sys_quotactl_path) 868 868 869 + #define __NR_landlock_create_ruleset 444 870 + __SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset) 871 + #define __NR_landlock_add_rule 445 872 + __SYSCALL(__NR_landlock_add_rule, sys_landlock_add_rule) 873 + #define __NR_landlock_restrict_self 446 874 + __SYSCALL(__NR_landlock_restrict_self, sys_landlock_restrict_self) 875 + 869 876 #undef __NR_syscalls 870 - #define __NR_syscalls 444 877 + #define __NR_syscalls 447 871 878 872 879 /* 873 880 * 32 bit systems traditionally used different
+137
include/uapi/linux/landlock.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + /* 3 + * Landlock - User space API 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _UAPI_LINUX_LANDLOCK_H 10 + #define _UAPI_LINUX_LANDLOCK_H 11 + 12 + #include <linux/types.h> 13 + 14 + /** 15 + * struct landlock_ruleset_attr - Ruleset definition 16 + * 17 + * Argument of sys_landlock_create_ruleset(). This structure can grow in 18 + * future versions. 19 + */ 20 + struct landlock_ruleset_attr { 21 + /** 22 + * @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) 23 + * that is handled by this ruleset and should then be forbidden if no 24 + * rule explicitly allow them. This is needed for backward 25 + * compatibility reasons. 26 + */ 27 + __u64 handled_access_fs; 28 + }; 29 + 30 + /* 31 + * sys_landlock_create_ruleset() flags: 32 + * 33 + * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI 34 + * version. 35 + */ 36 + #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) 37 + 38 + /** 39 + * enum landlock_rule_type - Landlock rule type 40 + * 41 + * Argument of sys_landlock_add_rule(). 42 + */ 43 + enum landlock_rule_type { 44 + /** 45 + * @LANDLOCK_RULE_PATH_BENEATH: Type of a &struct 46 + * landlock_path_beneath_attr . 47 + */ 48 + LANDLOCK_RULE_PATH_BENEATH = 1, 49 + }; 50 + 51 + /** 52 + * struct landlock_path_beneath_attr - Path hierarchy definition 53 + * 54 + * Argument of sys_landlock_add_rule(). 55 + */ 56 + struct landlock_path_beneath_attr { 57 + /** 58 + * @allowed_access: Bitmask of allowed actions for this file hierarchy 59 + * (cf. `Filesystem flags`_). 60 + */ 61 + __u64 allowed_access; 62 + /** 63 + * @parent_fd: File descriptor, open with ``O_PATH``, which identifies 64 + * the parent directory of a file hierarchy, or just a file. 65 + */ 66 + __s32 parent_fd; 67 + /* 68 + * This struct is packed to avoid trailing reserved members. 69 + * Cf. security/landlock/syscalls.c:build_check_abi() 70 + */ 71 + } __attribute__((packed)); 72 + 73 + /** 74 + * DOC: fs_access 75 + * 76 + * A set of actions on kernel objects may be defined by an attribute (e.g. 77 + * &struct landlock_path_beneath_attr) including a bitmask of access. 78 + * 79 + * Filesystem flags 80 + * ~~~~~~~~~~~~~~~~ 81 + * 82 + * These flags enable to restrict a sandboxed process to a set of actions on 83 + * files and directories. Files or directories opened before the sandboxing 84 + * are not subject to these restrictions. 85 + * 86 + * A file can only receive these access rights: 87 + * 88 + * - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file. 89 + * - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. 90 + * - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access. 91 + * 92 + * A directory can receive access rights related to files or directories. The 93 + * following access right is applied to the directory itself, and the 94 + * directories beneath it: 95 + * 96 + * - %LANDLOCK_ACCESS_FS_READ_DIR: Open a directory or list its content. 97 + * 98 + * However, the following access rights only apply to the content of a 99 + * directory, not the directory itself: 100 + * 101 + * - %LANDLOCK_ACCESS_FS_REMOVE_DIR: Remove an empty directory or rename one. 102 + * - %LANDLOCK_ACCESS_FS_REMOVE_FILE: Unlink (or rename) a file. 103 + * - %LANDLOCK_ACCESS_FS_MAKE_CHAR: Create (or rename or link) a character 104 + * device. 105 + * - %LANDLOCK_ACCESS_FS_MAKE_DIR: Create (or rename) a directory. 106 + * - %LANDLOCK_ACCESS_FS_MAKE_REG: Create (or rename or link) a regular file. 107 + * - %LANDLOCK_ACCESS_FS_MAKE_SOCK: Create (or rename or link) a UNIX domain 108 + * socket. 109 + * - %LANDLOCK_ACCESS_FS_MAKE_FIFO: Create (or rename or link) a named pipe. 110 + * - %LANDLOCK_ACCESS_FS_MAKE_BLOCK: Create (or rename or link) a block device. 111 + * - %LANDLOCK_ACCESS_FS_MAKE_SYM: Create (or rename or link) a symbolic link. 112 + * 113 + * .. warning:: 114 + * 115 + * It is currently not possible to restrict some file-related actions 116 + * accessible through these syscall families: :manpage:`chdir(2)`, 117 + * :manpage:`truncate(2)`, :manpage:`stat(2)`, :manpage:`flock(2)`, 118 + * :manpage:`chmod(2)`, :manpage:`chown(2)`, :manpage:`setxattr(2)`, 119 + * :manpage:`utime(2)`, :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, 120 + * :manpage:`access(2)`. 121 + * Future Landlock evolutions will enable to restrict them. 122 + */ 123 + #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) 124 + #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) 125 + #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) 126 + #define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3) 127 + #define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4) 128 + #define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5) 129 + #define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6) 130 + #define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7) 131 + #define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8) 132 + #define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9) 133 + #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) 134 + #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) 135 + #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) 136 + 137 + #endif /* _UAPI_LINUX_LANDLOCK_H */
+5
kernel/sys_ni.c
··· 267 267 COND_SYSCALL(keyctl); 268 268 COND_SYSCALL_COMPAT(keyctl); 269 269 270 + /* security/landlock/syscalls.c */ 271 + COND_SYSCALL(landlock_create_ruleset); 272 + COND_SYSCALL(landlock_add_rule); 273 + COND_SYSCALL(landlock_restrict_self); 274 + 270 275 /* arch/example/kernel/sys_example.c */ 271 276 272 277 /* mm/fadvise.c */
+7
samples/Kconfig
··· 124 124 bool "hidraw sample" 125 125 depends on CC_CAN_LINK && HEADERS_INSTALL 126 126 127 + config SAMPLE_LANDLOCK 128 + bool "Landlock example" 129 + depends on CC_CAN_LINK && HEADERS_INSTALL 130 + help 131 + Build a simple Landlock sandbox manager able to start a process 132 + restricted by a user-defined filesystem access control policy. 133 + 127 134 config SAMPLE_PIDFD 128 135 bool "pidfd sample" 129 136 depends on CC_CAN_LINK && HEADERS_INSTALL
+1
samples/Makefile
··· 11 11 obj-$(CONFIG_SAMPLE_KFIFO) += kfifo/ 12 12 obj-$(CONFIG_SAMPLE_KOBJECT) += kobject/ 13 13 obj-$(CONFIG_SAMPLE_KPROBES) += kprobes/ 14 + subdir-$(CONFIG_SAMPLE_LANDLOCK) += landlock 14 15 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch/ 15 16 subdir-$(CONFIG_SAMPLE_PIDFD) += pidfd 16 17 obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi/
+1
samples/landlock/.gitignore
··· 1 + /sandboxer
+13
samples/landlock/Makefile
··· 1 + # SPDX-License-Identifier: BSD-3-Clause 2 + 3 + userprogs-always-y := sandboxer 4 + 5 + userccflags += -I usr/include 6 + 7 + .PHONY: all clean 8 + 9 + all: 10 + $(MAKE) -C ../.. samples/landlock/ 11 + 12 + clean: 13 + $(MAKE) -C ../.. M=samples/landlock/ clean
+238
samples/landlock/sandboxer.c
··· 1 + // SPDX-License-Identifier: BSD-3-Clause 2 + /* 3 + * Simple Landlock sandbox manager able to launch a process restricted by a 4 + * user-defined filesystem access control policy. 5 + * 6 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 7 + * Copyright © 2020 ANSSI 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <errno.h> 12 + #include <fcntl.h> 13 + #include <linux/landlock.h> 14 + #include <linux/prctl.h> 15 + #include <stddef.h> 16 + #include <stdio.h> 17 + #include <stdlib.h> 18 + #include <string.h> 19 + #include <sys/prctl.h> 20 + #include <sys/stat.h> 21 + #include <sys/syscall.h> 22 + #include <unistd.h> 23 + 24 + #ifndef landlock_create_ruleset 25 + static inline int landlock_create_ruleset( 26 + const struct landlock_ruleset_attr *const attr, 27 + const size_t size, const __u32 flags) 28 + { 29 + return syscall(__NR_landlock_create_ruleset, attr, size, flags); 30 + } 31 + #endif 32 + 33 + #ifndef landlock_add_rule 34 + static inline int landlock_add_rule(const int ruleset_fd, 35 + const enum landlock_rule_type rule_type, 36 + const void *const rule_attr, const __u32 flags) 37 + { 38 + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, 39 + rule_attr, flags); 40 + } 41 + #endif 42 + 43 + #ifndef landlock_restrict_self 44 + static inline int landlock_restrict_self(const int ruleset_fd, 45 + const __u32 flags) 46 + { 47 + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 48 + } 49 + #endif 50 + 51 + #define ENV_FS_RO_NAME "LL_FS_RO" 52 + #define ENV_FS_RW_NAME "LL_FS_RW" 53 + #define ENV_PATH_TOKEN ":" 54 + 55 + static int parse_path(char *env_path, const char ***const path_list) 56 + { 57 + int i, num_paths = 0; 58 + 59 + if (env_path) { 60 + num_paths++; 61 + for (i = 0; env_path[i]; i++) { 62 + if (env_path[i] == ENV_PATH_TOKEN[0]) 63 + num_paths++; 64 + } 65 + } 66 + *path_list = malloc(num_paths * sizeof(**path_list)); 67 + for (i = 0; i < num_paths; i++) 68 + (*path_list)[i] = strsep(&env_path, ENV_PATH_TOKEN); 69 + 70 + return num_paths; 71 + } 72 + 73 + #define ACCESS_FILE ( \ 74 + LANDLOCK_ACCESS_FS_EXECUTE | \ 75 + LANDLOCK_ACCESS_FS_WRITE_FILE | \ 76 + LANDLOCK_ACCESS_FS_READ_FILE) 77 + 78 + static int populate_ruleset( 79 + const char *const env_var, const int ruleset_fd, 80 + const __u64 allowed_access) 81 + { 82 + int num_paths, i, ret = 1; 83 + char *env_path_name; 84 + const char **path_list = NULL; 85 + struct landlock_path_beneath_attr path_beneath = { 86 + .parent_fd = -1, 87 + }; 88 + 89 + env_path_name = getenv(env_var); 90 + if (!env_path_name) { 91 + /* Prevents users to forget a setting. */ 92 + fprintf(stderr, "Missing environment variable %s\n", env_var); 93 + return 1; 94 + } 95 + env_path_name = strdup(env_path_name); 96 + unsetenv(env_var); 97 + num_paths = parse_path(env_path_name, &path_list); 98 + if (num_paths == 1 && path_list[0][0] == '\0') { 99 + /* 100 + * Allows to not use all possible restrictions (e.g. use 101 + * LL_FS_RO without LL_FS_RW). 102 + */ 103 + ret = 0; 104 + goto out_free_name; 105 + } 106 + 107 + for (i = 0; i < num_paths; i++) { 108 + struct stat statbuf; 109 + 110 + path_beneath.parent_fd = open(path_list[i], O_PATH | 111 + O_CLOEXEC); 112 + if (path_beneath.parent_fd < 0) { 113 + fprintf(stderr, "Failed to open \"%s\": %s\n", 114 + path_list[i], 115 + strerror(errno)); 116 + goto out_free_name; 117 + } 118 + if (fstat(path_beneath.parent_fd, &statbuf)) { 119 + close(path_beneath.parent_fd); 120 + goto out_free_name; 121 + } 122 + path_beneath.allowed_access = allowed_access; 123 + if (!S_ISDIR(statbuf.st_mode)) 124 + path_beneath.allowed_access &= ACCESS_FILE; 125 + if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 126 + &path_beneath, 0)) { 127 + fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", 128 + path_list[i], strerror(errno)); 129 + close(path_beneath.parent_fd); 130 + goto out_free_name; 131 + } 132 + close(path_beneath.parent_fd); 133 + } 134 + ret = 0; 135 + 136 + out_free_name: 137 + free(env_path_name); 138 + return ret; 139 + } 140 + 141 + #define ACCESS_FS_ROUGHLY_READ ( \ 142 + LANDLOCK_ACCESS_FS_EXECUTE | \ 143 + LANDLOCK_ACCESS_FS_READ_FILE | \ 144 + LANDLOCK_ACCESS_FS_READ_DIR) 145 + 146 + #define ACCESS_FS_ROUGHLY_WRITE ( \ 147 + LANDLOCK_ACCESS_FS_WRITE_FILE | \ 148 + LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 149 + LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 150 + LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 151 + LANDLOCK_ACCESS_FS_MAKE_DIR | \ 152 + LANDLOCK_ACCESS_FS_MAKE_REG | \ 153 + LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 154 + LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 155 + LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 156 + LANDLOCK_ACCESS_FS_MAKE_SYM) 157 + 158 + int main(const int argc, char *const argv[], char *const *const envp) 159 + { 160 + const char *cmd_path; 161 + char *const *cmd_argv; 162 + int ruleset_fd; 163 + struct landlock_ruleset_attr ruleset_attr = { 164 + .handled_access_fs = ACCESS_FS_ROUGHLY_READ | 165 + ACCESS_FS_ROUGHLY_WRITE, 166 + }; 167 + 168 + if (argc < 2) { 169 + fprintf(stderr, "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", 170 + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); 171 + fprintf(stderr, "Launch a command in a restricted environment.\n\n"); 172 + fprintf(stderr, "Environment variables containing paths, " 173 + "each separated by a colon:\n"); 174 + fprintf(stderr, "* %s: list of paths allowed to be used in a read-only way.\n", 175 + ENV_FS_RO_NAME); 176 + fprintf(stderr, "* %s: list of paths allowed to be used in a read-write way.\n", 177 + ENV_FS_RW_NAME); 178 + fprintf(stderr, "\nexample:\n" 179 + "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " 180 + "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " 181 + "%s bash -i\n", 182 + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); 183 + return 1; 184 + } 185 + 186 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 187 + if (ruleset_fd < 0) { 188 + const int err = errno; 189 + 190 + perror("Failed to create a ruleset"); 191 + switch (err) { 192 + case ENOSYS: 193 + fprintf(stderr, "Hint: Landlock is not supported by the current kernel. " 194 + "To support it, build the kernel with " 195 + "CONFIG_SECURITY_LANDLOCK=y and prepend " 196 + "\"landlock,\" to the content of CONFIG_LSM.\n"); 197 + break; 198 + case EOPNOTSUPP: 199 + fprintf(stderr, "Hint: Landlock is currently disabled. " 200 + "It can be enabled in the kernel configuration by " 201 + "prepending \"landlock,\" to the content of CONFIG_LSM, " 202 + "or at boot time by setting the same content to the " 203 + "\"lsm\" kernel parameter.\n"); 204 + break; 205 + } 206 + return 1; 207 + } 208 + if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, 209 + ACCESS_FS_ROUGHLY_READ)) { 210 + goto err_close_ruleset; 211 + } 212 + if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, 213 + ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE)) { 214 + goto err_close_ruleset; 215 + } 216 + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 217 + perror("Failed to restrict privileges"); 218 + goto err_close_ruleset; 219 + } 220 + if (landlock_restrict_self(ruleset_fd, 0)) { 221 + perror("Failed to enforce ruleset"); 222 + goto err_close_ruleset; 223 + } 224 + close(ruleset_fd); 225 + 226 + cmd_path = argv[1]; 227 + cmd_argv = argv + 1; 228 + execvpe(cmd_path, cmd_argv, envp); 229 + fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path, 230 + strerror(errno)); 231 + fprintf(stderr, "Hint: access to the binary, the interpreter or " 232 + "shared libraries may be denied.\n"); 233 + return 1; 234 + 235 + err_close_ruleset: 236 + close(ruleset_fd); 237 + return 1; 238 + }
+6 -5
security/Kconfig
··· 238 238 source "security/yama/Kconfig" 239 239 source "security/safesetid/Kconfig" 240 240 source "security/lockdown/Kconfig" 241 + source "security/landlock/Kconfig" 241 242 242 243 source "security/integrity/Kconfig" 243 244 ··· 278 277 279 278 config LSM 280 279 string "Ordered list of enabled LSMs" 281 - default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK 282 - default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR 283 - default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO 284 - default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC 285 - default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf" 280 + default "landlock,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK 281 + default "landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR 282 + default "landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO 283 + default "landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC 284 + default "landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf" 286 285 help 287 286 A comma-separated list of LSMs, in initialization order. 288 287 Any LSMs left off this list will be ignored. This can be
+2
security/Makefile
··· 13 13 subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid 14 14 subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown 15 15 subdir-$(CONFIG_BPF_LSM) += bpf 16 + subdir-$(CONFIG_SECURITY_LANDLOCK) += landlock 16 17 17 18 # always enable default capabilities 18 19 obj-y += commoncap.o ··· 33 32 obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ 34 33 obj-$(CONFIG_CGROUPS) += device_cgroup.o 35 34 obj-$(CONFIG_BPF_LSM) += bpf/ 35 + obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/ 36 36 37 37 # Object integrity file lists 38 38 subdir-$(CONFIG_INTEGRITY) += integrity
+21
security/landlock/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + 3 + config SECURITY_LANDLOCK 4 + bool "Landlock support" 5 + depends on SECURITY && !ARCH_EPHEMERAL_INODES 6 + select SECURITY_PATH 7 + help 8 + Landlock is a sandboxing mechanism that enables processes to restrict 9 + themselves (and their future children) by gradually enforcing 10 + tailored access control policies. A Landlock security policy is a 11 + set of access rights (e.g. open a file in read-only, make a 12 + directory, etc.) tied to a file hierarchy. Such policy can be 13 + configured and enforced by any processes for themselves using the 14 + dedicated system calls: landlock_create_ruleset(), 15 + landlock_add_rule(), and landlock_restrict_self(). 16 + 17 + See Documentation/userspace-api/landlock.rst for further information. 18 + 19 + If you are unsure how to answer this question, answer N. Otherwise, 20 + you should also prepend "landlock," to the content of CONFIG_LSM to 21 + enable Landlock at boot time.
+4
security/landlock/Makefile
··· 1 + obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o 2 + 3 + landlock-y := setup.o syscalls.o object.o ruleset.o \ 4 + cred.o ptrace.o fs.o
+20
security/landlock/common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Common constants and helpers 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_COMMON_H 10 + #define _SECURITY_LANDLOCK_COMMON_H 11 + 12 + #define LANDLOCK_NAME "landlock" 13 + 14 + #ifdef pr_fmt 15 + #undef pr_fmt 16 + #endif 17 + 18 + #define pr_fmt(fmt) LANDLOCK_NAME ": " fmt 19 + 20 + #endif /* _SECURITY_LANDLOCK_COMMON_H */
+46
security/landlock/cred.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Credential hooks 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <linux/cred.h> 10 + #include <linux/lsm_hooks.h> 11 + 12 + #include "common.h" 13 + #include "cred.h" 14 + #include "ruleset.h" 15 + #include "setup.h" 16 + 17 + static int hook_cred_prepare(struct cred *const new, 18 + const struct cred *const old, const gfp_t gfp) 19 + { 20 + struct landlock_ruleset *const old_dom = landlock_cred(old)->domain; 21 + 22 + if (old_dom) { 23 + landlock_get_ruleset(old_dom); 24 + landlock_cred(new)->domain = old_dom; 25 + } 26 + return 0; 27 + } 28 + 29 + static void hook_cred_free(struct cred *const cred) 30 + { 31 + struct landlock_ruleset *const dom = landlock_cred(cred)->domain; 32 + 33 + if (dom) 34 + landlock_put_ruleset_deferred(dom); 35 + } 36 + 37 + static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 38 + LSM_HOOK_INIT(cred_prepare, hook_cred_prepare), 39 + LSM_HOOK_INIT(cred_free, hook_cred_free), 40 + }; 41 + 42 + __init void landlock_add_cred_hooks(void) 43 + { 44 + security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 45 + LANDLOCK_NAME); 46 + }
+58
security/landlock/cred.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Credential hooks 4 + * 5 + * Copyright © 2019-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_CRED_H 10 + #define _SECURITY_LANDLOCK_CRED_H 11 + 12 + #include <linux/cred.h> 13 + #include <linux/init.h> 14 + #include <linux/rcupdate.h> 15 + 16 + #include "ruleset.h" 17 + #include "setup.h" 18 + 19 + struct landlock_cred_security { 20 + struct landlock_ruleset *domain; 21 + }; 22 + 23 + static inline struct landlock_cred_security *landlock_cred( 24 + const struct cred *cred) 25 + { 26 + return cred->security + landlock_blob_sizes.lbs_cred; 27 + } 28 + 29 + static inline const struct landlock_ruleset *landlock_get_current_domain(void) 30 + { 31 + return landlock_cred(current_cred())->domain; 32 + } 33 + 34 + /* 35 + * The call needs to come from an RCU read-side critical section. 36 + */ 37 + static inline const struct landlock_ruleset *landlock_get_task_domain( 38 + const struct task_struct *const task) 39 + { 40 + return landlock_cred(__task_cred(task))->domain; 41 + } 42 + 43 + static inline bool landlocked(const struct task_struct *const task) 44 + { 45 + bool has_dom; 46 + 47 + if (task == current) 48 + return !!landlock_get_current_domain(); 49 + 50 + rcu_read_lock(); 51 + has_dom = !!landlock_get_task_domain(task); 52 + rcu_read_unlock(); 53 + return has_dom; 54 + } 55 + 56 + __init void landlock_add_cred_hooks(void); 57 + 58 + #endif /* _SECURITY_LANDLOCK_CRED_H */
+692
security/landlock/fs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Filesystem management and hooks 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <linux/atomic.h> 10 + #include <linux/bitops.h> 11 + #include <linux/bits.h> 12 + #include <linux/compiler_types.h> 13 + #include <linux/dcache.h> 14 + #include <linux/err.h> 15 + #include <linux/fs.h> 16 + #include <linux/init.h> 17 + #include <linux/kernel.h> 18 + #include <linux/limits.h> 19 + #include <linux/list.h> 20 + #include <linux/lsm_hooks.h> 21 + #include <linux/mount.h> 22 + #include <linux/namei.h> 23 + #include <linux/path.h> 24 + #include <linux/rcupdate.h> 25 + #include <linux/spinlock.h> 26 + #include <linux/stat.h> 27 + #include <linux/types.h> 28 + #include <linux/wait_bit.h> 29 + #include <linux/workqueue.h> 30 + #include <uapi/linux/landlock.h> 31 + 32 + #include "common.h" 33 + #include "cred.h" 34 + #include "fs.h" 35 + #include "limits.h" 36 + #include "object.h" 37 + #include "ruleset.h" 38 + #include "setup.h" 39 + 40 + /* Underlying object management */ 41 + 42 + static void release_inode(struct landlock_object *const object) 43 + __releases(object->lock) 44 + { 45 + struct inode *const inode = object->underobj; 46 + struct super_block *sb; 47 + 48 + if (!inode) { 49 + spin_unlock(&object->lock); 50 + return; 51 + } 52 + 53 + /* 54 + * Protects against concurrent use by hook_sb_delete() of the reference 55 + * to the underlying inode. 56 + */ 57 + object->underobj = NULL; 58 + /* 59 + * Makes sure that if the filesystem is concurrently unmounted, 60 + * hook_sb_delete() will wait for us to finish iput(). 61 + */ 62 + sb = inode->i_sb; 63 + atomic_long_inc(&landlock_superblock(sb)->inode_refs); 64 + spin_unlock(&object->lock); 65 + /* 66 + * Because object->underobj was not NULL, hook_sb_delete() and 67 + * get_inode_object() guarantee that it is safe to reset 68 + * landlock_inode(inode)->object while it is not NULL. It is therefore 69 + * not necessary to lock inode->i_lock. 70 + */ 71 + rcu_assign_pointer(landlock_inode(inode)->object, NULL); 72 + /* 73 + * Now, new rules can safely be tied to @inode with get_inode_object(). 74 + */ 75 + 76 + iput(inode); 77 + if (atomic_long_dec_and_test(&landlock_superblock(sb)->inode_refs)) 78 + wake_up_var(&landlock_superblock(sb)->inode_refs); 79 + } 80 + 81 + static const struct landlock_object_underops landlock_fs_underops = { 82 + .release = release_inode 83 + }; 84 + 85 + /* Ruleset management */ 86 + 87 + static struct landlock_object *get_inode_object(struct inode *const inode) 88 + { 89 + struct landlock_object *object, *new_object; 90 + struct landlock_inode_security *inode_sec = landlock_inode(inode); 91 + 92 + rcu_read_lock(); 93 + retry: 94 + object = rcu_dereference(inode_sec->object); 95 + if (object) { 96 + if (likely(refcount_inc_not_zero(&object->usage))) { 97 + rcu_read_unlock(); 98 + return object; 99 + } 100 + /* 101 + * We are racing with release_inode(), the object is going 102 + * away. Wait for release_inode(), then retry. 103 + */ 104 + spin_lock(&object->lock); 105 + spin_unlock(&object->lock); 106 + goto retry; 107 + } 108 + rcu_read_unlock(); 109 + 110 + /* 111 + * If there is no object tied to @inode, then create a new one (without 112 + * holding any locks). 113 + */ 114 + new_object = landlock_create_object(&landlock_fs_underops, inode); 115 + if (IS_ERR(new_object)) 116 + return new_object; 117 + 118 + /* 119 + * Protects against concurrent calls to get_inode_object() or 120 + * hook_sb_delete(). 121 + */ 122 + spin_lock(&inode->i_lock); 123 + if (unlikely(rcu_access_pointer(inode_sec->object))) { 124 + /* Someone else just created the object, bail out and retry. */ 125 + spin_unlock(&inode->i_lock); 126 + kfree(new_object); 127 + 128 + rcu_read_lock(); 129 + goto retry; 130 + } 131 + 132 + /* 133 + * @inode will be released by hook_sb_delete() on its superblock 134 + * shutdown, or by release_inode() when no more ruleset references the 135 + * related object. 136 + */ 137 + ihold(inode); 138 + rcu_assign_pointer(inode_sec->object, new_object); 139 + spin_unlock(&inode->i_lock); 140 + return new_object; 141 + } 142 + 143 + /* All access rights that can be tied to files. */ 144 + #define ACCESS_FILE ( \ 145 + LANDLOCK_ACCESS_FS_EXECUTE | \ 146 + LANDLOCK_ACCESS_FS_WRITE_FILE | \ 147 + LANDLOCK_ACCESS_FS_READ_FILE) 148 + 149 + /* 150 + * @path: Should have been checked by get_path_from_fd(). 151 + */ 152 + int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, 153 + const struct path *const path, u32 access_rights) 154 + { 155 + int err; 156 + struct landlock_object *object; 157 + 158 + /* Files only get access rights that make sense. */ 159 + if (!d_is_dir(path->dentry) && (access_rights | ACCESS_FILE) != 160 + ACCESS_FILE) 161 + return -EINVAL; 162 + if (WARN_ON_ONCE(ruleset->num_layers != 1)) 163 + return -EINVAL; 164 + 165 + /* Transforms relative access rights to absolute ones. */ 166 + access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0]; 167 + object = get_inode_object(d_backing_inode(path->dentry)); 168 + if (IS_ERR(object)) 169 + return PTR_ERR(object); 170 + mutex_lock(&ruleset->lock); 171 + err = landlock_insert_rule(ruleset, object, access_rights); 172 + mutex_unlock(&ruleset->lock); 173 + /* 174 + * No need to check for an error because landlock_insert_rule() 175 + * increments the refcount for the new object if needed. 176 + */ 177 + landlock_put_object(object); 178 + return err; 179 + } 180 + 181 + /* Access-control management */ 182 + 183 + static inline u64 unmask_layers( 184 + const struct landlock_ruleset *const domain, 185 + const struct path *const path, const u32 access_request, 186 + u64 layer_mask) 187 + { 188 + const struct landlock_rule *rule; 189 + const struct inode *inode; 190 + size_t i; 191 + 192 + if (d_is_negative(path->dentry)) 193 + /* Ignore nonexistent leafs. */ 194 + return layer_mask; 195 + inode = d_backing_inode(path->dentry); 196 + rcu_read_lock(); 197 + rule = landlock_find_rule(domain, 198 + rcu_dereference(landlock_inode(inode)->object)); 199 + rcu_read_unlock(); 200 + if (!rule) 201 + return layer_mask; 202 + 203 + /* 204 + * An access is granted if, for each policy layer, at least one rule 205 + * encountered on the pathwalk grants the requested accesses, 206 + * regardless of their position in the layer stack. We must then check 207 + * the remaining layers for each inode, from the first added layer to 208 + * the last one. 209 + */ 210 + for (i = 0; i < rule->num_layers; i++) { 211 + const struct landlock_layer *const layer = &rule->layers[i]; 212 + const u64 layer_level = BIT_ULL(layer->level - 1); 213 + 214 + /* Checks that the layer grants access to the full request. */ 215 + if ((layer->access & access_request) == access_request) { 216 + layer_mask &= ~layer_level; 217 + 218 + if (layer_mask == 0) 219 + return layer_mask; 220 + } 221 + } 222 + return layer_mask; 223 + } 224 + 225 + static int check_access_path(const struct landlock_ruleset *const domain, 226 + const struct path *const path, u32 access_request) 227 + { 228 + bool allowed = false; 229 + struct path walker_path; 230 + u64 layer_mask; 231 + size_t i; 232 + 233 + /* Make sure all layers can be checked. */ 234 + BUILD_BUG_ON(BITS_PER_TYPE(layer_mask) < LANDLOCK_MAX_NUM_LAYERS); 235 + 236 + if (!access_request) 237 + return 0; 238 + if (WARN_ON_ONCE(!domain || !path)) 239 + return 0; 240 + /* 241 + * Allows access to pseudo filesystems that will never be mountable 242 + * (e.g. sockfs, pipefs), but can still be reachable through 243 + * /proc/<pid>/fd/<file-descriptor> . 244 + */ 245 + if ((path->dentry->d_sb->s_flags & SB_NOUSER) || 246 + (d_is_positive(path->dentry) && 247 + unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))) 248 + return 0; 249 + if (WARN_ON_ONCE(domain->num_layers < 1)) 250 + return -EACCES; 251 + 252 + /* Saves all layers handling a subset of requested accesses. */ 253 + layer_mask = 0; 254 + for (i = 0; i < domain->num_layers; i++) { 255 + if (domain->fs_access_masks[i] & access_request) 256 + layer_mask |= BIT_ULL(i); 257 + } 258 + /* An access request not handled by the domain is allowed. */ 259 + if (layer_mask == 0) 260 + return 0; 261 + 262 + walker_path = *path; 263 + path_get(&walker_path); 264 + /* 265 + * We need to walk through all the hierarchy to not miss any relevant 266 + * restriction. 267 + */ 268 + while (true) { 269 + struct dentry *parent_dentry; 270 + 271 + layer_mask = unmask_layers(domain, &walker_path, 272 + access_request, layer_mask); 273 + if (layer_mask == 0) { 274 + /* Stops when a rule from each layer grants access. */ 275 + allowed = true; 276 + break; 277 + } 278 + 279 + jump_up: 280 + if (walker_path.dentry == walker_path.mnt->mnt_root) { 281 + if (follow_up(&walker_path)) { 282 + /* Ignores hidden mount points. */ 283 + goto jump_up; 284 + } else { 285 + /* 286 + * Stops at the real root. Denies access 287 + * because not all layers have granted access. 288 + */ 289 + allowed = false; 290 + break; 291 + } 292 + } 293 + if (unlikely(IS_ROOT(walker_path.dentry))) { 294 + /* 295 + * Stops at disconnected root directories. Only allows 296 + * access to internal filesystems (e.g. nsfs, which is 297 + * reachable through /proc/<pid>/ns/<namespace>). 298 + */ 299 + allowed = !!(walker_path.mnt->mnt_flags & MNT_INTERNAL); 300 + break; 301 + } 302 + parent_dentry = dget_parent(walker_path.dentry); 303 + dput(walker_path.dentry); 304 + walker_path.dentry = parent_dentry; 305 + } 306 + path_put(&walker_path); 307 + return allowed ? 0 : -EACCES; 308 + } 309 + 310 + static inline int current_check_access_path(const struct path *const path, 311 + const u32 access_request) 312 + { 313 + const struct landlock_ruleset *const dom = 314 + landlock_get_current_domain(); 315 + 316 + if (!dom) 317 + return 0; 318 + return check_access_path(dom, path, access_request); 319 + } 320 + 321 + /* Inode hooks */ 322 + 323 + static void hook_inode_free_security(struct inode *const inode) 324 + { 325 + /* 326 + * All inodes must already have been untied from their object by 327 + * release_inode() or hook_sb_delete(). 328 + */ 329 + WARN_ON_ONCE(landlock_inode(inode)->object); 330 + } 331 + 332 + /* Super-block hooks */ 333 + 334 + /* 335 + * Release the inodes used in a security policy. 336 + * 337 + * Cf. fsnotify_unmount_inodes() and invalidate_inodes() 338 + */ 339 + static void hook_sb_delete(struct super_block *const sb) 340 + { 341 + struct inode *inode, *prev_inode = NULL; 342 + 343 + if (!landlock_initialized) 344 + return; 345 + 346 + spin_lock(&sb->s_inode_list_lock); 347 + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { 348 + struct landlock_object *object; 349 + 350 + /* Only handles referenced inodes. */ 351 + if (!atomic_read(&inode->i_count)) 352 + continue; 353 + 354 + /* 355 + * Protects against concurrent modification of inode (e.g. 356 + * from get_inode_object()). 357 + */ 358 + spin_lock(&inode->i_lock); 359 + /* 360 + * Checks I_FREEING and I_WILL_FREE to protect against a race 361 + * condition when release_inode() just called iput(), which 362 + * could lead to a NULL dereference of inode->security or a 363 + * second call to iput() for the same Landlock object. Also 364 + * checks I_NEW because such inode cannot be tied to an object. 365 + */ 366 + if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { 367 + spin_unlock(&inode->i_lock); 368 + continue; 369 + } 370 + 371 + rcu_read_lock(); 372 + object = rcu_dereference(landlock_inode(inode)->object); 373 + if (!object) { 374 + rcu_read_unlock(); 375 + spin_unlock(&inode->i_lock); 376 + continue; 377 + } 378 + /* Keeps a reference to this inode until the next loop walk. */ 379 + __iget(inode); 380 + spin_unlock(&inode->i_lock); 381 + 382 + /* 383 + * If there is no concurrent release_inode() ongoing, then we 384 + * are in charge of calling iput() on this inode, otherwise we 385 + * will just wait for it to finish. 386 + */ 387 + spin_lock(&object->lock); 388 + if (object->underobj == inode) { 389 + object->underobj = NULL; 390 + spin_unlock(&object->lock); 391 + rcu_read_unlock(); 392 + 393 + /* 394 + * Because object->underobj was not NULL, 395 + * release_inode() and get_inode_object() guarantee 396 + * that it is safe to reset 397 + * landlock_inode(inode)->object while it is not NULL. 398 + * It is therefore not necessary to lock inode->i_lock. 399 + */ 400 + rcu_assign_pointer(landlock_inode(inode)->object, NULL); 401 + /* 402 + * At this point, we own the ihold() reference that was 403 + * originally set up by get_inode_object() and the 404 + * __iget() reference that we just set in this loop 405 + * walk. Therefore the following call to iput() will 406 + * not sleep nor drop the inode because there is now at 407 + * least two references to it. 408 + */ 409 + iput(inode); 410 + } else { 411 + spin_unlock(&object->lock); 412 + rcu_read_unlock(); 413 + } 414 + 415 + if (prev_inode) { 416 + /* 417 + * At this point, we still own the __iget() reference 418 + * that we just set in this loop walk. Therefore we 419 + * can drop the list lock and know that the inode won't 420 + * disappear from under us until the next loop walk. 421 + */ 422 + spin_unlock(&sb->s_inode_list_lock); 423 + /* 424 + * We can now actually put the inode reference from the 425 + * previous loop walk, which is not needed anymore. 426 + */ 427 + iput(prev_inode); 428 + cond_resched(); 429 + spin_lock(&sb->s_inode_list_lock); 430 + } 431 + prev_inode = inode; 432 + } 433 + spin_unlock(&sb->s_inode_list_lock); 434 + 435 + /* Puts the inode reference from the last loop walk, if any. */ 436 + if (prev_inode) 437 + iput(prev_inode); 438 + /* Waits for pending iput() in release_inode(). */ 439 + wait_var_event(&landlock_superblock(sb)->inode_refs, !atomic_long_read( 440 + &landlock_superblock(sb)->inode_refs)); 441 + } 442 + 443 + /* 444 + * Because a Landlock security policy is defined according to the filesystem 445 + * topology (i.e. the mount namespace), changing it may grant access to files 446 + * not previously allowed. 447 + * 448 + * To make it simple, deny any filesystem topology modification by landlocked 449 + * processes. Non-landlocked processes may still change the namespace of a 450 + * landlocked process, but this kind of threat must be handled by a system-wide 451 + * access-control security policy. 452 + * 453 + * This could be lifted in the future if Landlock can safely handle mount 454 + * namespace updates requested by a landlocked process. Indeed, we could 455 + * update the current domain (which is currently read-only) by taking into 456 + * account the accesses of the source and the destination of a new mount point. 457 + * However, it would also require to make all the child domains dynamically 458 + * inherit these new constraints. Anyway, for backward compatibility reasons, 459 + * a dedicated user space option would be required (e.g. as a ruleset flag). 460 + */ 461 + static int hook_sb_mount(const char *const dev_name, 462 + const struct path *const path, const char *const type, 463 + const unsigned long flags, void *const data) 464 + { 465 + if (!landlock_get_current_domain()) 466 + return 0; 467 + return -EPERM; 468 + } 469 + 470 + static int hook_move_mount(const struct path *const from_path, 471 + const struct path *const to_path) 472 + { 473 + if (!landlock_get_current_domain()) 474 + return 0; 475 + return -EPERM; 476 + } 477 + 478 + /* 479 + * Removing a mount point may reveal a previously hidden file hierarchy, which 480 + * may then grant access to files, which may have previously been forbidden. 481 + */ 482 + static int hook_sb_umount(struct vfsmount *const mnt, const int flags) 483 + { 484 + if (!landlock_get_current_domain()) 485 + return 0; 486 + return -EPERM; 487 + } 488 + 489 + static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts) 490 + { 491 + if (!landlock_get_current_domain()) 492 + return 0; 493 + return -EPERM; 494 + } 495 + 496 + /* 497 + * pivot_root(2), like mount(2), changes the current mount namespace. It must 498 + * then be forbidden for a landlocked process. 499 + * 500 + * However, chroot(2) may be allowed because it only changes the relative root 501 + * directory of the current process. Moreover, it can be used to restrict the 502 + * view of the filesystem. 503 + */ 504 + static int hook_sb_pivotroot(const struct path *const old_path, 505 + const struct path *const new_path) 506 + { 507 + if (!landlock_get_current_domain()) 508 + return 0; 509 + return -EPERM; 510 + } 511 + 512 + /* Path hooks */ 513 + 514 + static inline u32 get_mode_access(const umode_t mode) 515 + { 516 + switch (mode & S_IFMT) { 517 + case S_IFLNK: 518 + return LANDLOCK_ACCESS_FS_MAKE_SYM; 519 + case 0: 520 + /* A zero mode translates to S_IFREG. */ 521 + case S_IFREG: 522 + return LANDLOCK_ACCESS_FS_MAKE_REG; 523 + case S_IFDIR: 524 + return LANDLOCK_ACCESS_FS_MAKE_DIR; 525 + case S_IFCHR: 526 + return LANDLOCK_ACCESS_FS_MAKE_CHAR; 527 + case S_IFBLK: 528 + return LANDLOCK_ACCESS_FS_MAKE_BLOCK; 529 + case S_IFIFO: 530 + return LANDLOCK_ACCESS_FS_MAKE_FIFO; 531 + case S_IFSOCK: 532 + return LANDLOCK_ACCESS_FS_MAKE_SOCK; 533 + default: 534 + WARN_ON_ONCE(1); 535 + return 0; 536 + } 537 + } 538 + 539 + /* 540 + * Creating multiple links or renaming may lead to privilege escalations if not 541 + * handled properly. Indeed, we must be sure that the source doesn't gain more 542 + * privileges by being accessible from the destination. This is getting more 543 + * complex when dealing with multiple layers. The whole picture can be seen as 544 + * a multilayer partial ordering problem. A future version of Landlock will 545 + * deal with that. 546 + */ 547 + static int hook_path_link(struct dentry *const old_dentry, 548 + const struct path *const new_dir, 549 + struct dentry *const new_dentry) 550 + { 551 + const struct landlock_ruleset *const dom = 552 + landlock_get_current_domain(); 553 + 554 + if (!dom) 555 + return 0; 556 + /* The mount points are the same for old and new paths, cf. EXDEV. */ 557 + if (old_dentry->d_parent != new_dir->dentry) 558 + /* Gracefully forbids reparenting. */ 559 + return -EXDEV; 560 + if (unlikely(d_is_negative(old_dentry))) 561 + return -ENOENT; 562 + return check_access_path(dom, new_dir, 563 + get_mode_access(d_backing_inode(old_dentry)->i_mode)); 564 + } 565 + 566 + static inline u32 maybe_remove(const struct dentry *const dentry) 567 + { 568 + if (d_is_negative(dentry)) 569 + return 0; 570 + return d_is_dir(dentry) ? LANDLOCK_ACCESS_FS_REMOVE_DIR : 571 + LANDLOCK_ACCESS_FS_REMOVE_FILE; 572 + } 573 + 574 + static int hook_path_rename(const struct path *const old_dir, 575 + struct dentry *const old_dentry, 576 + const struct path *const new_dir, 577 + struct dentry *const new_dentry) 578 + { 579 + const struct landlock_ruleset *const dom = 580 + landlock_get_current_domain(); 581 + 582 + if (!dom) 583 + return 0; 584 + /* The mount points are the same for old and new paths, cf. EXDEV. */ 585 + if (old_dir->dentry != new_dir->dentry) 586 + /* Gracefully forbids reparenting. */ 587 + return -EXDEV; 588 + if (unlikely(d_is_negative(old_dentry))) 589 + return -ENOENT; 590 + /* RENAME_EXCHANGE is handled because directories are the same. */ 591 + return check_access_path(dom, old_dir, maybe_remove(old_dentry) | 592 + maybe_remove(new_dentry) | 593 + get_mode_access(d_backing_inode(old_dentry)->i_mode)); 594 + } 595 + 596 + static int hook_path_mkdir(const struct path *const dir, 597 + struct dentry *const dentry, const umode_t mode) 598 + { 599 + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR); 600 + } 601 + 602 + static int hook_path_mknod(const struct path *const dir, 603 + struct dentry *const dentry, const umode_t mode, 604 + const unsigned int dev) 605 + { 606 + const struct landlock_ruleset *const dom = 607 + landlock_get_current_domain(); 608 + 609 + if (!dom) 610 + return 0; 611 + return check_access_path(dom, dir, get_mode_access(mode)); 612 + } 613 + 614 + static int hook_path_symlink(const struct path *const dir, 615 + struct dentry *const dentry, const char *const old_name) 616 + { 617 + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM); 618 + } 619 + 620 + static int hook_path_unlink(const struct path *const dir, 621 + struct dentry *const dentry) 622 + { 623 + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE); 624 + } 625 + 626 + static int hook_path_rmdir(const struct path *const dir, 627 + struct dentry *const dentry) 628 + { 629 + return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); 630 + } 631 + 632 + /* File hooks */ 633 + 634 + static inline u32 get_file_access(const struct file *const file) 635 + { 636 + u32 access = 0; 637 + 638 + if (file->f_mode & FMODE_READ) { 639 + /* A directory can only be opened in read mode. */ 640 + if (S_ISDIR(file_inode(file)->i_mode)) 641 + return LANDLOCK_ACCESS_FS_READ_DIR; 642 + access = LANDLOCK_ACCESS_FS_READ_FILE; 643 + } 644 + if (file->f_mode & FMODE_WRITE) 645 + access |= LANDLOCK_ACCESS_FS_WRITE_FILE; 646 + /* __FMODE_EXEC is indeed part of f_flags, not f_mode. */ 647 + if (file->f_flags & __FMODE_EXEC) 648 + access |= LANDLOCK_ACCESS_FS_EXECUTE; 649 + return access; 650 + } 651 + 652 + static int hook_file_open(struct file *const file) 653 + { 654 + const struct landlock_ruleset *const dom = 655 + landlock_get_current_domain(); 656 + 657 + if (!dom) 658 + return 0; 659 + /* 660 + * Because a file may be opened with O_PATH, get_file_access() may 661 + * return 0. This case will be handled with a future Landlock 662 + * evolution. 663 + */ 664 + return check_access_path(dom, &file->f_path, get_file_access(file)); 665 + } 666 + 667 + static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 668 + LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), 669 + 670 + LSM_HOOK_INIT(sb_delete, hook_sb_delete), 671 + LSM_HOOK_INIT(sb_mount, hook_sb_mount), 672 + LSM_HOOK_INIT(move_mount, hook_move_mount), 673 + LSM_HOOK_INIT(sb_umount, hook_sb_umount), 674 + LSM_HOOK_INIT(sb_remount, hook_sb_remount), 675 + LSM_HOOK_INIT(sb_pivotroot, hook_sb_pivotroot), 676 + 677 + LSM_HOOK_INIT(path_link, hook_path_link), 678 + LSM_HOOK_INIT(path_rename, hook_path_rename), 679 + LSM_HOOK_INIT(path_mkdir, hook_path_mkdir), 680 + LSM_HOOK_INIT(path_mknod, hook_path_mknod), 681 + LSM_HOOK_INIT(path_symlink, hook_path_symlink), 682 + LSM_HOOK_INIT(path_unlink, hook_path_unlink), 683 + LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), 684 + 685 + LSM_HOOK_INIT(file_open, hook_file_open), 686 + }; 687 + 688 + __init void landlock_add_fs_hooks(void) 689 + { 690 + security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 691 + LANDLOCK_NAME); 692 + }
+70
security/landlock/fs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Filesystem management and hooks 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_FS_H 10 + #define _SECURITY_LANDLOCK_FS_H 11 + 12 + #include <linux/fs.h> 13 + #include <linux/init.h> 14 + #include <linux/rcupdate.h> 15 + 16 + #include "ruleset.h" 17 + #include "setup.h" 18 + 19 + /** 20 + * struct landlock_inode_security - Inode security blob 21 + * 22 + * Enable to reference a &struct landlock_object tied to an inode (i.e. 23 + * underlying object). 24 + */ 25 + struct landlock_inode_security { 26 + /** 27 + * @object: Weak pointer to an allocated object. All assignments of a 28 + * new object are protected by the underlying inode->i_lock. However, 29 + * atomically disassociating @object from the inode is only protected 30 + * by @object->lock, from the time @object's usage refcount drops to 31 + * zero to the time this pointer is nulled out (cf. release_inode() and 32 + * hook_sb_delete()). Indeed, such disassociation doesn't require 33 + * inode->i_lock thanks to the careful rcu_access_pointer() check 34 + * performed by get_inode_object(). 35 + */ 36 + struct landlock_object __rcu *object; 37 + }; 38 + 39 + /** 40 + * struct landlock_superblock_security - Superblock security blob 41 + * 42 + * Enable hook_sb_delete() to wait for concurrent calls to release_inode(). 43 + */ 44 + struct landlock_superblock_security { 45 + /** 46 + * @inode_refs: Number of pending inodes (from this superblock) that 47 + * are being released by release_inode(). 48 + * Cf. struct super_block->s_fsnotify_inode_refs . 49 + */ 50 + atomic_long_t inode_refs; 51 + }; 52 + 53 + static inline struct landlock_inode_security *landlock_inode( 54 + const struct inode *const inode) 55 + { 56 + return inode->i_security + landlock_blob_sizes.lbs_inode; 57 + } 58 + 59 + static inline struct landlock_superblock_security *landlock_superblock( 60 + const struct super_block *const superblock) 61 + { 62 + return superblock->s_security + landlock_blob_sizes.lbs_superblock; 63 + } 64 + 65 + __init void landlock_add_fs_hooks(void); 66 + 67 + int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, 68 + const struct path *const path, u32 access_hierarchy); 69 + 70 + #endif /* _SECURITY_LANDLOCK_FS_H */
+21
security/landlock/limits.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Limits for different components 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_LIMITS_H 10 + #define _SECURITY_LANDLOCK_LIMITS_H 11 + 12 + #include <linux/limits.h> 13 + #include <uapi/linux/landlock.h> 14 + 15 + #define LANDLOCK_MAX_NUM_LAYERS 64 16 + #define LANDLOCK_MAX_NUM_RULES U32_MAX 17 + 18 + #define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_MAKE_SYM 19 + #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) 20 + 21 + #endif /* _SECURITY_LANDLOCK_LIMITS_H */
+67
security/landlock/object.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Object management 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <linux/bug.h> 10 + #include <linux/compiler_types.h> 11 + #include <linux/err.h> 12 + #include <linux/kernel.h> 13 + #include <linux/rcupdate.h> 14 + #include <linux/refcount.h> 15 + #include <linux/slab.h> 16 + #include <linux/spinlock.h> 17 + 18 + #include "object.h" 19 + 20 + struct landlock_object *landlock_create_object( 21 + const struct landlock_object_underops *const underops, 22 + void *const underobj) 23 + { 24 + struct landlock_object *new_object; 25 + 26 + if (WARN_ON_ONCE(!underops || !underobj)) 27 + return ERR_PTR(-ENOENT); 28 + new_object = kzalloc(sizeof(*new_object), GFP_KERNEL_ACCOUNT); 29 + if (!new_object) 30 + return ERR_PTR(-ENOMEM); 31 + refcount_set(&new_object->usage, 1); 32 + spin_lock_init(&new_object->lock); 33 + new_object->underops = underops; 34 + new_object->underobj = underobj; 35 + return new_object; 36 + } 37 + 38 + /* 39 + * The caller must own the object (i.e. thanks to object->usage) to safely put 40 + * it. 41 + */ 42 + void landlock_put_object(struct landlock_object *const object) 43 + { 44 + /* 45 + * The call to @object->underops->release(object) might sleep, e.g. 46 + * because of iput(). 47 + */ 48 + might_sleep(); 49 + if (!object) 50 + return; 51 + 52 + /* 53 + * If the @object's refcount cannot drop to zero, we can just decrement 54 + * the refcount without holding a lock. Otherwise, the decrement must 55 + * happen under @object->lock for synchronization with things like 56 + * get_inode_object(). 57 + */ 58 + if (refcount_dec_and_lock(&object->usage, &object->lock)) { 59 + __acquire(&object->lock); 60 + /* 61 + * With @object->lock initially held, remove the reference from 62 + * @object->underobj to @object (if it still exists). 63 + */ 64 + object->underops->release(object); 65 + kfree_rcu(object, rcu_free); 66 + } 67 + }
+91
security/landlock/object.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Object management 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_OBJECT_H 10 + #define _SECURITY_LANDLOCK_OBJECT_H 11 + 12 + #include <linux/compiler_types.h> 13 + #include <linux/refcount.h> 14 + #include <linux/spinlock.h> 15 + 16 + struct landlock_object; 17 + 18 + /** 19 + * struct landlock_object_underops - Operations on an underlying object 20 + */ 21 + struct landlock_object_underops { 22 + /** 23 + * @release: Releases the underlying object (e.g. iput() for an inode). 24 + */ 25 + void (*release)(struct landlock_object *const object) 26 + __releases(object->lock); 27 + }; 28 + 29 + /** 30 + * struct landlock_object - Security blob tied to a kernel object 31 + * 32 + * The goal of this structure is to enable to tie a set of ephemeral access 33 + * rights (pertaining to different domains) to a kernel object (e.g an inode) 34 + * in a safe way. This implies to handle concurrent use and modification. 35 + * 36 + * The lifetime of a &struct landlock_object depends on the rules referring to 37 + * it. 38 + */ 39 + struct landlock_object { 40 + /** 41 + * @usage: This counter is used to tie an object to the rules matching 42 + * it or to keep it alive while adding a new rule. If this counter 43 + * reaches zero, this struct must not be modified, but this counter can 44 + * still be read from within an RCU read-side critical section. When 45 + * adding a new rule to an object with a usage counter of zero, we must 46 + * wait until the pointer to this object is set to NULL (or recycled). 47 + */ 48 + refcount_t usage; 49 + /** 50 + * @lock: Protects against concurrent modifications. This lock must be 51 + * held from the time @usage drops to zero until any weak references 52 + * from @underobj to this object have been cleaned up. 53 + * 54 + * Lock ordering: inode->i_lock nests inside this. 55 + */ 56 + spinlock_t lock; 57 + /** 58 + * @underobj: Used when cleaning up an object and to mark an object as 59 + * tied to its underlying kernel structure. This pointer is protected 60 + * by @lock. Cf. landlock_release_inodes() and release_inode(). 61 + */ 62 + void *underobj; 63 + union { 64 + /** 65 + * @rcu_free: Enables lockless use of @usage, @lock and 66 + * @underobj from within an RCU read-side critical section. 67 + * @rcu_free and @underops are only used by 68 + * landlock_put_object(). 69 + */ 70 + struct rcu_head rcu_free; 71 + /** 72 + * @underops: Enables landlock_put_object() to release the 73 + * underlying object (e.g. inode). 74 + */ 75 + const struct landlock_object_underops *underops; 76 + }; 77 + }; 78 + 79 + struct landlock_object *landlock_create_object( 80 + const struct landlock_object_underops *const underops, 81 + void *const underobj); 82 + 83 + void landlock_put_object(struct landlock_object *const object); 84 + 85 + static inline void landlock_get_object(struct landlock_object *const object) 86 + { 87 + if (object) 88 + refcount_inc(&object->usage); 89 + } 90 + 91 + #endif /* _SECURITY_LANDLOCK_OBJECT_H */
+120
security/landlock/ptrace.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Ptrace hooks 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #include <asm/current.h> 10 + #include <linux/cred.h> 11 + #include <linux/errno.h> 12 + #include <linux/kernel.h> 13 + #include <linux/lsm_hooks.h> 14 + #include <linux/rcupdate.h> 15 + #include <linux/sched.h> 16 + 17 + #include "common.h" 18 + #include "cred.h" 19 + #include "ptrace.h" 20 + #include "ruleset.h" 21 + #include "setup.h" 22 + 23 + /** 24 + * domain_scope_le - Checks domain ordering for scoped ptrace 25 + * 26 + * @parent: Parent domain. 27 + * @child: Potential child of @parent. 28 + * 29 + * Checks if the @parent domain is less or equal to (i.e. an ancestor, which 30 + * means a subset of) the @child domain. 31 + */ 32 + static bool domain_scope_le(const struct landlock_ruleset *const parent, 33 + const struct landlock_ruleset *const child) 34 + { 35 + const struct landlock_hierarchy *walker; 36 + 37 + if (!parent) 38 + return true; 39 + if (!child) 40 + return false; 41 + for (walker = child->hierarchy; walker; walker = walker->parent) { 42 + if (walker == parent->hierarchy) 43 + /* @parent is in the scoped hierarchy of @child. */ 44 + return true; 45 + } 46 + /* There is no relationship between @parent and @child. */ 47 + return false; 48 + } 49 + 50 + static bool task_is_scoped(const struct task_struct *const parent, 51 + const struct task_struct *const child) 52 + { 53 + bool is_scoped; 54 + const struct landlock_ruleset *dom_parent, *dom_child; 55 + 56 + rcu_read_lock(); 57 + dom_parent = landlock_get_task_domain(parent); 58 + dom_child = landlock_get_task_domain(child); 59 + is_scoped = domain_scope_le(dom_parent, dom_child); 60 + rcu_read_unlock(); 61 + return is_scoped; 62 + } 63 + 64 + static int task_ptrace(const struct task_struct *const parent, 65 + const struct task_struct *const child) 66 + { 67 + /* Quick return for non-landlocked tasks. */ 68 + if (!landlocked(parent)) 69 + return 0; 70 + if (task_is_scoped(parent, child)) 71 + return 0; 72 + return -EPERM; 73 + } 74 + 75 + /** 76 + * hook_ptrace_access_check - Determines whether the current process may access 77 + * another 78 + * 79 + * @child: Process to be accessed. 80 + * @mode: Mode of attachment. 81 + * 82 + * If the current task has Landlock rules, then the child must have at least 83 + * the same rules. Else denied. 84 + * 85 + * Determines whether a process may access another, returning 0 if permission 86 + * granted, -errno if denied. 87 + */ 88 + static int hook_ptrace_access_check(struct task_struct *const child, 89 + const unsigned int mode) 90 + { 91 + return task_ptrace(current, child); 92 + } 93 + 94 + /** 95 + * hook_ptrace_traceme - Determines whether another process may trace the 96 + * current one 97 + * 98 + * @parent: Task proposed to be the tracer. 99 + * 100 + * If the parent has Landlock rules, then the current task must have the same 101 + * or more rules. Else denied. 102 + * 103 + * Determines whether the nominated task is permitted to trace the current 104 + * process, returning 0 if permission is granted, -errno if denied. 105 + */ 106 + static int hook_ptrace_traceme(struct task_struct *const parent) 107 + { 108 + return task_ptrace(parent, current); 109 + } 110 + 111 + static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 112 + LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), 113 + LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), 114 + }; 115 + 116 + __init void landlock_add_ptrace_hooks(void) 117 + { 118 + security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 119 + LANDLOCK_NAME); 120 + }
+14
security/landlock/ptrace.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Ptrace hooks 4 + * 5 + * Copyright © 2017-2019 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_PTRACE_H 10 + #define _SECURITY_LANDLOCK_PTRACE_H 11 + 12 + __init void landlock_add_ptrace_hooks(void); 13 + 14 + #endif /* _SECURITY_LANDLOCK_PTRACE_H */
+473
security/landlock/ruleset.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Ruleset management 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <linux/bits.h> 10 + #include <linux/bug.h> 11 + #include <linux/compiler_types.h> 12 + #include <linux/err.h> 13 + #include <linux/errno.h> 14 + #include <linux/kernel.h> 15 + #include <linux/lockdep.h> 16 + #include <linux/overflow.h> 17 + #include <linux/rbtree.h> 18 + #include <linux/refcount.h> 19 + #include <linux/slab.h> 20 + #include <linux/spinlock.h> 21 + #include <linux/workqueue.h> 22 + 23 + #include "limits.h" 24 + #include "object.h" 25 + #include "ruleset.h" 26 + 27 + static struct landlock_ruleset *create_ruleset(const u32 num_layers) 28 + { 29 + struct landlock_ruleset *new_ruleset; 30 + 31 + new_ruleset = kzalloc(struct_size(new_ruleset, fs_access_masks, 32 + num_layers), GFP_KERNEL_ACCOUNT); 33 + if (!new_ruleset) 34 + return ERR_PTR(-ENOMEM); 35 + refcount_set(&new_ruleset->usage, 1); 36 + mutex_init(&new_ruleset->lock); 37 + new_ruleset->root = RB_ROOT; 38 + new_ruleset->num_layers = num_layers; 39 + /* 40 + * hierarchy = NULL 41 + * num_rules = 0 42 + * fs_access_masks[] = 0 43 + */ 44 + return new_ruleset; 45 + } 46 + 47 + struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask) 48 + { 49 + struct landlock_ruleset *new_ruleset; 50 + 51 + /* Informs about useless ruleset. */ 52 + if (!fs_access_mask) 53 + return ERR_PTR(-ENOMSG); 54 + new_ruleset = create_ruleset(1); 55 + if (!IS_ERR(new_ruleset)) 56 + new_ruleset->fs_access_masks[0] = fs_access_mask; 57 + return new_ruleset; 58 + } 59 + 60 + static void build_check_rule(void) 61 + { 62 + const struct landlock_rule rule = { 63 + .num_layers = ~0, 64 + }; 65 + 66 + BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS); 67 + } 68 + 69 + static struct landlock_rule *create_rule( 70 + struct landlock_object *const object, 71 + const struct landlock_layer (*const layers)[], 72 + const u32 num_layers, 73 + const struct landlock_layer *const new_layer) 74 + { 75 + struct landlock_rule *new_rule; 76 + u32 new_num_layers; 77 + 78 + build_check_rule(); 79 + if (new_layer) { 80 + /* Should already be checked by landlock_merge_ruleset(). */ 81 + if (WARN_ON_ONCE(num_layers >= LANDLOCK_MAX_NUM_LAYERS)) 82 + return ERR_PTR(-E2BIG); 83 + new_num_layers = num_layers + 1; 84 + } else { 85 + new_num_layers = num_layers; 86 + } 87 + new_rule = kzalloc(struct_size(new_rule, layers, new_num_layers), 88 + GFP_KERNEL_ACCOUNT); 89 + if (!new_rule) 90 + return ERR_PTR(-ENOMEM); 91 + RB_CLEAR_NODE(&new_rule->node); 92 + landlock_get_object(object); 93 + new_rule->object = object; 94 + new_rule->num_layers = new_num_layers; 95 + /* Copies the original layer stack. */ 96 + memcpy(new_rule->layers, layers, 97 + flex_array_size(new_rule, layers, num_layers)); 98 + if (new_layer) 99 + /* Adds a copy of @new_layer on the layer stack. */ 100 + new_rule->layers[new_rule->num_layers - 1] = *new_layer; 101 + return new_rule; 102 + } 103 + 104 + static void free_rule(struct landlock_rule *const rule) 105 + { 106 + might_sleep(); 107 + if (!rule) 108 + return; 109 + landlock_put_object(rule->object); 110 + kfree(rule); 111 + } 112 + 113 + static void build_check_ruleset(void) 114 + { 115 + const struct landlock_ruleset ruleset = { 116 + .num_rules = ~0, 117 + .num_layers = ~0, 118 + }; 119 + typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0; 120 + 121 + BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES); 122 + BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS); 123 + BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS); 124 + } 125 + 126 + /** 127 + * insert_rule - Create and insert a rule in a ruleset 128 + * 129 + * @ruleset: The ruleset to be updated. 130 + * @object: The object to build the new rule with. The underlying kernel 131 + * object must be held by the caller. 132 + * @layers: One or multiple layers to be copied into the new rule. 133 + * @num_layers: The number of @layers entries. 134 + * 135 + * When user space requests to add a new rule to a ruleset, @layers only 136 + * contains one entry and this entry is not assigned to any level. In this 137 + * case, the new rule will extend @ruleset, similarly to a boolean OR between 138 + * access rights. 139 + * 140 + * When merging a ruleset in a domain, or copying a domain, @layers will be 141 + * added to @ruleset as new constraints, similarly to a boolean AND between 142 + * access rights. 143 + */ 144 + static int insert_rule(struct landlock_ruleset *const ruleset, 145 + struct landlock_object *const object, 146 + const struct landlock_layer (*const layers)[], 147 + size_t num_layers) 148 + { 149 + struct rb_node **walker_node; 150 + struct rb_node *parent_node = NULL; 151 + struct landlock_rule *new_rule; 152 + 153 + might_sleep(); 154 + lockdep_assert_held(&ruleset->lock); 155 + if (WARN_ON_ONCE(!object || !layers)) 156 + return -ENOENT; 157 + walker_node = &(ruleset->root.rb_node); 158 + while (*walker_node) { 159 + struct landlock_rule *const this = rb_entry(*walker_node, 160 + struct landlock_rule, node); 161 + 162 + if (this->object != object) { 163 + parent_node = *walker_node; 164 + if (this->object < object) 165 + walker_node = &((*walker_node)->rb_right); 166 + else 167 + walker_node = &((*walker_node)->rb_left); 168 + continue; 169 + } 170 + 171 + /* Only a single-level layer should match an existing rule. */ 172 + if (WARN_ON_ONCE(num_layers != 1)) 173 + return -EINVAL; 174 + 175 + /* If there is a matching rule, updates it. */ 176 + if ((*layers)[0].level == 0) { 177 + /* 178 + * Extends access rights when the request comes from 179 + * landlock_add_rule(2), i.e. @ruleset is not a domain. 180 + */ 181 + if (WARN_ON_ONCE(this->num_layers != 1)) 182 + return -EINVAL; 183 + if (WARN_ON_ONCE(this->layers[0].level != 0)) 184 + return -EINVAL; 185 + this->layers[0].access |= (*layers)[0].access; 186 + return 0; 187 + } 188 + 189 + if (WARN_ON_ONCE(this->layers[0].level == 0)) 190 + return -EINVAL; 191 + 192 + /* 193 + * Intersects access rights when it is a merge between a 194 + * ruleset and a domain. 195 + */ 196 + new_rule = create_rule(object, &this->layers, this->num_layers, 197 + &(*layers)[0]); 198 + if (IS_ERR(new_rule)) 199 + return PTR_ERR(new_rule); 200 + rb_replace_node(&this->node, &new_rule->node, &ruleset->root); 201 + free_rule(this); 202 + return 0; 203 + } 204 + 205 + /* There is no match for @object. */ 206 + build_check_ruleset(); 207 + if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES) 208 + return -E2BIG; 209 + new_rule = create_rule(object, layers, num_layers, NULL); 210 + if (IS_ERR(new_rule)) 211 + return PTR_ERR(new_rule); 212 + rb_link_node(&new_rule->node, parent_node, walker_node); 213 + rb_insert_color(&new_rule->node, &ruleset->root); 214 + ruleset->num_rules++; 215 + return 0; 216 + } 217 + 218 + static void build_check_layer(void) 219 + { 220 + const struct landlock_layer layer = { 221 + .level = ~0, 222 + .access = ~0, 223 + }; 224 + 225 + BUILD_BUG_ON(layer.level < LANDLOCK_MAX_NUM_LAYERS); 226 + BUILD_BUG_ON(layer.access < LANDLOCK_MASK_ACCESS_FS); 227 + } 228 + 229 + /* @ruleset must be locked by the caller. */ 230 + int landlock_insert_rule(struct landlock_ruleset *const ruleset, 231 + struct landlock_object *const object, const u32 access) 232 + { 233 + struct landlock_layer layers[] = {{ 234 + .access = access, 235 + /* When @level is zero, insert_rule() extends @ruleset. */ 236 + .level = 0, 237 + }}; 238 + 239 + build_check_layer(); 240 + return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers)); 241 + } 242 + 243 + static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy) 244 + { 245 + if (hierarchy) 246 + refcount_inc(&hierarchy->usage); 247 + } 248 + 249 + static void put_hierarchy(struct landlock_hierarchy *hierarchy) 250 + { 251 + while (hierarchy && refcount_dec_and_test(&hierarchy->usage)) { 252 + const struct landlock_hierarchy *const freeme = hierarchy; 253 + 254 + hierarchy = hierarchy->parent; 255 + kfree(freeme); 256 + } 257 + } 258 + 259 + static int merge_ruleset(struct landlock_ruleset *const dst, 260 + struct landlock_ruleset *const src) 261 + { 262 + struct landlock_rule *walker_rule, *next_rule; 263 + int err = 0; 264 + 265 + might_sleep(); 266 + /* Should already be checked by landlock_merge_ruleset() */ 267 + if (WARN_ON_ONCE(!src)) 268 + return 0; 269 + /* Only merge into a domain. */ 270 + if (WARN_ON_ONCE(!dst || !dst->hierarchy)) 271 + return -EINVAL; 272 + 273 + /* Locks @dst first because we are its only owner. */ 274 + mutex_lock(&dst->lock); 275 + mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING); 276 + 277 + /* Stacks the new layer. */ 278 + if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) { 279 + err = -EINVAL; 280 + goto out_unlock; 281 + } 282 + dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0]; 283 + 284 + /* Merges the @src tree. */ 285 + rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, 286 + &src->root, node) { 287 + struct landlock_layer layers[] = {{ 288 + .level = dst->num_layers, 289 + }}; 290 + 291 + if (WARN_ON_ONCE(walker_rule->num_layers != 1)) { 292 + err = -EINVAL; 293 + goto out_unlock; 294 + } 295 + if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) { 296 + err = -EINVAL; 297 + goto out_unlock; 298 + } 299 + layers[0].access = walker_rule->layers[0].access; 300 + err = insert_rule(dst, walker_rule->object, &layers, 301 + ARRAY_SIZE(layers)); 302 + if (err) 303 + goto out_unlock; 304 + } 305 + 306 + out_unlock: 307 + mutex_unlock(&src->lock); 308 + mutex_unlock(&dst->lock); 309 + return err; 310 + } 311 + 312 + static int inherit_ruleset(struct landlock_ruleset *const parent, 313 + struct landlock_ruleset *const child) 314 + { 315 + struct landlock_rule *walker_rule, *next_rule; 316 + int err = 0; 317 + 318 + might_sleep(); 319 + if (!parent) 320 + return 0; 321 + 322 + /* Locks @child first because we are its only owner. */ 323 + mutex_lock(&child->lock); 324 + mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING); 325 + 326 + /* Copies the @parent tree. */ 327 + rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, 328 + &parent->root, node) { 329 + err = insert_rule(child, walker_rule->object, 330 + &walker_rule->layers, walker_rule->num_layers); 331 + if (err) 332 + goto out_unlock; 333 + } 334 + 335 + if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) { 336 + err = -EINVAL; 337 + goto out_unlock; 338 + } 339 + /* Copies the parent layer stack and leaves a space for the new layer. */ 340 + memcpy(child->fs_access_masks, parent->fs_access_masks, 341 + flex_array_size(parent, fs_access_masks, parent->num_layers)); 342 + 343 + if (WARN_ON_ONCE(!parent->hierarchy)) { 344 + err = -EINVAL; 345 + goto out_unlock; 346 + } 347 + get_hierarchy(parent->hierarchy); 348 + child->hierarchy->parent = parent->hierarchy; 349 + 350 + out_unlock: 351 + mutex_unlock(&parent->lock); 352 + mutex_unlock(&child->lock); 353 + return err; 354 + } 355 + 356 + static void free_ruleset(struct landlock_ruleset *const ruleset) 357 + { 358 + struct landlock_rule *freeme, *next; 359 + 360 + might_sleep(); 361 + rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, 362 + node) 363 + free_rule(freeme); 364 + put_hierarchy(ruleset->hierarchy); 365 + kfree(ruleset); 366 + } 367 + 368 + void landlock_put_ruleset(struct landlock_ruleset *const ruleset) 369 + { 370 + might_sleep(); 371 + if (ruleset && refcount_dec_and_test(&ruleset->usage)) 372 + free_ruleset(ruleset); 373 + } 374 + 375 + static void free_ruleset_work(struct work_struct *const work) 376 + { 377 + struct landlock_ruleset *ruleset; 378 + 379 + ruleset = container_of(work, struct landlock_ruleset, work_free); 380 + free_ruleset(ruleset); 381 + } 382 + 383 + void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset) 384 + { 385 + if (ruleset && refcount_dec_and_test(&ruleset->usage)) { 386 + INIT_WORK(&ruleset->work_free, free_ruleset_work); 387 + schedule_work(&ruleset->work_free); 388 + } 389 + } 390 + 391 + /** 392 + * landlock_merge_ruleset - Merge a ruleset with a domain 393 + * 394 + * @parent: Parent domain. 395 + * @ruleset: New ruleset to be merged. 396 + * 397 + * Returns the intersection of @parent and @ruleset, or returns @parent if 398 + * @ruleset is empty, or returns a duplicate of @ruleset if @parent is empty. 399 + */ 400 + struct landlock_ruleset *landlock_merge_ruleset( 401 + struct landlock_ruleset *const parent, 402 + struct landlock_ruleset *const ruleset) 403 + { 404 + struct landlock_ruleset *new_dom; 405 + u32 num_layers; 406 + int err; 407 + 408 + might_sleep(); 409 + if (WARN_ON_ONCE(!ruleset || parent == ruleset)) 410 + return ERR_PTR(-EINVAL); 411 + 412 + if (parent) { 413 + if (parent->num_layers >= LANDLOCK_MAX_NUM_LAYERS) 414 + return ERR_PTR(-E2BIG); 415 + num_layers = parent->num_layers + 1; 416 + } else { 417 + num_layers = 1; 418 + } 419 + 420 + /* Creates a new domain... */ 421 + new_dom = create_ruleset(num_layers); 422 + if (IS_ERR(new_dom)) 423 + return new_dom; 424 + new_dom->hierarchy = kzalloc(sizeof(*new_dom->hierarchy), 425 + GFP_KERNEL_ACCOUNT); 426 + if (!new_dom->hierarchy) { 427 + err = -ENOMEM; 428 + goto out_put_dom; 429 + } 430 + refcount_set(&new_dom->hierarchy->usage, 1); 431 + 432 + /* ...as a child of @parent... */ 433 + err = inherit_ruleset(parent, new_dom); 434 + if (err) 435 + goto out_put_dom; 436 + 437 + /* ...and including @ruleset. */ 438 + err = merge_ruleset(new_dom, ruleset); 439 + if (err) 440 + goto out_put_dom; 441 + 442 + return new_dom; 443 + 444 + out_put_dom: 445 + landlock_put_ruleset(new_dom); 446 + return ERR_PTR(err); 447 + } 448 + 449 + /* 450 + * The returned access has the same lifetime as @ruleset. 451 + */ 452 + const struct landlock_rule *landlock_find_rule( 453 + const struct landlock_ruleset *const ruleset, 454 + const struct landlock_object *const object) 455 + { 456 + const struct rb_node *node; 457 + 458 + if (!object) 459 + return NULL; 460 + node = ruleset->root.rb_node; 461 + while (node) { 462 + struct landlock_rule *this = rb_entry(node, 463 + struct landlock_rule, node); 464 + 465 + if (this->object == object) 466 + return this; 467 + if (this->object < object) 468 + node = node->rb_right; 469 + else 470 + node = node->rb_left; 471 + } 472 + return NULL; 473 + }
+165
security/landlock/ruleset.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Ruleset management 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_RULESET_H 10 + #define _SECURITY_LANDLOCK_RULESET_H 11 + 12 + #include <linux/mutex.h> 13 + #include <linux/rbtree.h> 14 + #include <linux/refcount.h> 15 + #include <linux/workqueue.h> 16 + 17 + #include "object.h" 18 + 19 + /** 20 + * struct landlock_layer - Access rights for a given layer 21 + */ 22 + struct landlock_layer { 23 + /** 24 + * @level: Position of this layer in the layer stack. 25 + */ 26 + u16 level; 27 + /** 28 + * @access: Bitfield of allowed actions on the kernel object. They are 29 + * relative to the object type (e.g. %LANDLOCK_ACTION_FS_READ). 30 + */ 31 + u16 access; 32 + }; 33 + 34 + /** 35 + * struct landlock_rule - Access rights tied to an object 36 + */ 37 + struct landlock_rule { 38 + /** 39 + * @node: Node in the ruleset's red-black tree. 40 + */ 41 + struct rb_node node; 42 + /** 43 + * @object: Pointer to identify a kernel object (e.g. an inode). This 44 + * is used as a key for this ruleset element. This pointer is set once 45 + * and never modified. It always points to an allocated object because 46 + * each rule increments the refcount of its object. 47 + */ 48 + struct landlock_object *object; 49 + /** 50 + * @num_layers: Number of entries in @layers. 51 + */ 52 + u32 num_layers; 53 + /** 54 + * @layers: Stack of layers, from the latest to the newest, implemented 55 + * as a flexible array member (FAM). 56 + */ 57 + struct landlock_layer layers[]; 58 + }; 59 + 60 + /** 61 + * struct landlock_hierarchy - Node in a ruleset hierarchy 62 + */ 63 + struct landlock_hierarchy { 64 + /** 65 + * @parent: Pointer to the parent node, or NULL if it is a root 66 + * Landlock domain. 67 + */ 68 + struct landlock_hierarchy *parent; 69 + /** 70 + * @usage: Number of potential children domains plus their parent 71 + * domain. 72 + */ 73 + refcount_t usage; 74 + }; 75 + 76 + /** 77 + * struct landlock_ruleset - Landlock ruleset 78 + * 79 + * This data structure must contain unique entries, be updatable, and quick to 80 + * match an object. 81 + */ 82 + struct landlock_ruleset { 83 + /** 84 + * @root: Root of a red-black tree containing &struct landlock_rule 85 + * nodes. Once a ruleset is tied to a process (i.e. as a domain), this 86 + * tree is immutable until @usage reaches zero. 87 + */ 88 + struct rb_root root; 89 + /** 90 + * @hierarchy: Enables hierarchy identification even when a parent 91 + * domain vanishes. This is needed for the ptrace protection. 92 + */ 93 + struct landlock_hierarchy *hierarchy; 94 + union { 95 + /** 96 + * @work_free: Enables to free a ruleset within a lockless 97 + * section. This is only used by 98 + * landlock_put_ruleset_deferred() when @usage reaches zero. 99 + * The fields @lock, @usage, @num_rules, @num_layers and 100 + * @fs_access_masks are then unused. 101 + */ 102 + struct work_struct work_free; 103 + struct { 104 + /** 105 + * @lock: Protects against concurrent modifications of 106 + * @root, if @usage is greater than zero. 107 + */ 108 + struct mutex lock; 109 + /** 110 + * @usage: Number of processes (i.e. domains) or file 111 + * descriptors referencing this ruleset. 112 + */ 113 + refcount_t usage; 114 + /** 115 + * @num_rules: Number of non-overlapping (i.e. not for 116 + * the same object) rules in this ruleset. 117 + */ 118 + u32 num_rules; 119 + /** 120 + * @num_layers: Number of layers that are used in this 121 + * ruleset. This enables to check that all the layers 122 + * allow an access request. A value of 0 identifies a 123 + * non-merged ruleset (i.e. not a domain). 124 + */ 125 + u32 num_layers; 126 + /** 127 + * @fs_access_masks: Contains the subset of filesystem 128 + * actions that are restricted by a ruleset. A domain 129 + * saves all layers of merged rulesets in a stack 130 + * (FAM), starting from the first layer to the last 131 + * one. These layers are used when merging rulesets, 132 + * for user space backward compatibility (i.e. 133 + * future-proof), and to properly handle merged 134 + * rulesets without overlapping access rights. These 135 + * layers are set once and never changed for the 136 + * lifetime of the ruleset. 137 + */ 138 + u16 fs_access_masks[]; 139 + }; 140 + }; 141 + }; 142 + 143 + struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask); 144 + 145 + void landlock_put_ruleset(struct landlock_ruleset *const ruleset); 146 + void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); 147 + 148 + int landlock_insert_rule(struct landlock_ruleset *const ruleset, 149 + struct landlock_object *const object, const u32 access); 150 + 151 + struct landlock_ruleset *landlock_merge_ruleset( 152 + struct landlock_ruleset *const parent, 153 + struct landlock_ruleset *const ruleset); 154 + 155 + const struct landlock_rule *landlock_find_rule( 156 + const struct landlock_ruleset *const ruleset, 157 + const struct landlock_object *const object); 158 + 159 + static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset) 160 + { 161 + if (ruleset) 162 + refcount_inc(&ruleset->usage); 163 + } 164 + 165 + #endif /* _SECURITY_LANDLOCK_RULESET_H */
+40
security/landlock/setup.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Security framework setup 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <linux/init.h> 10 + #include <linux/lsm_hooks.h> 11 + 12 + #include "common.h" 13 + #include "cred.h" 14 + #include "fs.h" 15 + #include "ptrace.h" 16 + #include "setup.h" 17 + 18 + bool landlock_initialized __lsm_ro_after_init = false; 19 + 20 + struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = { 21 + .lbs_cred = sizeof(struct landlock_cred_security), 22 + .lbs_inode = sizeof(struct landlock_inode_security), 23 + .lbs_superblock = sizeof(struct landlock_superblock_security), 24 + }; 25 + 26 + static int __init landlock_init(void) 27 + { 28 + landlock_add_cred_hooks(); 29 + landlock_add_ptrace_hooks(); 30 + landlock_add_fs_hooks(); 31 + landlock_initialized = true; 32 + pr_info("Up and running.\n"); 33 + return 0; 34 + } 35 + 36 + DEFINE_LSM(LANDLOCK_NAME) = { 37 + .name = LANDLOCK_NAME, 38 + .init = landlock_init, 39 + .blobs = &landlock_blob_sizes, 40 + };
+18
security/landlock/setup.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Security framework setup 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_SETUP_H 10 + #define _SECURITY_LANDLOCK_SETUP_H 11 + 12 + #include <linux/lsm_hooks.h> 13 + 14 + extern bool landlock_initialized; 15 + 16 + extern struct lsm_blob_sizes landlock_blob_sizes; 17 + 18 + #endif /* _SECURITY_LANDLOCK_SETUP_H */
+451
security/landlock/syscalls.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - System call implementations and user space interfaces 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + */ 8 + 9 + #include <asm/current.h> 10 + #include <linux/anon_inodes.h> 11 + #include <linux/build_bug.h> 12 + #include <linux/capability.h> 13 + #include <linux/compiler_types.h> 14 + #include <linux/dcache.h> 15 + #include <linux/err.h> 16 + #include <linux/errno.h> 17 + #include <linux/fs.h> 18 + #include <linux/limits.h> 19 + #include <linux/mount.h> 20 + #include <linux/path.h> 21 + #include <linux/sched.h> 22 + #include <linux/security.h> 23 + #include <linux/stddef.h> 24 + #include <linux/syscalls.h> 25 + #include <linux/types.h> 26 + #include <linux/uaccess.h> 27 + #include <uapi/linux/landlock.h> 28 + 29 + #include "cred.h" 30 + #include "fs.h" 31 + #include "limits.h" 32 + #include "ruleset.h" 33 + #include "setup.h" 34 + 35 + /** 36 + * copy_min_struct_from_user - Safe future-proof argument copying 37 + * 38 + * Extend copy_struct_from_user() to check for consistent user buffer. 39 + * 40 + * @dst: Kernel space pointer or NULL. 41 + * @ksize: Actual size of the data pointed to by @dst. 42 + * @ksize_min: Minimal required size to be copied. 43 + * @src: User space pointer or NULL. 44 + * @usize: (Alleged) size of the data pointed to by @src. 45 + */ 46 + static __always_inline int copy_min_struct_from_user(void *const dst, 47 + const size_t ksize, const size_t ksize_min, 48 + const void __user *const src, const size_t usize) 49 + { 50 + /* Checks buffer inconsistencies. */ 51 + BUILD_BUG_ON(!dst); 52 + if (!src) 53 + return -EFAULT; 54 + 55 + /* Checks size ranges. */ 56 + BUILD_BUG_ON(ksize <= 0); 57 + BUILD_BUG_ON(ksize < ksize_min); 58 + if (usize < ksize_min) 59 + return -EINVAL; 60 + if (usize > PAGE_SIZE) 61 + return -E2BIG; 62 + 63 + /* Copies user buffer and fills with zeros. */ 64 + return copy_struct_from_user(dst, ksize, src, usize); 65 + } 66 + 67 + /* 68 + * This function only contains arithmetic operations with constants, leading to 69 + * BUILD_BUG_ON(). The related code is evaluated and checked at build time, 70 + * but it is then ignored thanks to compiler optimizations. 71 + */ 72 + static void build_check_abi(void) 73 + { 74 + struct landlock_ruleset_attr ruleset_attr; 75 + struct landlock_path_beneath_attr path_beneath_attr; 76 + size_t ruleset_size, path_beneath_size; 77 + 78 + /* 79 + * For each user space ABI structures, first checks that there is no 80 + * hole in them, then checks that all architectures have the same 81 + * struct size. 82 + */ 83 + ruleset_size = sizeof(ruleset_attr.handled_access_fs); 84 + BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size); 85 + BUILD_BUG_ON(sizeof(ruleset_attr) != 8); 86 + 87 + path_beneath_size = sizeof(path_beneath_attr.allowed_access); 88 + path_beneath_size += sizeof(path_beneath_attr.parent_fd); 89 + BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size); 90 + BUILD_BUG_ON(sizeof(path_beneath_attr) != 12); 91 + } 92 + 93 + /* Ruleset handling */ 94 + 95 + static int fop_ruleset_release(struct inode *const inode, 96 + struct file *const filp) 97 + { 98 + struct landlock_ruleset *ruleset = filp->private_data; 99 + 100 + landlock_put_ruleset(ruleset); 101 + return 0; 102 + } 103 + 104 + static ssize_t fop_dummy_read(struct file *const filp, char __user *const buf, 105 + const size_t size, loff_t *const ppos) 106 + { 107 + /* Dummy handler to enable FMODE_CAN_READ. */ 108 + return -EINVAL; 109 + } 110 + 111 + static ssize_t fop_dummy_write(struct file *const filp, 112 + const char __user *const buf, const size_t size, 113 + loff_t *const ppos) 114 + { 115 + /* Dummy handler to enable FMODE_CAN_WRITE. */ 116 + return -EINVAL; 117 + } 118 + 119 + /* 120 + * A ruleset file descriptor enables to build a ruleset by adding (i.e. 121 + * writing) rule after rule, without relying on the task's context. This 122 + * reentrant design is also used in a read way to enforce the ruleset on the 123 + * current task. 124 + */ 125 + static const struct file_operations ruleset_fops = { 126 + .release = fop_ruleset_release, 127 + .read = fop_dummy_read, 128 + .write = fop_dummy_write, 129 + }; 130 + 131 + #define LANDLOCK_ABI_VERSION 1 132 + 133 + /** 134 + * sys_landlock_create_ruleset - Create a new ruleset 135 + * 136 + * @attr: Pointer to a &struct landlock_ruleset_attr identifying the scope of 137 + * the new ruleset. 138 + * @size: Size of the pointed &struct landlock_ruleset_attr (needed for 139 + * backward and forward compatibility). 140 + * @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION. 141 + * 142 + * This system call enables to create a new Landlock ruleset, and returns the 143 + * related file descriptor on success. 144 + * 145 + * If @flags is %LANDLOCK_CREATE_RULESET_VERSION and @attr is NULL and @size is 146 + * 0, then the returned value is the highest supported Landlock ABI version 147 + * (starting at 1). 148 + * 149 + * Possible returned errors are: 150 + * 151 + * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 152 + * - EINVAL: unknown @flags, or unknown access, or too small @size; 153 + * - E2BIG or EFAULT: @attr or @size inconsistencies; 154 + * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. 155 + */ 156 + SYSCALL_DEFINE3(landlock_create_ruleset, 157 + const struct landlock_ruleset_attr __user *const, attr, 158 + const size_t, size, const __u32, flags) 159 + { 160 + struct landlock_ruleset_attr ruleset_attr; 161 + struct landlock_ruleset *ruleset; 162 + int err, ruleset_fd; 163 + 164 + /* Build-time checks. */ 165 + build_check_abi(); 166 + 167 + if (!landlock_initialized) 168 + return -EOPNOTSUPP; 169 + 170 + if (flags) { 171 + if ((flags == LANDLOCK_CREATE_RULESET_VERSION) 172 + && !attr && !size) 173 + return LANDLOCK_ABI_VERSION; 174 + return -EINVAL; 175 + } 176 + 177 + /* Copies raw user space buffer. */ 178 + err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr), 179 + offsetofend(typeof(ruleset_attr), handled_access_fs), 180 + attr, size); 181 + if (err) 182 + return err; 183 + 184 + /* Checks content (and 32-bits cast). */ 185 + if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) != 186 + LANDLOCK_MASK_ACCESS_FS) 187 + return -EINVAL; 188 + 189 + /* Checks arguments and transforms to kernel struct. */ 190 + ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs); 191 + if (IS_ERR(ruleset)) 192 + return PTR_ERR(ruleset); 193 + 194 + /* Creates anonymous FD referring to the ruleset. */ 195 + ruleset_fd = anon_inode_getfd("landlock-ruleset", &ruleset_fops, 196 + ruleset, O_RDWR | O_CLOEXEC); 197 + if (ruleset_fd < 0) 198 + landlock_put_ruleset(ruleset); 199 + return ruleset_fd; 200 + } 201 + 202 + /* 203 + * Returns an owned ruleset from a FD. It is thus needed to call 204 + * landlock_put_ruleset() on the return value. 205 + */ 206 + static struct landlock_ruleset *get_ruleset_from_fd(const int fd, 207 + const fmode_t mode) 208 + { 209 + struct fd ruleset_f; 210 + struct landlock_ruleset *ruleset; 211 + 212 + ruleset_f = fdget(fd); 213 + if (!ruleset_f.file) 214 + return ERR_PTR(-EBADF); 215 + 216 + /* Checks FD type and access right. */ 217 + if (ruleset_f.file->f_op != &ruleset_fops) { 218 + ruleset = ERR_PTR(-EBADFD); 219 + goto out_fdput; 220 + } 221 + if (!(ruleset_f.file->f_mode & mode)) { 222 + ruleset = ERR_PTR(-EPERM); 223 + goto out_fdput; 224 + } 225 + ruleset = ruleset_f.file->private_data; 226 + if (WARN_ON_ONCE(ruleset->num_layers != 1)) { 227 + ruleset = ERR_PTR(-EINVAL); 228 + goto out_fdput; 229 + } 230 + landlock_get_ruleset(ruleset); 231 + 232 + out_fdput: 233 + fdput(ruleset_f); 234 + return ruleset; 235 + } 236 + 237 + /* Path handling */ 238 + 239 + /* 240 + * @path: Must call put_path(@path) after the call if it succeeded. 241 + */ 242 + static int get_path_from_fd(const s32 fd, struct path *const path) 243 + { 244 + struct fd f; 245 + int err = 0; 246 + 247 + BUILD_BUG_ON(!__same_type(fd, 248 + ((struct landlock_path_beneath_attr *)NULL)->parent_fd)); 249 + 250 + /* Handles O_PATH. */ 251 + f = fdget_raw(fd); 252 + if (!f.file) 253 + return -EBADF; 254 + /* 255 + * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including 256 + * pseudo filesystems that will never be mountable (e.g. sockfs, 257 + * pipefs). 258 + */ 259 + if ((f.file->f_op == &ruleset_fops) || 260 + (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) || 261 + (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) || 262 + d_is_negative(f.file->f_path.dentry) || 263 + IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) { 264 + err = -EBADFD; 265 + goto out_fdput; 266 + } 267 + *path = f.file->f_path; 268 + path_get(path); 269 + 270 + out_fdput: 271 + fdput(f); 272 + return err; 273 + } 274 + 275 + /** 276 + * sys_landlock_add_rule - Add a new rule to a ruleset 277 + * 278 + * @ruleset_fd: File descriptor tied to the ruleset that should be extended 279 + * with the new rule. 280 + * @rule_type: Identify the structure type pointed to by @rule_attr (only 281 + * LANDLOCK_RULE_PATH_BENEATH for now). 282 + * @rule_attr: Pointer to a rule (only of type &struct 283 + * landlock_path_beneath_attr for now). 284 + * @flags: Must be 0. 285 + * 286 + * This system call enables to define a new rule and add it to an existing 287 + * ruleset. 288 + * 289 + * Possible returned errors are: 290 + * 291 + * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 292 + * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e. 293 + * &landlock_path_beneath_attr.allowed_access is not a subset of the rule's 294 + * accesses); 295 + * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access); 296 + * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a 297 + * member of @rule_attr is not a file descriptor as expected; 298 + * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of 299 + * @rule_attr is not the expected file descriptor type (e.g. file open 300 + * without O_PATH); 301 + * - EPERM: @ruleset_fd has no write access to the underlying ruleset; 302 + * - EFAULT: @rule_attr inconsistency. 303 + */ 304 + SYSCALL_DEFINE4(landlock_add_rule, 305 + const int, ruleset_fd, const enum landlock_rule_type, rule_type, 306 + const void __user *const, rule_attr, const __u32, flags) 307 + { 308 + struct landlock_path_beneath_attr path_beneath_attr; 309 + struct path path; 310 + struct landlock_ruleset *ruleset; 311 + int res, err; 312 + 313 + if (!landlock_initialized) 314 + return -EOPNOTSUPP; 315 + 316 + /* No flag for now. */ 317 + if (flags) 318 + return -EINVAL; 319 + 320 + if (rule_type != LANDLOCK_RULE_PATH_BENEATH) 321 + return -EINVAL; 322 + 323 + /* Copies raw user space buffer, only one type for now. */ 324 + res = copy_from_user(&path_beneath_attr, rule_attr, 325 + sizeof(path_beneath_attr)); 326 + if (res) 327 + return -EFAULT; 328 + 329 + /* Gets and checks the ruleset. */ 330 + ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE); 331 + if (IS_ERR(ruleset)) 332 + return PTR_ERR(ruleset); 333 + 334 + /* 335 + * Informs about useless rule: empty allowed_access (i.e. deny rules) 336 + * are ignored in path walks. 337 + */ 338 + if (!path_beneath_attr.allowed_access) { 339 + err = -ENOMSG; 340 + goto out_put_ruleset; 341 + } 342 + /* 343 + * Checks that allowed_access matches the @ruleset constraints 344 + * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits). 345 + */ 346 + if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) != 347 + ruleset->fs_access_masks[0]) { 348 + err = -EINVAL; 349 + goto out_put_ruleset; 350 + } 351 + 352 + /* Gets and checks the new rule. */ 353 + err = get_path_from_fd(path_beneath_attr.parent_fd, &path); 354 + if (err) 355 + goto out_put_ruleset; 356 + 357 + /* Imports the new rule. */ 358 + err = landlock_append_fs_rule(ruleset, &path, 359 + path_beneath_attr.allowed_access); 360 + path_put(&path); 361 + 362 + out_put_ruleset: 363 + landlock_put_ruleset(ruleset); 364 + return err; 365 + } 366 + 367 + /* Enforcement */ 368 + 369 + /** 370 + * sys_landlock_restrict_self - Enforce a ruleset on the calling thread 371 + * 372 + * @ruleset_fd: File descriptor tied to the ruleset to merge with the target. 373 + * @flags: Must be 0. 374 + * 375 + * This system call enables to enforce a Landlock ruleset on the current 376 + * thread. Enforcing a ruleset requires that the task has CAP_SYS_ADMIN in its 377 + * namespace or is running with no_new_privs. This avoids scenarios where 378 + * unprivileged tasks can affect the behavior of privileged children. 379 + * 380 + * Possible returned errors are: 381 + * 382 + * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 383 + * - EINVAL: @flags is not 0. 384 + * - EBADF: @ruleset_fd is not a file descriptor for the current thread; 385 + * - EBADFD: @ruleset_fd is not a ruleset file descriptor; 386 + * - EPERM: @ruleset_fd has no read access to the underlying ruleset, or the 387 + * current thread is not running with no_new_privs, or it doesn't have 388 + * CAP_SYS_ADMIN in its namespace. 389 + * - E2BIG: The maximum number of stacked rulesets is reached for the current 390 + * thread. 391 + */ 392 + SYSCALL_DEFINE2(landlock_restrict_self, 393 + const int, ruleset_fd, const __u32, flags) 394 + { 395 + struct landlock_ruleset *new_dom, *ruleset; 396 + struct cred *new_cred; 397 + struct landlock_cred_security *new_llcred; 398 + int err; 399 + 400 + if (!landlock_initialized) 401 + return -EOPNOTSUPP; 402 + 403 + /* No flag for now. */ 404 + if (flags) 405 + return -EINVAL; 406 + 407 + /* 408 + * Similar checks as for seccomp(2), except that an -EPERM may be 409 + * returned. 410 + */ 411 + if (!task_no_new_privs(current) && 412 + !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) 413 + return -EPERM; 414 + 415 + /* Gets and checks the ruleset. */ 416 + ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); 417 + if (IS_ERR(ruleset)) 418 + return PTR_ERR(ruleset); 419 + 420 + /* Prepares new credentials. */ 421 + new_cred = prepare_creds(); 422 + if (!new_cred) { 423 + err = -ENOMEM; 424 + goto out_put_ruleset; 425 + } 426 + new_llcred = landlock_cred(new_cred); 427 + 428 + /* 429 + * There is no possible race condition while copying and manipulating 430 + * the current credentials because they are dedicated per thread. 431 + */ 432 + new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset); 433 + if (IS_ERR(new_dom)) { 434 + err = PTR_ERR(new_dom); 435 + goto out_put_creds; 436 + } 437 + 438 + /* Replaces the old (prepared) domain. */ 439 + landlock_put_ruleset(new_llcred->domain); 440 + new_llcred->domain = new_dom; 441 + 442 + landlock_put_ruleset(ruleset); 443 + return commit_creds(new_cred); 444 + 445 + out_put_creds: 446 + abort_creds(new_cred); 447 + 448 + out_put_ruleset: 449 + landlock_put_ruleset(ruleset); 450 + return err; 451 + }
+44 -7
security/security.c
··· 203 203 lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); 204 204 lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc); 205 205 lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg); 206 + lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock); 206 207 lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); 207 208 } 208 209 ··· 334 333 for (lsm = ordered_lsms; *lsm; lsm++) 335 334 prepare_lsm(*lsm); 336 335 337 - init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); 338 - init_debug("file blob size = %d\n", blob_sizes.lbs_file); 339 - init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); 340 - init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); 341 - init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); 342 - init_debug("task blob size = %d\n", blob_sizes.lbs_task); 336 + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); 337 + init_debug("file blob size = %d\n", blob_sizes.lbs_file); 338 + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); 339 + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); 340 + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); 341 + init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); 342 + init_debug("task blob size = %d\n", blob_sizes.lbs_task); 343 343 344 344 /* 345 345 * Create any kmem_caches needed for blobs ··· 672 670 panic("%s: Early task alloc failed.\n", __func__); 673 671 } 674 672 673 + /** 674 + * lsm_superblock_alloc - allocate a composite superblock blob 675 + * @sb: the superblock that needs a blob 676 + * 677 + * Allocate the superblock blob for all the modules 678 + * 679 + * Returns 0, or -ENOMEM if memory can't be allocated. 680 + */ 681 + static int lsm_superblock_alloc(struct super_block *sb) 682 + { 683 + if (blob_sizes.lbs_superblock == 0) { 684 + sb->s_security = NULL; 685 + return 0; 686 + } 687 + 688 + sb->s_security = kzalloc(blob_sizes.lbs_superblock, GFP_KERNEL); 689 + if (sb->s_security == NULL) 690 + return -ENOMEM; 691 + return 0; 692 + } 693 + 675 694 /* 676 695 * The default value of the LSM hook is defined in linux/lsm_hook_defs.h and 677 696 * can be accessed with: ··· 890 867 891 868 int security_sb_alloc(struct super_block *sb) 892 869 { 893 - return call_int_hook(sb_alloc_security, 0, sb); 870 + int rc = lsm_superblock_alloc(sb); 871 + 872 + if (unlikely(rc)) 873 + return rc; 874 + rc = call_int_hook(sb_alloc_security, 0, sb); 875 + if (unlikely(rc)) 876 + security_sb_free(sb); 877 + return rc; 878 + } 879 + 880 + void security_sb_delete(struct super_block *sb) 881 + { 882 + call_void_hook(sb_delete, sb); 894 883 } 895 884 896 885 void security_sb_free(struct super_block *sb) 897 886 { 898 887 call_void_hook(sb_free_security, sb); 888 + kfree(sb->s_security); 889 + sb->s_security = NULL; 899 890 } 900 891 901 892 void security_free_mnt_opts(void **mnt_opts)
+22 -36
security/selinux/hooks.c
··· 358 358 359 359 if (!isec) 360 360 return; 361 - sbsec = inode->i_sb->s_security; 361 + sbsec = selinux_superblock(inode->i_sb); 362 362 /* 363 363 * As not all inode security structures are in a list, we check for 364 364 * empty list outside of the lock to make sure that we won't waste ··· 374 374 list_del_init(&isec->list); 375 375 spin_unlock(&sbsec->isec_lock); 376 376 } 377 - } 378 - 379 - static void superblock_free_security(struct super_block *sb) 380 - { 381 - struct superblock_security_struct *sbsec = sb->s_security; 382 - sb->s_security = NULL; 383 - kfree(sbsec); 384 377 } 385 378 386 379 struct selinux_mnt_opts { ··· 487 494 488 495 static int selinux_is_sblabel_mnt(struct super_block *sb) 489 496 { 490 - struct superblock_security_struct *sbsec = sb->s_security; 497 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 491 498 492 499 /* 493 500 * IMPORTANT: Double-check logic in this function when adding a new ··· 564 571 565 572 static int sb_finish_set_opts(struct super_block *sb) 566 573 { 567 - struct superblock_security_struct *sbsec = sb->s_security; 574 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 568 575 struct dentry *root = sb->s_root; 569 576 struct inode *root_inode = d_backing_inode(root); 570 577 int rc = 0; ··· 655 662 unsigned long *set_kern_flags) 656 663 { 657 664 const struct cred *cred = current_cred(); 658 - struct superblock_security_struct *sbsec = sb->s_security; 665 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 659 666 struct dentry *root = sb->s_root; 660 667 struct selinux_mnt_opts *opts = mnt_opts; 661 668 struct inode_security_struct *root_isec; ··· 893 900 static int selinux_cmp_sb_context(const struct super_block *oldsb, 894 901 const struct super_block *newsb) 895 902 { 896 - struct superblock_security_struct *old = oldsb->s_security; 897 - struct superblock_security_struct *new = newsb->s_security; 903 + struct superblock_security_struct *old = selinux_superblock(oldsb); 904 + struct superblock_security_struct *new = selinux_superblock(newsb); 898 905 char oldflags = old->flags & SE_MNTMASK; 899 906 char newflags = new->flags & SE_MNTMASK; 900 907 ··· 926 933 unsigned long *set_kern_flags) 927 934 { 928 935 int rc = 0; 929 - const struct superblock_security_struct *oldsbsec = oldsb->s_security; 930 - struct superblock_security_struct *newsbsec = newsb->s_security; 936 + const struct superblock_security_struct *oldsbsec = 937 + selinux_superblock(oldsb); 938 + struct superblock_security_struct *newsbsec = selinux_superblock(newsb); 931 939 932 940 int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT); 933 941 int set_context = (oldsbsec->flags & CONTEXT_MNT); ··· 1107 1113 1108 1114 static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) 1109 1115 { 1110 - struct superblock_security_struct *sbsec = sb->s_security; 1116 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 1111 1117 int rc; 1112 1118 1113 1119 if (!(sbsec->flags & SE_SBINITIALIZED)) ··· 1458 1464 if (isec->sclass == SECCLASS_FILE) 1459 1465 isec->sclass = inode_mode_to_security_class(inode->i_mode); 1460 1466 1461 - sbsec = inode->i_sb->s_security; 1467 + sbsec = selinux_superblock(inode->i_sb); 1462 1468 if (!(sbsec->flags & SE_SBINITIALIZED)) { 1463 1469 /* Defer initialization until selinux_complete_init, 1464 1470 after the initial policy is loaded and the security ··· 1809 1815 const struct qstr *name, u16 tclass, 1810 1816 u32 *_new_isid) 1811 1817 { 1812 - const struct superblock_security_struct *sbsec = dir->i_sb->s_security; 1818 + const struct superblock_security_struct *sbsec = 1819 + selinux_superblock(dir->i_sb); 1813 1820 1814 1821 if ((sbsec->flags & SE_SBINITIALIZED) && 1815 1822 (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) { ··· 1841 1846 int rc; 1842 1847 1843 1848 dsec = inode_security(dir); 1844 - sbsec = dir->i_sb->s_security; 1849 + sbsec = selinux_superblock(dir->i_sb); 1845 1850 1846 1851 sid = tsec->sid; 1847 1852 ··· 1990 1995 struct superblock_security_struct *sbsec; 1991 1996 u32 sid = cred_sid(cred); 1992 1997 1993 - sbsec = sb->s_security; 1998 + sbsec = selinux_superblock(sb); 1994 1999 return avc_has_perm(&selinux_state, 1995 2000 sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); 1996 2001 } ··· 2612 2617 2613 2618 static int selinux_sb_alloc_security(struct super_block *sb) 2614 2619 { 2615 - struct superblock_security_struct *sbsec; 2616 - 2617 - sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL); 2618 - if (!sbsec) 2619 - return -ENOMEM; 2620 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 2620 2621 2621 2622 mutex_init(&sbsec->lock); 2622 2623 INIT_LIST_HEAD(&sbsec->isec_head); ··· 2620 2629 sbsec->sid = SECINITSID_UNLABELED; 2621 2630 sbsec->def_sid = SECINITSID_FILE; 2622 2631 sbsec->mntpoint_sid = SECINITSID_UNLABELED; 2623 - sb->s_security = sbsec; 2624 2632 2625 2633 return 0; 2626 - } 2627 - 2628 - static void selinux_sb_free_security(struct super_block *sb) 2629 - { 2630 - superblock_free_security(sb); 2631 2634 } 2632 2635 2633 2636 static inline int opt_len(const char *s) ··· 2757 2772 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts) 2758 2773 { 2759 2774 struct selinux_mnt_opts *opts = mnt_opts; 2760 - struct superblock_security_struct *sbsec = sb->s_security; 2775 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 2761 2776 u32 sid; 2762 2777 int rc; 2763 2778 ··· 2995 3010 int rc; 2996 3011 char *context; 2997 3012 2998 - sbsec = dir->i_sb->s_security; 3013 + sbsec = selinux_superblock(dir->i_sb); 2999 3014 3000 3015 newsid = tsec->create_sid; 3001 3016 ··· 3297 3312 if (!selinux_initialized(&selinux_state)) 3298 3313 return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM); 3299 3314 3300 - sbsec = inode->i_sb->s_security; 3315 + sbsec = selinux_superblock(inode->i_sb); 3301 3316 if (!(sbsec->flags & SBLABEL_MNT)) 3302 3317 return -EOPNOTSUPP; 3303 3318 ··· 3542 3557 const void *value, size_t size, int flags) 3543 3558 { 3544 3559 struct inode_security_struct *isec = inode_security_novalidate(inode); 3545 - struct superblock_security_struct *sbsec = inode->i_sb->s_security; 3560 + struct superblock_security_struct *sbsec; 3546 3561 u32 newsid; 3547 3562 int rc; 3548 3563 3549 3564 if (strcmp(name, XATTR_SELINUX_SUFFIX)) 3550 3565 return -EOPNOTSUPP; 3551 3566 3567 + sbsec = selinux_superblock(inode->i_sb); 3552 3568 if (!(sbsec->flags & SBLABEL_MNT)) 3553 3569 return -EOPNOTSUPP; 3554 3570 ··· 7051 7065 .lbs_inode = sizeof(struct inode_security_struct), 7052 7066 .lbs_ipc = sizeof(struct ipc_security_struct), 7053 7067 .lbs_msg_msg = sizeof(struct msg_security_struct), 7068 + .lbs_superblock = sizeof(struct superblock_security_struct), 7054 7069 }; 7055 7070 7056 7071 #ifdef CONFIG_PERF_EVENTS ··· 7152 7165 LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds), 7153 7166 LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds), 7154 7167 7155 - LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security), 7156 7168 LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts), 7157 7169 LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat), 7158 7170 LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
+6
security/selinux/include/objsec.h
··· 188 188 return tsec->sid; 189 189 } 190 190 191 + static inline struct superblock_security_struct *selinux_superblock( 192 + const struct super_block *superblock) 193 + { 194 + return superblock->s_security + selinux_blob_sizes.lbs_superblock; 195 + } 196 + 191 197 #endif /* _SELINUX_OBJSEC_H_ */
+2 -1
security/selinux/ss/services.c
··· 47 47 #include <linux/sched.h> 48 48 #include <linux/audit.h> 49 49 #include <linux/vmalloc.h> 50 + #include <linux/lsm_hooks.h> 50 51 #include <net/netlabel.h> 51 52 52 53 #include "flask.h" ··· 2956 2955 struct sidtab *sidtab; 2957 2956 int rc; 2958 2957 struct ocontext *c; 2959 - struct superblock_security_struct *sbsec = sb->s_security; 2958 + struct superblock_security_struct *sbsec = selinux_superblock(sb); 2960 2959 const char *fstype = sb->s_type->name; 2961 2960 2962 2961 if (!selinux_initialized(state)) {
+6
security/smack/smack.h
··· 357 357 return ipc->security + smack_blob_sizes.lbs_ipc; 358 358 } 359 359 360 + static inline struct superblock_smack *smack_superblock( 361 + const struct super_block *superblock) 362 + { 363 + return superblock->s_security + smack_blob_sizes.lbs_superblock; 364 + } 365 + 360 366 /* 361 367 * Is the directory transmuting? 362 368 */
+9 -26
security/smack/smack_lsm.c
··· 535 535 */ 536 536 static int smack_sb_alloc_security(struct super_block *sb) 537 537 { 538 - struct superblock_smack *sbsp; 539 - 540 - sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL); 541 - 542 - if (sbsp == NULL) 543 - return -ENOMEM; 538 + struct superblock_smack *sbsp = smack_superblock(sb); 544 539 545 540 sbsp->smk_root = &smack_known_floor; 546 541 sbsp->smk_default = &smack_known_floor; ··· 544 549 /* 545 550 * SMK_SB_INITIALIZED will be zero from kzalloc. 546 551 */ 547 - sb->s_security = sbsp; 548 552 549 553 return 0; 550 - } 551 - 552 - /** 553 - * smack_sb_free_security - free a superblock blob 554 - * @sb: the superblock getting the blob 555 - * 556 - */ 557 - static void smack_sb_free_security(struct super_block *sb) 558 - { 559 - kfree(sb->s_security); 560 - sb->s_security = NULL; 561 554 } 562 555 563 556 struct smack_mnt_opts { ··· 755 772 { 756 773 struct dentry *root = sb->s_root; 757 774 struct inode *inode = d_backing_inode(root); 758 - struct superblock_smack *sp = sb->s_security; 775 + struct superblock_smack *sp = smack_superblock(sb); 759 776 struct inode_smack *isp; 760 777 struct smack_known *skp; 761 778 struct smack_mnt_opts *opts = mnt_opts; ··· 854 871 */ 855 872 static int smack_sb_statfs(struct dentry *dentry) 856 873 { 857 - struct superblock_smack *sbp = dentry->d_sb->s_security; 874 + struct superblock_smack *sbp = smack_superblock(dentry->d_sb); 858 875 int rc; 859 876 struct smk_audit_info ad; 860 877 ··· 888 905 if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) 889 906 return 0; 890 907 891 - sbsp = inode->i_sb->s_security; 908 + sbsp = smack_superblock(inode->i_sb); 892 909 if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) && 893 910 isp->smk_task != sbsp->smk_root) 894 911 return 0; ··· 1140 1157 */ 1141 1158 static int smack_inode_permission(struct inode *inode, int mask) 1142 1159 { 1143 - struct superblock_smack *sbsp = inode->i_sb->s_security; 1160 + struct superblock_smack *sbsp = smack_superblock(inode->i_sb); 1144 1161 struct smk_audit_info ad; 1145 1162 int no_block = mask & MAY_NOT_BLOCK; 1146 1163 int rc; ··· 1383 1400 */ 1384 1401 if (strcmp(name, XATTR_NAME_SMACK) == 0) { 1385 1402 struct super_block *sbp = dentry->d_sb; 1386 - struct superblock_smack *sbsp = sbp->s_security; 1403 + struct superblock_smack *sbsp = smack_superblock(sbp); 1387 1404 1388 1405 isp->smk_inode = sbsp->smk_default; 1389 1406 } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) ··· 1653 1670 isp = smack_inode(file_inode(file)); 1654 1671 if (isp->smk_mmap == NULL) 1655 1672 return 0; 1656 - sbsp = file_inode(file)->i_sb->s_security; 1673 + sbsp = smack_superblock(file_inode(file)->i_sb); 1657 1674 if (sbsp->smk_flags & SMK_SB_UNTRUSTED && 1658 1675 isp->smk_mmap != sbsp->smk_root) 1659 1676 return -EACCES; ··· 3282 3299 return; 3283 3300 3284 3301 sbp = inode->i_sb; 3285 - sbsp = sbp->s_security; 3302 + sbsp = smack_superblock(sbp); 3286 3303 /* 3287 3304 * We're going to use the superblock default label 3288 3305 * if there's no label on the file. ··· 4697 4714 .lbs_inode = sizeof(struct inode_smack), 4698 4715 .lbs_ipc = sizeof(struct smack_known *), 4699 4716 .lbs_msg_msg = sizeof(struct smack_known *), 4717 + .lbs_superblock = sizeof(struct superblock_smack), 4700 4718 }; 4701 4719 4702 4720 static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { ··· 4709 4725 LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param), 4710 4726 4711 4727 LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security), 4712 - LSM_HOOK_INIT(sb_free_security, smack_sb_free_security), 4713 4728 LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts), 4714 4729 LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts), 4715 4730 LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
+1
tools/testing/selftests/Makefile
··· 25 25 TARGETS += kcmp 26 26 TARGETS += kexec 27 27 TARGETS += kvm 28 + TARGETS += landlock 28 29 TARGETS += lib 29 30 TARGETS += livepatch 30 31 TARGETS += lkdtm
+2
tools/testing/selftests/landlock/.gitignore
··· 1 + /*_test 2 + /true
+24
tools/testing/selftests/landlock/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + CFLAGS += -Wall -O2 4 + 5 + src_test := $(wildcard *_test.c) 6 + 7 + TEST_GEN_PROGS := $(src_test:.c=) 8 + 9 + TEST_GEN_PROGS_EXTENDED := true 10 + 11 + KSFT_KHDR_INSTALL := 1 12 + OVERRIDE_TARGETS := 1 13 + include ../lib.mk 14 + 15 + khdr_dir = $(top_srcdir)/usr/include 16 + 17 + $(khdr_dir)/linux/landlock.h: khdr 18 + @: 19 + 20 + $(OUTPUT)/true: true.c 21 + $(LINK.c) $< $(LDLIBS) -o $@ -static 22 + 23 + $(OUTPUT)/%_test: %_test.c $(khdr_dir)/linux/landlock.h ../kselftest_harness.h common.h 24 + $(LINK.c) $< $(LDLIBS) -o $@ -lcap -I$(khdr_dir)
+266
tools/testing/selftests/landlock/base_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Common user space base 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #define _GNU_SOURCE 10 + #include <errno.h> 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <string.h> 14 + #include <sys/prctl.h> 15 + #include <sys/socket.h> 16 + #include <sys/types.h> 17 + 18 + #include "common.h" 19 + 20 + #ifndef O_PATH 21 + #define O_PATH 010000000 22 + #endif 23 + 24 + TEST(inconsistent_attr) { 25 + const long page_size = sysconf(_SC_PAGESIZE); 26 + char *const buf = malloc(page_size + 1); 27 + struct landlock_ruleset_attr *const ruleset_attr = (void *)buf; 28 + 29 + ASSERT_NE(NULL, buf); 30 + 31 + /* Checks copy_from_user(). */ 32 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 0, 0)); 33 + /* The size if less than sizeof(struct landlock_attr_enforce). */ 34 + ASSERT_EQ(EINVAL, errno); 35 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0)); 36 + ASSERT_EQ(EINVAL, errno); 37 + 38 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0)); 39 + /* The size if less than sizeof(struct landlock_attr_enforce). */ 40 + ASSERT_EQ(EFAULT, errno); 41 + 42 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 43 + sizeof(struct landlock_ruleset_attr), 0)); 44 + ASSERT_EQ(EFAULT, errno); 45 + 46 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0)); 47 + ASSERT_EQ(E2BIG, errno); 48 + 49 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 50 + sizeof(struct landlock_ruleset_attr), 0)); 51 + ASSERT_EQ(ENOMSG, errno); 52 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0)); 53 + ASSERT_EQ(ENOMSG, errno); 54 + 55 + /* Checks non-zero value. */ 56 + buf[page_size - 2] = '.'; 57 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0)); 58 + ASSERT_EQ(E2BIG, errno); 59 + 60 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0)); 61 + ASSERT_EQ(E2BIG, errno); 62 + 63 + free(buf); 64 + } 65 + 66 + TEST(abi_version) { 67 + const struct landlock_ruleset_attr ruleset_attr = { 68 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 69 + }; 70 + ASSERT_EQ(1, landlock_create_ruleset(NULL, 0, 71 + LANDLOCK_CREATE_RULESET_VERSION)); 72 + 73 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, 74 + LANDLOCK_CREATE_RULESET_VERSION)); 75 + ASSERT_EQ(EINVAL, errno); 76 + 77 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr), 78 + LANDLOCK_CREATE_RULESET_VERSION)); 79 + ASSERT_EQ(EINVAL, errno); 80 + 81 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 82 + sizeof(ruleset_attr), 83 + LANDLOCK_CREATE_RULESET_VERSION)); 84 + ASSERT_EQ(EINVAL, errno); 85 + 86 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, 87 + LANDLOCK_CREATE_RULESET_VERSION | 1 << 31)); 88 + ASSERT_EQ(EINVAL, errno); 89 + } 90 + 91 + TEST(inval_create_ruleset_flags) { 92 + const int last_flag = LANDLOCK_CREATE_RULESET_VERSION; 93 + const int invalid_flag = last_flag << 1; 94 + const struct landlock_ruleset_attr ruleset_attr = { 95 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 96 + }; 97 + 98 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag)); 99 + ASSERT_EQ(EINVAL, errno); 100 + 101 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, invalid_flag)); 102 + ASSERT_EQ(EINVAL, errno); 103 + 104 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr), 105 + invalid_flag)); 106 + ASSERT_EQ(EINVAL, errno); 107 + 108 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 109 + sizeof(ruleset_attr), invalid_flag)); 110 + ASSERT_EQ(EINVAL, errno); 111 + } 112 + 113 + TEST(empty_path_beneath_attr) { 114 + const struct landlock_ruleset_attr ruleset_attr = { 115 + .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 116 + }; 117 + const int ruleset_fd = landlock_create_ruleset(&ruleset_attr, 118 + sizeof(ruleset_attr), 0); 119 + 120 + ASSERT_LE(0, ruleset_fd); 121 + 122 + /* Similar to struct landlock_path_beneath_attr.parent_fd = 0 */ 123 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 124 + NULL, 0)); 125 + ASSERT_EQ(EFAULT, errno); 126 + ASSERT_EQ(0, close(ruleset_fd)); 127 + } 128 + 129 + TEST(inval_fd_enforce) { 130 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 131 + 132 + ASSERT_EQ(-1, landlock_restrict_self(-1, 0)); 133 + ASSERT_EQ(EBADF, errno); 134 + } 135 + 136 + TEST(unpriv_enforce_without_no_new_privs) { 137 + int err; 138 + 139 + drop_caps(_metadata); 140 + err = landlock_restrict_self(-1, 0); 141 + ASSERT_EQ(EPERM, errno); 142 + ASSERT_EQ(err, -1); 143 + } 144 + 145 + TEST(ruleset_fd_io) 146 + { 147 + struct landlock_ruleset_attr ruleset_attr = { 148 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 149 + }; 150 + int ruleset_fd; 151 + char buf; 152 + 153 + drop_caps(_metadata); 154 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 155 + sizeof(ruleset_attr), 0); 156 + ASSERT_LE(0, ruleset_fd); 157 + 158 + ASSERT_EQ(-1, write(ruleset_fd, ".", 1)); 159 + ASSERT_EQ(EINVAL, errno); 160 + ASSERT_EQ(-1, read(ruleset_fd, &buf, 1)); 161 + ASSERT_EQ(EINVAL, errno); 162 + 163 + ASSERT_EQ(0, close(ruleset_fd)); 164 + } 165 + 166 + /* Tests enforcement of a ruleset FD transferred through a UNIX socket. */ 167 + TEST(ruleset_fd_transfer) 168 + { 169 + struct landlock_ruleset_attr ruleset_attr = { 170 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 171 + }; 172 + struct landlock_path_beneath_attr path_beneath_attr = { 173 + .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, 174 + }; 175 + int ruleset_fd_tx, dir_fd; 176 + union { 177 + /* Aligned ancillary data buffer. */ 178 + char buf[CMSG_SPACE(sizeof(ruleset_fd_tx))]; 179 + struct cmsghdr _align; 180 + } cmsg_tx = {}; 181 + char data_tx = '.'; 182 + struct iovec io = { 183 + .iov_base = &data_tx, 184 + .iov_len = sizeof(data_tx), 185 + }; 186 + struct msghdr msg = { 187 + .msg_iov = &io, 188 + .msg_iovlen = 1, 189 + .msg_control = &cmsg_tx.buf, 190 + .msg_controllen = sizeof(cmsg_tx.buf), 191 + }; 192 + struct cmsghdr *cmsg; 193 + int socket_fds[2]; 194 + pid_t child; 195 + int status; 196 + 197 + drop_caps(_metadata); 198 + 199 + /* Creates a test ruleset with a simple rule. */ 200 + ruleset_fd_tx = landlock_create_ruleset(&ruleset_attr, 201 + sizeof(ruleset_attr), 0); 202 + ASSERT_LE(0, ruleset_fd_tx); 203 + path_beneath_attr.parent_fd = open("/tmp", O_PATH | O_NOFOLLOW | 204 + O_DIRECTORY | O_CLOEXEC); 205 + ASSERT_LE(0, path_beneath_attr.parent_fd); 206 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH, 207 + &path_beneath_attr, 0)); 208 + ASSERT_EQ(0, close(path_beneath_attr.parent_fd)); 209 + 210 + cmsg = CMSG_FIRSTHDR(&msg); 211 + ASSERT_NE(NULL, cmsg); 212 + cmsg->cmsg_len = CMSG_LEN(sizeof(ruleset_fd_tx)); 213 + cmsg->cmsg_level = SOL_SOCKET; 214 + cmsg->cmsg_type = SCM_RIGHTS; 215 + memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx)); 216 + 217 + /* Sends the ruleset FD over a socketpair and then close it. */ 218 + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socket_fds)); 219 + ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0)); 220 + ASSERT_EQ(0, close(socket_fds[0])); 221 + ASSERT_EQ(0, close(ruleset_fd_tx)); 222 + 223 + child = fork(); 224 + ASSERT_LE(0, child); 225 + if (child == 0) { 226 + int ruleset_fd_rx; 227 + 228 + *(char *)msg.msg_iov->iov_base = '\0'; 229 + ASSERT_EQ(sizeof(data_tx), recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC)); 230 + ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base); 231 + ASSERT_EQ(0, close(socket_fds[1])); 232 + cmsg = CMSG_FIRSTHDR(&msg); 233 + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(ruleset_fd_tx))); 234 + memcpy(&ruleset_fd_rx, CMSG_DATA(cmsg), sizeof(ruleset_fd_tx)); 235 + 236 + /* Enforces the received ruleset on the child. */ 237 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 238 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd_rx, 0)); 239 + ASSERT_EQ(0, close(ruleset_fd_rx)); 240 + 241 + /* Checks that the ruleset enforcement. */ 242 + ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); 243 + ASSERT_EQ(EACCES, errno); 244 + dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 245 + ASSERT_LE(0, dir_fd); 246 + ASSERT_EQ(0, close(dir_fd)); 247 + _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 248 + return; 249 + } 250 + 251 + ASSERT_EQ(0, close(socket_fds[1])); 252 + 253 + /* Checks that the parent is unrestricted. */ 254 + dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 255 + ASSERT_LE(0, dir_fd); 256 + ASSERT_EQ(0, close(dir_fd)); 257 + dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 258 + ASSERT_LE(0, dir_fd); 259 + ASSERT_EQ(0, close(dir_fd)); 260 + 261 + ASSERT_EQ(child, waitpid(child, &status, 0)); 262 + ASSERT_EQ(1, WIFEXITED(status)); 263 + ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 264 + } 265 + 266 + TEST_HARNESS_MAIN
+183
tools/testing/selftests/landlock/common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Landlock test helpers 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + * Copyright © 2021 Microsoft Corporation 8 + */ 9 + 10 + #include <errno.h> 11 + #include <linux/landlock.h> 12 + #include <sys/capability.h> 13 + #include <sys/syscall.h> 14 + #include <sys/types.h> 15 + #include <sys/wait.h> 16 + #include <unistd.h> 17 + 18 + #include "../kselftest_harness.h" 19 + 20 + #ifndef ARRAY_SIZE 21 + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 22 + #endif 23 + 24 + /* 25 + * TEST_F_FORK() is useful when a test drop privileges but the corresponding 26 + * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory 27 + * where write actions are denied). For convenience, FIXTURE_TEARDOWN() is 28 + * also called when the test failed, but not when FIXTURE_SETUP() failed. For 29 + * this to be possible, we must not call abort() but instead exit smoothly 30 + * (hence the step print). 31 + */ 32 + #define TEST_F_FORK(fixture_name, test_name) \ 33 + static void fixture_name##_##test_name##_child( \ 34 + struct __test_metadata *_metadata, \ 35 + FIXTURE_DATA(fixture_name) *self, \ 36 + const FIXTURE_VARIANT(fixture_name) *variant); \ 37 + TEST_F(fixture_name, test_name) \ 38 + { \ 39 + int status; \ 40 + const pid_t child = fork(); \ 41 + if (child < 0) \ 42 + abort(); \ 43 + if (child == 0) { \ 44 + _metadata->no_print = 1; \ 45 + fixture_name##_##test_name##_child(_metadata, self, variant); \ 46 + if (_metadata->skip) \ 47 + _exit(255); \ 48 + if (_metadata->passed) \ 49 + _exit(0); \ 50 + _exit(_metadata->step); \ 51 + } \ 52 + if (child != waitpid(child, &status, 0)) \ 53 + abort(); \ 54 + if (WIFSIGNALED(status) || !WIFEXITED(status)) { \ 55 + _metadata->passed = 0; \ 56 + _metadata->step = 1; \ 57 + return; \ 58 + } \ 59 + switch (WEXITSTATUS(status)) { \ 60 + case 0: \ 61 + _metadata->passed = 1; \ 62 + break; \ 63 + case 255: \ 64 + _metadata->passed = 1; \ 65 + _metadata->skip = 1; \ 66 + break; \ 67 + default: \ 68 + _metadata->passed = 0; \ 69 + _metadata->step = WEXITSTATUS(status); \ 70 + break; \ 71 + } \ 72 + } \ 73 + static void fixture_name##_##test_name##_child( \ 74 + struct __test_metadata __attribute__((unused)) *_metadata, \ 75 + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ 76 + const FIXTURE_VARIANT(fixture_name) \ 77 + __attribute__((unused)) *variant) 78 + 79 + #ifndef landlock_create_ruleset 80 + static inline int landlock_create_ruleset( 81 + const struct landlock_ruleset_attr *const attr, 82 + const size_t size, const __u32 flags) 83 + { 84 + return syscall(__NR_landlock_create_ruleset, attr, size, flags); 85 + } 86 + #endif 87 + 88 + #ifndef landlock_add_rule 89 + static inline int landlock_add_rule(const int ruleset_fd, 90 + const enum landlock_rule_type rule_type, 91 + const void *const rule_attr, const __u32 flags) 92 + { 93 + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, 94 + rule_attr, flags); 95 + } 96 + #endif 97 + 98 + #ifndef landlock_restrict_self 99 + static inline int landlock_restrict_self(const int ruleset_fd, 100 + const __u32 flags) 101 + { 102 + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 103 + } 104 + #endif 105 + 106 + static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 107 + { 108 + cap_t cap_p; 109 + /* Only these three capabilities are useful for the tests. */ 110 + const cap_value_t caps[] = { 111 + CAP_DAC_OVERRIDE, 112 + CAP_MKNOD, 113 + CAP_SYS_ADMIN, 114 + CAP_SYS_CHROOT, 115 + }; 116 + 117 + cap_p = cap_get_proc(); 118 + EXPECT_NE(NULL, cap_p) { 119 + TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 120 + } 121 + EXPECT_NE(-1, cap_clear(cap_p)) { 122 + TH_LOG("Failed to cap_clear: %s", strerror(errno)); 123 + } 124 + if (!drop_all) { 125 + EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 126 + ARRAY_SIZE(caps), caps, CAP_SET)) { 127 + TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 128 + } 129 + } 130 + EXPECT_NE(-1, cap_set_proc(cap_p)) { 131 + TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 132 + } 133 + EXPECT_NE(-1, cap_free(cap_p)) { 134 + TH_LOG("Failed to cap_free: %s", strerror(errno)); 135 + } 136 + } 137 + 138 + /* We cannot put such helpers in a library because of kselftest_harness.h . */ 139 + __attribute__((__unused__)) 140 + static void disable_caps(struct __test_metadata *const _metadata) 141 + { 142 + _init_caps(_metadata, false); 143 + } 144 + 145 + __attribute__((__unused__)) 146 + static void drop_caps(struct __test_metadata *const _metadata) 147 + { 148 + _init_caps(_metadata, true); 149 + } 150 + 151 + static void _effective_cap(struct __test_metadata *const _metadata, 152 + const cap_value_t caps, const cap_flag_value_t value) 153 + { 154 + cap_t cap_p; 155 + 156 + cap_p = cap_get_proc(); 157 + EXPECT_NE(NULL, cap_p) { 158 + TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 159 + } 160 + EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) { 161 + TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 162 + } 163 + EXPECT_NE(-1, cap_set_proc(cap_p)) { 164 + TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 165 + } 166 + EXPECT_NE(-1, cap_free(cap_p)) { 167 + TH_LOG("Failed to cap_free: %s", strerror(errno)); 168 + } 169 + } 170 + 171 + __attribute__((__unused__)) 172 + static void set_cap(struct __test_metadata *const _metadata, 173 + const cap_value_t caps) 174 + { 175 + _effective_cap(_metadata, caps, CAP_SET); 176 + } 177 + 178 + __attribute__((__unused__)) 179 + static void clear_cap(struct __test_metadata *const _metadata, 180 + const cap_value_t caps) 181 + { 182 + _effective_cap(_metadata, caps, CAP_CLEAR); 183 + }
+7
tools/testing/selftests/landlock/config
··· 1 + CONFIG_OVERLAY_FS=y 2 + CONFIG_SECURITY_LANDLOCK=y 3 + CONFIG_SECURITY_PATH=y 4 + CONFIG_SECURITY=y 5 + CONFIG_SHMEM=y 6 + CONFIG_TMPFS_XATTR=y 7 + CONFIG_TMPFS=y
+2791
tools/testing/selftests/landlock/fs_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Filesystem 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2020 ANSSI 7 + * Copyright © 2020-2021 Microsoft Corporation 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <sched.h> 14 + #include <string.h> 15 + #include <sys/capability.h> 16 + #include <sys/mount.h> 17 + #include <sys/prctl.h> 18 + #include <sys/sendfile.h> 19 + #include <sys/stat.h> 20 + #include <sys/sysmacros.h> 21 + #include <unistd.h> 22 + 23 + #include "common.h" 24 + 25 + #define TMP_DIR "tmp" 26 + #define BINARY_PATH "./true" 27 + 28 + /* Paths (sibling number and depth) */ 29 + static const char dir_s1d1[] = TMP_DIR "/s1d1"; 30 + static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 31 + static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 32 + static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 33 + static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 34 + static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 35 + static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 36 + static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 37 + static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 38 + 39 + static const char dir_s2d1[] = TMP_DIR "/s2d1"; 40 + static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 41 + static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 42 + static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 43 + static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 44 + static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 45 + static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 46 + 47 + static const char dir_s3d1[] = TMP_DIR "/s3d1"; 48 + /* dir_s3d2 is a mount point. */ 49 + static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 50 + static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 51 + 52 + /* 53 + * layout1 hierarchy: 54 + * 55 + * tmp 56 + * ├── s1d1 57 + * │   ├── f1 58 + * │   ├── f2 59 + * │   └── s1d2 60 + * │   ├── f1 61 + * │   ├── f2 62 + * │   └── s1d3 63 + * │   ├── f1 64 + * │   └── f2 65 + * ├── s2d1 66 + * │   ├── f1 67 + * │   └── s2d2 68 + * │   ├── f1 69 + * │   └── s2d3 70 + * │   ├── f1 71 + * │   └── f2 72 + * └── s3d1 73 + * └── s3d2 74 + * └── s3d3 75 + */ 76 + 77 + static void mkdir_parents(struct __test_metadata *const _metadata, 78 + const char *const path) 79 + { 80 + char *walker; 81 + const char *parent; 82 + int i, err; 83 + 84 + ASSERT_NE(path[0], '\0'); 85 + walker = strdup(path); 86 + ASSERT_NE(NULL, walker); 87 + parent = walker; 88 + for (i = 1; walker[i]; i++) { 89 + if (walker[i] != '/') 90 + continue; 91 + walker[i] = '\0'; 92 + err = mkdir(parent, 0700); 93 + ASSERT_FALSE(err && errno != EEXIST) { 94 + TH_LOG("Failed to create directory \"%s\": %s", 95 + parent, strerror(errno)); 96 + } 97 + walker[i] = '/'; 98 + } 99 + free(walker); 100 + } 101 + 102 + static void create_directory(struct __test_metadata *const _metadata, 103 + const char *const path) 104 + { 105 + mkdir_parents(_metadata, path); 106 + ASSERT_EQ(0, mkdir(path, 0700)) { 107 + TH_LOG("Failed to create directory \"%s\": %s", path, 108 + strerror(errno)); 109 + } 110 + } 111 + 112 + static void create_file(struct __test_metadata *const _metadata, 113 + const char *const path) 114 + { 115 + mkdir_parents(_metadata, path); 116 + ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) { 117 + TH_LOG("Failed to create file \"%s\": %s", path, 118 + strerror(errno)); 119 + } 120 + } 121 + 122 + static int remove_path(const char *const path) 123 + { 124 + char *walker; 125 + int i, ret, err = 0; 126 + 127 + walker = strdup(path); 128 + if (!walker) { 129 + err = ENOMEM; 130 + goto out; 131 + } 132 + if (unlink(path) && rmdir(path)) { 133 + if (errno != ENOENT) 134 + err = errno; 135 + goto out; 136 + } 137 + for (i = strlen(walker); i > 0; i--) { 138 + if (walker[i] != '/') 139 + continue; 140 + walker[i] = '\0'; 141 + ret = rmdir(walker); 142 + if (ret) { 143 + if (errno != ENOTEMPTY && errno != EBUSY) 144 + err = errno; 145 + goto out; 146 + } 147 + if (strcmp(walker, TMP_DIR) == 0) 148 + goto out; 149 + } 150 + 151 + out: 152 + free(walker); 153 + return err; 154 + } 155 + 156 + static void prepare_layout(struct __test_metadata *const _metadata) 157 + { 158 + disable_caps(_metadata); 159 + umask(0077); 160 + create_directory(_metadata, TMP_DIR); 161 + 162 + /* 163 + * Do not pollute the rest of the system: creates a private mount point 164 + * for tests relying on pivot_root(2) and move_mount(2). 165 + */ 166 + set_cap(_metadata, CAP_SYS_ADMIN); 167 + ASSERT_EQ(0, unshare(CLONE_NEWNS)); 168 + ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700")); 169 + ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 170 + clear_cap(_metadata, CAP_SYS_ADMIN); 171 + } 172 + 173 + static void cleanup_layout(struct __test_metadata *const _metadata) 174 + { 175 + set_cap(_metadata, CAP_SYS_ADMIN); 176 + EXPECT_EQ(0, umount(TMP_DIR)); 177 + clear_cap(_metadata, CAP_SYS_ADMIN); 178 + EXPECT_EQ(0, remove_path(TMP_DIR)); 179 + } 180 + 181 + static void create_layout1(struct __test_metadata *const _metadata) 182 + { 183 + create_file(_metadata, file1_s1d1); 184 + create_file(_metadata, file1_s1d2); 185 + create_file(_metadata, file1_s1d3); 186 + create_file(_metadata, file2_s1d1); 187 + create_file(_metadata, file2_s1d2); 188 + create_file(_metadata, file2_s1d3); 189 + 190 + create_file(_metadata, file1_s2d1); 191 + create_file(_metadata, file1_s2d2); 192 + create_file(_metadata, file1_s2d3); 193 + create_file(_metadata, file2_s2d3); 194 + 195 + create_directory(_metadata, dir_s3d2); 196 + set_cap(_metadata, CAP_SYS_ADMIN); 197 + ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); 198 + clear_cap(_metadata, CAP_SYS_ADMIN); 199 + 200 + ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 201 + } 202 + 203 + static void remove_layout1(struct __test_metadata *const _metadata) 204 + { 205 + EXPECT_EQ(0, remove_path(file2_s1d3)); 206 + EXPECT_EQ(0, remove_path(file2_s1d2)); 207 + EXPECT_EQ(0, remove_path(file2_s1d1)); 208 + EXPECT_EQ(0, remove_path(file1_s1d3)); 209 + EXPECT_EQ(0, remove_path(file1_s1d2)); 210 + EXPECT_EQ(0, remove_path(file1_s1d1)); 211 + 212 + EXPECT_EQ(0, remove_path(file2_s2d3)); 213 + EXPECT_EQ(0, remove_path(file1_s2d3)); 214 + EXPECT_EQ(0, remove_path(file1_s2d2)); 215 + EXPECT_EQ(0, remove_path(file1_s2d1)); 216 + 217 + EXPECT_EQ(0, remove_path(dir_s3d3)); 218 + set_cap(_metadata, CAP_SYS_ADMIN); 219 + umount(dir_s3d2); 220 + clear_cap(_metadata, CAP_SYS_ADMIN); 221 + EXPECT_EQ(0, remove_path(dir_s3d2)); 222 + } 223 + 224 + FIXTURE(layout1) { 225 + }; 226 + 227 + FIXTURE_SETUP(layout1) 228 + { 229 + prepare_layout(_metadata); 230 + 231 + create_layout1(_metadata); 232 + } 233 + 234 + FIXTURE_TEARDOWN(layout1) 235 + { 236 + remove_layout1(_metadata); 237 + 238 + cleanup_layout(_metadata); 239 + } 240 + 241 + /* 242 + * This helper enables to use the ASSERT_* macros and print the line number 243 + * pointing to the test caller. 244 + */ 245 + static int test_open_rel(const int dirfd, const char *const path, const int flags) 246 + { 247 + int fd; 248 + 249 + /* Works with file and directories. */ 250 + fd = openat(dirfd, path, flags | O_CLOEXEC); 251 + if (fd < 0) 252 + return errno; 253 + /* 254 + * Mixing error codes from close(2) and open(2) should not lead to any 255 + * (access type) confusion for this test. 256 + */ 257 + if (close(fd) != 0) 258 + return errno; 259 + return 0; 260 + } 261 + 262 + static int test_open(const char *const path, const int flags) 263 + { 264 + return test_open_rel(AT_FDCWD, path, flags); 265 + } 266 + 267 + TEST_F_FORK(layout1, no_restriction) 268 + { 269 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 270 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 271 + ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 272 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 273 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 274 + ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 275 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 276 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 277 + 278 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 279 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 280 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 281 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 282 + ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 283 + ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 284 + 285 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 286 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 287 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 288 + } 289 + 290 + TEST_F_FORK(layout1, inval) 291 + { 292 + struct landlock_path_beneath_attr path_beneath = { 293 + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 294 + LANDLOCK_ACCESS_FS_WRITE_FILE, 295 + .parent_fd = -1, 296 + }; 297 + struct landlock_ruleset_attr ruleset_attr = { 298 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 299 + LANDLOCK_ACCESS_FS_WRITE_FILE, 300 + }; 301 + int ruleset_fd; 302 + 303 + path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY | 304 + O_CLOEXEC); 305 + ASSERT_LE(0, path_beneath.parent_fd); 306 + 307 + ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 308 + ASSERT_LE(0, ruleset_fd); 309 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 310 + &path_beneath, 0)); 311 + /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 312 + ASSERT_EQ(EBADF, errno); 313 + ASSERT_EQ(0, close(ruleset_fd)); 314 + 315 + ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 316 + ASSERT_LE(0, ruleset_fd); 317 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 318 + &path_beneath, 0)); 319 + /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 320 + ASSERT_EQ(EBADFD, errno); 321 + ASSERT_EQ(0, close(ruleset_fd)); 322 + 323 + /* Gets a real ruleset. */ 324 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 325 + sizeof(ruleset_attr), 0); 326 + ASSERT_LE(0, ruleset_fd); 327 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 328 + &path_beneath, 0)); 329 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 330 + 331 + /* Tests without O_PATH. */ 332 + path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 333 + ASSERT_LE(0, path_beneath.parent_fd); 334 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 335 + &path_beneath, 0)); 336 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 337 + 338 + /* Tests with a ruleset FD. */ 339 + path_beneath.parent_fd = ruleset_fd; 340 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 341 + &path_beneath, 0)); 342 + ASSERT_EQ(EBADFD, errno); 343 + 344 + /* Checks unhandled allowed_access. */ 345 + path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY | 346 + O_CLOEXEC); 347 + ASSERT_LE(0, path_beneath.parent_fd); 348 + 349 + /* Test with legitimate values. */ 350 + path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 351 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 352 + &path_beneath, 0)); 353 + ASSERT_EQ(EINVAL, errno); 354 + path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 355 + 356 + /* Test with unknown (64-bits) value. */ 357 + path_beneath.allowed_access |= (1ULL << 60); 358 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 359 + &path_beneath, 0)); 360 + ASSERT_EQ(EINVAL, errno); 361 + path_beneath.allowed_access &= ~(1ULL << 60); 362 + 363 + /* Test with no access. */ 364 + path_beneath.allowed_access = 0; 365 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 366 + &path_beneath, 0)); 367 + ASSERT_EQ(ENOMSG, errno); 368 + path_beneath.allowed_access &= ~(1ULL << 60); 369 + 370 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 371 + 372 + /* Enforces the ruleset. */ 373 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 374 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 375 + 376 + ASSERT_EQ(0, close(ruleset_fd)); 377 + } 378 + 379 + #define ACCESS_FILE ( \ 380 + LANDLOCK_ACCESS_FS_EXECUTE | \ 381 + LANDLOCK_ACCESS_FS_WRITE_FILE | \ 382 + LANDLOCK_ACCESS_FS_READ_FILE) 383 + 384 + #define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_SYM 385 + 386 + #define ACCESS_ALL ( \ 387 + ACCESS_FILE | \ 388 + LANDLOCK_ACCESS_FS_READ_DIR | \ 389 + LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 390 + LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 391 + LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 392 + LANDLOCK_ACCESS_FS_MAKE_DIR | \ 393 + LANDLOCK_ACCESS_FS_MAKE_REG | \ 394 + LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 395 + LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 396 + LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 397 + ACCESS_LAST) 398 + 399 + TEST_F_FORK(layout1, file_access_rights) 400 + { 401 + __u64 access; 402 + int err; 403 + struct landlock_path_beneath_attr path_beneath = {}; 404 + struct landlock_ruleset_attr ruleset_attr = { 405 + .handled_access_fs = ACCESS_ALL, 406 + }; 407 + const int ruleset_fd = landlock_create_ruleset(&ruleset_attr, 408 + sizeof(ruleset_attr), 0); 409 + 410 + ASSERT_LE(0, ruleset_fd); 411 + 412 + /* Tests access rights for files. */ 413 + path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 414 + ASSERT_LE(0, path_beneath.parent_fd); 415 + for (access = 1; access <= ACCESS_LAST; access <<= 1) { 416 + path_beneath.allowed_access = access; 417 + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 418 + &path_beneath, 0); 419 + if ((access | ACCESS_FILE) == ACCESS_FILE) { 420 + ASSERT_EQ(0, err); 421 + } else { 422 + ASSERT_EQ(-1, err); 423 + ASSERT_EQ(EINVAL, errno); 424 + } 425 + } 426 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 427 + } 428 + 429 + static void add_path_beneath(struct __test_metadata *const _metadata, 430 + const int ruleset_fd, const __u64 allowed_access, 431 + const char *const path) 432 + { 433 + struct landlock_path_beneath_attr path_beneath = { 434 + .allowed_access = allowed_access, 435 + }; 436 + 437 + path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 438 + ASSERT_LE(0, path_beneath.parent_fd) { 439 + TH_LOG("Failed to open directory \"%s\": %s", path, 440 + strerror(errno)); 441 + } 442 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 443 + &path_beneath, 0)) { 444 + TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 445 + strerror(errno)); 446 + } 447 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 448 + } 449 + 450 + struct rule { 451 + const char *path; 452 + __u64 access; 453 + }; 454 + 455 + #define ACCESS_RO ( \ 456 + LANDLOCK_ACCESS_FS_READ_FILE | \ 457 + LANDLOCK_ACCESS_FS_READ_DIR) 458 + 459 + #define ACCESS_RW ( \ 460 + ACCESS_RO | \ 461 + LANDLOCK_ACCESS_FS_WRITE_FILE) 462 + 463 + static int create_ruleset(struct __test_metadata *const _metadata, 464 + const __u64 handled_access_fs, const struct rule rules[]) 465 + { 466 + int ruleset_fd, i; 467 + struct landlock_ruleset_attr ruleset_attr = { 468 + .handled_access_fs = handled_access_fs, 469 + }; 470 + 471 + ASSERT_NE(NULL, rules) { 472 + TH_LOG("No rule list"); 473 + } 474 + ASSERT_NE(NULL, rules[0].path) { 475 + TH_LOG("Empty rule list"); 476 + } 477 + 478 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 479 + sizeof(ruleset_attr), 0); 480 + ASSERT_LE(0, ruleset_fd) { 481 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 482 + } 483 + 484 + for (i = 0; rules[i].path; i++) { 485 + add_path_beneath(_metadata, ruleset_fd, rules[i].access, 486 + rules[i].path); 487 + } 488 + return ruleset_fd; 489 + } 490 + 491 + static void enforce_ruleset(struct __test_metadata *const _metadata, 492 + const int ruleset_fd) 493 + { 494 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 495 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) { 496 + TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 497 + } 498 + } 499 + 500 + TEST_F_FORK(layout1, proc_nsfs) 501 + { 502 + const struct rule rules[] = { 503 + { 504 + .path = "/dev/null", 505 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 506 + LANDLOCK_ACCESS_FS_WRITE_FILE, 507 + }, 508 + {} 509 + }; 510 + struct landlock_path_beneath_attr path_beneath; 511 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access | 512 + LANDLOCK_ACCESS_FS_READ_DIR, rules); 513 + 514 + ASSERT_LE(0, ruleset_fd); 515 + ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 516 + 517 + enforce_ruleset(_metadata, ruleset_fd); 518 + 519 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 520 + ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 521 + ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 522 + ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 523 + 524 + ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 525 + ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 526 + ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 527 + /* 528 + * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 529 + * disconnected path. Such path cannot be identified and must then be 530 + * allowed. 531 + */ 532 + ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 533 + 534 + /* 535 + * Checks that it is not possible to add nsfs-like filesystem 536 + * references to a ruleset. 537 + */ 538 + path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 539 + LANDLOCK_ACCESS_FS_WRITE_FILE, 540 + path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 541 + ASSERT_LE(0, path_beneath.parent_fd); 542 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 543 + &path_beneath, 0)); 544 + ASSERT_EQ(EBADFD, errno); 545 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 546 + } 547 + 548 + TEST_F_FORK(layout1, unpriv) { 549 + const struct rule rules[] = { 550 + { 551 + .path = dir_s1d2, 552 + .access = ACCESS_RO, 553 + }, 554 + {} 555 + }; 556 + int ruleset_fd; 557 + 558 + drop_caps(_metadata); 559 + 560 + ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 561 + ASSERT_LE(0, ruleset_fd); 562 + ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 563 + ASSERT_EQ(EPERM, errno); 564 + 565 + /* enforce_ruleset() calls prctl(no_new_privs). */ 566 + enforce_ruleset(_metadata, ruleset_fd); 567 + ASSERT_EQ(0, close(ruleset_fd)); 568 + } 569 + 570 + TEST_F_FORK(layout1, effective_access) 571 + { 572 + const struct rule rules[] = { 573 + { 574 + .path = dir_s1d2, 575 + .access = ACCESS_RO, 576 + }, 577 + { 578 + .path = file1_s2d2, 579 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 580 + LANDLOCK_ACCESS_FS_WRITE_FILE, 581 + }, 582 + {} 583 + }; 584 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 585 + char buf; 586 + int reg_fd; 587 + 588 + ASSERT_LE(0, ruleset_fd); 589 + enforce_ruleset(_metadata, ruleset_fd); 590 + ASSERT_EQ(0, close(ruleset_fd)); 591 + 592 + /* Tests on a directory. */ 593 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 594 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 595 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 596 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 597 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 598 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 599 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 600 + 601 + /* Tests on a file. */ 602 + ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 603 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 604 + 605 + /* Checks effective read and write actions. */ 606 + reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 607 + ASSERT_LE(0, reg_fd); 608 + ASSERT_EQ(1, write(reg_fd, ".", 1)); 609 + ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 610 + ASSERT_EQ(1, read(reg_fd, &buf, 1)); 611 + ASSERT_EQ('.', buf); 612 + ASSERT_EQ(0, close(reg_fd)); 613 + 614 + /* Just in case, double-checks effective actions. */ 615 + reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 616 + ASSERT_LE(0, reg_fd); 617 + ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 618 + ASSERT_EQ(EBADF, errno); 619 + ASSERT_EQ(0, close(reg_fd)); 620 + } 621 + 622 + TEST_F_FORK(layout1, unhandled_access) 623 + { 624 + const struct rule rules[] = { 625 + { 626 + .path = dir_s1d2, 627 + .access = ACCESS_RO, 628 + }, 629 + {} 630 + }; 631 + /* Here, we only handle read accesses, not write accesses. */ 632 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 633 + 634 + ASSERT_LE(0, ruleset_fd); 635 + enforce_ruleset(_metadata, ruleset_fd); 636 + ASSERT_EQ(0, close(ruleset_fd)); 637 + 638 + /* 639 + * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 640 + * opening for write-only should be allowed, but not read-write. 641 + */ 642 + ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 643 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 644 + 645 + ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 646 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 647 + } 648 + 649 + TEST_F_FORK(layout1, ruleset_overlap) 650 + { 651 + const struct rule rules[] = { 652 + /* These rules should be ORed among them. */ 653 + { 654 + .path = dir_s1d2, 655 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 656 + LANDLOCK_ACCESS_FS_WRITE_FILE, 657 + }, 658 + { 659 + .path = dir_s1d2, 660 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 661 + LANDLOCK_ACCESS_FS_READ_DIR, 662 + }, 663 + {} 664 + }; 665 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 666 + 667 + ASSERT_LE(0, ruleset_fd); 668 + enforce_ruleset(_metadata, ruleset_fd); 669 + ASSERT_EQ(0, close(ruleset_fd)); 670 + 671 + /* Checks s1d1 hierarchy. */ 672 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 673 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 674 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 675 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 676 + 677 + /* Checks s1d2 hierarchy. */ 678 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 679 + ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 680 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 681 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 682 + 683 + /* Checks s1d3 hierarchy. */ 684 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 685 + ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 686 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 687 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 688 + } 689 + 690 + TEST_F_FORK(layout1, non_overlapping_accesses) 691 + { 692 + const struct rule layer1[] = { 693 + { 694 + .path = dir_s1d2, 695 + .access = LANDLOCK_ACCESS_FS_MAKE_REG, 696 + }, 697 + {} 698 + }; 699 + const struct rule layer2[] = { 700 + { 701 + .path = dir_s1d3, 702 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 703 + }, 704 + {} 705 + }; 706 + int ruleset_fd; 707 + 708 + ASSERT_EQ(0, unlink(file1_s1d1)); 709 + ASSERT_EQ(0, unlink(file1_s1d2)); 710 + 711 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 712 + layer1); 713 + ASSERT_LE(0, ruleset_fd); 714 + enforce_ruleset(_metadata, ruleset_fd); 715 + ASSERT_EQ(0, close(ruleset_fd)); 716 + 717 + ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 718 + ASSERT_EQ(EACCES, errno); 719 + ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 720 + ASSERT_EQ(0, unlink(file1_s1d2)); 721 + 722 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 723 + layer2); 724 + ASSERT_LE(0, ruleset_fd); 725 + enforce_ruleset(_metadata, ruleset_fd); 726 + ASSERT_EQ(0, close(ruleset_fd)); 727 + 728 + /* Unchanged accesses for file creation. */ 729 + ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 730 + ASSERT_EQ(EACCES, errno); 731 + ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 732 + 733 + /* Checks file removing. */ 734 + ASSERT_EQ(-1, unlink(file1_s1d2)); 735 + ASSERT_EQ(EACCES, errno); 736 + ASSERT_EQ(0, unlink(file1_s1d3)); 737 + } 738 + 739 + TEST_F_FORK(layout1, interleaved_masked_accesses) 740 + { 741 + /* 742 + * Checks overly restrictive rules: 743 + * layer 1: allows R s1d1/s1d2/s1d3/file1 744 + * layer 2: allows RW s1d1/s1d2/s1d3 745 + * allows W s1d1/s1d2 746 + * denies R s1d1/s1d2 747 + * layer 3: allows R s1d1 748 + * layer 4: allows R s1d1/s1d2 749 + * denies W s1d1/s1d2 750 + * layer 5: allows R s1d1/s1d2 751 + * layer 6: allows X ---- 752 + * layer 7: allows W s1d1/s1d2 753 + * denies R s1d1/s1d2 754 + */ 755 + const struct rule layer1_read[] = { 756 + /* Allows read access to file1_s1d3 with the first layer. */ 757 + { 758 + .path = file1_s1d3, 759 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 760 + }, 761 + {} 762 + }; 763 + /* First rule with write restrictions. */ 764 + const struct rule layer2_read_write[] = { 765 + /* Start by granting read-write access via its parent directory... */ 766 + { 767 + .path = dir_s1d3, 768 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 769 + LANDLOCK_ACCESS_FS_WRITE_FILE, 770 + }, 771 + /* ...but also denies read access via its grandparent directory. */ 772 + { 773 + .path = dir_s1d2, 774 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 775 + }, 776 + {} 777 + }; 778 + const struct rule layer3_read[] = { 779 + /* Allows read access via its great-grandparent directory. */ 780 + { 781 + .path = dir_s1d1, 782 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 783 + }, 784 + {} 785 + }; 786 + const struct rule layer4_read_write[] = { 787 + /* 788 + * Try to confuse the deny access by denying write (but not 789 + * read) access via its grandparent directory. 790 + */ 791 + { 792 + .path = dir_s1d2, 793 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 794 + }, 795 + {} 796 + }; 797 + const struct rule layer5_read[] = { 798 + /* 799 + * Try to override layer2's deny read access by explicitly 800 + * allowing read access via file1_s1d3's grandparent. 801 + */ 802 + { 803 + .path = dir_s1d2, 804 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 805 + }, 806 + {} 807 + }; 808 + const struct rule layer6_execute[] = { 809 + /* 810 + * Restricts an unrelated file hierarchy with a new access 811 + * (non-overlapping) type. 812 + */ 813 + { 814 + .path = dir_s2d1, 815 + .access = LANDLOCK_ACCESS_FS_EXECUTE, 816 + }, 817 + {} 818 + }; 819 + const struct rule layer7_read_write[] = { 820 + /* 821 + * Finally, denies read access to file1_s1d3 via its 822 + * grandparent. 823 + */ 824 + { 825 + .path = dir_s1d2, 826 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 827 + }, 828 + {} 829 + }; 830 + int ruleset_fd; 831 + 832 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 833 + layer1_read); 834 + ASSERT_LE(0, ruleset_fd); 835 + enforce_ruleset(_metadata, ruleset_fd); 836 + ASSERT_EQ(0, close(ruleset_fd)); 837 + 838 + /* Checks that read access is granted for file1_s1d3 with layer 1. */ 839 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 840 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 841 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 842 + 843 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 844 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer2_read_write); 845 + ASSERT_LE(0, ruleset_fd); 846 + enforce_ruleset(_metadata, ruleset_fd); 847 + ASSERT_EQ(0, close(ruleset_fd)); 848 + 849 + /* Checks that previous access rights are unchanged with layer 2. */ 850 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 851 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 852 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 853 + 854 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 855 + layer3_read); 856 + ASSERT_LE(0, ruleset_fd); 857 + enforce_ruleset(_metadata, ruleset_fd); 858 + ASSERT_EQ(0, close(ruleset_fd)); 859 + 860 + /* Checks that previous access rights are unchanged with layer 3. */ 861 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 862 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 863 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 864 + 865 + /* This time, denies write access for the file hierarchy. */ 866 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 867 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer4_read_write); 868 + ASSERT_LE(0, ruleset_fd); 869 + enforce_ruleset(_metadata, ruleset_fd); 870 + ASSERT_EQ(0, close(ruleset_fd)); 871 + 872 + /* 873 + * Checks that the only change with layer 4 is that write access is 874 + * denied. 875 + */ 876 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 877 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 878 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 879 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 880 + 881 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 882 + layer5_read); 883 + ASSERT_LE(0, ruleset_fd); 884 + enforce_ruleset(_metadata, ruleset_fd); 885 + ASSERT_EQ(0, close(ruleset_fd)); 886 + 887 + /* Checks that previous access rights are unchanged with layer 5. */ 888 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 889 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 890 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 891 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 892 + 893 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 894 + layer6_execute); 895 + ASSERT_LE(0, ruleset_fd); 896 + enforce_ruleset(_metadata, ruleset_fd); 897 + ASSERT_EQ(0, close(ruleset_fd)); 898 + 899 + /* Checks that previous access rights are unchanged with layer 6. */ 900 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 901 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 902 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 903 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 904 + 905 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 906 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer7_read_write); 907 + ASSERT_LE(0, ruleset_fd); 908 + enforce_ruleset(_metadata, ruleset_fd); 909 + ASSERT_EQ(0, close(ruleset_fd)); 910 + 911 + /* Checks read access is now denied with layer 7. */ 912 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 913 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 914 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 915 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 916 + } 917 + 918 + TEST_F_FORK(layout1, inherit_subset) 919 + { 920 + const struct rule rules[] = { 921 + { 922 + .path = dir_s1d2, 923 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 924 + LANDLOCK_ACCESS_FS_READ_DIR, 925 + }, 926 + {} 927 + }; 928 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 929 + 930 + ASSERT_LE(0, ruleset_fd); 931 + enforce_ruleset(_metadata, ruleset_fd); 932 + 933 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 934 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 935 + 936 + /* Write access is forbidden. */ 937 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 938 + /* Readdir access is allowed. */ 939 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 940 + 941 + /* Write access is forbidden. */ 942 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 943 + /* Readdir access is allowed. */ 944 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 945 + 946 + /* 947 + * Tests shared rule extension: the following rules should not grant 948 + * any new access, only remove some. Once enforced, these rules are 949 + * ANDed with the previous ones. 950 + */ 951 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 952 + dir_s1d2); 953 + /* 954 + * According to ruleset_fd, dir_s1d2 should now have the 955 + * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 956 + * access rights (even if this directory is opened a second time). 957 + * However, when enforcing this updated ruleset, the ruleset tied to 958 + * the current process (i.e. its domain) will still only have the 959 + * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 960 + * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 961 + * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 962 + * be a privilege escalation. 963 + */ 964 + enforce_ruleset(_metadata, ruleset_fd); 965 + 966 + /* Same tests and results as above. */ 967 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 968 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 969 + 970 + /* It is still forbidden to write in file1_s1d2. */ 971 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 972 + /* Readdir access is still allowed. */ 973 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 974 + 975 + /* It is still forbidden to write in file1_s1d3. */ 976 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 977 + /* Readdir access is still allowed. */ 978 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 979 + 980 + /* 981 + * Try to get more privileges by adding new access rights to the parent 982 + * directory: dir_s1d1. 983 + */ 984 + add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 985 + enforce_ruleset(_metadata, ruleset_fd); 986 + 987 + /* Same tests and results as above. */ 988 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 989 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 990 + 991 + /* It is still forbidden to write in file1_s1d2. */ 992 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 993 + /* Readdir access is still allowed. */ 994 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 995 + 996 + /* It is still forbidden to write in file1_s1d3. */ 997 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 998 + /* Readdir access is still allowed. */ 999 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1000 + 1001 + /* 1002 + * Now, dir_s1d3 get a new rule tied to it, only allowing 1003 + * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1004 + * that there was no rule tied to it before. 1005 + */ 1006 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1007 + dir_s1d3); 1008 + enforce_ruleset(_metadata, ruleset_fd); 1009 + ASSERT_EQ(0, close(ruleset_fd)); 1010 + 1011 + /* 1012 + * Same tests and results as above, except for open(dir_s1d3) which is 1013 + * now denied because the new rule mask the rule previously inherited 1014 + * from dir_s1d2. 1015 + */ 1016 + 1017 + /* Same tests and results as above. */ 1018 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1019 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1020 + 1021 + /* It is still forbidden to write in file1_s1d2. */ 1022 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1023 + /* Readdir access is still allowed. */ 1024 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1025 + 1026 + /* It is still forbidden to write in file1_s1d3. */ 1027 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1028 + /* 1029 + * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1030 + * the same layer. 1031 + */ 1032 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1033 + } 1034 + 1035 + TEST_F_FORK(layout1, inherit_superset) 1036 + { 1037 + const struct rule rules[] = { 1038 + { 1039 + .path = dir_s1d3, 1040 + .access = ACCESS_RO, 1041 + }, 1042 + {} 1043 + }; 1044 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1045 + 1046 + ASSERT_LE(0, ruleset_fd); 1047 + enforce_ruleset(_metadata, ruleset_fd); 1048 + 1049 + /* Readdir access is denied for dir_s1d2. */ 1050 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1051 + /* Readdir access is allowed for dir_s1d3. */ 1052 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1053 + /* File access is allowed for file1_s1d3. */ 1054 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1055 + 1056 + /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1057 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_READ_FILE | 1058 + LANDLOCK_ACCESS_FS_READ_DIR, dir_s1d2); 1059 + enforce_ruleset(_metadata, ruleset_fd); 1060 + ASSERT_EQ(0, close(ruleset_fd)); 1061 + 1062 + /* Readdir access is still denied for dir_s1d2. */ 1063 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1064 + /* Readdir access is still allowed for dir_s1d3. */ 1065 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1066 + /* File access is still allowed for file1_s1d3. */ 1067 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1068 + } 1069 + 1070 + TEST_F_FORK(layout1, max_layers) 1071 + { 1072 + int i, err; 1073 + const struct rule rules[] = { 1074 + { 1075 + .path = dir_s1d2, 1076 + .access = ACCESS_RO, 1077 + }, 1078 + {} 1079 + }; 1080 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1081 + 1082 + ASSERT_LE(0, ruleset_fd); 1083 + for (i = 0; i < 64; i++) 1084 + enforce_ruleset(_metadata, ruleset_fd); 1085 + 1086 + for (i = 0; i < 2; i++) { 1087 + err = landlock_restrict_self(ruleset_fd, 0); 1088 + ASSERT_EQ(-1, err); 1089 + ASSERT_EQ(E2BIG, errno); 1090 + } 1091 + ASSERT_EQ(0, close(ruleset_fd)); 1092 + } 1093 + 1094 + TEST_F_FORK(layout1, empty_or_same_ruleset) 1095 + { 1096 + struct landlock_ruleset_attr ruleset_attr = {}; 1097 + int ruleset_fd; 1098 + 1099 + /* Tests empty handled_access_fs. */ 1100 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1101 + sizeof(ruleset_attr), 0); 1102 + ASSERT_LE(-1, ruleset_fd); 1103 + ASSERT_EQ(ENOMSG, errno); 1104 + 1105 + /* Enforces policy which deny read access to all files. */ 1106 + ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1107 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1108 + sizeof(ruleset_attr), 0); 1109 + ASSERT_LE(0, ruleset_fd); 1110 + enforce_ruleset(_metadata, ruleset_fd); 1111 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1112 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1113 + 1114 + /* Nests a policy which deny read access to all directories. */ 1115 + ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1116 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1117 + sizeof(ruleset_attr), 0); 1118 + ASSERT_LE(0, ruleset_fd); 1119 + enforce_ruleset(_metadata, ruleset_fd); 1120 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1121 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1122 + 1123 + /* Enforces a second time with the same ruleset. */ 1124 + enforce_ruleset(_metadata, ruleset_fd); 1125 + ASSERT_EQ(0, close(ruleset_fd)); 1126 + } 1127 + 1128 + TEST_F_FORK(layout1, rule_on_mountpoint) 1129 + { 1130 + const struct rule rules[] = { 1131 + { 1132 + .path = dir_s1d1, 1133 + .access = ACCESS_RO, 1134 + }, 1135 + { 1136 + /* dir_s3d2 is a mount point. */ 1137 + .path = dir_s3d2, 1138 + .access = ACCESS_RO, 1139 + }, 1140 + {} 1141 + }; 1142 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1143 + 1144 + ASSERT_LE(0, ruleset_fd); 1145 + enforce_ruleset(_metadata, ruleset_fd); 1146 + ASSERT_EQ(0, close(ruleset_fd)); 1147 + 1148 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1149 + 1150 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1151 + 1152 + ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1153 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1154 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1155 + } 1156 + 1157 + TEST_F_FORK(layout1, rule_over_mountpoint) 1158 + { 1159 + const struct rule rules[] = { 1160 + { 1161 + .path = dir_s1d1, 1162 + .access = ACCESS_RO, 1163 + }, 1164 + { 1165 + /* dir_s3d2 is a mount point. */ 1166 + .path = dir_s3d1, 1167 + .access = ACCESS_RO, 1168 + }, 1169 + {} 1170 + }; 1171 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1172 + 1173 + ASSERT_LE(0, ruleset_fd); 1174 + enforce_ruleset(_metadata, ruleset_fd); 1175 + ASSERT_EQ(0, close(ruleset_fd)); 1176 + 1177 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1178 + 1179 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1180 + 1181 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1182 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1183 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1184 + } 1185 + 1186 + /* 1187 + * This test verifies that we can apply a landlock rule on the root directory 1188 + * (which might require special handling). 1189 + */ 1190 + TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1191 + { 1192 + struct rule rules[] = { 1193 + { 1194 + .path = "/", 1195 + .access = ACCESS_RO, 1196 + }, 1197 + {} 1198 + }; 1199 + int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1200 + 1201 + ASSERT_LE(0, ruleset_fd); 1202 + enforce_ruleset(_metadata, ruleset_fd); 1203 + ASSERT_EQ(0, close(ruleset_fd)); 1204 + 1205 + /* Checks allowed access. */ 1206 + ASSERT_EQ(0, test_open("/", O_RDONLY)); 1207 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1208 + 1209 + rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1210 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1211 + ASSERT_LE(0, ruleset_fd); 1212 + enforce_ruleset(_metadata, ruleset_fd); 1213 + ASSERT_EQ(0, close(ruleset_fd)); 1214 + 1215 + /* Checks denied access (on a directory). */ 1216 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1217 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1218 + } 1219 + 1220 + TEST_F_FORK(layout1, rule_over_root_deny) 1221 + { 1222 + const struct rule rules[] = { 1223 + { 1224 + .path = "/", 1225 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 1226 + }, 1227 + {} 1228 + }; 1229 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1230 + 1231 + ASSERT_LE(0, ruleset_fd); 1232 + enforce_ruleset(_metadata, ruleset_fd); 1233 + ASSERT_EQ(0, close(ruleset_fd)); 1234 + 1235 + /* Checks denied access (on a directory). */ 1236 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1237 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1238 + } 1239 + 1240 + TEST_F_FORK(layout1, rule_inside_mount_ns) 1241 + { 1242 + const struct rule rules[] = { 1243 + { 1244 + .path = "s3d3", 1245 + .access = ACCESS_RO, 1246 + }, 1247 + {} 1248 + }; 1249 + int ruleset_fd; 1250 + 1251 + set_cap(_metadata, CAP_SYS_ADMIN); 1252 + ASSERT_EQ(0, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3)) { 1253 + TH_LOG("Failed to pivot root: %s", strerror(errno)); 1254 + }; 1255 + ASSERT_EQ(0, chdir("/")); 1256 + clear_cap(_metadata, CAP_SYS_ADMIN); 1257 + 1258 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1259 + ASSERT_LE(0, ruleset_fd); 1260 + enforce_ruleset(_metadata, ruleset_fd); 1261 + ASSERT_EQ(0, close(ruleset_fd)); 1262 + 1263 + ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1264 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1265 + } 1266 + 1267 + TEST_F_FORK(layout1, mount_and_pivot) 1268 + { 1269 + const struct rule rules[] = { 1270 + { 1271 + .path = dir_s3d2, 1272 + .access = ACCESS_RO, 1273 + }, 1274 + {} 1275 + }; 1276 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1277 + 1278 + ASSERT_LE(0, ruleset_fd); 1279 + enforce_ruleset(_metadata, ruleset_fd); 1280 + ASSERT_EQ(0, close(ruleset_fd)); 1281 + 1282 + set_cap(_metadata, CAP_SYS_ADMIN); 1283 + ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1284 + ASSERT_EQ(EPERM, errno); 1285 + ASSERT_EQ(-1, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3)); 1286 + ASSERT_EQ(EPERM, errno); 1287 + clear_cap(_metadata, CAP_SYS_ADMIN); 1288 + } 1289 + 1290 + TEST_F_FORK(layout1, move_mount) 1291 + { 1292 + const struct rule rules[] = { 1293 + { 1294 + .path = dir_s3d2, 1295 + .access = ACCESS_RO, 1296 + }, 1297 + {} 1298 + }; 1299 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1300 + 1301 + ASSERT_LE(0, ruleset_fd); 1302 + 1303 + set_cap(_metadata, CAP_SYS_ADMIN); 1304 + ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1305 + dir_s1d2, 0)) { 1306 + TH_LOG("Failed to move mount: %s", strerror(errno)); 1307 + } 1308 + 1309 + ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1310 + dir_s3d2, 0)); 1311 + clear_cap(_metadata, CAP_SYS_ADMIN); 1312 + 1313 + enforce_ruleset(_metadata, ruleset_fd); 1314 + ASSERT_EQ(0, close(ruleset_fd)); 1315 + 1316 + set_cap(_metadata, CAP_SYS_ADMIN); 1317 + ASSERT_EQ(-1, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1318 + dir_s1d2, 0)); 1319 + ASSERT_EQ(EPERM, errno); 1320 + clear_cap(_metadata, CAP_SYS_ADMIN); 1321 + } 1322 + 1323 + TEST_F_FORK(layout1, release_inodes) 1324 + { 1325 + const struct rule rules[] = { 1326 + { 1327 + .path = dir_s1d1, 1328 + .access = ACCESS_RO, 1329 + }, 1330 + { 1331 + .path = dir_s3d2, 1332 + .access = ACCESS_RO, 1333 + }, 1334 + { 1335 + .path = dir_s3d3, 1336 + .access = ACCESS_RO, 1337 + }, 1338 + {} 1339 + }; 1340 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1341 + 1342 + ASSERT_LE(0, ruleset_fd); 1343 + /* Unmount a file hierarchy while it is being used by a ruleset. */ 1344 + set_cap(_metadata, CAP_SYS_ADMIN); 1345 + ASSERT_EQ(0, umount(dir_s3d2)); 1346 + clear_cap(_metadata, CAP_SYS_ADMIN); 1347 + 1348 + enforce_ruleset(_metadata, ruleset_fd); 1349 + ASSERT_EQ(0, close(ruleset_fd)); 1350 + 1351 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1352 + ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1353 + /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1354 + ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1355 + } 1356 + 1357 + enum relative_access { 1358 + REL_OPEN, 1359 + REL_CHDIR, 1360 + REL_CHROOT_ONLY, 1361 + REL_CHROOT_CHDIR, 1362 + }; 1363 + 1364 + static void test_relative_path(struct __test_metadata *const _metadata, 1365 + const enum relative_access rel) 1366 + { 1367 + /* 1368 + * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1369 + * is not a disconnected root directory). 1370 + */ 1371 + const struct rule layer1_base[] = { 1372 + { 1373 + .path = TMP_DIR, 1374 + .access = ACCESS_RO, 1375 + }, 1376 + {} 1377 + }; 1378 + const struct rule layer2_subs[] = { 1379 + { 1380 + .path = dir_s1d2, 1381 + .access = ACCESS_RO, 1382 + }, 1383 + { 1384 + .path = dir_s2d2, 1385 + .access = ACCESS_RO, 1386 + }, 1387 + {} 1388 + }; 1389 + int dirfd, ruleset_fd; 1390 + 1391 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1392 + ASSERT_LE(0, ruleset_fd); 1393 + enforce_ruleset(_metadata, ruleset_fd); 1394 + ASSERT_EQ(0, close(ruleset_fd)); 1395 + 1396 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1397 + 1398 + ASSERT_LE(0, ruleset_fd); 1399 + switch (rel) { 1400 + case REL_OPEN: 1401 + case REL_CHDIR: 1402 + break; 1403 + case REL_CHROOT_ONLY: 1404 + ASSERT_EQ(0, chdir(dir_s2d2)); 1405 + break; 1406 + case REL_CHROOT_CHDIR: 1407 + ASSERT_EQ(0, chdir(dir_s1d2)); 1408 + break; 1409 + default: 1410 + ASSERT_TRUE(false); 1411 + return; 1412 + } 1413 + 1414 + set_cap(_metadata, CAP_SYS_CHROOT); 1415 + enforce_ruleset(_metadata, ruleset_fd); 1416 + 1417 + switch (rel) { 1418 + case REL_OPEN: 1419 + dirfd = open(dir_s1d2, O_DIRECTORY); 1420 + ASSERT_LE(0, dirfd); 1421 + break; 1422 + case REL_CHDIR: 1423 + ASSERT_EQ(0, chdir(dir_s1d2)); 1424 + dirfd = AT_FDCWD; 1425 + break; 1426 + case REL_CHROOT_ONLY: 1427 + /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1428 + ASSERT_EQ(0, chroot("../../s1d1/s1d2")) { 1429 + TH_LOG("Failed to chroot: %s", strerror(errno)); 1430 + } 1431 + dirfd = AT_FDCWD; 1432 + break; 1433 + case REL_CHROOT_CHDIR: 1434 + /* Do chroot into dir_s1d2. */ 1435 + ASSERT_EQ(0, chroot(".")) { 1436 + TH_LOG("Failed to chroot: %s", strerror(errno)); 1437 + } 1438 + dirfd = AT_FDCWD; 1439 + break; 1440 + } 1441 + 1442 + ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1443 + test_open_rel(dirfd, "..", O_RDONLY)); 1444 + ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1445 + 1446 + if (rel == REL_CHROOT_ONLY) { 1447 + /* The current directory is dir_s2d2. */ 1448 + ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1449 + } else { 1450 + /* The current directory is dir_s1d2. */ 1451 + ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1452 + } 1453 + 1454 + if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1455 + /* Checks the root dir_s1d2. */ 1456 + ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1457 + ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1458 + ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1459 + ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1460 + } 1461 + 1462 + if (rel != REL_CHROOT_CHDIR) { 1463 + ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1464 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1465 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", O_RDONLY)); 1466 + 1467 + ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1468 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1469 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", O_RDONLY)); 1470 + } 1471 + 1472 + if (rel == REL_OPEN) 1473 + ASSERT_EQ(0, close(dirfd)); 1474 + ASSERT_EQ(0, close(ruleset_fd)); 1475 + } 1476 + 1477 + TEST_F_FORK(layout1, relative_open) 1478 + { 1479 + test_relative_path(_metadata, REL_OPEN); 1480 + } 1481 + 1482 + TEST_F_FORK(layout1, relative_chdir) 1483 + { 1484 + test_relative_path(_metadata, REL_CHDIR); 1485 + } 1486 + 1487 + TEST_F_FORK(layout1, relative_chroot_only) 1488 + { 1489 + test_relative_path(_metadata, REL_CHROOT_ONLY); 1490 + } 1491 + 1492 + TEST_F_FORK(layout1, relative_chroot_chdir) 1493 + { 1494 + test_relative_path(_metadata, REL_CHROOT_CHDIR); 1495 + } 1496 + 1497 + static void copy_binary(struct __test_metadata *const _metadata, 1498 + const char *const dst_path) 1499 + { 1500 + int dst_fd, src_fd; 1501 + struct stat statbuf; 1502 + 1503 + dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1504 + ASSERT_LE(0, dst_fd) { 1505 + TH_LOG("Failed to open \"%s\": %s", dst_path, 1506 + strerror(errno)); 1507 + } 1508 + src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1509 + ASSERT_LE(0, src_fd) { 1510 + TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1511 + strerror(errno)); 1512 + } 1513 + ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1514 + ASSERT_EQ(statbuf.st_size, sendfile(dst_fd, src_fd, 0, 1515 + statbuf.st_size)); 1516 + ASSERT_EQ(0, close(src_fd)); 1517 + ASSERT_EQ(0, close(dst_fd)); 1518 + } 1519 + 1520 + static void test_execute(struct __test_metadata *const _metadata, 1521 + const int err, const char *const path) 1522 + { 1523 + int status; 1524 + char *const argv[] = {(char *)path, NULL}; 1525 + const pid_t child = fork(); 1526 + 1527 + ASSERT_LE(0, child); 1528 + if (child == 0) { 1529 + ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) { 1530 + TH_LOG("Failed to execute \"%s\": %s", path, 1531 + strerror(errno)); 1532 + }; 1533 + ASSERT_EQ(err, errno); 1534 + _exit(_metadata->passed ? 2 : 1); 1535 + return; 1536 + } 1537 + ASSERT_EQ(child, waitpid(child, &status, 0)); 1538 + ASSERT_EQ(1, WIFEXITED(status)); 1539 + ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) { 1540 + TH_LOG("Unexpected return code for \"%s\": %s", path, 1541 + strerror(errno)); 1542 + }; 1543 + } 1544 + 1545 + TEST_F_FORK(layout1, execute) 1546 + { 1547 + const struct rule rules[] = { 1548 + { 1549 + .path = dir_s1d2, 1550 + .access = LANDLOCK_ACCESS_FS_EXECUTE, 1551 + }, 1552 + {} 1553 + }; 1554 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1555 + rules); 1556 + 1557 + ASSERT_LE(0, ruleset_fd); 1558 + copy_binary(_metadata, file1_s1d1); 1559 + copy_binary(_metadata, file1_s1d2); 1560 + copy_binary(_metadata, file1_s1d3); 1561 + 1562 + enforce_ruleset(_metadata, ruleset_fd); 1563 + ASSERT_EQ(0, close(ruleset_fd)); 1564 + 1565 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1566 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1567 + test_execute(_metadata, EACCES, file1_s1d1); 1568 + 1569 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1570 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1571 + test_execute(_metadata, 0, file1_s1d2); 1572 + 1573 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1574 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1575 + test_execute(_metadata, 0, file1_s1d3); 1576 + } 1577 + 1578 + TEST_F_FORK(layout1, link) 1579 + { 1580 + const struct rule rules[] = { 1581 + { 1582 + .path = dir_s1d2, 1583 + .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1584 + }, 1585 + {} 1586 + }; 1587 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1588 + rules); 1589 + 1590 + ASSERT_LE(0, ruleset_fd); 1591 + 1592 + ASSERT_EQ(0, unlink(file1_s1d1)); 1593 + ASSERT_EQ(0, unlink(file1_s1d2)); 1594 + ASSERT_EQ(0, unlink(file1_s1d3)); 1595 + 1596 + enforce_ruleset(_metadata, ruleset_fd); 1597 + ASSERT_EQ(0, close(ruleset_fd)); 1598 + 1599 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1600 + ASSERT_EQ(EACCES, errno); 1601 + /* Denies linking because of reparenting. */ 1602 + ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1603 + ASSERT_EQ(EXDEV, errno); 1604 + ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1605 + ASSERT_EQ(EXDEV, errno); 1606 + 1607 + ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1608 + ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 1609 + } 1610 + 1611 + TEST_F_FORK(layout1, rename_file) 1612 + { 1613 + const struct rule rules[] = { 1614 + { 1615 + .path = dir_s1d3, 1616 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1617 + }, 1618 + { 1619 + .path = dir_s2d2, 1620 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1621 + }, 1622 + {} 1623 + }; 1624 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1625 + rules); 1626 + 1627 + ASSERT_LE(0, ruleset_fd); 1628 + 1629 + ASSERT_EQ(0, unlink(file1_s1d1)); 1630 + ASSERT_EQ(0, unlink(file1_s1d2)); 1631 + 1632 + enforce_ruleset(_metadata, ruleset_fd); 1633 + ASSERT_EQ(0, close(ruleset_fd)); 1634 + 1635 + /* 1636 + * Tries to replace a file, from a directory that allows file removal, 1637 + * but to a different directory (which also allows file removal). 1638 + */ 1639 + ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1640 + ASSERT_EQ(EXDEV, errno); 1641 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1642 + RENAME_EXCHANGE)); 1643 + ASSERT_EQ(EXDEV, errno); 1644 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 1645 + RENAME_EXCHANGE)); 1646 + ASSERT_EQ(EXDEV, errno); 1647 + 1648 + /* 1649 + * Tries to replace a file, from a directory that denies file removal, 1650 + * to a different directory (which allows file removal). 1651 + */ 1652 + ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1653 + ASSERT_EQ(EXDEV, errno); 1654 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 1655 + RENAME_EXCHANGE)); 1656 + ASSERT_EQ(EXDEV, errno); 1657 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 1658 + RENAME_EXCHANGE)); 1659 + ASSERT_EQ(EXDEV, errno); 1660 + 1661 + /* Exchanges files and directories that partially allow removal. */ 1662 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 1663 + RENAME_EXCHANGE)); 1664 + ASSERT_EQ(EACCES, errno); 1665 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 1666 + RENAME_EXCHANGE)); 1667 + ASSERT_EQ(EACCES, errno); 1668 + 1669 + /* Renames files with different parents. */ 1670 + ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 1671 + ASSERT_EQ(EXDEV, errno); 1672 + ASSERT_EQ(0, unlink(file1_s1d3)); 1673 + ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1674 + ASSERT_EQ(EXDEV, errno); 1675 + 1676 + /* Exchanges and renames files with same parent. */ 1677 + ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 1678 + RENAME_EXCHANGE)); 1679 + ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 1680 + 1681 + /* Exchanges files and directories with same parent, twice. */ 1682 + ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1683 + RENAME_EXCHANGE)); 1684 + ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1685 + RENAME_EXCHANGE)); 1686 + } 1687 + 1688 + TEST_F_FORK(layout1, rename_dir) 1689 + { 1690 + const struct rule rules[] = { 1691 + { 1692 + .path = dir_s1d2, 1693 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1694 + }, 1695 + { 1696 + .path = dir_s2d1, 1697 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1698 + }, 1699 + {} 1700 + }; 1701 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1702 + rules); 1703 + 1704 + ASSERT_LE(0, ruleset_fd); 1705 + 1706 + /* Empties dir_s1d3 to allow renaming. */ 1707 + ASSERT_EQ(0, unlink(file1_s1d3)); 1708 + ASSERT_EQ(0, unlink(file2_s1d3)); 1709 + 1710 + enforce_ruleset(_metadata, ruleset_fd); 1711 + ASSERT_EQ(0, close(ruleset_fd)); 1712 + 1713 + /* Exchanges and renames directory to a different parent. */ 1714 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 1715 + RENAME_EXCHANGE)); 1716 + ASSERT_EQ(EXDEV, errno); 1717 + ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 1718 + ASSERT_EQ(EXDEV, errno); 1719 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 1720 + RENAME_EXCHANGE)); 1721 + ASSERT_EQ(EXDEV, errno); 1722 + 1723 + /* 1724 + * Exchanges directory to the same parent, which doesn't allow 1725 + * directory removal. 1726 + */ 1727 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 1728 + RENAME_EXCHANGE)); 1729 + ASSERT_EQ(EACCES, errno); 1730 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 1731 + RENAME_EXCHANGE)); 1732 + ASSERT_EQ(EACCES, errno); 1733 + 1734 + /* 1735 + * Exchanges and renames directory to the same parent, which allows 1736 + * directory removal. 1737 + */ 1738 + ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 1739 + RENAME_EXCHANGE)); 1740 + ASSERT_EQ(0, unlink(dir_s1d3)); 1741 + ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1742 + ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 1743 + ASSERT_EQ(0, rmdir(dir_s1d3)); 1744 + } 1745 + 1746 + TEST_F_FORK(layout1, remove_dir) 1747 + { 1748 + const struct rule rules[] = { 1749 + { 1750 + .path = dir_s1d2, 1751 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1752 + }, 1753 + {} 1754 + }; 1755 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1756 + rules); 1757 + 1758 + ASSERT_LE(0, ruleset_fd); 1759 + 1760 + ASSERT_EQ(0, unlink(file1_s1d1)); 1761 + ASSERT_EQ(0, unlink(file1_s1d2)); 1762 + ASSERT_EQ(0, unlink(file1_s1d3)); 1763 + ASSERT_EQ(0, unlink(file2_s1d3)); 1764 + 1765 + enforce_ruleset(_metadata, ruleset_fd); 1766 + ASSERT_EQ(0, close(ruleset_fd)); 1767 + 1768 + ASSERT_EQ(0, rmdir(dir_s1d3)); 1769 + ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1770 + ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 1771 + 1772 + /* dir_s1d2 itself cannot be removed. */ 1773 + ASSERT_EQ(-1, rmdir(dir_s1d2)); 1774 + ASSERT_EQ(EACCES, errno); 1775 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 1776 + ASSERT_EQ(EACCES, errno); 1777 + ASSERT_EQ(-1, rmdir(dir_s1d1)); 1778 + ASSERT_EQ(EACCES, errno); 1779 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 1780 + ASSERT_EQ(EACCES, errno); 1781 + } 1782 + 1783 + TEST_F_FORK(layout1, remove_file) 1784 + { 1785 + const struct rule rules[] = { 1786 + { 1787 + .path = dir_s1d2, 1788 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1789 + }, 1790 + {} 1791 + }; 1792 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1793 + rules); 1794 + 1795 + ASSERT_LE(0, ruleset_fd); 1796 + enforce_ruleset(_metadata, ruleset_fd); 1797 + ASSERT_EQ(0, close(ruleset_fd)); 1798 + 1799 + ASSERT_EQ(-1, unlink(file1_s1d1)); 1800 + ASSERT_EQ(EACCES, errno); 1801 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 1802 + ASSERT_EQ(EACCES, errno); 1803 + ASSERT_EQ(0, unlink(file1_s1d2)); 1804 + ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 1805 + } 1806 + 1807 + static void test_make_file(struct __test_metadata *const _metadata, 1808 + const __u64 access, const mode_t mode, const dev_t dev) 1809 + { 1810 + const struct rule rules[] = { 1811 + { 1812 + .path = dir_s1d2, 1813 + .access = access, 1814 + }, 1815 + {} 1816 + }; 1817 + const int ruleset_fd = create_ruleset(_metadata, access, rules); 1818 + 1819 + ASSERT_LE(0, ruleset_fd); 1820 + 1821 + ASSERT_EQ(0, unlink(file1_s1d1)); 1822 + ASSERT_EQ(0, unlink(file2_s1d1)); 1823 + ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) { 1824 + TH_LOG("Failed to make file \"%s\": %s", 1825 + file2_s1d1, strerror(errno)); 1826 + }; 1827 + 1828 + ASSERT_EQ(0, unlink(file1_s1d2)); 1829 + ASSERT_EQ(0, unlink(file2_s1d2)); 1830 + 1831 + ASSERT_EQ(0, unlink(file1_s1d3)); 1832 + ASSERT_EQ(0, unlink(file2_s1d3)); 1833 + 1834 + enforce_ruleset(_metadata, ruleset_fd); 1835 + ASSERT_EQ(0, close(ruleset_fd)); 1836 + 1837 + ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 1838 + ASSERT_EQ(EACCES, errno); 1839 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1840 + ASSERT_EQ(EACCES, errno); 1841 + ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 1842 + ASSERT_EQ(EACCES, errno); 1843 + 1844 + ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) { 1845 + TH_LOG("Failed to make file \"%s\": %s", 1846 + file1_s1d2, strerror(errno)); 1847 + }; 1848 + ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1849 + ASSERT_EQ(0, unlink(file2_s1d2)); 1850 + ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 1851 + 1852 + ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 1853 + ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1854 + ASSERT_EQ(0, unlink(file2_s1d3)); 1855 + ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 1856 + } 1857 + 1858 + TEST_F_FORK(layout1, make_char) 1859 + { 1860 + /* Creates a /dev/null device. */ 1861 + set_cap(_metadata, CAP_MKNOD); 1862 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 1863 + makedev(1, 3)); 1864 + } 1865 + 1866 + TEST_F_FORK(layout1, make_block) 1867 + { 1868 + /* Creates a /dev/loop0 device. */ 1869 + set_cap(_metadata, CAP_MKNOD); 1870 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 1871 + makedev(7, 0)); 1872 + } 1873 + 1874 + TEST_F_FORK(layout1, make_reg_1) 1875 + { 1876 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 1877 + } 1878 + 1879 + TEST_F_FORK(layout1, make_reg_2) 1880 + { 1881 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 1882 + } 1883 + 1884 + TEST_F_FORK(layout1, make_sock) 1885 + { 1886 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 1887 + } 1888 + 1889 + TEST_F_FORK(layout1, make_fifo) 1890 + { 1891 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 1892 + } 1893 + 1894 + TEST_F_FORK(layout1, make_sym) 1895 + { 1896 + const struct rule rules[] = { 1897 + { 1898 + .path = dir_s1d2, 1899 + .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 1900 + }, 1901 + {} 1902 + }; 1903 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1904 + rules); 1905 + 1906 + ASSERT_LE(0, ruleset_fd); 1907 + 1908 + ASSERT_EQ(0, unlink(file1_s1d1)); 1909 + ASSERT_EQ(0, unlink(file2_s1d1)); 1910 + ASSERT_EQ(0, symlink("none", file2_s1d1)); 1911 + 1912 + ASSERT_EQ(0, unlink(file1_s1d2)); 1913 + ASSERT_EQ(0, unlink(file2_s1d2)); 1914 + 1915 + ASSERT_EQ(0, unlink(file1_s1d3)); 1916 + ASSERT_EQ(0, unlink(file2_s1d3)); 1917 + 1918 + enforce_ruleset(_metadata, ruleset_fd); 1919 + ASSERT_EQ(0, close(ruleset_fd)); 1920 + 1921 + ASSERT_EQ(-1, symlink("none", file1_s1d1)); 1922 + ASSERT_EQ(EACCES, errno); 1923 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1924 + ASSERT_EQ(EACCES, errno); 1925 + ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 1926 + ASSERT_EQ(EACCES, errno); 1927 + 1928 + ASSERT_EQ(0, symlink("none", file1_s1d2)); 1929 + ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1930 + ASSERT_EQ(0, unlink(file2_s1d2)); 1931 + ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 1932 + 1933 + ASSERT_EQ(0, symlink("none", file1_s1d3)); 1934 + ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1935 + ASSERT_EQ(0, unlink(file2_s1d3)); 1936 + ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 1937 + } 1938 + 1939 + TEST_F_FORK(layout1, make_dir) 1940 + { 1941 + const struct rule rules[] = { 1942 + { 1943 + .path = dir_s1d2, 1944 + .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 1945 + }, 1946 + {} 1947 + }; 1948 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1949 + rules); 1950 + 1951 + ASSERT_LE(0, ruleset_fd); 1952 + 1953 + ASSERT_EQ(0, unlink(file1_s1d1)); 1954 + ASSERT_EQ(0, unlink(file1_s1d2)); 1955 + ASSERT_EQ(0, unlink(file1_s1d3)); 1956 + 1957 + enforce_ruleset(_metadata, ruleset_fd); 1958 + ASSERT_EQ(0, close(ruleset_fd)); 1959 + 1960 + /* Uses file_* as directory names. */ 1961 + ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 1962 + ASSERT_EQ(EACCES, errno); 1963 + ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 1964 + ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 1965 + } 1966 + 1967 + static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 1968 + const int open_flags) 1969 + { 1970 + static const char path_template[] = "/proc/self/fd/%d"; 1971 + char procfd_path[sizeof(path_template) + 10]; 1972 + const int procfd_path_size = snprintf(procfd_path, sizeof(procfd_path), 1973 + path_template, fd); 1974 + 1975 + ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 1976 + return open(procfd_path, open_flags); 1977 + } 1978 + 1979 + TEST_F_FORK(layout1, proc_unlinked_file) 1980 + { 1981 + const struct rule rules[] = { 1982 + { 1983 + .path = file1_s1d2, 1984 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 1985 + }, 1986 + {} 1987 + }; 1988 + int reg_fd, proc_fd; 1989 + const int ruleset_fd = create_ruleset(_metadata, 1990 + LANDLOCK_ACCESS_FS_READ_FILE | 1991 + LANDLOCK_ACCESS_FS_WRITE_FILE, rules); 1992 + 1993 + ASSERT_LE(0, ruleset_fd); 1994 + enforce_ruleset(_metadata, ruleset_fd); 1995 + ASSERT_EQ(0, close(ruleset_fd)); 1996 + 1997 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1998 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1999 + reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 2000 + ASSERT_LE(0, reg_fd); 2001 + ASSERT_EQ(0, unlink(file1_s1d2)); 2002 + 2003 + proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 2004 + ASSERT_LE(0, proc_fd); 2005 + ASSERT_EQ(0, close(proc_fd)); 2006 + 2007 + proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 2008 + ASSERT_EQ(-1, proc_fd) { 2009 + TH_LOG("Successfully opened /proc/self/fd/%d: %s", 2010 + reg_fd, strerror(errno)); 2011 + } 2012 + ASSERT_EQ(EACCES, errno); 2013 + 2014 + ASSERT_EQ(0, close(reg_fd)); 2015 + } 2016 + 2017 + TEST_F_FORK(layout1, proc_pipe) 2018 + { 2019 + int proc_fd; 2020 + int pipe_fds[2]; 2021 + char buf = '\0'; 2022 + const struct rule rules[] = { 2023 + { 2024 + .path = dir_s1d2, 2025 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2026 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2027 + }, 2028 + {} 2029 + }; 2030 + /* Limits read and write access to files tied to the filesystem. */ 2031 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 2032 + rules); 2033 + 2034 + ASSERT_LE(0, ruleset_fd); 2035 + enforce_ruleset(_metadata, ruleset_fd); 2036 + ASSERT_EQ(0, close(ruleset_fd)); 2037 + 2038 + /* Checks enforcement for normal files. */ 2039 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 2040 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 2041 + 2042 + /* Checks access to pipes through FD. */ 2043 + ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 2044 + ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) { 2045 + TH_LOG("Failed to write in pipe: %s", strerror(errno)); 2046 + } 2047 + ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 2048 + ASSERT_EQ('.', buf); 2049 + 2050 + /* Checks write access to pipe through /proc/self/fd . */ 2051 + proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 2052 + ASSERT_LE(0, proc_fd); 2053 + ASSERT_EQ(1, write(proc_fd, ".", 1)) { 2054 + TH_LOG("Failed to write through /proc/self/fd/%d: %s", 2055 + pipe_fds[1], strerror(errno)); 2056 + } 2057 + ASSERT_EQ(0, close(proc_fd)); 2058 + 2059 + /* Checks read access to pipe through /proc/self/fd . */ 2060 + proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 2061 + ASSERT_LE(0, proc_fd); 2062 + buf = '\0'; 2063 + ASSERT_EQ(1, read(proc_fd, &buf, 1)) { 2064 + TH_LOG("Failed to read through /proc/self/fd/%d: %s", 2065 + pipe_fds[1], strerror(errno)); 2066 + } 2067 + ASSERT_EQ(0, close(proc_fd)); 2068 + 2069 + ASSERT_EQ(0, close(pipe_fds[0])); 2070 + ASSERT_EQ(0, close(pipe_fds[1])); 2071 + } 2072 + 2073 + FIXTURE(layout1_bind) { 2074 + }; 2075 + 2076 + FIXTURE_SETUP(layout1_bind) 2077 + { 2078 + prepare_layout(_metadata); 2079 + 2080 + create_layout1(_metadata); 2081 + 2082 + set_cap(_metadata, CAP_SYS_ADMIN); 2083 + ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 2084 + clear_cap(_metadata, CAP_SYS_ADMIN); 2085 + } 2086 + 2087 + FIXTURE_TEARDOWN(layout1_bind) 2088 + { 2089 + set_cap(_metadata, CAP_SYS_ADMIN); 2090 + EXPECT_EQ(0, umount(dir_s2d2)); 2091 + clear_cap(_metadata, CAP_SYS_ADMIN); 2092 + 2093 + remove_layout1(_metadata); 2094 + 2095 + cleanup_layout(_metadata); 2096 + } 2097 + 2098 + static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 2099 + static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 2100 + 2101 + /* 2102 + * layout1_bind hierarchy: 2103 + * 2104 + * tmp 2105 + * ├── s1d1 2106 + * │   ├── f1 2107 + * │   ├── f2 2108 + * │   └── s1d2 2109 + * │   ├── f1 2110 + * │   ├── f2 2111 + * │   └── s1d3 2112 + * │   ├── f1 2113 + * │   └── f2 2114 + * ├── s2d1 2115 + * │   ├── f1 2116 + * │   └── s2d2 2117 + * │   ├── f1 2118 + * │   ├── f2 2119 + * │   └── s1d3 2120 + * │   ├── f1 2121 + * │   └── f2 2122 + * └── s3d1 2123 + * └── s3d2 2124 + * └── s3d3 2125 + */ 2126 + 2127 + TEST_F_FORK(layout1_bind, no_restriction) 2128 + { 2129 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2130 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2131 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2132 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2133 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2134 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2135 + 2136 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 2137 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 2138 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 2139 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 2140 + ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 2141 + ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 2142 + 2143 + ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 2144 + ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2145 + 2146 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 2147 + } 2148 + 2149 + TEST_F_FORK(layout1_bind, same_content_same_file) 2150 + { 2151 + /* 2152 + * Sets access right on parent directories of both source and 2153 + * destination mount points. 2154 + */ 2155 + const struct rule layer1_parent[] = { 2156 + { 2157 + .path = dir_s1d1, 2158 + .access = ACCESS_RO, 2159 + }, 2160 + { 2161 + .path = dir_s2d1, 2162 + .access = ACCESS_RW, 2163 + }, 2164 + {} 2165 + }; 2166 + /* 2167 + * Sets access rights on the same bind-mounted directories. The result 2168 + * should be ACCESS_RW for both directories, but not both hierarchies 2169 + * because of the first layer. 2170 + */ 2171 + const struct rule layer2_mount_point[] = { 2172 + { 2173 + .path = dir_s1d2, 2174 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2175 + }, 2176 + { 2177 + .path = dir_s2d2, 2178 + .access = ACCESS_RW, 2179 + }, 2180 + {} 2181 + }; 2182 + /* Only allow read-access to the s1d3 hierarchies. */ 2183 + const struct rule layer3_source[] = { 2184 + { 2185 + .path = dir_s1d3, 2186 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2187 + }, 2188 + {} 2189 + }; 2190 + /* Removes all access rights. */ 2191 + const struct rule layer4_destination[] = { 2192 + { 2193 + .path = bind_file1_s1d3, 2194 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 2195 + }, 2196 + {} 2197 + }; 2198 + int ruleset_fd; 2199 + 2200 + /* Sets rules for the parent directories. */ 2201 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 2202 + ASSERT_LE(0, ruleset_fd); 2203 + enforce_ruleset(_metadata, ruleset_fd); 2204 + ASSERT_EQ(0, close(ruleset_fd)); 2205 + 2206 + /* Checks source hierarchy. */ 2207 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2208 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2209 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2210 + 2211 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2212 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2213 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2214 + 2215 + /* Checks destination hierarchy. */ 2216 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 2217 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2218 + 2219 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2220 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2221 + 2222 + /* Sets rules for the mount points. */ 2223 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 2224 + ASSERT_LE(0, ruleset_fd); 2225 + enforce_ruleset(_metadata, ruleset_fd); 2226 + ASSERT_EQ(0, close(ruleset_fd)); 2227 + 2228 + /* Checks source hierarchy. */ 2229 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 2230 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2231 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2232 + 2233 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2234 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2235 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2236 + 2237 + /* Checks destination hierarchy. */ 2238 + ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 2239 + ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 2240 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2241 + 2242 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2243 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2244 + ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2245 + 2246 + /* Sets a (shared) rule only on the source. */ 2247 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 2248 + ASSERT_LE(0, ruleset_fd); 2249 + enforce_ruleset(_metadata, ruleset_fd); 2250 + ASSERT_EQ(0, close(ruleset_fd)); 2251 + 2252 + /* Checks source hierarchy. */ 2253 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 2254 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2255 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2256 + 2257 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2258 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2259 + ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 2260 + 2261 + /* Checks destination hierarchy. */ 2262 + ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 2263 + ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 2264 + ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2265 + 2266 + ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2267 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2268 + ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2269 + 2270 + /* Sets a (shared) rule only on the destination. */ 2271 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 2272 + ASSERT_LE(0, ruleset_fd); 2273 + enforce_ruleset(_metadata, ruleset_fd); 2274 + ASSERT_EQ(0, close(ruleset_fd)); 2275 + 2276 + /* Checks source hierarchy. */ 2277 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 2278 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2279 + 2280 + /* Checks destination hierarchy. */ 2281 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 2282 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2283 + } 2284 + 2285 + #define LOWER_BASE TMP_DIR "/lower" 2286 + #define LOWER_DATA LOWER_BASE "/data" 2287 + static const char lower_fl1[] = LOWER_DATA "/fl1"; 2288 + static const char lower_dl1[] = LOWER_DATA "/dl1"; 2289 + static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 2290 + static const char lower_fo1[] = LOWER_DATA "/fo1"; 2291 + static const char lower_do1[] = LOWER_DATA "/do1"; 2292 + static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 2293 + static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 2294 + 2295 + static const char (*lower_base_files[])[] = { 2296 + &lower_fl1, 2297 + &lower_fo1, 2298 + NULL 2299 + }; 2300 + static const char (*lower_base_directories[])[] = { 2301 + &lower_dl1, 2302 + &lower_do1, 2303 + NULL 2304 + }; 2305 + static const char (*lower_sub_files[])[] = { 2306 + &lower_dl1_fl2, 2307 + &lower_do1_fo2, 2308 + &lower_do1_fl3, 2309 + NULL 2310 + }; 2311 + 2312 + #define UPPER_BASE TMP_DIR "/upper" 2313 + #define UPPER_DATA UPPER_BASE "/data" 2314 + #define UPPER_WORK UPPER_BASE "/work" 2315 + static const char upper_fu1[] = UPPER_DATA "/fu1"; 2316 + static const char upper_du1[] = UPPER_DATA "/du1"; 2317 + static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 2318 + static const char upper_fo1[] = UPPER_DATA "/fo1"; 2319 + static const char upper_do1[] = UPPER_DATA "/do1"; 2320 + static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 2321 + static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 2322 + 2323 + static const char (*upper_base_files[])[] = { 2324 + &upper_fu1, 2325 + &upper_fo1, 2326 + NULL 2327 + }; 2328 + static const char (*upper_base_directories[])[] = { 2329 + &upper_du1, 2330 + &upper_do1, 2331 + NULL 2332 + }; 2333 + static const char (*upper_sub_files[])[] = { 2334 + &upper_du1_fu2, 2335 + &upper_do1_fo2, 2336 + &upper_do1_fu3, 2337 + NULL 2338 + }; 2339 + 2340 + #define MERGE_BASE TMP_DIR "/merge" 2341 + #define MERGE_DATA MERGE_BASE "/data" 2342 + static const char merge_fl1[] = MERGE_DATA "/fl1"; 2343 + static const char merge_dl1[] = MERGE_DATA "/dl1"; 2344 + static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 2345 + static const char merge_fu1[] = MERGE_DATA "/fu1"; 2346 + static const char merge_du1[] = MERGE_DATA "/du1"; 2347 + static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 2348 + static const char merge_fo1[] = MERGE_DATA "/fo1"; 2349 + static const char merge_do1[] = MERGE_DATA "/do1"; 2350 + static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 2351 + static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 2352 + static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 2353 + 2354 + static const char (*merge_base_files[])[] = { 2355 + &merge_fl1, 2356 + &merge_fu1, 2357 + &merge_fo1, 2358 + NULL 2359 + }; 2360 + static const char (*merge_base_directories[])[] = { 2361 + &merge_dl1, 2362 + &merge_du1, 2363 + &merge_do1, 2364 + NULL 2365 + }; 2366 + static const char (*merge_sub_files[])[] = { 2367 + &merge_dl1_fl2, 2368 + &merge_du1_fu2, 2369 + &merge_do1_fo2, 2370 + &merge_do1_fl3, 2371 + &merge_do1_fu3, 2372 + NULL 2373 + }; 2374 + 2375 + /* 2376 + * layout2_overlay hierarchy: 2377 + * 2378 + * tmp 2379 + * ├── lower 2380 + * │   └── data 2381 + * │   ├── dl1 2382 + * │   │   └── fl2 2383 + * │   ├── do1 2384 + * │   │   ├── fl3 2385 + * │   │   └── fo2 2386 + * │   ├── fl1 2387 + * │   └── fo1 2388 + * ├── merge 2389 + * │   └── data 2390 + * │   ├── dl1 2391 + * │   │   └── fl2 2392 + * │   ├── do1 2393 + * │   │   ├── fl3 2394 + * │   │   ├── fo2 2395 + * │   │   └── fu3 2396 + * │   ├── du1 2397 + * │   │   └── fu2 2398 + * │   ├── fl1 2399 + * │   ├── fo1 2400 + * │   └── fu1 2401 + * └── upper 2402 + * ├── data 2403 + * │   ├── do1 2404 + * │   │   ├── fo2 2405 + * │   │   └── fu3 2406 + * │   ├── du1 2407 + * │   │   └── fu2 2408 + * │   ├── fo1 2409 + * │   └── fu1 2410 + * └── work 2411 + * └── work 2412 + */ 2413 + 2414 + FIXTURE(layout2_overlay) { 2415 + }; 2416 + 2417 + FIXTURE_SETUP(layout2_overlay) 2418 + { 2419 + prepare_layout(_metadata); 2420 + 2421 + create_directory(_metadata, LOWER_BASE); 2422 + set_cap(_metadata, CAP_SYS_ADMIN); 2423 + /* Creates tmpfs mount points to get deterministic overlayfs. */ 2424 + ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2425 + clear_cap(_metadata, CAP_SYS_ADMIN); 2426 + create_file(_metadata, lower_fl1); 2427 + create_file(_metadata, lower_dl1_fl2); 2428 + create_file(_metadata, lower_fo1); 2429 + create_file(_metadata, lower_do1_fo2); 2430 + create_file(_metadata, lower_do1_fl3); 2431 + 2432 + create_directory(_metadata, UPPER_BASE); 2433 + set_cap(_metadata, CAP_SYS_ADMIN); 2434 + ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2435 + clear_cap(_metadata, CAP_SYS_ADMIN); 2436 + create_file(_metadata, upper_fu1); 2437 + create_file(_metadata, upper_du1_fu2); 2438 + create_file(_metadata, upper_fo1); 2439 + create_file(_metadata, upper_do1_fo2); 2440 + create_file(_metadata, upper_do1_fu3); 2441 + ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 2442 + 2443 + create_directory(_metadata, MERGE_DATA); 2444 + set_cap(_metadata, CAP_SYS_ADMIN); 2445 + set_cap(_metadata, CAP_DAC_OVERRIDE); 2446 + ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 2447 + "lowerdir=" LOWER_DATA 2448 + ",upperdir=" UPPER_DATA 2449 + ",workdir=" UPPER_WORK)); 2450 + clear_cap(_metadata, CAP_DAC_OVERRIDE); 2451 + clear_cap(_metadata, CAP_SYS_ADMIN); 2452 + } 2453 + 2454 + FIXTURE_TEARDOWN(layout2_overlay) 2455 + { 2456 + EXPECT_EQ(0, remove_path(lower_do1_fl3)); 2457 + EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 2458 + EXPECT_EQ(0, remove_path(lower_fl1)); 2459 + EXPECT_EQ(0, remove_path(lower_do1_fo2)); 2460 + EXPECT_EQ(0, remove_path(lower_fo1)); 2461 + set_cap(_metadata, CAP_SYS_ADMIN); 2462 + EXPECT_EQ(0, umount(LOWER_BASE)); 2463 + clear_cap(_metadata, CAP_SYS_ADMIN); 2464 + EXPECT_EQ(0, remove_path(LOWER_BASE)); 2465 + 2466 + EXPECT_EQ(0, remove_path(upper_do1_fu3)); 2467 + EXPECT_EQ(0, remove_path(upper_du1_fu2)); 2468 + EXPECT_EQ(0, remove_path(upper_fu1)); 2469 + EXPECT_EQ(0, remove_path(upper_do1_fo2)); 2470 + EXPECT_EQ(0, remove_path(upper_fo1)); 2471 + EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 2472 + set_cap(_metadata, CAP_SYS_ADMIN); 2473 + EXPECT_EQ(0, umount(UPPER_BASE)); 2474 + clear_cap(_metadata, CAP_SYS_ADMIN); 2475 + EXPECT_EQ(0, remove_path(UPPER_BASE)); 2476 + 2477 + set_cap(_metadata, CAP_SYS_ADMIN); 2478 + EXPECT_EQ(0, umount(MERGE_DATA)); 2479 + clear_cap(_metadata, CAP_SYS_ADMIN); 2480 + EXPECT_EQ(0, remove_path(MERGE_DATA)); 2481 + 2482 + cleanup_layout(_metadata); 2483 + } 2484 + 2485 + TEST_F_FORK(layout2_overlay, no_restriction) 2486 + { 2487 + ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 2488 + ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 2489 + ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 2490 + ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 2491 + ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 2492 + ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 2493 + ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 2494 + 2495 + ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 2496 + ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 2497 + ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 2498 + ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 2499 + ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 2500 + ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 2501 + ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 2502 + 2503 + ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 2504 + ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 2505 + ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 2506 + ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 2507 + ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 2508 + ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 2509 + ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 2510 + ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 2511 + ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 2512 + ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 2513 + ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 2514 + } 2515 + 2516 + #define for_each_path(path_list, path_entry, i) \ 2517 + for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 2518 + path_entry = *path_list[++i]) 2519 + 2520 + TEST_F_FORK(layout2_overlay, same_content_different_file) 2521 + { 2522 + /* Sets access right on parent directories of both layers. */ 2523 + const struct rule layer1_base[] = { 2524 + { 2525 + .path = LOWER_BASE, 2526 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2527 + }, 2528 + { 2529 + .path = UPPER_BASE, 2530 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2531 + }, 2532 + { 2533 + .path = MERGE_BASE, 2534 + .access = ACCESS_RW, 2535 + }, 2536 + {} 2537 + }; 2538 + const struct rule layer2_data[] = { 2539 + { 2540 + .path = LOWER_DATA, 2541 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2542 + }, 2543 + { 2544 + .path = UPPER_DATA, 2545 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2546 + }, 2547 + { 2548 + .path = MERGE_DATA, 2549 + .access = ACCESS_RW, 2550 + }, 2551 + {} 2552 + }; 2553 + /* Sets access right on directories inside both layers. */ 2554 + const struct rule layer3_subdirs[] = { 2555 + { 2556 + .path = lower_dl1, 2557 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2558 + }, 2559 + { 2560 + .path = lower_do1, 2561 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2562 + }, 2563 + { 2564 + .path = upper_du1, 2565 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2566 + }, 2567 + { 2568 + .path = upper_do1, 2569 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2570 + }, 2571 + { 2572 + .path = merge_dl1, 2573 + .access = ACCESS_RW, 2574 + }, 2575 + { 2576 + .path = merge_du1, 2577 + .access = ACCESS_RW, 2578 + }, 2579 + { 2580 + .path = merge_do1, 2581 + .access = ACCESS_RW, 2582 + }, 2583 + {} 2584 + }; 2585 + /* Tighten access rights to the files. */ 2586 + const struct rule layer4_files[] = { 2587 + { 2588 + .path = lower_dl1_fl2, 2589 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2590 + }, 2591 + { 2592 + .path = lower_do1_fo2, 2593 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2594 + }, 2595 + { 2596 + .path = lower_do1_fl3, 2597 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2598 + }, 2599 + { 2600 + .path = upper_du1_fu2, 2601 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2602 + }, 2603 + { 2604 + .path = upper_do1_fo2, 2605 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2606 + }, 2607 + { 2608 + .path = upper_do1_fu3, 2609 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2610 + }, 2611 + { 2612 + .path = merge_dl1_fl2, 2613 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2614 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2615 + }, 2616 + { 2617 + .path = merge_du1_fu2, 2618 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2619 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2620 + }, 2621 + { 2622 + .path = merge_do1_fo2, 2623 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2624 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2625 + }, 2626 + { 2627 + .path = merge_do1_fl3, 2628 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2629 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2630 + }, 2631 + { 2632 + .path = merge_do1_fu3, 2633 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2634 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2635 + }, 2636 + {} 2637 + }; 2638 + const struct rule layer5_merge_only[] = { 2639 + { 2640 + .path = MERGE_DATA, 2641 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2642 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2643 + }, 2644 + {} 2645 + }; 2646 + int ruleset_fd; 2647 + size_t i; 2648 + const char *path_entry; 2649 + 2650 + /* Sets rules on base directories (i.e. outside overlay scope). */ 2651 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 2652 + ASSERT_LE(0, ruleset_fd); 2653 + enforce_ruleset(_metadata, ruleset_fd); 2654 + ASSERT_EQ(0, close(ruleset_fd)); 2655 + 2656 + /* Checks lower layer. */ 2657 + for_each_path(lower_base_files, path_entry, i) { 2658 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2659 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2660 + } 2661 + for_each_path(lower_base_directories, path_entry, i) { 2662 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2663 + } 2664 + for_each_path(lower_sub_files, path_entry, i) { 2665 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2666 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2667 + } 2668 + /* Checks upper layer. */ 2669 + for_each_path(upper_base_files, path_entry, i) { 2670 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2671 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2672 + } 2673 + for_each_path(upper_base_directories, path_entry, i) { 2674 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2675 + } 2676 + for_each_path(upper_sub_files, path_entry, i) { 2677 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2678 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2679 + } 2680 + /* 2681 + * Checks that access rights are independent from the lower and upper 2682 + * layers: write access to upper files viewed through the merge point 2683 + * is still allowed, and write access to lower file viewed (and copied) 2684 + * through the merge point is still allowed. 2685 + */ 2686 + for_each_path(merge_base_files, path_entry, i) { 2687 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2688 + } 2689 + for_each_path(merge_base_directories, path_entry, i) { 2690 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2691 + } 2692 + for_each_path(merge_sub_files, path_entry, i) { 2693 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2694 + } 2695 + 2696 + /* Sets rules on data directories (i.e. inside overlay scope). */ 2697 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 2698 + ASSERT_LE(0, ruleset_fd); 2699 + enforce_ruleset(_metadata, ruleset_fd); 2700 + ASSERT_EQ(0, close(ruleset_fd)); 2701 + 2702 + /* Checks merge. */ 2703 + for_each_path(merge_base_files, path_entry, i) { 2704 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2705 + } 2706 + for_each_path(merge_base_directories, path_entry, i) { 2707 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2708 + } 2709 + for_each_path(merge_sub_files, path_entry, i) { 2710 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2711 + } 2712 + 2713 + /* Same checks with tighter rules. */ 2714 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 2715 + ASSERT_LE(0, ruleset_fd); 2716 + enforce_ruleset(_metadata, ruleset_fd); 2717 + ASSERT_EQ(0, close(ruleset_fd)); 2718 + 2719 + /* Checks changes for lower layer. */ 2720 + for_each_path(lower_base_files, path_entry, i) { 2721 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2722 + } 2723 + /* Checks changes for upper layer. */ 2724 + for_each_path(upper_base_files, path_entry, i) { 2725 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2726 + } 2727 + /* Checks all merge accesses. */ 2728 + for_each_path(merge_base_files, path_entry, i) { 2729 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2730 + } 2731 + for_each_path(merge_base_directories, path_entry, i) { 2732 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2733 + } 2734 + for_each_path(merge_sub_files, path_entry, i) { 2735 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2736 + } 2737 + 2738 + /* Sets rules directly on overlayed files. */ 2739 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 2740 + ASSERT_LE(0, ruleset_fd); 2741 + enforce_ruleset(_metadata, ruleset_fd); 2742 + ASSERT_EQ(0, close(ruleset_fd)); 2743 + 2744 + /* Checks unchanged accesses on lower layer. */ 2745 + for_each_path(lower_sub_files, path_entry, i) { 2746 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2747 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2748 + } 2749 + /* Checks unchanged accesses on upper layer. */ 2750 + for_each_path(upper_sub_files, path_entry, i) { 2751 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2752 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2753 + } 2754 + /* Checks all merge accesses. */ 2755 + for_each_path(merge_base_files, path_entry, i) { 2756 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2757 + } 2758 + for_each_path(merge_base_directories, path_entry, i) { 2759 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2760 + } 2761 + for_each_path(merge_sub_files, path_entry, i) { 2762 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2763 + } 2764 + 2765 + /* Only allowes access to the merge hierarchy. */ 2766 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 2767 + ASSERT_LE(0, ruleset_fd); 2768 + enforce_ruleset(_metadata, ruleset_fd); 2769 + ASSERT_EQ(0, close(ruleset_fd)); 2770 + 2771 + /* Checks new accesses on lower layer. */ 2772 + for_each_path(lower_sub_files, path_entry, i) { 2773 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2774 + } 2775 + /* Checks new accesses on upper layer. */ 2776 + for_each_path(upper_sub_files, path_entry, i) { 2777 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2778 + } 2779 + /* Checks all merge accesses. */ 2780 + for_each_path(merge_base_files, path_entry, i) { 2781 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2782 + } 2783 + for_each_path(merge_base_directories, path_entry, i) { 2784 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2785 + } 2786 + for_each_path(merge_sub_files, path_entry, i) { 2787 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2788 + } 2789 + } 2790 + 2791 + TEST_HARNESS_MAIN
+337
tools/testing/selftests/landlock/ptrace_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Ptrace 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #define _GNU_SOURCE 10 + #include <errno.h> 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <signal.h> 14 + #include <sys/prctl.h> 15 + #include <sys/ptrace.h> 16 + #include <sys/types.h> 17 + #include <sys/wait.h> 18 + #include <unistd.h> 19 + 20 + #include "common.h" 21 + 22 + static void create_domain(struct __test_metadata *const _metadata) 23 + { 24 + int ruleset_fd; 25 + struct landlock_ruleset_attr ruleset_attr = { 26 + .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK, 27 + }; 28 + 29 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 30 + sizeof(ruleset_attr), 0); 31 + EXPECT_LE(0, ruleset_fd) { 32 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 33 + } 34 + EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 35 + EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 36 + EXPECT_EQ(0, close(ruleset_fd)); 37 + } 38 + 39 + static int test_ptrace_read(const pid_t pid) 40 + { 41 + static const char path_template[] = "/proc/%d/environ"; 42 + char procenv_path[sizeof(path_template) + 10]; 43 + int procenv_path_size, fd; 44 + 45 + procenv_path_size = snprintf(procenv_path, sizeof(procenv_path), 46 + path_template, pid); 47 + if (procenv_path_size >= sizeof(procenv_path)) 48 + return E2BIG; 49 + 50 + fd = open(procenv_path, O_RDONLY | O_CLOEXEC); 51 + if (fd < 0) 52 + return errno; 53 + /* 54 + * Mixing error codes from close(2) and open(2) should not lead to any 55 + * (access type) confusion for this test. 56 + */ 57 + if (close(fd) != 0) 58 + return errno; 59 + return 0; 60 + } 61 + 62 + FIXTURE(hierarchy) { }; 63 + 64 + FIXTURE_VARIANT(hierarchy) { 65 + const bool domain_both; 66 + const bool domain_parent; 67 + const bool domain_child; 68 + }; 69 + 70 + /* 71 + * Test multiple tracing combinations between a parent process P1 and a child 72 + * process P2. 73 + * 74 + * Yama's scoped ptrace is presumed disabled. If enabled, this optional 75 + * restriction is enforced in addition to any Landlock check, which means that 76 + * all P2 requests to trace P1 would be denied. 77 + */ 78 + 79 + /* 80 + * No domain 81 + * 82 + * P1-. P1 -> P2 : allow 83 + * \ P2 -> P1 : allow 84 + * 'P2 85 + */ 86 + FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { 87 + .domain_both = false, 88 + .domain_parent = false, 89 + .domain_child = false, 90 + }; 91 + 92 + /* 93 + * Child domain 94 + * 95 + * P1--. P1 -> P2 : allow 96 + * \ P2 -> P1 : deny 97 + * .'-----. 98 + * | P2 | 99 + * '------' 100 + */ 101 + FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { 102 + .domain_both = false, 103 + .domain_parent = false, 104 + .domain_child = true, 105 + }; 106 + 107 + /* 108 + * Parent domain 109 + * .------. 110 + * | P1 --. P1 -> P2 : deny 111 + * '------' \ P2 -> P1 : allow 112 + * ' 113 + * P2 114 + */ 115 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { 116 + .domain_both = false, 117 + .domain_parent = true, 118 + .domain_child = false, 119 + }; 120 + 121 + /* 122 + * Parent + child domain (siblings) 123 + * .------. 124 + * | P1 ---. P1 -> P2 : deny 125 + * '------' \ P2 -> P1 : deny 126 + * .---'--. 127 + * | P2 | 128 + * '------' 129 + */ 130 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { 131 + .domain_both = false, 132 + .domain_parent = true, 133 + .domain_child = true, 134 + }; 135 + 136 + /* 137 + * Same domain (inherited) 138 + * .-------------. 139 + * | P1----. | P1 -> P2 : allow 140 + * | \ | P2 -> P1 : allow 141 + * | ' | 142 + * | P2 | 143 + * '-------------' 144 + */ 145 + FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { 146 + .domain_both = true, 147 + .domain_parent = false, 148 + .domain_child = false, 149 + }; 150 + 151 + /* 152 + * Inherited + child domain 153 + * .-----------------. 154 + * | P1----. | P1 -> P2 : allow 155 + * | \ | P2 -> P1 : deny 156 + * | .-'----. | 157 + * | | P2 | | 158 + * | '------' | 159 + * '-----------------' 160 + */ 161 + FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { 162 + .domain_both = true, 163 + .domain_parent = false, 164 + .domain_child = true, 165 + }; 166 + 167 + /* 168 + * Inherited + parent domain 169 + * .-----------------. 170 + * |.------. | P1 -> P2 : deny 171 + * || P1 ----. | P2 -> P1 : allow 172 + * |'------' \ | 173 + * | ' | 174 + * | P2 | 175 + * '-----------------' 176 + */ 177 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { 178 + .domain_both = true, 179 + .domain_parent = true, 180 + .domain_child = false, 181 + }; 182 + 183 + /* 184 + * Inherited + parent and child domain (siblings) 185 + * .-----------------. 186 + * | .------. | P1 -> P2 : deny 187 + * | | P1 . | P2 -> P1 : deny 188 + * | '------'\ | 189 + * | \ | 190 + * | .--'---. | 191 + * | | P2 | | 192 + * | '------' | 193 + * '-----------------' 194 + */ 195 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) { 196 + .domain_both = true, 197 + .domain_parent = true, 198 + .domain_child = true, 199 + }; 200 + 201 + FIXTURE_SETUP(hierarchy) 202 + { } 203 + 204 + FIXTURE_TEARDOWN(hierarchy) 205 + { } 206 + 207 + /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */ 208 + TEST_F(hierarchy, trace) 209 + { 210 + pid_t child, parent; 211 + int status, err_proc_read; 212 + int pipe_child[2], pipe_parent[2]; 213 + char buf_parent; 214 + long ret; 215 + 216 + /* 217 + * Removes all effective and permitted capabilities to not interfere 218 + * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS. 219 + */ 220 + drop_caps(_metadata); 221 + 222 + parent = getpid(); 223 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 224 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 225 + if (variant->domain_both) { 226 + create_domain(_metadata); 227 + if (!_metadata->passed) 228 + /* Aborts before forking. */ 229 + return; 230 + } 231 + 232 + child = fork(); 233 + ASSERT_LE(0, child); 234 + if (child == 0) { 235 + char buf_child; 236 + 237 + ASSERT_EQ(0, close(pipe_parent[1])); 238 + ASSERT_EQ(0, close(pipe_child[0])); 239 + if (variant->domain_child) 240 + create_domain(_metadata); 241 + 242 + /* Waits for the parent to be in a domain, if any. */ 243 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 244 + 245 + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the parent. */ 246 + err_proc_read = test_ptrace_read(parent); 247 + ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); 248 + if (variant->domain_child) { 249 + EXPECT_EQ(-1, ret); 250 + EXPECT_EQ(EPERM, errno); 251 + EXPECT_EQ(EACCES, err_proc_read); 252 + } else { 253 + EXPECT_EQ(0, ret); 254 + EXPECT_EQ(0, err_proc_read); 255 + } 256 + if (ret == 0) { 257 + ASSERT_EQ(parent, waitpid(parent, &status, 0)); 258 + ASSERT_EQ(1, WIFSTOPPED(status)); 259 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); 260 + } 261 + 262 + /* Tests child PTRACE_TRACEME. */ 263 + ret = ptrace(PTRACE_TRACEME); 264 + if (variant->domain_parent) { 265 + EXPECT_EQ(-1, ret); 266 + EXPECT_EQ(EPERM, errno); 267 + } else { 268 + EXPECT_EQ(0, ret); 269 + } 270 + 271 + /* 272 + * Signals that the PTRACE_ATTACH test is done and the 273 + * PTRACE_TRACEME test is ongoing. 274 + */ 275 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 276 + 277 + if (!variant->domain_parent) { 278 + ASSERT_EQ(0, raise(SIGSTOP)); 279 + } 280 + 281 + /* Waits for the parent PTRACE_ATTACH test. */ 282 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 283 + _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 284 + return; 285 + } 286 + 287 + ASSERT_EQ(0, close(pipe_child[1])); 288 + ASSERT_EQ(0, close(pipe_parent[0])); 289 + if (variant->domain_parent) 290 + create_domain(_metadata); 291 + 292 + /* Signals that the parent is in a domain, if any. */ 293 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 294 + 295 + /* 296 + * Waits for the child to test PTRACE_ATTACH on the parent and start 297 + * testing PTRACE_TRACEME. 298 + */ 299 + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 300 + 301 + /* Tests child PTRACE_TRACEME. */ 302 + if (!variant->domain_parent) { 303 + ASSERT_EQ(child, waitpid(child, &status, 0)); 304 + ASSERT_EQ(1, WIFSTOPPED(status)); 305 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 306 + } else { 307 + /* The child should not be traced by the parent. */ 308 + EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); 309 + EXPECT_EQ(ESRCH, errno); 310 + } 311 + 312 + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ 313 + err_proc_read = test_ptrace_read(child); 314 + ret = ptrace(PTRACE_ATTACH, child, NULL, 0); 315 + if (variant->domain_parent) { 316 + EXPECT_EQ(-1, ret); 317 + EXPECT_EQ(EPERM, errno); 318 + EXPECT_EQ(EACCES, err_proc_read); 319 + } else { 320 + EXPECT_EQ(0, ret); 321 + EXPECT_EQ(0, err_proc_read); 322 + } 323 + if (ret == 0) { 324 + ASSERT_EQ(child, waitpid(child, &status, 0)); 325 + ASSERT_EQ(1, WIFSTOPPED(status)); 326 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 327 + } 328 + 329 + /* Signals that the parent PTRACE_ATTACH test is done. */ 330 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 331 + ASSERT_EQ(child, waitpid(child, &status, 0)); 332 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 333 + WEXITSTATUS(status) != EXIT_SUCCESS) 334 + _metadata->passed = 0; 335 + } 336 + 337 + TEST_HARNESS_MAIN
+5
tools/testing/selftests/landlock/true.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + int main(void) 3 + { 4 + return 0; 5 + }