Landlock updates for v6.9-rc1
-----BEGIN PGP SIGNATURE----- iIYEABYKAC4WIQSVyBthFV4iTW/VU1/l49DojIL20gUCZfHmqxAcbWljQGRpZ2lr b2QubmV0AAoJEOXj0OiMgvbSvbABAIUF7nujsgnE9AykjhTKzg+by86mvXK0fdLG WVW0cwfgAP49daJb8JyZP9d6PvcgDfH4vV8E7r5PFeaICPdoOwg2Bg== =xJV1 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
35e886e88c
@ -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
|
||||
|
@ -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));
|
||||
|
4
security/landlock/.kunitconfig
Normal file
4
security/landlock/.kunitconfig
Normal file
@ -0,0 +1,4 @@
|
||||
CONFIG_KUNIT=y
|
||||
CONFIG_SECURITY=y
|
||||
CONFIG_SECURITY_LANDLOCK=y
|
||||
CONFIG_SECURITY_LANDLOCK_KUNIT_TEST=y
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -17,4 +17,6 @@
|
||||
|
||||
#define pr_fmt(fmt) LANDLOCK_NAME ": " fmt
|
||||
|
||||
#define BIT_INDEX(bit) HWEIGHT(bit - 1)
|
||||
|
||||
#endif /* _SECURITY_LANDLOCK_COMMON_H */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
@ -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 */
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user