History

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

Linus Torvalds committed 5 years ago

Commit 17ae69a2 parents e6f0bf0 3532b0b72 files · +6987 −77Browse files

Unified · Split

72 files
  1. Documentation/security/index.rst +1 −0
  2. Documentation/security/landlock.rst +85 −0
  3. Documentation/userspace-api/index.rst +1 −0
  4. Documentation/userspace-api/landlock.rst +311 −0
  5. MAINTAINERS +15 −0
  6. arch/Kconfig +7 −0
  7. arch/alpha/kernel/syscalls/syscall.tbl +3 −0
  8. arch/arm/tools/syscall.tbl +3 −0
  9. arch/arm64/include/asm/unistd.h +1 −1
  10. arch/arm64/include/asm/unistd32.h +6 −0
  11. arch/ia64/kernel/syscalls/syscall.tbl +3 −0
  12. arch/m68k/kernel/syscalls/syscall.tbl +3 −0
  13. arch/microblaze/kernel/syscalls/syscall.tbl +3 −0
  14. arch/mips/kernel/syscalls/syscall_n32.tbl +3 −0
  15. arch/mips/kernel/syscalls/syscall_n64.tbl +3 −0
  16. arch/mips/kernel/syscalls/syscall_o32.tbl +3 −0
  17. arch/parisc/kernel/syscalls/syscall.tbl +3 −0
  18. arch/powerpc/kernel/syscalls/syscall.tbl +3 −0
  19. arch/s390/kernel/syscalls/syscall.tbl +3 −0
  20. arch/sh/kernel/syscalls/syscall.tbl +3 −0
  21. arch/sparc/kernel/syscalls/syscall.tbl +3 −0
  22. arch/um/Kconfig +1 −0
  23. arch/x86/entry/syscalls/syscall_32.tbl +3 −0
  24. arch/x86/entry/syscalls/syscall_64.tbl +3 −0
  25. arch/xtensa/kernel/syscalls/syscall.tbl +3 −0
  26. fs/super.c +1 −0
  27. include/linux/lsm_hook_defs.h +1 −0
  28. include/linux/lsm_hooks.h +4 −0
  29. include/linux/security.h +4 −0
  30. include/linux/syscalls.h +7 −0
  31. include/uapi/asm-generic/unistd.h +8 −1
  32. include/uapi/linux/landlock.h +137 −0
  33. kernel/sys_ni.c +5 −0
  34. samples/Kconfig +7 −0
  35. samples/Makefile +1 −0
  36. samples/landlock/.gitignore +1 −0
  37. samples/landlock/Makefile +13 −0
  38. samples/landlock/sandboxer.c +238 −0
  39. security/Kconfig +6 −5
  40. security/Makefile +2 −0
  41. security/landlock/Kconfig +21 −0
  42. security/landlock/Makefile +4 −0
  43. security/landlock/common.h +20 −0
  44. security/landlock/cred.c +46 −0
  45. security/landlock/cred.h +58 −0
  46. security/landlock/fs.c +692 −0
  47. security/landlock/fs.h +70 −0
  48. security/landlock/limits.h +21 −0
  49. security/landlock/object.c +67 −0
  50. security/landlock/object.h +91 −0
  51. security/landlock/ptrace.c +120 −0
  52. security/landlock/ptrace.h +14 −0
  53. security/landlock/ruleset.c +473 −0
  54. security/landlock/ruleset.h +165 −0
  55. security/landlock/setup.c +40 −0
  56. security/landlock/setup.h +18 −0
  57. security/landlock/syscalls.c +451 −0
  58. security/security.c +44 −7
  59. security/selinux/hooks.c +22 −36
  60. security/selinux/include/objsec.h +6 −0
  61. security/selinux/ss/services.c +2 −1
  62. security/smack/smack.h +6 −0
  63. security/smack/smack_lsm.c +9 −26
  64. tools/testing/selftests/Makefile +1 −0
  65. tools/testing/selftests/landlock/.gitignore +2 −0
  66. tools/testing/selftests/landlock/Makefile +24 −0
  67. tools/testing/selftests/landlock/base_test.c +266 −0
  68. tools/testing/selftests/landlock/common.h +183 −0
  69. tools/testing/selftests/landlock/config +7 −0
  70. tools/testing/selftests/landlock/fs_test.c +2791 −0
  71. tools/testing/selftests/landlock/ptrace_test.c +337 −0
  72. tools/testing/selftests/landlock/true.c +5 −0
Documentation/security/index.rst+1−0
@@ -16,3 +16,4 @@ Security Documentation · 15 unmodified lines
1616 siphash
1717 tpm/index
1818 digsig
19+ landlock
Documentation/security/landlock.rst+85−0
@@ -0,0 +1,85 @@
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/
Documentation/userspace-api/index.rst+1−0
@@ -18,6 +18,7 @@ place where this information is gathered. · 17 unmodified lines
1818
1919 no_new_privs
2020 seccomp_filter
21+ landlock
2122 unshare
2223 spec_ctrl
2324 accelerators/ocxl
Documentation/userspace-api/landlock.rst+311−0
@@ -0,0 +1,311 @@
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
MAINTAINERS+15−0
@@ -10191,6 +10191,21 @@ F: net/core/sock_map.c · 10190 unmodified lines
1019110191 F: net/ipv4/tcp_bpf.c
1019210192 F: net/ipv4/udp_bpf.c
1019310193
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+
1019410209 LANTIQ / INTEL Ethernet drivers
1019510210 M: Hauke Mehrtens <hauke@hauke-m.de>
1019610211 L: netdev@vger.kernel.org
arch/Kconfig+7−0
@@ -1068,6 +1068,13 @@ config COMPAT_32BIT_TIME · 1067 unmodified lines
10681068 config ARCH_NO_PREEMPT
10691069 bool
10701070
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+
10711078 config ARCH_SUPPORTS_RT
10721079 bool
10731080
arch/alpha/kernel/syscalls/syscall.tbl+3−0
@@ -483,3 +483,6 @@ · 482 unmodified lines
483483 551 common epoll_pwait2 sys_epoll_pwait2
484484 552 common mount_setattr sys_mount_setattr
485485 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
arch/arm/tools/syscall.tbl+3−0
@@ -457,3 +457,6 @@ · 456 unmodified lines
457457 441 common epoll_pwait2 sys_epoll_pwait2
458458 442 common mount_setattr sys_mount_setattr
459459 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
arch/arm64/include/asm/unistd.h+1−1
@@ -38,7 +38,7 @@ · 37 unmodified lines
3838 #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
3939 #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
4040
41−#define __NR_compat_syscalls 444
41+#define __NR_compat_syscalls 447
4242 #endif
4343
4444 #define __ARCH_WANT_SYS_CLONE
arch/arm64/include/asm/unistd32.h+6−0
@@ -895,6 +895,12 @@ __SYSCALL(__NR_epoll_pwait2, compat_sys_epoll_pwait2) · 894 unmodified lines
895895 __SYSCALL(__NR_mount_setattr, sys_mount_setattr)
896896 #define __NR_quotactl_path 443
897897 __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)
898904
899905 /*
900906 * Please add new compat syscalls above this comment and update
arch/ia64/kernel/syscalls/syscall.tbl+3−0
@@ -364,3 +364,6 @@ · 363 unmodified lines
364364 441 common epoll_pwait2 sys_epoll_pwait2
365365 442 common mount_setattr sys_mount_setattr
366366 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
arch/m68k/kernel/syscalls/syscall.tbl+3−0
@@ -443,3 +443,6 @@ · 442 unmodified lines
443443 441 common epoll_pwait2 sys_epoll_pwait2
444444 442 common mount_setattr sys_mount_setattr
445445 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
arch/microblaze/kernel/syscalls/syscall.tbl+3−0
@@ -449,3 +449,6 @@ · 448 unmodified lines
449449 441 common epoll_pwait2 sys_epoll_pwait2
450450 442 common mount_setattr sys_mount_setattr
451451 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
arch/mips/kernel/syscalls/syscall_n32.tbl+3−0
@@ -382,3 +382,6 @@ · 381 unmodified lines
382382 441 n32 epoll_pwait2 compat_sys_epoll_pwait2
383383 442 n32 mount_setattr sys_mount_setattr
384384 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
arch/mips/kernel/syscalls/syscall_n64.tbl+3−0
@@ -358,3 +358,6 @@ · 357 unmodified lines
358358 441 n64 epoll_pwait2 sys_epoll_pwait2
359359 442 n64 mount_setattr sys_mount_setattr
360360 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
arch/mips/kernel/syscalls/syscall_o32.tbl+3−0
@@ -431,3 +431,6 @@ · 430 unmodified lines
431431 441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
432432 442 o32 mount_setattr sys_mount_setattr
433433 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
arch/parisc/kernel/syscalls/syscall.tbl+3−0
@@ -441,3 +441,6 @@ · 440 unmodified lines
441441 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
442442 442 common mount_setattr sys_mount_setattr
443443 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
arch/powerpc/kernel/syscalls/syscall.tbl+3−0
@@ -523,3 +523,6 @@ · 522 unmodified lines
523523 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
524524 442 common mount_setattr sys_mount_setattr
525525 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
arch/s390/kernel/syscalls/syscall.tbl+3−0
@@ -446,3 +446,6 @@ · 445 unmodified lines
446446 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
447447 442 common mount_setattr sys_mount_setattr sys_mount_setattr
448448 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
arch/sh/kernel/syscalls/syscall.tbl+3−0
@@ -446,3 +446,6 @@ · 445 unmodified lines
446446 441 common epoll_pwait2 sys_epoll_pwait2
447447 442 common mount_setattr sys_mount_setattr
448448 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
arch/sparc/kernel/syscalls/syscall.tbl+3−0
@@ -489,3 +489,6 @@ · 488 unmodified lines
489489 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
490490 442 common mount_setattr sys_mount_setattr
491491 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
arch/um/Kconfig+1−0
@@ -5,6 +5,7 @@ menu "UML-specific options" · 4 unmodified lines
55 config UML
66 bool
77 default y
8+ select ARCH_EPHEMERAL_INODES
89 select ARCH_HAS_KCOV
910 select ARCH_NO_PREEMPT
1011 select HAVE_ARCH_AUDITSYSCALL
arch/x86/entry/syscalls/syscall_32.tbl+3−0
@@ -448,3 +448,6 @@ · 447 unmodified lines
448448 441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
449449 442 i386 mount_setattr sys_mount_setattr
450450 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
arch/x86/entry/syscalls/syscall_64.tbl+3−0
@@ -365,6 +365,9 @@ · 364 unmodified lines
365365 441 common epoll_pwait2 sys_epoll_pwait2
366366 442 common mount_setattr sys_mount_setattr
367367 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
368371
369372 #
370373 # Due to a historical design error, certain syscalls are numbered differently
arch/xtensa/kernel/syscalls/syscall.tbl+3−0
@@ -414,3 +414,6 @@ · 413 unmodified lines
414414 441 common epoll_pwait2 sys_epoll_pwait2
415415 442 common mount_setattr sys_mount_setattr
416416 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
fs/super.c+1−0
@@ -454,6 +454,7 @@ void generic_shutdown_super(struct super_block *sb) · 453 unmodified lines
454454 evict_inodes(sb);
455455 /* only nonzero refcount inodes can have marks */
456456 fsnotify_sb_delete(sb);
457+ security_sb_delete(sb);
457458
458459 if (sb->s_dio_done_wq) {
459460 destroy_workqueue(sb->s_dio_done_wq);
include/linux/lsm_hook_defs.h+1−0
@@ -59,6 +59,7 @@ LSM_HOOK(int, 0, fs_context_dup, struct fs_context *fc, · 58 unmodified lines
5959 LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,
6060 struct fs_parameter *param)
6161 LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
62+LSM_HOOK(void, LSM_RET_VOID, sb_delete, struct super_block *sb)
6263 LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
6364 LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
6465 LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
include/linux/lsm_hooks.h+4−0
@@ -108,6 +108,9 @@ · 107 unmodified lines
108108 * allocated.
109109 * @sb contains the super_block structure to be modified.
110110 * 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.
111114 * @sb_free_security:
112115 * Deallocate and clear the sb->s_security field.
113116 * @sb contains the super_block structure to be modified.
@@ -1585,6 +1588,7 @@ struct lsm_blob_sizes { · 1471 unmodified lines
15851588 int lbs_cred;
15861589 int lbs_file;
15871590 int lbs_inode;
1591+ int lbs_superblock;
15881592 int lbs_ipc;
15891593 int lbs_msg_msg;
15901594 int lbs_task;
include/linux/security.h+4−0
@@ -291,6 +291,7 @@ void security_bprm_committed_creds(struct linux_binprm *bprm); · 290 unmodified lines
291291 int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);
292292 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);
293293 int security_sb_alloc(struct super_block *sb);
294+void security_sb_delete(struct super_block *sb);
294295 void security_sb_free(struct super_block *sb);
295296 void security_free_mnt_opts(void **mnt_opts);
296297 int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
@@ -633,6 +634,9 @@ static inline int security_sb_alloc(struct super_block *sb) · 336 unmodified lines
633634 return 0;
634635 }
635636
637+static inline void security_sb_delete(struct super_block *sb)
638+{ }
639+
636640 static inline void security_sb_free(struct super_block *sb)
637641 { }
638642
include/linux/syscalls.h+7−0
@@ -69,6 +69,8 @@ struct io_uring_params; · 68 unmodified lines
6969 struct clone_args;
7070 struct open_how;
7171 struct mount_attr;
72+struct landlock_ruleset_attr;
73+enum landlock_rule_type;
7274
7375 #include <linux/types.h>
7476 #include <linux/aio_abi.h>
@@ -1043,6 +1045,11 @@ asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, · 968 unmodified lines
10431045 siginfo_t __user *info,
10441046 unsigned int flags);
10451047 asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags);
1048+asmlinkage long sys_landlock_create_ruleset(const struct landlock_ruleset_attr __user *attr,
1049+ size_t size, __u32 flags);
1050+asmlinkage long sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type,
1051+ const void __user *rule_attr, __u32 flags);
1052+asmlinkage long sys_landlock_restrict_self(int ruleset_fd, __u32 flags);
10461053
10471054 /*
10481055 * Architecture-specific system calls
include/uapi/asm-generic/unistd.h+8−1
@@ -866,8 +866,15 @@ __SYSCALL(__NR_mount_setattr, sys_mount_setattr) · 865 unmodified lines
866866 #define __NR_quotactl_path 443
867867 __SYSCALL(__NR_quotactl_path, sys_quotactl_path)
868868
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+
869876 #undef __NR_syscalls
870−#define __NR_syscalls 444
877+#define __NR_syscalls 447
871878
872879 /*
873880 * 32 bit systems traditionally used different
include/uapi/linux/landlock.h+137−0
@@ -0,0 +1,137 @@
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 */
kernel/sys_ni.c+5−0
@@ -267,6 +267,11 @@ COND_SYSCALL(request_key); · 266 unmodified lines
267267 COND_SYSCALL(keyctl);
268268 COND_SYSCALL_COMPAT(keyctl);
269269
270+/* security/landlock/syscalls.c */
271+COND_SYSCALL(landlock_create_ruleset);
272+COND_SYSCALL(landlock_add_rule);
273+COND_SYSCALL(landlock_restrict_self);
274+
270275 /* arch/example/kernel/sys_example.c */
271276
272277 /* mm/fadvise.c */
samples/Kconfig+7−0
@@ -124,6 +124,13 @@ config SAMPLE_HIDRAW · 123 unmodified lines
124124 bool "hidraw sample"
125125 depends on CC_CAN_LINK && HEADERS_INSTALL
126126
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+
127134 config SAMPLE_PIDFD
128135 bool "pidfd sample"
129136 depends on CC_CAN_LINK && HEADERS_INSTALL
samples/Makefile+1−0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SAMPLE_KDB) += kdb/ · 10 unmodified lines
1111 obj-$(CONFIG_SAMPLE_KFIFO) += kfifo/
1212 obj-$(CONFIG_SAMPLE_KOBJECT) += kobject/
1313 obj-$(CONFIG_SAMPLE_KPROBES) += kprobes/
14+subdir-$(CONFIG_SAMPLE_LANDLOCK) += landlock
1415 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch/
1516 subdir-$(CONFIG_SAMPLE_PIDFD) += pidfd
1617 obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi/
samples/landlock/.gitignore+1−0
@@ -0,0 +1 @@
1+/sandboxer
samples/landlock/Makefile+13−0
@@ -0,0 +1,13 @@
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
samples/landlock/sandboxer.c+238−0
@@ -0,0 +1,238 @@
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+}
security/Kconfig+6−5
@@ -238,6 +238,7 @@ source "security/loadpin/Kconfig" · 237 unmodified lines
238238 source "security/yama/Kconfig"
239239 source "security/safesetid/Kconfig"
240240 source "security/lockdown/Kconfig"
241+source "security/landlock/Kconfig"
241242
242243 source "security/integrity/Kconfig"
243244
@@ -277,11 +278,11 @@ endchoice · 33 unmodified lines
277278
278279 config LSM
279280 string "Ordered list of enabled LSMs"
280− default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
281− default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
282− default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
283− default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
284− default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
281+ default "landlock,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
282+ default "landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
283+ default "landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
284+ default "landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
285+ default "landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
285286 help
286287 A comma-separated list of LSMs, in initialization order.
287288 Any LSMs left off this list will be ignored. This can be
security/Makefile+2−0
@@ -13,6 +13,7 @@ subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin · 12 unmodified lines
1313 subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
1414 subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
1515 subdir-$(CONFIG_BPF_LSM) += bpf
16+subdir-$(CONFIG_SECURITY_LANDLOCK) += landlock
1617
1718 # always enable default capabilities
1819 obj-y += commoncap.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ · 13 unmodified lines
3233 obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
3334 obj-$(CONFIG_CGROUPS) += device_cgroup.o
3435 obj-$(CONFIG_BPF_LSM) += bpf/
36+obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
3537
3638 # Object integrity file lists
3739 subdir-$(CONFIG_INTEGRITY) += integrity
security/landlock/Kconfig+21−0
@@ -0,0 +1,21 @@
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.
security/landlock/Makefile+4−0
@@ -0,0 +1,4 @@
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
security/landlock/common.h+20−0
@@ -0,0 +1,20 @@
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 */
security/landlock/cred.c+46−0
@@ -0,0 +1,46 @@
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+}
security/landlock/cred.h+58−0
@@ -0,0 +1,58 @@
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 */
security/landlock/fs.c+692−0
@@ -0,0 +1,692 @@
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+}
security/landlock/fs.h+70−0
@@ -0,0 +1,70 @@
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 */
security/landlock/limits.h+21−0
@@ -0,0 +1,21 @@
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 */
security/landlock/object.c+67−0
@@ -0,0 +1,67 @@
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+}
security/landlock/object.h+91−0
@@ -0,0 +1,91 @@
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 */
security/landlock/ptrace.c+120−0
@@ -0,0 +1,120 @@
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+}
security/landlock/ptrace.h+14−0
@@ -0,0 +1,14 @@
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 */
security/landlock/ruleset.c+473−0
@@ -0,0 +1,473 @@
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+}
security/landlock/ruleset.h+165−0
@@ -0,0 +1,165 @@
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 */
security/landlock/setup.c+40−0
@@ -0,0 +1,40 @@
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+};
security/landlock/setup.h+18−0
@@ -0,0 +1,18 @@
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 */
security/landlock/syscalls.c+451−0
@@ -0,0 +1,451 @@
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+}
security/security.c+44−7
@@ -203,6 +203,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) · 202 unmodified lines
203203 lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
204204 lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
205205 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);
206207 lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
207208 }
208209
@@ -333,12 +334,13 @@ static void __init ordered_lsm_init(void) · 124 unmodified lines
333334 for (lsm = ordered_lsms; *lsm; lsm++)
334335 prepare_lsm(*lsm);
335336
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("task blob size = %d\n", blob_sizes.lbs_task);
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("superblock blob size = %d\n", blob_sizes.lbs_superblock);
343+ init_debug("task blob size = %d\n", blob_sizes.lbs_task);
342344
343345 /*
344346 * Create any kmem_caches needed for blobs
@@ -670,6 +672,27 @@ static void __init lsm_early_task(struct task_struct *task) · 325 unmodified lines
670672 panic("%s: Early task alloc failed.\n", __func__);
671673 }
672674
675+/**
676+ * lsm_superblock_alloc - allocate a composite superblock blob
677+ * @sb: the superblock that needs a blob
678+ *
679+ * Allocate the superblock blob for all the modules
680+ *
681+ * Returns 0, or -ENOMEM if memory can't be allocated.
682+ */
683+static int lsm_superblock_alloc(struct super_block *sb)
684+{
685+ if (blob_sizes.lbs_superblock == 0) {
686+ sb->s_security = NULL;
687+ return 0;
688+ }
689+
690+ sb->s_security = kzalloc(blob_sizes.lbs_superblock, GFP_KERNEL);
691+ if (sb->s_security == NULL)
692+ return -ENOMEM;
693+ return 0;
694+}
695+
673696 /*
674697 * The default value of the LSM hook is defined in linux/lsm_hook_defs.h and
675698 * can be accessed with:
@@ -867,12 +890,26 @@ int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter * · 191 unmodified lines
867890
868891 int security_sb_alloc(struct super_block *sb)
869892 {
870− return call_int_hook(sb_alloc_security, 0, sb);
893+ int rc = lsm_superblock_alloc(sb);
894+
895+ if (unlikely(rc))
896+ return rc;
897+ rc = call_int_hook(sb_alloc_security, 0, sb);
898+ if (unlikely(rc))
899+ security_sb_free(sb);
900+ return rc;
901+}
902+
903+void security_sb_delete(struct super_block *sb)
904+{
905+ call_void_hook(sb_delete, sb);
871906 }
872907
873908 void security_sb_free(struct super_block *sb)
874909 {
875910 call_void_hook(sb_free_security, sb);
911+ kfree(sb->s_security);
912+ sb->s_security = NULL;
876913 }
877914
878915 void security_free_mnt_opts(void **mnt_opts)
security/selinux/hooks.c+22−36
@@ -358,7 +358,7 @@ static void inode_free_security(struct inode *inode) · 357 unmodified lines
358358
359359 if (!isec)
360360 return;
361− sbsec = inode->i_sb->s_security;
361+ sbsec = selinux_superblock(inode->i_sb);
362362 /*
363363 * As not all inode security structures are in a list, we check for
364364 * empty list outside of the lock to make sure that we won't waste
@@ -376,13 +376,6 @@ static void inode_free_security(struct inode *inode) · 11 unmodified lines
376376 }
377377 }
378378
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−}
385−
386379 struct selinux_mnt_opts {
387380 const char *fscontext, *context, *rootcontext, *defcontext;
388381 };
@@ -494,7 +487,7 @@ static int selinux_is_genfs_special_handling(struct super_block *sb) · 105 unmodified lines
494487
495488 static int selinux_is_sblabel_mnt(struct super_block *sb)
496489 {
497− struct superblock_security_struct *sbsec = sb->s_security;
490+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
498491
499492 /*
500493 * IMPORTANT: Double-check logic in this function when adding a new
@@ -571,7 +564,7 @@ fallback: · 70 unmodified lines
571564
572565 static int sb_finish_set_opts(struct super_block *sb)
573566 {
574− struct superblock_security_struct *sbsec = sb->s_security;
567+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
575568 struct dentry *root = sb->s_root;
576569 struct inode *root_inode = d_backing_inode(root);
577570 int rc = 0;
@@ -662,7 +655,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, · 84 unmodified lines
662655 unsigned long *set_kern_flags)
663656 {
664657 const struct cred *cred = current_cred();
665− struct superblock_security_struct *sbsec = sb->s_security;
658+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
666659 struct dentry *root = sb->s_root;
667660 struct selinux_mnt_opts *opts = mnt_opts;
668661 struct inode_security_struct *root_isec;
@@ -900,8 +893,8 @@ out_double_mount: · 231 unmodified lines
900893 static int selinux_cmp_sb_context(const struct super_block *oldsb,
901894 const struct super_block *newsb)
902895 {
903− struct superblock_security_struct *old = oldsb->s_security;
904− struct superblock_security_struct *new = newsb->s_security;
896+ struct superblock_security_struct *old = selinux_superblock(oldsb);
897+ struct superblock_security_struct *new = selinux_superblock(newsb);
905898 char oldflags = old->flags & SE_MNTMASK;
906899 char newflags = new->flags & SE_MNTMASK;
907900
@@ -933,8 +926,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, · 25 unmodified lines
933926 unsigned long *set_kern_flags)
934927 {
935928 int rc = 0;
936− const struct superblock_security_struct *oldsbsec = oldsb->s_security;
937− struct superblock_security_struct *newsbsec = newsb->s_security;
929+ const struct superblock_security_struct *oldsbsec =
930+ selinux_superblock(oldsb);
931+ struct superblock_security_struct *newsbsec = selinux_superblock(newsb);
938932
939933 int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
940934 int set_context = (oldsbsec->flags & CONTEXT_MNT);
@@ -1113,7 +1107,7 @@ static int show_sid(struct seq_file *m, u32 sid) · 172 unmodified lines
11131107
11141108 static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
11151109 {
1116− struct superblock_security_struct *sbsec = sb->s_security;
1110+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
11171111 int rc;
11181112
11191113 if (!(sbsec->flags & SE_SBINITIALIZED))
@@ -1464,7 +1458,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent · 344 unmodified lines
14641458 if (isec->sclass == SECCLASS_FILE)
14651459 isec->sclass = inode_mode_to_security_class(inode->i_mode);
14661460
1467− sbsec = inode->i_sb->s_security;
1461+ sbsec = selinux_superblock(inode->i_sb);
14681462 if (!(sbsec->flags & SE_SBINITIALIZED)) {
14691463 /* Defer initialization until selinux_complete_init,
14701464 after the initial policy is loaded and the security
@@ -1815,7 +1809,8 @@ selinux_determine_inode_label(const struct task_security_struct *tsec, · 344 unmodified lines
18151809 const struct qstr *name, u16 tclass,
18161810 u32 *_new_isid)
18171811 {
1818− const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
1812+ const struct superblock_security_struct *sbsec =
1813+ selinux_superblock(dir->i_sb);
18191814
18201815 if ((sbsec->flags & SE_SBINITIALIZED) &&
18211816 (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
@@ -1846,7 +1841,7 @@ static int may_create(struct inode *dir, · 24 unmodified lines
18461841 int rc;
18471842
18481843 dsec = inode_security(dir);
1849− sbsec = dir->i_sb->s_security;
1844+ sbsec = selinux_superblock(dir->i_sb);
18501845
18511846 sid = tsec->sid;
18521847
@@ -1995,7 +1990,7 @@ static int superblock_has_perm(const struct cred *cred, · 142 unmodified lines
19951990 struct superblock_security_struct *sbsec;
19961991 u32 sid = cred_sid(cred);
19971992
1998− sbsec = sb->s_security;
1993+ sbsec = selinux_superblock(sb);
19991994 return avc_has_perm(&selinux_state,
20001995 sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
20011996 }
@@ -2617,11 +2612,7 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) · 615 unmodified lines
26172612
26182613 static int selinux_sb_alloc_security(struct super_block *sb)
26192614 {
2620− struct superblock_security_struct *sbsec;
2621−
2622− sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
2623− if (!sbsec)
2624− return -ENOMEM;
2615+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
26252616
26262617 mutex_init(&sbsec->lock);
26272618 INIT_LIST_HEAD(&sbsec->isec_head);
@@ -2629,16 +2620,10 @@ static int selinux_sb_alloc_security(struct super_block *sb) · 1 unmodified line
26292620 sbsec->sid = SECINITSID_UNLABELED;
26302621 sbsec->def_sid = SECINITSID_FILE;
26312622 sbsec->mntpoint_sid = SECINITSID_UNLABELED;
2632− sb->s_security = sbsec;
26332623
26342624 return 0;
26352625 }
26362626
2637−static void selinux_sb_free_security(struct super_block *sb)
2638−{
2639− superblock_free_security(sb);
2640−}
2641−
26422627 static inline int opt_len(const char *s)
26432628 {
26442629 bool open_quote = false;
@@ -2772,7 +2757,7 @@ static int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts) · 127 unmodified lines
27722757 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
27732758 {
27742759 struct selinux_mnt_opts *opts = mnt_opts;
2775− struct superblock_security_struct *sbsec = sb->s_security;
2760+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
27762761 u32 sid;
27772762 int rc;
27782763
@@ -3010,7 +2995,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, · 231 unmodified lines
30102995 int rc;
30112996 char *context;
30122997
3013− sbsec = dir->i_sb->s_security;
2998+ sbsec = selinux_superblock(dir->i_sb);
30142999
30153000 newsid = tsec->create_sid;
30163001
@@ -3312,7 +3297,7 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns, · 295 unmodified lines
33123297 if (!selinux_initialized(&selinux_state))
33133298 return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM);
33143299
3315− sbsec = inode->i_sb->s_security;
3300+ sbsec = selinux_superblock(inode->i_sb);
33163301 if (!(sbsec->flags & SBLABEL_MNT))
33173302 return -EOPNOTSUPP;
33183303
@@ -3557,13 +3542,14 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, · 238 unmodified lines
35573542 const void *value, size_t size, int flags)
35583543 {
35593544 struct inode_security_struct *isec = inode_security_novalidate(inode);
3560− struct superblock_security_struct *sbsec = inode->i_sb->s_security;
3545+ struct superblock_security_struct *sbsec;
35613546 u32 newsid;
35623547 int rc;
35633548
35643549 if (strcmp(name, XATTR_SELINUX_SUFFIX))
35653550 return -EOPNOTSUPP;
35663551
3552+ sbsec = selinux_superblock(inode->i_sb);
35673553 if (!(sbsec->flags & SBLABEL_MNT))
35683554 return -EOPNOTSUPP;
35693555
@@ -7065,6 +7051,7 @@ struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { · 3495 unmodified lines
70657051 .lbs_inode = sizeof(struct inode_security_struct),
70667052 .lbs_ipc = sizeof(struct ipc_security_struct),
70677053 .lbs_msg_msg = sizeof(struct msg_security_struct),
7054+ .lbs_superblock = sizeof(struct superblock_security_struct),
70687055 };
70697056
70707057 #ifdef CONFIG_PERF_EVENTS
@@ -7165,7 +7152,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { · 94 unmodified lines
71657152 LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
71667153 LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
71677154
7168− LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
71697155 LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
71707156 LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat),
71717157 LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
security/selinux/include/objsec.h+6−0
@@ -188,4 +188,10 @@ static inline u32 current_sid(void) · 187 unmodified lines
188188 return tsec->sid;
189189 }
190190
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+
191197 #endif /* _SELINUX_OBJSEC_H_ */
security/selinux/ss/services.c+2−1
@@ -47,6 +47,7 @@ · 46 unmodified lines
4747 #include <linux/sched.h>
4848 #include <linux/audit.h>
4949 #include <linux/vmalloc.h>
50+#include <linux/lsm_hooks.h>
5051 #include <net/netlabel.h>
5152
5253 #include "flask.h"
@@ -2955,7 +2956,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) · 2902 unmodified lines
29552956 struct sidtab *sidtab;
29562957 int rc;
29572958 struct ocontext *c;
2958− struct superblock_security_struct *sbsec = sb->s_security;
2959+ struct superblock_security_struct *sbsec = selinux_superblock(sb);
29592960 const char *fstype = sb->s_type->name;
29602961
29612962 if (!selinux_initialized(state)) {
security/smack/smack.h+6−0
@@ -357,6 +357,12 @@ static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc) · 356 unmodified lines
357357 return ipc->security + smack_blob_sizes.lbs_ipc;
358358 }
359359
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+
360366 /*
361367 * Is the directory transmuting?
362368 */
security/smack/smack_lsm.c+9−26
@@ -535,12 +535,7 @@ static int smack_syslog(int typefrom_file) · 534 unmodified lines
535535 */
536536 static int smack_sb_alloc_security(struct super_block *sb)
537537 {
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);
544539
545540 sbsp->smk_root = &smack_known_floor;
546541 sbsp->smk_default = &smack_known_floor;
@@ -549,22 +544,10 @@ static int smack_sb_alloc_security(struct super_block *sb) · 2 unmodified lines
549544 /*
550545 * SMK_SB_INITIALIZED will be zero from kzalloc.
551546 */
552− sb->s_security = sbsp;
553547
554548 return 0;
555549 }
556550
557−/**
558− * smack_sb_free_security - free a superblock blob
559− * @sb: the superblock getting the blob
560− *
561− */
562−static void smack_sb_free_security(struct super_block *sb)
563−{
564− kfree(sb->s_security);
565− sb->s_security = NULL;
566−}
567−
568551 struct smack_mnt_opts {
569552 const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
570553 };
@@ -772,7 +755,7 @@ static int smack_set_mnt_opts(struct super_block *sb, · 201 unmodified lines
772755 {
773756 struct dentry *root = sb->s_root;
774757 struct inode *inode = d_backing_inode(root);
775− struct superblock_smack *sp = sb->s_security;
758+ struct superblock_smack *sp = smack_superblock(sb);
776759 struct inode_smack *isp;
777760 struct smack_known *skp;
778761 struct smack_mnt_opts *opts = mnt_opts;
@@ -871,7 +854,7 @@ static int smack_set_mnt_opts(struct super_block *sb, · 92 unmodified lines
871854 */
872855 static int smack_sb_statfs(struct dentry *dentry)
873856 {
874− struct superblock_smack *sbp = dentry->d_sb->s_security;
857+ struct superblock_smack *sbp = smack_superblock(dentry->d_sb);
875858 int rc;
876859 struct smk_audit_info ad;
877860
@@ -905,7 +888,7 @@ static int smack_bprm_creds_for_exec(struct linux_binprm *bprm) · 27 unmodified lines
905888 if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
906889 return 0;
907890
908− sbsp = inode->i_sb->s_security;
891+ sbsp = smack_superblock(inode->i_sb);
909892 if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) &&
910893 isp->smk_task != sbsp->smk_root)
911894 return 0;
@@ -1157,7 +1140,7 @@ static int smack_inode_rename(struct inode *old_inode, · 245 unmodified lines
11571140 */
11581141 static int smack_inode_permission(struct inode *inode, int mask)
11591142 {
1160− struct superblock_smack *sbsp = inode->i_sb->s_security;
1143+ struct superblock_smack *sbsp = smack_superblock(inode->i_sb);
11611144 struct smk_audit_info ad;
11621145 int no_block = mask & MAY_NOT_BLOCK;
11631146 int rc;
@@ -1400,7 +1383,7 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns, · 236 unmodified lines
14001383 */
14011384 if (strcmp(name, XATTR_NAME_SMACK) == 0) {
14021385 struct super_block *sbp = dentry->d_sb;
1403− struct superblock_smack *sbsp = sbp->s_security;
1386+ struct superblock_smack *sbsp = smack_superblock(sbp);
14041387
14051388 isp->smk_inode = sbsp->smk_default;
14061389 } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
@@ -1670,7 +1653,7 @@ static int smack_mmap_file(struct file *file, · 263 unmodified lines
16701653 isp = smack_inode(file_inode(file));
16711654 if (isp->smk_mmap == NULL)
16721655 return 0;
1673− sbsp = file_inode(file)->i_sb->s_security;
1656+ sbsp = smack_superblock(file_inode(file)->i_sb);
16741657 if (sbsp->smk_flags & SMK_SB_UNTRUSTED &&
16751658 isp->smk_mmap != sbsp->smk_root)
16761659 return -EACCES;
@@ -3299,7 +3282,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) · 1622 unmodified lines
32993282 return;
33003283
33013284 sbp = inode->i_sb;
3302− sbsp = sbp->s_security;
3285+ sbsp = smack_superblock(sbp);
33033286 /*
33043287 * We're going to use the superblock default label
33053288 * if there's no label on the file.
@@ -4714,6 +4697,7 @@ struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { · 1408 unmodified lines
47144697 .lbs_inode = sizeof(struct inode_smack),
47154698 .lbs_ipc = sizeof(struct smack_known *),
47164699 .lbs_msg_msg = sizeof(struct smack_known *),
4700+ .lbs_superblock = sizeof(struct superblock_smack),
47174701 };
47184702
47194703 static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
@@ -4725,7 +4709,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { · 5 unmodified lines
47254709 LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
47264710
47274711 LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
4728− LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
47294712 LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
47304713 LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
47314714 LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
tools/testing/selftests/Makefile+1−0
@@ -25,6 +25,7 @@ TARGETS += ir · 24 unmodified lines
2525 TARGETS += kcmp
2626 TARGETS += kexec
2727 TARGETS += kvm
28+TARGETS += landlock
2829 TARGETS += lib
2930 TARGETS += livepatch
3031 TARGETS += lkdtm
tools/testing/selftests/landlock/.gitignore+2−0
@@ -0,0 +1,2 @@
1+/*_test
2+/true
tools/testing/selftests/landlock/Makefile+24−0
@@ -0,0 +1,24 @@
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)
tools/testing/selftests/landlock/base_test.c+266−0
@@ -0,0 +1,266 @@
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
tools/testing/selftests/landlock/common.h+183−0
@@ -0,0 +1,183 @@
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+}
tools/testing/selftests/landlock/config+7−0
@@ -0,0 +1,7 @@
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
tools/testing/selftests/landlock/fs_test.c+2791−0
@@ -0,0 +1,2791 @@
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
tools/testing/selftests/landlock/ptrace_test.c+337−0
@@ -0,0 +1,337 @@
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
tools/testing/selftests/landlock/true.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0
2+int main(void)
3+{
4+ return 0;
5+}