summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-03-14 16:00:27 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-03-14 16:00:27 -0700
commit35e886e88c803920644c9d3abb45a9ecb7f1e761 (patch)
tree9595c4b62518a00de2040a199a4653f3abeff8b9
parent29da654bd20842d4c1e17c6d4dc1b12642ca16ac (diff)
parenta17c60e533f5cd832e77e0d194e2e0bb663371b6 (diff)
Merge tag 'landlock-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün: "Some miscellaneous improvements, including new KUnit tests, extended documentation and boot help, and some cosmetic cleanups. Additional test changes already went through the net tree" * tag 'landlock-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: samples/landlock: Don't error out if a file path cannot be opened landlock: Use f_cred in security_file_open() hook landlock: Rename "ptrace" files to "task" landlock: Simplify current_check_access_socket() landlock: Warn once if a Landlock action is requested while disabled landlock: Extend documentation for kernel support landlock: Add support for KUnit tests selftests/landlock: Clean up error logs related to capabilities
-rw-r--r--Documentation/userspace-api/landlock.rst59
-rw-r--r--samples/landlock/sandboxer.c13
-rw-r--r--security/landlock/.kunitconfig4
-rw-r--r--security/landlock/Kconfig15
-rw-r--r--security/landlock/Makefile2
-rw-r--r--security/landlock/common.h2
-rw-r--r--security/landlock/fs.c252
-rw-r--r--security/landlock/net.c7
-rw-r--r--security/landlock/setup.c4
-rw-r--r--security/landlock/syscalls.c18
-rw-r--r--security/landlock/task.c (renamed from security/landlock/ptrace.c)4
-rw-r--r--security/landlock/task.h (renamed from security/landlock/ptrace.h)8
-rw-r--r--tools/testing/kunit/configs/all_tests.config1
-rw-r--r--tools/testing/selftests/landlock/common.h39
14 files changed, 363 insertions, 65 deletions
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 2e3822677061..9dd636aaa829 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -19,11 +19,12 @@ unexpected/malicious behaviors in user space applications. Landlock empowers
any process, including unprivileged ones, to securely restrict themselves.
We can quickly make sure that Landlock is enabled in the running system by
-looking for "landlock: Up and running" in kernel logs (as root): ``dmesg | grep
-landlock || journalctl -kg landlock`` . Developers can also easily check for
-Landlock support with a :ref:`related system call <landlock_abi_versions>`. If
-Landlock is not currently supported, we need to :ref:`configure the kernel
-appropriately <kernel_support>`.
+looking for "landlock: Up and running" in kernel logs (as root):
+``dmesg | grep landlock || journalctl -kb -g landlock`` .
+Developers can also easily check for Landlock support with a
+:ref:`related system call <landlock_abi_versions>`.
+If Landlock is not currently supported, we need to
+:ref:`configure the kernel appropriately <kernel_support>`.
Landlock rules
==============
@@ -499,6 +500,9 @@ access rights.
Kernel support
==============
+Build time configuration
+------------------------
+
Landlock was first introduced in Linux 5.13 but it must be configured at build
time with ``CONFIG_SECURITY_LANDLOCK=y``. Landlock must also be enabled at boot
time as the other security modules. The list of security modules enabled by
@@ -507,11 +511,52 @@ contains ``CONFIG_LSM=landlock,[...]`` with ``[...]`` as the list of other
potentially useful security modules for the running system (see the
``CONFIG_LSM`` help).
+Boot time configuration
+-----------------------
+
If the running kernel does not have ``landlock`` in ``CONFIG_LSM``, then we can
-still enable it by adding ``lsm=landlock,[...]`` to
-Documentation/admin-guide/kernel-parameters.rst thanks to the bootloader
+enable Landlock by adding ``lsm=landlock,[...]`` to
+Documentation/admin-guide/kernel-parameters.rst in the boot loader
configuration.
+For example, if the current built-in configuration is:
+
+.. code-block:: console
+
+ $ zgrep -h "^CONFIG_LSM=" "/boot/config-$(uname -r)" /proc/config.gz 2>/dev/null
+ CONFIG_LSM="lockdown,yama,integrity,apparmor"
+
+...and if the cmdline doesn't contain ``landlock`` either:
+
+.. code-block:: console
+
+ $ sed -n 's/.*\(\<lsm=\S\+\).*/\1/p' /proc/cmdline
+ lsm=lockdown,yama,integrity,apparmor
+
+...we should configure the boot loader to set a cmdline extending the ``lsm``
+list with the ``landlock,`` prefix::
+
+ lsm=landlock,lockdown,yama,integrity,apparmor
+
+After a reboot, we can check that Landlock is up and running by looking at
+kernel logs:
+
+.. code-block:: console
+
+ # dmesg | grep landlock || journalctl -kb -g landlock
+ [ 0.000000] Command line: [...] lsm=landlock,lockdown,yama,integrity,apparmor
+ [ 0.000000] Kernel command line: [...] lsm=landlock,lockdown,yama,integrity,apparmor
+ [ 0.000000] LSM: initializing lsm=lockdown,capability,landlock,yama,integrity,apparmor
+ [ 0.000000] landlock: Up and running.
+
+The kernel may be configured at build time to always load the ``lockdown`` and
+``capability`` LSMs. In that case, these LSMs will appear at the beginning of
+the ``LSM: initializing`` log line as well, even if they are not configured in
+the boot loader.
+
+Network support
+---------------
+
To be able to explicitly allow TCP operations (e.g., adding a network rule with
``LANDLOCK_ACCESS_NET_BIND_TCP``), the kernel must support TCP
(``CONFIG_INET=y``). Otherwise, sys_landlock_add_rule() returns an
diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 08596c0ef070..32e930c853bb 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
- * Simple Landlock sandbox manager able to launch a process restricted by a
- * user-defined filesystem access control policy.
+ * Simple Landlock sandbox manager able to execute a process restricted by
+ * user-defined file system and network access control policies.
*
* Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
* Copyright © 2020 ANSSI
@@ -120,9 +120,11 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
if (path_beneath.parent_fd < 0) {
fprintf(stderr, "Failed to open \"%s\": %s\n",
path_list[i], strerror(errno));
- goto out_free_name;
+ continue;
}
if (fstat(path_beneath.parent_fd, &statbuf)) {
+ fprintf(stderr, "Failed to stat \"%s\": %s\n",
+ path_list[i], strerror(errno));
close(path_beneath.parent_fd);
goto out_free_name;
}
@@ -227,7 +229,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
ENV_TCP_CONNECT_NAME, argv[0]);
fprintf(stderr,
- "Launch a command in a restricted environment.\n\n");
+ "Execute a command in a restricted environment.\n\n");
fprintf(stderr,
"Environment variables containing paths and ports "
"each separated by a colon:\n");
@@ -248,7 +250,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
ENV_TCP_CONNECT_NAME);
fprintf(stderr,
"\nexample:\n"
- "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
+ "%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
"%s=\"9418\" "
"%s=\"80:443\" "
@@ -383,6 +385,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
cmd_path = argv[1];
cmd_argv = argv + 1;
+ fprintf(stderr, "Executing the sandboxed command...\n");
execvpe(cmd_path, cmd_argv, envp);
fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path,
strerror(errno));
diff --git a/security/landlock/.kunitconfig b/security/landlock/.kunitconfig
new file mode 100644
index 000000000000..03e119466604
--- /dev/null
+++ b/security/landlock/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_LANDLOCK=y
+CONFIG_SECURITY_LANDLOCK_KUNIT_TEST=y
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
index c4bf0d5eff39..3f1493402052 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -20,3 +20,18 @@ config SECURITY_LANDLOCK
If you are unsure how to answer this question, answer N. Otherwise,
you should also prepend "landlock," to the content of CONFIG_LSM to
enable Landlock at boot time.
+
+config SECURITY_LANDLOCK_KUNIT_TEST
+ bool "KUnit tests for Landlock" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ depends on SECURITY_LANDLOCK
+ default KUNIT_ALL_TESTS
+ help
+ Build KUnit tests for Landlock.
+
+ See the KUnit documentation in Documentation/dev-tools/kunit
+
+ Run all KUnit tests for Landlock with:
+ ./tools/testing/kunit/kunit.py run --kunitconfig security/landlock
+
+ If you are unsure how to answer this question, answer N.
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index c2e116f2a299..b4538b7cf7d2 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
landlock-y := setup.o syscalls.o object.o ruleset.o \
- cred.o ptrace.o fs.o
+ cred.o task.o fs.o
landlock-$(CONFIG_INET) += net.o
diff --git a/security/landlock/common.h b/security/landlock/common.h
index 5dc0fe15707d..0eb1d34c2eae 100644
--- a/security/landlock/common.h
+++ b/security/landlock/common.h
@@ -17,4 +17,6 @@
#define pr_fmt(fmt) LANDLOCK_NAME ": " fmt
+#define BIT_INDEX(bit) HWEIGHT(bit - 1)
+
#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 0171f7eb6ee1..c15559432d3d 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -7,6 +7,7 @@
* Copyright © 2021-2022 Microsoft Corporation
*/
+#include <kunit/test.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bits.h>
@@ -247,15 +248,18 @@ get_handled_fs_accesses(const struct landlock_ruleset *const domain)
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
}
-static const struct landlock_ruleset *get_current_fs_domain(void)
+static const struct landlock_ruleset *
+get_fs_domain(const struct landlock_ruleset *const domain)
{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
-
- if (!dom || !get_raw_handled_fs_accesses(dom))
+ if (!domain || !get_raw_handled_fs_accesses(domain))
return NULL;
- return dom;
+ return domain;
+}
+
+static const struct landlock_ruleset *get_current_fs_domain(void)
+{
+ return get_fs_domain(landlock_get_current_domain());
}
/*
@@ -311,6 +315,119 @@ static bool no_more_access(
return true;
}
+#define NMA_TRUE(...) KUNIT_EXPECT_TRUE(test, no_more_access(__VA_ARGS__))
+#define NMA_FALSE(...) KUNIT_EXPECT_FALSE(test, no_more_access(__VA_ARGS__))
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_no_more_access(struct kunit *const test)
+{
+ const layer_mask_t rx0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT_ULL(0),
+ };
+ const layer_mask_t mx0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = BIT_ULL(0),
+ };
+ const layer_mask_t x0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ };
+ const layer_mask_t x1[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(1),
+ };
+ const layer_mask_t x01[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) |
+ BIT_ULL(1),
+ };
+ const layer_mask_t allows_all[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ /* Checks without restriction. */
+ NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false);
+ NMA_TRUE(&allows_all, &x0, false, &allows_all, NULL, false);
+ NMA_FALSE(&x0, &x0, false, &allows_all, NULL, false);
+
+ /*
+ * Checks that we can only refer a file if no more access could be
+ * inherited.
+ */
+ NMA_TRUE(&x0, &x0, false, &rx0, NULL, false);
+ NMA_TRUE(&rx0, &rx0, false, &rx0, NULL, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, NULL, false);
+ NMA_FALSE(&rx0, &rx0, false, &x1, NULL, false);
+
+ /* Checks allowed referring with different nested domains. */
+ NMA_TRUE(&x0, &x1, false, &x0, NULL, false);
+ NMA_TRUE(&x1, &x0, false, &x0, NULL, false);
+ NMA_TRUE(&x0, &x01, false, &x0, NULL, false);
+ NMA_TRUE(&x0, &x01, false, &rx0, NULL, false);
+ NMA_TRUE(&x01, &x0, false, &x0, NULL, false);
+ NMA_TRUE(&x01, &x0, false, &rx0, NULL, false);
+ NMA_FALSE(&x01, &x01, false, &x0, NULL, false);
+
+ /* Checks that file access rights are also enforced for a directory. */
+ NMA_FALSE(&rx0, &rx0, true, &x0, NULL, false);
+
+ /* Checks that directory access rights don't impact file referring... */
+ NMA_TRUE(&mx0, &mx0, false, &x0, NULL, false);
+ /* ...but only directory referring. */
+ NMA_FALSE(&mx0, &mx0, true, &x0, NULL, false);
+
+ /* Checks directory exchange. */
+ NMA_TRUE(&mx0, &mx0, true, &mx0, &mx0, true);
+ NMA_TRUE(&mx0, &mx0, true, &mx0, &x0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x0, &mx0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x0, &x0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x1, &x1, true);
+
+ /* Checks file exchange with directory access rights... */
+ NMA_TRUE(&mx0, &mx0, false, &mx0, &mx0, false);
+ NMA_TRUE(&mx0, &mx0, false, &mx0, &x0, false);
+ NMA_TRUE(&mx0, &mx0, false, &x0, &mx0, false);
+ NMA_TRUE(&mx0, &mx0, false, &x0, &x0, false);
+ /* ...and with file access rights. */
+ NMA_TRUE(&rx0, &rx0, false, &rx0, &rx0, false);
+ NMA_TRUE(&rx0, &rx0, false, &rx0, &x0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, &rx0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, &x0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x1, &x1, false);
+
+ /*
+ * Allowing the following requests should not be a security risk
+ * because domain 0 denies execute access, and domain 1 is always
+ * nested with domain 0. However, adding an exception for this case
+ * would mean to check all nested domains to make sure none can get
+ * more privileges (e.g. processes only sandboxed by domain 0).
+ * Moreover, this behavior (i.e. composition of N domains) could then
+ * be inconsistent compared to domain 1's ruleset alone (e.g. it might
+ * be denied to link/rename with domain 1's ruleset, whereas it would
+ * be allowed if nested on top of domain 0). Another drawback would be
+ * to create a cover channel that could enable sandboxed processes to
+ * infer most of the filesystem restrictions from their domain. To
+ * make it simple, efficient, safe, and more consistent, this case is
+ * always denied.
+ */
+ NMA_FALSE(&x1, &x1, false, &x0, NULL, false);
+ NMA_FALSE(&x1, &x1, false, &rx0, NULL, false);
+ NMA_FALSE(&x1, &x1, true, &x0, NULL, false);
+ NMA_FALSE(&x1, &x1, true, &rx0, NULL, false);
+
+ /* Checks the same case of exclusive domains with a file... */
+ NMA_TRUE(&x1, &x1, false, &x01, NULL, false);
+ NMA_FALSE(&x1, &x1, false, &x01, &x0, false);
+ NMA_FALSE(&x1, &x1, false, &x01, &x01, false);
+ NMA_FALSE(&x1, &x1, false, &x0, &x0, false);
+ /* ...and with a directory. */
+ NMA_FALSE(&x1, &x1, false, &x0, &x0, true);
+ NMA_FALSE(&x1, &x1, true, &x0, &x0, false);
+ NMA_FALSE(&x1, &x1, true, &x0, &x0, true);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
+#undef NMA_TRUE
+#undef NMA_FALSE
+
/*
* Removes @layer_masks accesses that are not requested.
*
@@ -331,6 +448,57 @@ scope_to_request(const access_mask_t access_request,
return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
}
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_scope_to_request_with_exec_none(struct kunit *const test)
+{
+ /* Allows everything. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ /* Checks and scopes with execute. */
+ KUNIT_EXPECT_TRUE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
+ &layer_masks));
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+static void test_scope_to_request_with_exec_some(struct kunit *const test)
+{
+ /* Denies execute and write. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ };
+
+ /* Checks and scopes with execute. */
+ KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
+ &layer_masks));
+ KUNIT_EXPECT_EQ(test, BIT_ULL(0),
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+static void test_scope_to_request_without_access(struct kunit *const test)
+{
+ /* Denies execute and write. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ };
+
+ /* Checks and scopes without access request. */
+ KUNIT_EXPECT_TRUE(test, scope_to_request(0, &layer_masks));
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
/*
* Returns true if there is at least one access right different than
* LANDLOCK_ACCESS_FS_REFER.
@@ -354,6 +522,51 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
return false;
}
+#define IE_TRUE(...) KUNIT_EXPECT_TRUE(test, is_eacces(__VA_ARGS__))
+#define IE_FALSE(...) KUNIT_EXPECT_FALSE(test, is_eacces(__VA_ARGS__))
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_is_eacces_with_none(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+static void test_is_eacces_with_refer(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = BIT_ULL(0),
+ };
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+static void test_is_eacces_with_write(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(0),
+ };
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+
+ IE_TRUE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
+#undef IE_TRUE
+#undef IE_FALSE
+
/**
* is_access_to_paths_allowed - Check accesses for requests with a common path
*
@@ -1124,7 +1337,8 @@ static int hook_file_open(struct file *const file)
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
access_mask_t open_access_request, full_access_request, allowed_access;
const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
- const struct landlock_ruleset *const dom = get_current_fs_domain();
+ const struct landlock_ruleset *const dom =
+ get_fs_domain(landlock_cred(file->f_cred)->domain);
if (!dom)
return 0;
@@ -1225,3 +1439,27 @@ __init void landlock_add_fs_hooks(void)
security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
&landlock_lsmid);
}
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+/* clang-format off */
+static struct kunit_case test_cases[] = {
+ KUNIT_CASE(test_no_more_access),
+ KUNIT_CASE(test_scope_to_request_with_exec_none),
+ KUNIT_CASE(test_scope_to_request_with_exec_some),
+ KUNIT_CASE(test_scope_to_request_without_access),
+ KUNIT_CASE(test_is_eacces_with_none),
+ KUNIT_CASE(test_is_eacces_with_refer),
+ KUNIT_CASE(test_is_eacces_with_write),
+ {}
+};
+/* clang-format on */
+
+static struct kunit_suite test_suite = {
+ .name = "landlock_fs",
+ .test_cases = test_cases,
+};
+
+kunit_test_suite(test_suite);
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
diff --git a/security/landlock/net.c b/security/landlock/net.c
index efa1b644a4af..c8bcd29bde09 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -64,12 +64,11 @@ static const struct landlock_ruleset *get_current_net_domain(void)
static int current_check_access_socket(struct socket *const sock,
struct sockaddr *const address,
const int addrlen,
- const access_mask_t access_request)
+ access_mask_t access_request)
{
__be16 port;
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
const struct landlock_rule *rule;
- access_mask_t handled_access;
struct landlock_id id = {
.type = LANDLOCK_KEY_NET_PORT,
};
@@ -164,9 +163,9 @@ static int current_check_access_socket(struct socket *const sock,
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
rule = landlock_find_rule(dom, id);
- handled_access = landlock_init_layer_masks(
+ access_request = landlock_init_layer_masks(
dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
- if (landlock_unmask_layers(rule, handled_access, &layer_masks,
+ if (landlock_unmask_layers(rule, access_request, &layer_masks,
ARRAY_SIZE(layer_masks)))
return 0;
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index f6dd33143b7f..28519a45b11f 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -14,8 +14,8 @@
#include "cred.h"
#include "fs.h"
#include "net.h"
-#include "ptrace.h"
#include "setup.h"
+#include "task.h"
bool landlock_initialized __ro_after_init = false;
@@ -34,7 +34,7 @@ const struct lsm_id landlock_lsmid = {
static int __init landlock_init(void)
{
landlock_add_cred_hooks();
- landlock_add_ptrace_hooks();
+ landlock_add_task_hooks();
landlock_add_fs_hooks();
landlock_add_net_hooks();
landlock_initialized = true;
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 898358f57fa0..6788e73b6681 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -33,6 +33,18 @@
#include "ruleset.h"
#include "setup.h"
+static bool is_initialized(void)
+{
+ if (likely(landlock_initialized))
+ return true;
+
+ pr_warn_once(
+ "Disabled but requested by user space. "
+ "You should enable Landlock at boot time: "
+ "https://docs.kernel.org/userspace-api/landlock.html#boot-time-configuration\n");
+ return false;
+}
+
/**
* copy_min_struct_from_user - Safe future-proof argument copying
*
@@ -173,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
/* Build-time checks. */
build_check_abi();
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
if (flags) {
@@ -398,7 +410,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
struct landlock_ruleset *ruleset;
int err;
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
/* No flag for now. */
@@ -458,7 +470,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
struct landlock_cred_security *new_llcred;
int err;
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
/*
diff --git a/security/landlock/ptrace.c b/security/landlock/task.c
index 2bfc533d36e4..849f5123610b 100644
--- a/security/landlock/ptrace.c
+++ b/security/landlock/task.c
@@ -16,9 +16,9 @@
#include "common.h"
#include "cred.h"
-#include "ptrace.h"
#include "ruleset.h"
#include "setup.h"
+#include "task.h"
/**
* domain_scope_le - Checks domain ordering for scoped ptrace
@@ -113,7 +113,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
};
-__init void landlock_add_ptrace_hooks(void)
+__init void landlock_add_task_hooks(void)
{
security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
&landlock_lsmid);
diff --git a/security/landlock/ptrace.h b/security/landlock/task.h
index 265b220ae3bf..7c00360219a2 100644
--- a/security/landlock/ptrace.h
+++ b/security/landlock/task.h
@@ -6,9 +6,9 @@
* Copyright © 2019 ANSSI
*/
-#ifndef _SECURITY_LANDLOCK_PTRACE_H
-#define _SECURITY_LANDLOCK_PTRACE_H
+#ifndef _SECURITY_LANDLOCK_TASK_H
+#define _SECURITY_LANDLOCK_TASK_H
-__init void landlock_add_ptrace_hooks(void);
+__init void landlock_add_task_hooks(void);
-#endif /* _SECURITY_LANDLOCK_PTRACE_H */
+#endif /* _SECURITY_LANDLOCK_TASK_H */
diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config
index a6cf69a665e8..aa5ec149f96c 100644
--- a/tools/testing/kunit/configs/all_tests.config
+++ b/tools/testing/kunit/configs/all_tests.config
@@ -43,6 +43,7 @@ CONFIG_REGMAP_BUILD=y
CONFIG_SECURITY=y
CONFIG_SECURITY_APPARMOR=y
+CONFIG_SECURITY_LANDLOCK=y
CONFIG_SOUND=y
CONFIG_SND=y
diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 401e2eb092a3..7e2b431b9f90 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -74,31 +74,19 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
EXPECT_EQ(0, cap_set_secbits(noroot));
cap_p = cap_get_proc();
- EXPECT_NE(NULL, cap_p)
- {
- TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
- }
- EXPECT_NE(-1, cap_clear(cap_p))
- {
- TH_LOG("Failed to cap_clear: %s", strerror(errno));
- }
+ EXPECT_NE(NULL, cap_p);
+ EXPECT_NE(-1, cap_clear(cap_p));
if (!drop_all) {
EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED,
- ARRAY_SIZE(caps), caps, CAP_SET))
- {
- TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
- }
+ ARRAY_SIZE(caps), caps, CAP_SET));
}
/* Automatically resets ambient capabilities. */
EXPECT_NE(-1, cap_set_proc(cap_p))
{
- TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
- }
- EXPECT_NE(-1, cap_free(cap_p))
- {
- TH_LOG("Failed to cap_free: %s", strerror(errno));
+ TH_LOG("Failed to set capabilities: %s", strerror(errno));
}
+ EXPECT_NE(-1, cap_free(cap_p));
/* Quickly checks that ambient capabilities are cleared. */
EXPECT_NE(-1, cap_get_ambient(caps[0]));
@@ -122,22 +110,13 @@ static void _change_cap(struct __test_metadata *const _metadata,
cap_t cap_p;
cap_p = cap_get_proc();
- EXPECT_NE(NULL, cap_p)
- {
- TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
- }
- EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value))
- {
- TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
- }
+ EXPECT_NE(NULL, cap_p);
+ EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value));
EXPECT_NE(-1, cap_set_proc(cap_p))
{
- TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
- }
- EXPECT_NE(-1, cap_free(cap_p))
- {
- TH_LOG("Failed to cap_free: %s", strerror(errno));
+ TH_LOG("Failed to set capability %d: %s", cap, strerror(errno));
}
+ EXPECT_NE(-1, cap_free(cap_p));
}
static void __maybe_unused set_cap(struct __test_metadata *const _metadata,