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
16 siphash16 siphash
17 tpm/index17 tpm/index
18 digsig18 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==================================
6Landlock LSM: kernel documentation
7==================================
8
9:Author: Mickaël Salaün
10:Date: March 2021
11
12Landlock's goal is to create scoped access-control (i.e. sandboxing). To
13harden a whole system, this feature should be available to any process,
14including unprivileged ones. Because such process may be compromised or
15backdoored (i.e. untrusted), Landlock's features must be safe to use from the
16kernel and other processes point of view. Landlock's interface must therefore
17expose a minimal attack surface.
18
19Landlock is designed to be usable by unprivileged processes while following the
20system security policy enforced by other access control mechanisms (e.g. DAC,
21LSM). Indeed, a Landlock rule shall not interfere with other access-controls
22enforced on the system, only add more restrictions.
23
24Any user can enforce Landlock rulesets on their processes. They are merged and
25evaluated according to the inherited ones in a way that ensures that only more
26constraints can be added.
27
28User space documentation can be found here: :doc:`/userspace-api/landlock`.
29
30Guiding 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
44Tests
45=====
46
47Userspace tests for backward compatibility, ptrace restrictions and filesystem
48support can be found here: `tools/testing/selftests/landlock/`_.
49
50Kernel structures
51=================
52
53Object
54------
55
56.. kernel-doc:: security/landlock/object.h
57 :identifiers:
58
59Filesystem
60----------
61
62.. kernel-doc:: security/landlock/fs.h
63 :identifiers:
64
65Ruleset and domain
66------------------
67
68A domain is a read-only ruleset tied to a set of subjects (i.e. tasks'
69credentials). Each time a ruleset is enforced on a task, the current domain is
70duplicated and the ruleset is imported as a new layer of rules in the new
71domain. Indeed, once in a domain, each rule is tied to a layer level. To
72grant access to an object, at least one rule of each layer must allow the
73requested action on the object. A task can then only transit to a new domain
74that is the intersection of the constraints from the current domain and those
75of a ruleset provided by the task.
76
77The definition of a subject is implicit for a task sandboxing itself, which
78makes 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
18 18
19 no_new_privs19 no_new_privs
20 seccomp_filter20 seccomp_filter
21 landlock
21 unshare22 unshare
22 spec_ctrl23 spec_ctrl
23 accelerators/ocxl24 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=====================================
7Landlock: unprivileged access control
8=====================================
9
10:Author: Mickaël Salaün
11:Date: March 2021
12
13The goal of Landlock is to enable to restrict ambient rights (e.g. global
14filesystem access) for a set of processes. Because Landlock is a stackable
15LSM, it makes possible to create safe security sandboxes as new security layers
16in addition to the existing system-wide access-controls. This kind of sandbox
17is expected to help mitigate the security impact of bugs or
18unexpected/malicious behaviors in user space applications. Landlock empowers
19any process, including unprivileged ones, to securely restrict themselves.
20
21Landlock rules
22==============
23
24A Landlock rule describes an action on an object. An object is currently a
25file hierarchy, and the related filesystem actions are defined with `access
26rights`_. A set of rules is aggregated in a ruleset, which can then restrict
27the thread enforcing it, and its future children.
28
29Defining and enforcing a security policy
30----------------------------------------
31
32We first need to create the ruleset that will contain our rules. For this
33example, the ruleset will contain rules that only allow read actions, but write
34actions will be denied. The ruleset then needs to handle both of these kind of
35actions.
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
63We can now add a new rule to this ruleset thanks to the returned file
64descriptor referring to this ruleset. The rule will only allow reading the
65file hierarchy ``/usr``. Without another rule, write actions would then be
66denied 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
68descriptor.
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
95We now have a ruleset with one rule allowing read access to ``/usr`` while
96denying all other handled accesses for the filesystem. The next step is to
97restrict the current thread from gaining more privileges (e.g. thanks to a SUID
98binary).
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
108The 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
119If the `landlock_restrict_self` system call succeeds, the current thread is now
120restricted and this policy will be enforced on all its subsequently created
121children as well. Once a thread is landlocked, there is no way to remove its
122security policy; only adding more restrictions is allowed. These threads are
123now in a new Landlock domain, merge of their parent one (if any) with the new
124ruleset.
125
126Full working code can be found in `samples/landlock/sandboxer.c`_.
127
128Layers of file path access rights
129---------------------------------
130
131Each time a thread enforces a ruleset on itself, it updates its Landlock domain
132with a new layer of policy. Indeed, this complementary policy is stacked with
133the potentially other rulesets already restricting this thread. A sandboxed
134thread can then safely add more constraints to itself with a new enforced
135ruleset.
136
137One policy layer grants access to a file path if at least one of its rules
138encountered on the path grants the access. A sandboxed thread can only access
139a file path if all its enforced policy layers grant the access as well as all
140the other system access controls (e.g. filesystem DAC, other LSM policies,
141etc.).
142
143Bind mounts and OverlayFS
144-------------------------
145
146Landlock enables to restrict access to file hierarchies, which means that these
147access rights can be propagated with bind mounts (cf.
148:doc:`/filesystems/sharedsubtree`) but not with :doc:`/filesystems/overlayfs`.
149
150A bind mount mirrors a source file hierarchy to a destination. The destination
151hierarchy is then composed of the exact same files, on which Landlock rules can
152be tied, either via the source or the destination path. These rules restrict
153access when they are encountered on a path, which means that they can restrict
154access to multiple file hierarchies at the same time, whether these hierarchies
155are the result of bind mounts or not.
156
157An OverlayFS mount point consists of upper and lower layers. These layers are
158combined in a merge directory, result of the mount point. This merge hierarchy
159may include files from the upper and lower layers, but modifications performed
160on the merge hierarchy only reflects on the upper layer. From a Landlock
161policy point of view, each OverlayFS layers and merge hierarchies are
162standalone and contains their own set of files and directories, which is
163different from bind mounts. A policy restricting an OverlayFS layer will not
164restrict the resulted merged hierarchy, and vice versa. Landlock users should
165then only think about file hierarchies they want to allow access to, regardless
166of the underlying filesystem.
167
168Inheritance
169-----------
170
171Every new thread resulting from a :manpage:`clone(2)` inherits Landlock domain
172restrictions 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
175Landlock rules to itself, but they will not be automatically applied to other
176sibling threads (unlike POSIX thread credential changes, cf.
177:manpage:`nptl(7)`).
178
179When a thread sandboxes itself, we have the guarantee that the related security
180policy will stay enforced on all this thread's descendants. This allows
181creating standalone and modular security policies per application, which will
182automatically be composed between themselves according to their runtime parent
183policies.
184
185Ptrace restrictions
186-------------------
187
188A sandboxed process has less privileges than a non-sandboxed process and must
189then be subject to additional restrictions when manipulating another process.
190To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target
191process, a sandboxed process should have a subset of the target process rules,
192which means the tracee must be in a sub-domain of the tracer.
193
194Kernel interface
195================
196
197Access rights
198-------------
199
200.. kernel-doc:: include/uapi/linux/landlock.h
201 :identifiers: fs_access
202
203Creating 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
212Extending 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
221Enforcing a ruleset
222-------------------
223
224.. kernel-doc:: security/landlock/syscalls.c
225 :identifiers: sys_landlock_restrict_self
226
227Current limitations
228===================
229
230File renaming and linking
231-------------------------
232
233Because Landlock targets unprivileged access controls, it is needed to properly
234handle composition of rules. Such property also implies rules nesting.
235Properly handling multiple layers of ruleset, each one of them able to restrict
236access to files, also implies to inherit the ruleset restrictions from a parent
237to its hierarchy. Because files are identified and restricted by their
238hierarchy, moving or linking a file from one directory to another implies to
239propagate the hierarchy constraints. To protect against privilege escalations
240through renaming or linking, and for the sake of simplicity, Landlock currently
241limits linking and renaming to the same directory. Future Landlock evolutions
242will enable more flexibility for renaming and linking, with dedicated ruleset
243flags.
244
245Filesystem topology modification
246--------------------------------
247
248As for file renaming and linking, a sandboxed thread cannot modify its
249filesystem topology, whether via :manpage:`mount(2)` or
250:manpage:`pivot_root(2)`. However, :manpage:`chroot(2)` calls are not denied.
251
252Special filesystems
253-------------------
254
255Access to regular files and directories can be restricted by Landlock,
256according to the handled accesses of a ruleset. However, files that do not
257come from a user-visible filesystem (e.g. pipe, socket), but can still be
258accessed through ``/proc/<pid>/fd/*``, cannot currently be explicitly
259restricted. Likewise, some special kernel filesystems such as nsfs, which can
260be accessed through ``/proc/<pid>/ns/*``, cannot currently be explicitly
261restricted. However, thanks to the `ptrace restrictions`_, access to such
262sensitive ``/proc`` files are automatically restricted according to domain
263hierarchies. Future Landlock evolutions could still enable to explicitly
264restrict such paths with dedicated ruleset flags.
265
266Ruleset layers
267--------------
268
269There is a limit of 64 layers of stacked rulesets. This can be an issue for a
270task willing to enforce a new ruleset in complement to its 64 inherited
271rulesets. Once this limit is reached, sys_landlock_restrict_self() returns
272E2BIG. It is then strongly suggested to carefully build rulesets once in the
273life of a thread, especially for applications able to launch other applications
274that may also want to sandbox themselves (e.g. shells, container managers,
275etc.).
276
277Memory usage
278------------
279
280Kernel memory allocated to create rulesets is accounted and can be restricted
281by the :doc:`/admin-guide/cgroup-v1/memory`.
282
283Questions and answers
284=====================
285
286What about user space sandbox managers?
287---------------------------------------
288
289Using user space process to enforce restrictions on kernel resources can lead
290to race conditions or inconsistent evaluations (i.e. `Incorrect mirroring of
291the OS code and state
292<https://www.ndss-symposium.org/ndss2003/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools/>`_).
293
294What about namespaces and containers?
295-------------------------------------
296
297Namespaces can help create sandboxes but they are not designed for
298access-control and then miss useful features for such use case (e.g. no
299fine-grained restrictions). Moreover, their complexity can lead to security
300issues, especially when untrusted processes can manipulate them (cf.
301`Controlling access to user namespaces <https://lwn.net/Articles/673597/>`_).
302
303Additional 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
10191F: net/ipv4/tcp_bpf.c10191F: net/ipv4/tcp_bpf.c
10192F: net/ipv4/udp_bpf.c10192F: net/ipv4/udp_bpf.c
10193 10193
10194LANDLOCK SECURITY MODULE
10195M: Mickaël Salaün <mic@digikod.net>
10196L: linux-security-module@vger.kernel.org
10197S: Supported
10198W: https://landlock.io
10199T: git https://github.com/landlock-lsm/linux.git
10200F: Documentation/security/landlock.rst
10201F: Documentation/userspace-api/landlock.rst
10202F: include/uapi/linux/landlock.h
10203F: samples/landlock/
10204F: security/landlock/
10205F: tools/testing/selftests/landlock/
10206K: landlock
10207K: LANDLOCK
10208
10194LANTIQ / INTEL Ethernet drivers10209LANTIQ / INTEL Ethernet drivers
10195M: Hauke Mehrtens <hauke@hauke-m.de>10210M: Hauke Mehrtens <hauke@hauke-m.de>
10196L: netdev@vger.kernel.org10211L: netdev@vger.kernel.org
arch/Kconfig+7−0
@@ -1068,6 +1068,13 @@ config COMPAT_32BIT_TIME · 1067 unmodified lines
1068config ARCH_NO_PREEMPT1068config ARCH_NO_PREEMPT
1069 bool1069 bool
1070 1070
1071config 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
1071config ARCH_SUPPORTS_RT1078config ARCH_SUPPORTS_RT
1072 bool1079 bool
1073 1080
arch/alpha/kernel/syscalls/syscall.tbl+3−0
@@ -483,3 +483,6 @@ · 482 unmodified lines
483551 common epoll_pwait2 sys_epoll_pwait2483551 common epoll_pwait2 sys_epoll_pwait2
484552 common mount_setattr sys_mount_setattr484552 common mount_setattr sys_mount_setattr
485553 common quotactl_path sys_quotactl_path485553 common quotactl_path sys_quotactl_path
486554 common landlock_create_ruleset sys_landlock_create_ruleset
487555 common landlock_add_rule sys_landlock_add_rule
488556 common landlock_restrict_self sys_landlock_restrict_self
arch/arm/tools/syscall.tbl+3−0
@@ -457,3 +457,6 @@ · 456 unmodified lines
457441 common epoll_pwait2 sys_epoll_pwait2457441 common epoll_pwait2 sys_epoll_pwait2
458442 common mount_setattr sys_mount_setattr458442 common mount_setattr sys_mount_setattr
459443 common quotactl_path sys_quotactl_path459443 common quotactl_path sys_quotactl_path
460444 common landlock_create_ruleset sys_landlock_create_ruleset
461445 common landlock_add_rule sys_landlock_add_rule
462446 common landlock_restrict_self sys_landlock_restrict_self
arch/arm64/include/asm/unistd.h+1−1
@@ -38,7 +38,7 @@ · 37 unmodified lines
38#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)38#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
39#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)39#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
40 40
41#define __NR_compat_syscalls 44441#define __NR_compat_syscalls 447
42#endif42#endif
43 43
44#define __ARCH_WANT_SYS_CLONE44#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
895__SYSCALL(__NR_mount_setattr, sys_mount_setattr)895__SYSCALL(__NR_mount_setattr, sys_mount_setattr)
896#define __NR_quotactl_path 443896#define __NR_quotactl_path 443
897__SYSCALL(__NR_quotactl_path, sys_quotactl_path)897__SYSCALL(__NR_quotactl_path, sys_quotactl_path)
898#define __NR_landlock_create_ruleset 444
899__SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset)
900#define __NR_landlock_add_rule 445
901__SYSCALL(__NR_landlock_add_rule, sys_landlock_add_rule)
902#define __NR_landlock_restrict_self 446
903__SYSCALL(__NR_landlock_restrict_self, sys_landlock_restrict_self)
898 904
899/*905/*
900 * Please add new compat syscalls above this comment and update906 * 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
364441 common epoll_pwait2 sys_epoll_pwait2364441 common epoll_pwait2 sys_epoll_pwait2
365442 common mount_setattr sys_mount_setattr365442 common mount_setattr sys_mount_setattr
366443 common quotactl_path sys_quotactl_path366443 common quotactl_path sys_quotactl_path
367444 common landlock_create_ruleset sys_landlock_create_ruleset
368445 common landlock_add_rule sys_landlock_add_rule
369446 common landlock_restrict_self sys_landlock_restrict_self
arch/m68k/kernel/syscalls/syscall.tbl+3−0
@@ -443,3 +443,6 @@ · 442 unmodified lines
443441 common epoll_pwait2 sys_epoll_pwait2443441 common epoll_pwait2 sys_epoll_pwait2
444442 common mount_setattr sys_mount_setattr444442 common mount_setattr sys_mount_setattr
445443 common quotactl_path sys_quotactl_path445443 common quotactl_path sys_quotactl_path
446444 common landlock_create_ruleset sys_landlock_create_ruleset
447445 common landlock_add_rule sys_landlock_add_rule
448446 common landlock_restrict_self sys_landlock_restrict_self
arch/microblaze/kernel/syscalls/syscall.tbl+3−0
@@ -449,3 +449,6 @@ · 448 unmodified lines
449441 common epoll_pwait2 sys_epoll_pwait2449441 common epoll_pwait2 sys_epoll_pwait2
450442 common mount_setattr sys_mount_setattr450442 common mount_setattr sys_mount_setattr
451443 common quotactl_path sys_quotactl_path451443 common quotactl_path sys_quotactl_path
452444 common landlock_create_ruleset sys_landlock_create_ruleset
453445 common landlock_add_rule sys_landlock_add_rule
454446 common landlock_restrict_self sys_landlock_restrict_self
arch/mips/kernel/syscalls/syscall_n32.tbl+3−0
@@ -382,3 +382,6 @@ · 381 unmodified lines
382441 n32 epoll_pwait2 compat_sys_epoll_pwait2382441 n32 epoll_pwait2 compat_sys_epoll_pwait2
383442 n32 mount_setattr sys_mount_setattr383442 n32 mount_setattr sys_mount_setattr
384443 n32 quotactl_path sys_quotactl_path384443 n32 quotactl_path sys_quotactl_path
385444 n32 landlock_create_ruleset sys_landlock_create_ruleset
386445 n32 landlock_add_rule sys_landlock_add_rule
387446 n32 landlock_restrict_self sys_landlock_restrict_self
arch/mips/kernel/syscalls/syscall_n64.tbl+3−0
@@ -358,3 +358,6 @@ · 357 unmodified lines
358441 n64 epoll_pwait2 sys_epoll_pwait2358441 n64 epoll_pwait2 sys_epoll_pwait2
359442 n64 mount_setattr sys_mount_setattr359442 n64 mount_setattr sys_mount_setattr
360443 n64 quotactl_path sys_quotactl_path360443 n64 quotactl_path sys_quotactl_path
361444 n64 landlock_create_ruleset sys_landlock_create_ruleset
362445 n64 landlock_add_rule sys_landlock_add_rule
363446 n64 landlock_restrict_self sys_landlock_restrict_self
arch/mips/kernel/syscalls/syscall_o32.tbl+3−0
@@ -431,3 +431,6 @@ · 430 unmodified lines
431441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2431441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
432442 o32 mount_setattr sys_mount_setattr432442 o32 mount_setattr sys_mount_setattr
433443 o32 quotactl_path sys_quotactl_path433443 o32 quotactl_path sys_quotactl_path
434444 o32 landlock_create_ruleset sys_landlock_create_ruleset
435445 o32 landlock_add_rule sys_landlock_add_rule
436446 o32 landlock_restrict_self sys_landlock_restrict_self
arch/parisc/kernel/syscalls/syscall.tbl+3−0
@@ -441,3 +441,6 @@ · 440 unmodified lines
441441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2441441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
442442 common mount_setattr sys_mount_setattr442442 common mount_setattr sys_mount_setattr
443443 common quotactl_path sys_quotactl_path443443 common quotactl_path sys_quotactl_path
444444 common landlock_create_ruleset sys_landlock_create_ruleset
445445 common landlock_add_rule sys_landlock_add_rule
446446 common landlock_restrict_self sys_landlock_restrict_self
arch/powerpc/kernel/syscalls/syscall.tbl+3−0
@@ -523,3 +523,6 @@ · 522 unmodified lines
523441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2523441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
524442 common mount_setattr sys_mount_setattr524442 common mount_setattr sys_mount_setattr
525443 common quotactl_path sys_quotactl_path525443 common quotactl_path sys_quotactl_path
526444 common landlock_create_ruleset sys_landlock_create_ruleset
527445 common landlock_add_rule sys_landlock_add_rule
528446 common landlock_restrict_self sys_landlock_restrict_self
arch/s390/kernel/syscalls/syscall.tbl+3−0
@@ -446,3 +446,6 @@ · 445 unmodified lines
446441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2446441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
447442 common mount_setattr sys_mount_setattr sys_mount_setattr447442 common mount_setattr sys_mount_setattr sys_mount_setattr
448443 common quotactl_path sys_quotactl_path sys_quotactl_path448443 common quotactl_path sys_quotactl_path sys_quotactl_path
449444 common landlock_create_ruleset sys_landlock_create_ruleset sys_landlock_create_ruleset
450445 common landlock_add_rule sys_landlock_add_rule sys_landlock_add_rule
451446 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
446441 common epoll_pwait2 sys_epoll_pwait2446441 common epoll_pwait2 sys_epoll_pwait2
447442 common mount_setattr sys_mount_setattr447442 common mount_setattr sys_mount_setattr
448443 common quotactl_path sys_quotactl_path448443 common quotactl_path sys_quotactl_path
449444 common landlock_create_ruleset sys_landlock_create_ruleset
450445 common landlock_add_rule sys_landlock_add_rule
451446 common landlock_restrict_self sys_landlock_restrict_self
arch/sparc/kernel/syscalls/syscall.tbl+3−0
@@ -489,3 +489,6 @@ · 488 unmodified lines
489441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2489441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
490442 common mount_setattr sys_mount_setattr490442 common mount_setattr sys_mount_setattr
491443 common quotactl_path sys_quotactl_path491443 common quotactl_path sys_quotactl_path
492444 common landlock_create_ruleset sys_landlock_create_ruleset
493445 common landlock_add_rule sys_landlock_add_rule
494446 common landlock_restrict_self sys_landlock_restrict_self
arch/um/Kconfig+1−0
@@ -5,6 +5,7 @@ menu "UML-specific options" · 4 unmodified lines
5config UML5config UML
6 bool6 bool
7 default y7 default y
8 select ARCH_EPHEMERAL_INODES
8 select ARCH_HAS_KCOV9 select ARCH_HAS_KCOV
9 select ARCH_NO_PREEMPT10 select ARCH_NO_PREEMPT
10 select HAVE_ARCH_AUDITSYSCALL11 select HAVE_ARCH_AUDITSYSCALL
arch/x86/entry/syscalls/syscall_32.tbl+3−0
@@ -448,3 +448,6 @@ · 447 unmodified lines
448441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2448441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
449442 i386 mount_setattr sys_mount_setattr449442 i386 mount_setattr sys_mount_setattr
450443 i386 quotactl_path sys_quotactl_path450443 i386 quotactl_path sys_quotactl_path
451444 i386 landlock_create_ruleset sys_landlock_create_ruleset
452445 i386 landlock_add_rule sys_landlock_add_rule
453446 i386 landlock_restrict_self sys_landlock_restrict_self
arch/x86/entry/syscalls/syscall_64.tbl+3−0
@@ -365,6 +365,9 @@ · 364 unmodified lines
365441 common epoll_pwait2 sys_epoll_pwait2365441 common epoll_pwait2 sys_epoll_pwait2
366442 common mount_setattr sys_mount_setattr366442 common mount_setattr sys_mount_setattr
367443 common quotactl_path sys_quotactl_path367443 common quotactl_path sys_quotactl_path
368444 common landlock_create_ruleset sys_landlock_create_ruleset
369445 common landlock_add_rule sys_landlock_add_rule
370446 common landlock_restrict_self sys_landlock_restrict_self
368 371
369#372#
370# Due to a historical design error, certain syscalls are numbered differently373# 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
414441 common epoll_pwait2 sys_epoll_pwait2414441 common epoll_pwait2 sys_epoll_pwait2
415442 common mount_setattr sys_mount_setattr415442 common mount_setattr sys_mount_setattr
416443 common quotactl_path sys_quotactl_path416443 common quotactl_path sys_quotactl_path
417444 common landlock_create_ruleset sys_landlock_create_ruleset
418445 common landlock_add_rule sys_landlock_add_rule
419446 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
454 evict_inodes(sb);454 evict_inodes(sb);
455 /* only nonzero refcount inodes can have marks */455 /* only nonzero refcount inodes can have marks */
456 fsnotify_sb_delete(sb);456 fsnotify_sb_delete(sb);
457 security_sb_delete(sb);
457 458
458 if (sb->s_dio_done_wq) {459 if (sb->s_dio_done_wq) {
459 destroy_workqueue(sb->s_dio_done_wq);460 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
59LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,59LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,
60 struct fs_parameter *param)60 struct fs_parameter *param)
61LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)61LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
62LSM_HOOK(void, LSM_RET_VOID, sb_delete, struct super_block *sb)
62LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)63LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
63LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)64LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
64LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)65LSM_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
108 * allocated.108 * allocated.
109 * @sb contains the super_block structure to be modified.109 * @sb contains the super_block structure to be modified.
110 * Return 0 if operation was successful.110 * Return 0 if operation was successful.
111 * @sb_delete:
112 * Release objects tied to a superblock (e.g. inodes).
113 * @sb contains the super_block structure being released.
111 * @sb_free_security:114 * @sb_free_security:
112 * Deallocate and clear the sb->s_security field.115 * Deallocate and clear the sb->s_security field.
113 * @sb contains the super_block structure to be modified.116 * @sb contains the super_block structure to be modified.
@@ -1585,6 +1588,7 @@ struct lsm_blob_sizes { · 1471 unmodified lines
1585 int lbs_cred;1588 int lbs_cred;
1586 int lbs_file;1589 int lbs_file;
1587 int lbs_inode;1590 int lbs_inode;
1591 int lbs_superblock;
1588 int lbs_ipc;1592 int lbs_ipc;
1589 int lbs_msg_msg;1593 int lbs_msg_msg;
1590 int lbs_task;1594 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
291int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);291int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);
292int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);292int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);
293int security_sb_alloc(struct super_block *sb);293int security_sb_alloc(struct super_block *sb);
294void security_sb_delete(struct super_block *sb);
294void security_sb_free(struct super_block *sb);295void security_sb_free(struct super_block *sb);
295void security_free_mnt_opts(void **mnt_opts);296void security_free_mnt_opts(void **mnt_opts);
296int security_sb_eat_lsm_opts(char *options, void **mnt_opts);297int 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
633 return 0;634 return 0;
634}635}
635 636
637static inline void security_sb_delete(struct super_block *sb)
638{ }
639
636static inline void security_sb_free(struct super_block *sb)640static inline void security_sb_free(struct super_block *sb)
637{ }641{ }
638 642
include/linux/syscalls.h+7−0
@@ -69,6 +69,8 @@ struct io_uring_params; · 68 unmodified lines
69struct clone_args;69struct clone_args;
70struct open_how;70struct open_how;
71struct mount_attr;71struct mount_attr;
72struct landlock_ruleset_attr;
73enum landlock_rule_type;
72 74
73#include <linux/types.h>75#include <linux/types.h>
74#include <linux/aio_abi.h>76#include <linux/aio_abi.h>
@@ -1043,6 +1045,11 @@ asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, · 968 unmodified lines
1043 siginfo_t __user *info,1045 siginfo_t __user *info,
1044 unsigned int flags);1046 unsigned int flags);
1045asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags);1047asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags);
1048asmlinkage long sys_landlock_create_ruleset(const struct landlock_ruleset_attr __user *attr,
1049 size_t size, __u32 flags);
1050asmlinkage long sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type,
1051 const void __user *rule_attr, __u32 flags);
1052asmlinkage long sys_landlock_restrict_self(int ruleset_fd, __u32 flags);
1046 1053
1047/*1054/*
1048 * Architecture-specific system calls1055 * 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
866#define __NR_quotactl_path 443866#define __NR_quotactl_path 443
867__SYSCALL(__NR_quotactl_path, sys_quotactl_path)867__SYSCALL(__NR_quotactl_path, sys_quotactl_path)
868 868
869#define __NR_landlock_create_ruleset 444
870__SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset)
871#define __NR_landlock_add_rule 445
872__SYSCALL(__NR_landlock_add_rule, sys_landlock_add_rule)
873#define __NR_landlock_restrict_self 446
874__SYSCALL(__NR_landlock_restrict_self, sys_landlock_restrict_self)
875
869#undef __NR_syscalls876#undef __NR_syscalls
870#define __NR_syscalls 444877#define __NR_syscalls 447
871 878
872/*879/*
873 * 32 bit systems traditionally used different880 * 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 */
20struct 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 */
43enum 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 */
56struct 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
267COND_SYSCALL(keyctl);267COND_SYSCALL(keyctl);
268COND_SYSCALL_COMPAT(keyctl);268COND_SYSCALL_COMPAT(keyctl);
269 269
270/* security/landlock/syscalls.c */
271COND_SYSCALL(landlock_create_ruleset);
272COND_SYSCALL(landlock_add_rule);
273COND_SYSCALL(landlock_restrict_self);
274
270/* arch/example/kernel/sys_example.c */275/* arch/example/kernel/sys_example.c */
271 276
272/* mm/fadvise.c */277/* mm/fadvise.c */
samples/Kconfig+7−0
@@ -124,6 +124,13 @@ config SAMPLE_HIDRAW · 123 unmodified lines
124 bool "hidraw sample"124 bool "hidraw sample"
125 depends on CC_CAN_LINK && HEADERS_INSTALL125 depends on CC_CAN_LINK && HEADERS_INSTALL
126 126
127config 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
127config SAMPLE_PIDFD134config SAMPLE_PIDFD
128 bool "pidfd sample"135 bool "pidfd sample"
129 depends on CC_CAN_LINK && HEADERS_INSTALL136 depends on CC_CAN_LINK && HEADERS_INSTALL
samples/Makefile+1−0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SAMPLE_KDB) += kdb/ · 10 unmodified lines
11obj-$(CONFIG_SAMPLE_KFIFO) += kfifo/11obj-$(CONFIG_SAMPLE_KFIFO) += kfifo/
12obj-$(CONFIG_SAMPLE_KOBJECT) += kobject/12obj-$(CONFIG_SAMPLE_KOBJECT) += kobject/
13obj-$(CONFIG_SAMPLE_KPROBES) += kprobes/13obj-$(CONFIG_SAMPLE_KPROBES) += kprobes/
14subdir-$(CONFIG_SAMPLE_LANDLOCK) += landlock
14obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch/15obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch/
15subdir-$(CONFIG_SAMPLE_PIDFD) += pidfd16subdir-$(CONFIG_SAMPLE_PIDFD) += pidfd
16obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi/17obj-$(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
3userprogs-always-y := sandboxer
4
5userccflags += -I usr/include
6
7.PHONY: all clean
8
9all:
10 $(MAKE) -C ../.. samples/landlock/
11
12clean:
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
25static 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
34static 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
44static 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
55static 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
78static 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
136out_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
158int 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
235err_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
238source "security/yama/Kconfig"238source "security/yama/Kconfig"
239source "security/safesetid/Kconfig"239source "security/safesetid/Kconfig"
240source "security/lockdown/Kconfig"240source "security/lockdown/Kconfig"
241source "security/landlock/Kconfig"
241 242
242source "security/integrity/Kconfig"243source "security/integrity/Kconfig"
243 244
@@ -277,11 +278,11 @@ endchoice · 33 unmodified lines
277 278
278config LSM279config LSM
279 string "Ordered list of enabled LSMs"280 string "Ordered list of enabled LSMs"
280 default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK281 default "landlock,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_APPARMOR282 default "landlock,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_TOMOYO283 default "landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
283 default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC284 default "landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
284 default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"285 default "landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
285 help286 help
286 A comma-separated list of LSMs, in initialization order.287 A comma-separated list of LSMs, in initialization order.
287 Any LSMs left off this list will be ignored. This can be288 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
13subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid13subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
14subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown14subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
15subdir-$(CONFIG_BPF_LSM) += bpf15subdir-$(CONFIG_BPF_LSM) += bpf
16subdir-$(CONFIG_SECURITY_LANDLOCK) += landlock
16 17
17# always enable default capabilities18# always enable default capabilities
18obj-y += commoncap.o19obj-y += commoncap.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ · 13 unmodified lines
32obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/33obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
33obj-$(CONFIG_CGROUPS) += device_cgroup.o34obj-$(CONFIG_CGROUPS) += device_cgroup.o
34obj-$(CONFIG_BPF_LSM) += bpf/35obj-$(CONFIG_BPF_LSM) += bpf/
36obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
35 37
36# Object integrity file lists38# Object integrity file lists
37subdir-$(CONFIG_INTEGRITY) += integrity39subdir-$(CONFIG_INTEGRITY) += integrity
security/landlock/Kconfig+21−0
@@ -0,0 +1,21 @@
1# SPDX-License-Identifier: GPL-2.0-only
2
3config 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 @@
1obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
2
3landlock-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
17static 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
29static 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
37static 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
19struct landlock_cred_security {
20 struct landlock_ruleset *domain;
21};
22
23static inline struct landlock_cred_security *landlock_cred(
24 const struct cred *cred)
25{
26 return cred->security + landlock_blob_sizes.lbs_cred;
27}
28
29static 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 */
37static 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
43static 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
42static 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
81static const struct landlock_object_underops landlock_fs_underops = {
82 .release = release_inode
83};
84
85/* Ruleset management */
86
87static 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();
93retry:
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 */
152int 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
183static 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
225static 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
279jump_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
310static 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
323static 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 */
339static 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 */
461static 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
470static 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 */
482static 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
489static 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 */
504static 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
514static 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 */
547static 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
566static 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
574static 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
596static 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
602static 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
614static 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
620static 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
626static 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
634static 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
652static 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
667static 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 */
25struct 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 */
44struct 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
53static 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
59static 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
67int 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
20struct 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 */
42void 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
16struct landlock_object;
17
18/**
19 * struct landlock_object_underops - Operations on an underlying object
20 */
21struct 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 */
39struct 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
79struct landlock_object *landlock_create_object(
80 const struct landlock_object_underops *const underops,
81 void *const underobj);
82
83void landlock_put_object(struct landlock_object *const object);
84
85static 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 */
32static 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
50static 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
64static 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 */
88static 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 */
106static int hook_ptrace_traceme(struct task_struct *const parent)
107{
108 return task_ptrace(parent, current);
109}
110
111static 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
27static 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
47struct 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
60static 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
69static 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
104static 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
113static 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 */
144static 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
218static 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. */
230int 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
243static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
244{
245 if (hierarchy)
246 refcount_inc(&hierarchy->usage);
247}
248
249static 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
259static 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
306out_unlock:
307 mutex_unlock(&src->lock);
308 mutex_unlock(&dst->lock);
309 return err;
310}
311
312static 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
350out_unlock:
351 mutex_unlock(&parent->lock);
352 mutex_unlock(&child->lock);
353 return err;
354}
355
356static 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
368void 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
375static 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
383void 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 */
400struct 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
444out_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 */
452const 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 */
22struct 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 */
37struct 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 */
63struct 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 */
82struct 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
143struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask);
144
145void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
146void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
147
148int landlock_insert_rule(struct landlock_ruleset *const ruleset,
149 struct landlock_object *const object, const u32 access);
150
151struct landlock_ruleset *landlock_merge_ruleset(
152 struct landlock_ruleset *const parent,
153 struct landlock_ruleset *const ruleset);
154
155const struct landlock_rule *landlock_find_rule(
156 const struct landlock_ruleset *const ruleset,
157 const struct landlock_object *const object);
158
159static 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
18bool landlock_initialized __lsm_ro_after_init = false;
19
20struct 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
26static 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
36DEFINE_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
14extern bool landlock_initialized;
15
16extern 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 */
46static __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 */
72static 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
95static 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
104static 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
111static 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 */
125static 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 */
156SYSCALL_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 */
206static 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
232out_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 */
242static 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
270out_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 */
304SYSCALL_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
362out_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 */
392SYSCALL_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
445out_put_creds:
446 abort_creds(new_cred);
447
448out_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
203 lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);203 lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
204 lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);204 lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
205 lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);205 lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
206 lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
206 lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);207 lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
207}208}
208 209
@@ -333,12 +334,13 @@ static void __init ordered_lsm_init(void) · 124 unmodified lines
333 for (lsm = ordered_lsms; *lsm; lsm++)334 for (lsm = ordered_lsms; *lsm; lsm++)
334 prepare_lsm(*lsm);335 prepare_lsm(*lsm);
335 336
336 init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);337 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("file blob size = %d\n", blob_sizes.lbs_file);
338 init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);339 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("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("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
341 init_debug("task blob size = %d\n", blob_sizes.lbs_task);342 init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
343 init_debug("task blob size = %d\n", blob_sizes.lbs_task);
342 344
343 /*345 /*
344 * Create any kmem_caches needed for blobs346 * Create any kmem_caches needed for blobs
@@ -670,6 +672,27 @@ static void __init lsm_early_task(struct task_struct *task) · 325 unmodified lines
670 panic("%s: Early task alloc failed.\n", __func__);672 panic("%s: Early task alloc failed.\n", __func__);
671}673}
672 674
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 */
683static 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
673/*696/*
674 * The default value of the LSM hook is defined in linux/lsm_hook_defs.h and697 * The default value of the LSM hook is defined in linux/lsm_hook_defs.h and
675 * can be accessed with:698 * can be accessed with:
@@ -867,12 +890,26 @@ int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter * · 191 unmodified lines
867 890
868int security_sb_alloc(struct super_block *sb)891int security_sb_alloc(struct super_block *sb)
869{892{
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
903void security_sb_delete(struct super_block *sb)
904{
905 call_void_hook(sb_delete, sb);
871}906}
872 907
873void security_sb_free(struct super_block *sb)908void security_sb_free(struct super_block *sb)
874{909{
875 call_void_hook(sb_free_security, sb);910 call_void_hook(sb_free_security, sb);
911 kfree(sb->s_security);
912 sb->s_security = NULL;
876}913}
877 914
878void security_free_mnt_opts(void **mnt_opts)915void 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
358 358
359 if (!isec)359 if (!isec)
360 return;360 return;
361 sbsec = inode->i_sb->s_security;361 sbsec = selinux_superblock(inode->i_sb);
362 /*362 /*
363 * As not all inode security structures are in a list, we check for363 * As not all inode security structures are in a list, we check for
364 * empty list outside of the lock to make sure that we won't waste364 * 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
376 }376 }
377}377}
378 378
379static 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
386struct selinux_mnt_opts {379struct selinux_mnt_opts {
387 const char *fscontext, *context, *rootcontext, *defcontext;380 const char *fscontext, *context, *rootcontext, *defcontext;
388};381};
@@ -494,7 +487,7 @@ static int selinux_is_genfs_special_handling(struct super_block *sb) · 105 unmodified lines
494 487
495static int selinux_is_sblabel_mnt(struct super_block *sb)488static int selinux_is_sblabel_mnt(struct super_block *sb)
496{489{
497 struct superblock_security_struct *sbsec = sb->s_security;490 struct superblock_security_struct *sbsec = selinux_superblock(sb);
498 491
499 /*492 /*
500 * IMPORTANT: Double-check logic in this function when adding a new493 * IMPORTANT: Double-check logic in this function when adding a new
@@ -571,7 +564,7 @@ fallback: · 70 unmodified lines
571 564
572static int sb_finish_set_opts(struct super_block *sb)565static int sb_finish_set_opts(struct super_block *sb)
573{566{
574 struct superblock_security_struct *sbsec = sb->s_security;567 struct superblock_security_struct *sbsec = selinux_superblock(sb);
575 struct dentry *root = sb->s_root;568 struct dentry *root = sb->s_root;
576 struct inode *root_inode = d_backing_inode(root);569 struct inode *root_inode = d_backing_inode(root);
577 int rc = 0;570 int rc = 0;
@@ -662,7 +655,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, · 84 unmodified lines
662 unsigned long *set_kern_flags)655 unsigned long *set_kern_flags)
663{656{
664 const struct cred *cred = current_cred();657 const struct cred *cred = current_cred();
665 struct superblock_security_struct *sbsec = sb->s_security;658 struct superblock_security_struct *sbsec = selinux_superblock(sb);
666 struct dentry *root = sb->s_root;659 struct dentry *root = sb->s_root;
667 struct selinux_mnt_opts *opts = mnt_opts;660 struct selinux_mnt_opts *opts = mnt_opts;
668 struct inode_security_struct *root_isec;661 struct inode_security_struct *root_isec;
@@ -900,8 +893,8 @@ out_double_mount: · 231 unmodified lines
900static int selinux_cmp_sb_context(const struct super_block *oldsb,893static int selinux_cmp_sb_context(const struct super_block *oldsb,
901 const struct super_block *newsb)894 const struct super_block *newsb)
902{895{
903 struct superblock_security_struct *old = oldsb->s_security;896 struct superblock_security_struct *old = selinux_superblock(oldsb);
904 struct superblock_security_struct *new = newsb->s_security;897 struct superblock_security_struct *new = selinux_superblock(newsb);
905 char oldflags = old->flags & SE_MNTMASK;898 char oldflags = old->flags & SE_MNTMASK;
906 char newflags = new->flags & SE_MNTMASK;899 char newflags = new->flags & SE_MNTMASK;
907 900
@@ -933,8 +926,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, · 25 unmodified lines
933 unsigned long *set_kern_flags)926 unsigned long *set_kern_flags)
934{927{
935 int rc = 0;928 int rc = 0;
936 const struct superblock_security_struct *oldsbsec = oldsb->s_security;929 const struct superblock_security_struct *oldsbsec =
937 struct superblock_security_struct *newsbsec = newsb->s_security;930 selinux_superblock(oldsb);
931 struct superblock_security_struct *newsbsec = selinux_superblock(newsb);
938 932
939 int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);933 int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
940 int set_context = (oldsbsec->flags & CONTEXT_MNT);934 int set_context = (oldsbsec->flags & CONTEXT_MNT);
@@ -1113,7 +1107,7 @@ static int show_sid(struct seq_file *m, u32 sid) · 172 unmodified lines
1113 1107
1114static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)1108static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
1115{1109{
1116 struct superblock_security_struct *sbsec = sb->s_security;1110 struct superblock_security_struct *sbsec = selinux_superblock(sb);
1117 int rc;1111 int rc;
1118 1112
1119 if (!(sbsec->flags & SE_SBINITIALIZED))1113 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
1464 if (isec->sclass == SECCLASS_FILE)1458 if (isec->sclass == SECCLASS_FILE)
1465 isec->sclass = inode_mode_to_security_class(inode->i_mode);1459 isec->sclass = inode_mode_to_security_class(inode->i_mode);
1466 1460
1467 sbsec = inode->i_sb->s_security;1461 sbsec = selinux_superblock(inode->i_sb);
1468 if (!(sbsec->flags & SE_SBINITIALIZED)) {1462 if (!(sbsec->flags & SE_SBINITIALIZED)) {
1469 /* Defer initialization until selinux_complete_init,1463 /* Defer initialization until selinux_complete_init,
1470 after the initial policy is loaded and the security1464 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
1815 const struct qstr *name, u16 tclass,1809 const struct qstr *name, u16 tclass,
1816 u32 *_new_isid)1810 u32 *_new_isid)
1817{1811{
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);
1819 1814
1820 if ((sbsec->flags & SE_SBINITIALIZED) &&1815 if ((sbsec->flags & SE_SBINITIALIZED) &&
1821 (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {1816 (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
@@ -1846,7 +1841,7 @@ static int may_create(struct inode *dir, · 24 unmodified lines
1846 int rc;1841 int rc;
1847 1842
1848 dsec = inode_security(dir);1843 dsec = inode_security(dir);
1849 sbsec = dir->i_sb->s_security;1844 sbsec = selinux_superblock(dir->i_sb);
1850 1845
1851 sid = tsec->sid;1846 sid = tsec->sid;
1852 1847
@@ -1995,7 +1990,7 @@ static int superblock_has_perm(const struct cred *cred, · 142 unmodified lines
1995 struct superblock_security_struct *sbsec;1990 struct superblock_security_struct *sbsec;
1996 u32 sid = cred_sid(cred);1991 u32 sid = cred_sid(cred);
1997 1992
1998 sbsec = sb->s_security;1993 sbsec = selinux_superblock(sb);
1999 return avc_has_perm(&selinux_state,1994 return avc_has_perm(&selinux_state,
2000 sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);1995 sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
2001}1996}
@@ -2617,11 +2612,7 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) · 615 unmodified lines
2617 2612
2618static int selinux_sb_alloc_security(struct super_block *sb)2613static int selinux_sb_alloc_security(struct super_block *sb)
2619{2614{
2620 struct superblock_security_struct *sbsec;2615 struct superblock_security_struct *sbsec = selinux_superblock(sb);
2621
2622 sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
2623 if (!sbsec)
2624 return -ENOMEM;
2625 2616
2626 mutex_init(&sbsec->lock);2617 mutex_init(&sbsec->lock);
2627 INIT_LIST_HEAD(&sbsec->isec_head);2618 INIT_LIST_HEAD(&sbsec->isec_head);
@@ -2629,16 +2620,10 @@ static int selinux_sb_alloc_security(struct super_block *sb) · 1 unmodified line
2629 sbsec->sid = SECINITSID_UNLABELED;2620 sbsec->sid = SECINITSID_UNLABELED;
2630 sbsec->def_sid = SECINITSID_FILE;2621 sbsec->def_sid = SECINITSID_FILE;
2631 sbsec->mntpoint_sid = SECINITSID_UNLABELED;2622 sbsec->mntpoint_sid = SECINITSID_UNLABELED;
2632 sb->s_security = sbsec;
2633 2623
2634 return 0;2624 return 0;
2635}2625}
2636 2626
2637static void selinux_sb_free_security(struct super_block *sb)
2638{
2639 superblock_free_security(sb);
2640}
2641
2642static inline int opt_len(const char *s)2627static inline int opt_len(const char *s)
2643{2628{
2644 bool open_quote = false;2629 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
2772static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)2757static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
2773{2758{
2774 struct selinux_mnt_opts *opts = mnt_opts;2759 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);
2776 u32 sid;2761 u32 sid;
2777 int rc;2762 int rc;
2778 2763
@@ -3010,7 +2995,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, · 231 unmodified lines
3010 int rc;2995 int rc;
3011 char *context;2996 char *context;
3012 2997
3013 sbsec = dir->i_sb->s_security;2998 sbsec = selinux_superblock(dir->i_sb);
3014 2999
3015 newsid = tsec->create_sid;3000 newsid = tsec->create_sid;
3016 3001
@@ -3312,7 +3297,7 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns, · 295 unmodified lines
3312 if (!selinux_initialized(&selinux_state))3297 if (!selinux_initialized(&selinux_state))
3313 return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM);3298 return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM);
3314 3299
3315 sbsec = inode->i_sb->s_security;3300 sbsec = selinux_superblock(inode->i_sb);
3316 if (!(sbsec->flags & SBLABEL_MNT))3301 if (!(sbsec->flags & SBLABEL_MNT))
3317 return -EOPNOTSUPP;3302 return -EOPNOTSUPP;
3318 3303
@@ -3557,13 +3542,14 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, · 238 unmodified lines
3557 const void *value, size_t size, int flags)3542 const void *value, size_t size, int flags)
3558{3543{
3559 struct inode_security_struct *isec = inode_security_novalidate(inode);3544 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;
3561 u32 newsid;3546 u32 newsid;
3562 int rc;3547 int rc;
3563 3548
3564 if (strcmp(name, XATTR_SELINUX_SUFFIX))3549 if (strcmp(name, XATTR_SELINUX_SUFFIX))
3565 return -EOPNOTSUPP;3550 return -EOPNOTSUPP;
3566 3551
3552 sbsec = selinux_superblock(inode->i_sb);
3567 if (!(sbsec->flags & SBLABEL_MNT))3553 if (!(sbsec->flags & SBLABEL_MNT))
3568 return -EOPNOTSUPP;3554 return -EOPNOTSUPP;
3569 3555
@@ -7065,6 +7051,7 @@ struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { · 3495 unmodified lines
7065 .lbs_inode = sizeof(struct inode_security_struct),7051 .lbs_inode = sizeof(struct inode_security_struct),
7066 .lbs_ipc = sizeof(struct ipc_security_struct),7052 .lbs_ipc = sizeof(struct ipc_security_struct),
7067 .lbs_msg_msg = sizeof(struct msg_security_struct),7053 .lbs_msg_msg = sizeof(struct msg_security_struct),
7054 .lbs_superblock = sizeof(struct superblock_security_struct),
7068};7055};
7069 7056
7070#ifdef CONFIG_PERF_EVENTS7057#ifdef CONFIG_PERF_EVENTS
@@ -7165,7 +7152,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { · 94 unmodified lines
7165 LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),7152 LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
7166 LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),7153 LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
7167 7154
7168 LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
7169 LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),7155 LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
7170 LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat),7156 LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat),
7171 LSM_HOOK_INIT(sb_remount, selinux_sb_remount),7157 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
188 return tsec->sid;188 return tsec->sid;
189}189}
190 190
191static inline struct superblock_security_struct *selinux_superblock(
192 const struct super_block *superblock)
193{
194 return superblock->s_security + selinux_blob_sizes.lbs_superblock;
195}
196
191#endif /* _SELINUX_OBJSEC_H_ */197#endif /* _SELINUX_OBJSEC_H_ */
security/selinux/ss/services.c+2−1
@@ -47,6 +47,7 @@ · 46 unmodified lines
47#include <linux/sched.h>47#include <linux/sched.h>
48#include <linux/audit.h>48#include <linux/audit.h>
49#include <linux/vmalloc.h>49#include <linux/vmalloc.h>
50#include <linux/lsm_hooks.h>
50#include <net/netlabel.h>51#include <net/netlabel.h>
51 52
52#include "flask.h"53#include "flask.h"
@@ -2955,7 +2956,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) · 2902 unmodified lines
2955 struct sidtab *sidtab;2956 struct sidtab *sidtab;
2956 int rc;2957 int rc;
2957 struct ocontext *c;2958 struct ocontext *c;
2958 struct superblock_security_struct *sbsec = sb->s_security;2959 struct superblock_security_struct *sbsec = selinux_superblock(sb);
2959 const char *fstype = sb->s_type->name;2960 const char *fstype = sb->s_type->name;
2960 2961
2961 if (!selinux_initialized(state)) {2962 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
357 return ipc->security + smack_blob_sizes.lbs_ipc;357 return ipc->security + smack_blob_sizes.lbs_ipc;
358}358}
359 359
360static inline struct superblock_smack *smack_superblock(
361 const struct super_block *superblock)
362{
363 return superblock->s_security + smack_blob_sizes.lbs_superblock;
364}
365
360/*366/*
361 * Is the directory transmuting?367 * Is the directory transmuting?
362 */368 */
security/smack/smack_lsm.c+9−26
@@ -535,12 +535,7 @@ static int smack_syslog(int typefrom_file) · 534 unmodified lines
535 */535 */
536static int smack_sb_alloc_security(struct super_block *sb)536static int smack_sb_alloc_security(struct super_block *sb)
537{537{
538 struct superblock_smack *sbsp;538 struct superblock_smack *sbsp = smack_superblock(sb);
539
540 sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL);
541
542 if (sbsp == NULL)
543 return -ENOMEM;
544 539
545 sbsp->smk_root = &smack_known_floor;540 sbsp->smk_root = &smack_known_floor;
546 sbsp->smk_default = &smack_known_floor;541 sbsp->smk_default = &smack_known_floor;
@@ -549,22 +544,10 @@ static int smack_sb_alloc_security(struct super_block *sb) · 2 unmodified lines
549 /*544 /*
550 * SMK_SB_INITIALIZED will be zero from kzalloc.545 * SMK_SB_INITIALIZED will be zero from kzalloc.
551 */546 */
552 sb->s_security = sbsp;
553 547
554 return 0;548 return 0;
555}549}
556 550
557/**
558 * smack_sb_free_security - free a superblock blob
559 * @sb: the superblock getting the blob
560 *
561 */
562static void smack_sb_free_security(struct super_block *sb)
563{
564 kfree(sb->s_security);
565 sb->s_security = NULL;
566}
567
568struct smack_mnt_opts {551struct smack_mnt_opts {
569 const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;552 const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
570};553};
@@ -772,7 +755,7 @@ static int smack_set_mnt_opts(struct super_block *sb, · 201 unmodified lines
772{755{
773 struct dentry *root = sb->s_root;756 struct dentry *root = sb->s_root;
774 struct inode *inode = d_backing_inode(root);757 struct inode *inode = d_backing_inode(root);
775 struct superblock_smack *sp = sb->s_security;758 struct superblock_smack *sp = smack_superblock(sb);
776 struct inode_smack *isp;759 struct inode_smack *isp;
777 struct smack_known *skp;760 struct smack_known *skp;
778 struct smack_mnt_opts *opts = mnt_opts;761 struct smack_mnt_opts *opts = mnt_opts;
@@ -871,7 +854,7 @@ static int smack_set_mnt_opts(struct super_block *sb, · 92 unmodified lines
871 */854 */
872static int smack_sb_statfs(struct dentry *dentry)855static int smack_sb_statfs(struct dentry *dentry)
873{856{
874 struct superblock_smack *sbp = dentry->d_sb->s_security;857 struct superblock_smack *sbp = smack_superblock(dentry->d_sb);
875 int rc;858 int rc;
876 struct smk_audit_info ad;859 struct smk_audit_info ad;
877 860
@@ -905,7 +888,7 @@ static int smack_bprm_creds_for_exec(struct linux_binprm *bprm) · 27 unmodified lines
905 if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)888 if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
906 return 0;889 return 0;
907 890
908 sbsp = inode->i_sb->s_security;891 sbsp = smack_superblock(inode->i_sb);
909 if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) &&892 if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) &&
910 isp->smk_task != sbsp->smk_root)893 isp->smk_task != sbsp->smk_root)
911 return 0;894 return 0;
@@ -1157,7 +1140,7 @@ static int smack_inode_rename(struct inode *old_inode, · 245 unmodified lines
1157 */1140 */
1158static int smack_inode_permission(struct inode *inode, int mask)1141static int smack_inode_permission(struct inode *inode, int mask)
1159{1142{
1160 struct superblock_smack *sbsp = inode->i_sb->s_security;1143 struct superblock_smack *sbsp = smack_superblock(inode->i_sb);
1161 struct smk_audit_info ad;1144 struct smk_audit_info ad;
1162 int no_block = mask & MAY_NOT_BLOCK;1145 int no_block = mask & MAY_NOT_BLOCK;
1163 int rc;1146 int rc;
@@ -1400,7 +1383,7 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns, · 236 unmodified lines
1400 */1383 */
1401 if (strcmp(name, XATTR_NAME_SMACK) == 0) {1384 if (strcmp(name, XATTR_NAME_SMACK) == 0) {
1402 struct super_block *sbp = dentry->d_sb;1385 struct super_block *sbp = dentry->d_sb;
1403 struct superblock_smack *sbsp = sbp->s_security;1386 struct superblock_smack *sbsp = smack_superblock(sbp);
1404 1387
1405 isp->smk_inode = sbsp->smk_default;1388 isp->smk_inode = sbsp->smk_default;
1406 } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)1389 } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
@@ -1670,7 +1653,7 @@ static int smack_mmap_file(struct file *file, · 263 unmodified lines
1670 isp = smack_inode(file_inode(file));1653 isp = smack_inode(file_inode(file));
1671 if (isp->smk_mmap == NULL)1654 if (isp->smk_mmap == NULL)
1672 return 0;1655 return 0;
1673 sbsp = file_inode(file)->i_sb->s_security;1656 sbsp = smack_superblock(file_inode(file)->i_sb);
1674 if (sbsp->smk_flags & SMK_SB_UNTRUSTED &&1657 if (sbsp->smk_flags & SMK_SB_UNTRUSTED &&
1675 isp->smk_mmap != sbsp->smk_root)1658 isp->smk_mmap != sbsp->smk_root)
1676 return -EACCES;1659 return -EACCES;
@@ -3299,7 +3282,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) · 1622 unmodified lines
3299 return;3282 return;
3300 3283
3301 sbp = inode->i_sb;3284 sbp = inode->i_sb;
3302 sbsp = sbp->s_security;3285 sbsp = smack_superblock(sbp);
3303 /*3286 /*
3304 * We're going to use the superblock default label3287 * We're going to use the superblock default label
3305 * if there's no label on the file.3288 * 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
4714 .lbs_inode = sizeof(struct inode_smack),4697 .lbs_inode = sizeof(struct inode_smack),
4715 .lbs_ipc = sizeof(struct smack_known *),4698 .lbs_ipc = sizeof(struct smack_known *),
4716 .lbs_msg_msg = sizeof(struct smack_known *),4699 .lbs_msg_msg = sizeof(struct smack_known *),
4700 .lbs_superblock = sizeof(struct superblock_smack),
4717};4701};
4718 4702
4719static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {4703static 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
4725 LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),4709 LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
4726 4710
4727 LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),4711 LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
4728 LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
4729 LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),4712 LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
4730 LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),4713 LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
4731 LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),4714 LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
tools/testing/selftests/Makefile+1−0
@@ -25,6 +25,7 @@ TARGETS += ir · 24 unmodified lines
25TARGETS += kcmp25TARGETS += kcmp
26TARGETS += kexec26TARGETS += kexec
27TARGETS += kvm27TARGETS += kvm
28TARGETS += landlock
28TARGETS += lib29TARGETS += lib
29TARGETS += livepatch30TARGETS += livepatch
30TARGETS += lkdtm31TARGETS += 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
3CFLAGS += -Wall -O2
4
5src_test := $(wildcard *_test.c)
6
7TEST_GEN_PROGS := $(src_test:.c=)
8
9TEST_GEN_PROGS_EXTENDED := true
10
11KSFT_KHDR_INSTALL := 1
12OVERRIDE_TARGETS := 1
13include ../lib.mk
14
15khdr_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
24TEST(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
66TEST(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
91TEST(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
113TEST(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
129TEST(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
136TEST(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
145TEST(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. */
167TEST(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
266TEST_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
80static 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
89static 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
99static 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
106static 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__))
140static void disable_caps(struct __test_metadata *const _metadata)
141{
142 _init_caps(_metadata, false);
143}
144
145__attribute__((__unused__))
146static void drop_caps(struct __test_metadata *const _metadata)
147{
148 _init_caps(_metadata, true);
149}
150
151static 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__))
172static 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__))
179static 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 @@
1CONFIG_OVERLAY_FS=y
2CONFIG_SECURITY_LANDLOCK=y
3CONFIG_SECURITY_PATH=y
4CONFIG_SECURITY=y
5CONFIG_SHMEM=y
6CONFIG_TMPFS_XATTR=y
7CONFIG_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) */
29static const char dir_s1d1[] = TMP_DIR "/s1d1";
30static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
31static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
32static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
33static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
34static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
35static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
36static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
37static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
38
39static const char dir_s2d1[] = TMP_DIR "/s2d1";
40static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
41static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
42static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
43static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
44static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
45static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
46
47static const char dir_s3d1[] = TMP_DIR "/s3d1";
48/* dir_s3d2 is a mount point. */
49static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
50static 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
77static 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
102static 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
112static 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
122static 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
151out:
152 free(walker);
153 return err;
154}
155
156static 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
173static 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
181static 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
203static 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
224FIXTURE(layout1) {
225};
226
227FIXTURE_SETUP(layout1)
228{
229 prepare_layout(_metadata);
230
231 create_layout1(_metadata);
232}
233
234FIXTURE_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 */
245static 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
262static int test_open(const char *const path, const int flags)
263{
264 return test_open_rel(AT_FDCWD, path, flags);
265}
266
267TEST_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
290TEST_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
399TEST_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
429static 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
450struct 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
463static 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
491static 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
500TEST_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
548TEST_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
570TEST_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
622TEST_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
649TEST_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
690TEST_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
739TEST_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
918TEST_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
1035TEST_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
1070TEST_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
1094TEST_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
1128TEST_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
1157TEST_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 */
1190TEST_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
1220TEST_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
1240TEST_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
1267TEST_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
1290TEST_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
1323TEST_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
1357enum relative_access {
1358 REL_OPEN,
1359 REL_CHDIR,
1360 REL_CHROOT_ONLY,
1361 REL_CHROOT_CHDIR,
1362};
1363
1364static 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
1477TEST_F_FORK(layout1, relative_open)
1478{
1479 test_relative_path(_metadata, REL_OPEN);
1480}
1481
1482TEST_F_FORK(layout1, relative_chdir)
1483{
1484 test_relative_path(_metadata, REL_CHDIR);
1485}
1486
1487TEST_F_FORK(layout1, relative_chroot_only)
1488{
1489 test_relative_path(_metadata, REL_CHROOT_ONLY);
1490}
1491
1492TEST_F_FORK(layout1, relative_chroot_chdir)
1493{
1494 test_relative_path(_metadata, REL_CHROOT_CHDIR);
1495}
1496
1497static 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
1520static 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
1545TEST_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
1578TEST_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
1611TEST_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
1688TEST_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
1746TEST_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
1783TEST_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
1807static 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
1858TEST_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
1866TEST_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
1874TEST_F_FORK(layout1, make_reg_1)
1875{
1876 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
1877}
1878
1879TEST_F_FORK(layout1, make_reg_2)
1880{
1881 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
1882}
1883
1884TEST_F_FORK(layout1, make_sock)
1885{
1886 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
1887}
1888
1889TEST_F_FORK(layout1, make_fifo)
1890{
1891 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
1892}
1893
1894TEST_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
1939TEST_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
1967static 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
1979TEST_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
2017TEST_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
2073FIXTURE(layout1_bind) {
2074};
2075
2076FIXTURE_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
2087FIXTURE_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
2098static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
2099static 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
2127TEST_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
2149TEST_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"
2287static const char lower_fl1[] = LOWER_DATA "/fl1";
2288static const char lower_dl1[] = LOWER_DATA "/dl1";
2289static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
2290static const char lower_fo1[] = LOWER_DATA "/fo1";
2291static const char lower_do1[] = LOWER_DATA "/do1";
2292static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
2293static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
2294
2295static const char (*lower_base_files[])[] = {
2296 &lower_fl1,
2297 &lower_fo1,
2298 NULL
2299};
2300static const char (*lower_base_directories[])[] = {
2301 &lower_dl1,
2302 &lower_do1,
2303 NULL
2304};
2305static 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"
2315static const char upper_fu1[] = UPPER_DATA "/fu1";
2316static const char upper_du1[] = UPPER_DATA "/du1";
2317static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
2318static const char upper_fo1[] = UPPER_DATA "/fo1";
2319static const char upper_do1[] = UPPER_DATA "/do1";
2320static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
2321static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
2322
2323static const char (*upper_base_files[])[] = {
2324 &upper_fu1,
2325 &upper_fo1,
2326 NULL
2327};
2328static const char (*upper_base_directories[])[] = {
2329 &upper_du1,
2330 &upper_do1,
2331 NULL
2332};
2333static 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"
2342static const char merge_fl1[] = MERGE_DATA "/fl1";
2343static const char merge_dl1[] = MERGE_DATA "/dl1";
2344static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
2345static const char merge_fu1[] = MERGE_DATA "/fu1";
2346static const char merge_du1[] = MERGE_DATA "/du1";
2347static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
2348static const char merge_fo1[] = MERGE_DATA "/fo1";
2349static const char merge_do1[] = MERGE_DATA "/do1";
2350static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
2351static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
2352static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
2353
2354static const char (*merge_base_files[])[] = {
2355 &merge_fl1,
2356 &merge_fu1,
2357 &merge_fo1,
2358 NULL
2359};
2360static const char (*merge_base_directories[])[] = {
2361 &merge_dl1,
2362 &merge_du1,
2363 &merge_do1,
2364 NULL
2365};
2366static 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
2414FIXTURE(layout2_overlay) {
2415};
2416
2417FIXTURE_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
2454FIXTURE_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
2485TEST_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
2520TEST_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
2791TEST_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
22static 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
39static 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
62FIXTURE(hierarchy) { };
63
64FIXTURE_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 */
86FIXTURE_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 */
101FIXTURE_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 */
115FIXTURE_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 */
130FIXTURE_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 */
145FIXTURE_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 */
161FIXTURE_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 */
177FIXTURE_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 */
195FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
196 .domain_both = true,
197 .domain_parent = true,
198 .domain_child = true,
199};
200
201FIXTURE_SETUP(hierarchy)
202{ }
203
204FIXTURE_TEARDOWN(hierarchy)
205{ }
206
207/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
208TEST_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
337TEST_HARNESS_MAIN
tools/testing/selftests/landlock/true.c+5−0
@@ -0,0 +1,5 @@
1// SPDX-License-Identifier: GPL-2.0
2int main(void)
3{
4 return 0;
5}