diff options
-rw-r--r-- | include/uapi/linux/landlock.h | 21 | ||||
-rw-r--r-- | security/landlock/audit.c | 16 | ||||
-rw-r--r-- | security/landlock/domain.c | 2 | ||||
-rw-r--r-- | security/landlock/domain.h | 11 | ||||
-rw-r--r-- | security/landlock/limits.h | 7 | ||||
-rw-r--r-- | security/landlock/syscalls.c | 39 | ||||
-rw-r--r-- | tools/testing/selftests/landlock/base_test.c | 2 |
7 files changed, 85 insertions, 13 deletions
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 8806a132d7b8..56b0094ef792 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -4,6 +4,7 @@ * * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2018-2020 ANSSI + * Copyright © 2021-2025 Microsoft Corporation */ #ifndef _UAPI_LINUX_LANDLOCK_H @@ -64,6 +65,26 @@ struct landlock_ruleset_attr { #define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1) /* clang-format on */ +/* + * sys_landlock_restrict_self() flags: + * + * - %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF: Do not create any log related to the + * enforced restrictions. This should only be set by tools launching unknown + * or untrusted programs (e.g. a sandbox tool, container runtime, system + * service manager). Because programs sandboxing themselves should fix any + * denied access, they should not set this flag to be aware of potential + * issues reported by system's logs (i.e. audit). + * - %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON: Explicitly ask to continue + * logging denied access requests even after an :manpage:`execve(2)` call. + * This flag should only be set if all the programs than can legitimately be + * executed will not try to request a denied access (which could spam audit + * logs). + */ +/* clang-format off */ +#define LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF (1U << 0) +#define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1) +/* clang-format on */ + /** * enum landlock_rule_type - Landlock rule type * diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 77d11355f6ed..7e5e0ed0e4e5 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -422,6 +422,9 @@ void landlock_log_denial(const struct landlock_cred_security *const subject, get_hierarchy(subject->domain, youngest_layer); } + if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED) + return; + /* * Consistently keeps track of the number of denied access requests * even if audit is currently disabled, or if audit rules currently @@ -433,9 +436,16 @@ void landlock_log_denial(const struct landlock_cred_security *const subject, if (!audit_enabled) return; - /* Ignores denials after an execution. */ - if (!(subject->domain_exec & (1 << youngest_layer))) - return; + /* Checks if the current exec was restricting itself. */ + if (subject->domain_exec & (1 << youngest_layer)) { + /* Ignores denials for the same execution. */ + if (!youngest_denied->log_same_exec) + return; + } else { + /* Ignores denials after a new execution. */ + if (!youngest_denied->log_new_exec) + return; + } /* Uses consistent allocation flags wrt common_lsm_audit(). */ ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, diff --git a/security/landlock/domain.c b/security/landlock/domain.c index f61fa5ae8e56..bae2e9909013 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -127,6 +127,8 @@ int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy) hierarchy->details = details; hierarchy->id = landlock_get_id_range(1); hierarchy->log_status = LANDLOCK_LOG_PENDING; + hierarchy->log_same_exec = true; + hierarchy->log_new_exec = false; atomic64_set(&hierarchy->num_denials, 0); return 0; } diff --git a/security/landlock/domain.h b/security/landlock/domain.h index 96642dd87b33..ed0d348e214c 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -24,6 +24,7 @@ enum landlock_log_status { LANDLOCK_LOG_PENDING = 0, LANDLOCK_LOG_RECORDED, + LANDLOCK_LOG_DISABLED, }; /** @@ -103,6 +104,16 @@ struct landlock_hierarchy { * @details: Information about the related domain. */ const struct landlock_details *details; + /** + * @log_same_exec: Set if the domain is *not* configured with + * %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF. Set to true by default. + */ + u32 log_same_exec : 1, + /** + * @log_new_exec: Set if the domain is configured with + * %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default. + */ + log_new_exec : 1; #endif /* CONFIG_AUDIT */ }; diff --git a/security/landlock/limits.h b/security/landlock/limits.h index 15f7606066c8..404e880cccf9 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Landlock LSM - Limits for different components + * Landlock - Limits for different components * * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2018-2020 ANSSI + * Copyright © 2021-2025 Microsoft Corporation */ #ifndef _SECURITY_LANDLOCK_LIMITS_H @@ -29,6 +30,10 @@ #define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) + +#define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON +#define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1) + /* clang-format on */ #endif /* _SECURITY_LANDLOCK_LIMITS_H */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index b7b268f43a3b..75bc9fcd0a8f 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Landlock LSM - System call implementations and user space interfaces + * Landlock - System call implementations and user space interfaces * * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> * Copyright © 2018-2020 ANSSI + * Copyright © 2021-2025 Microsoft Corporation */ #include <asm/current.h> @@ -28,6 +29,7 @@ #include <uapi/linux/landlock.h> #include "cred.h" +#include "domain.h" #include "fs.h" #include "limits.h" #include "net.h" @@ -151,7 +153,14 @@ static const struct file_operations ruleset_fops = { .write = fop_dummy_write, }; -#define LANDLOCK_ABI_VERSION 6 +/* + * The Landlock ABI version should be incremented for each new Landlock-related + * user space visible change (e.g. Landlock syscalls). This version should + * only be incremented once per Linux release, and the date in + * Documentation/userspace-api/landlock.rst should be updated to reflect the + * UAPI change. + */ +const int landlock_abi_version = 7; /** * sys_landlock_create_ruleset - Create a new ruleset @@ -247,8 +256,6 @@ SYSCALL_DEFINE3(landlock_create_ruleset, return ruleset_fd; } -const int landlock_abi_version = LANDLOCK_ABI_VERSION; - /* * Returns an owned ruleset from a FD. It is thus needed to call * landlock_put_ruleset() on the return value. @@ -443,7 +450,10 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, * sys_landlock_restrict_self - Enforce a ruleset on the calling thread * * @ruleset_fd: File descriptor tied to the ruleset to merge with the target. - * @flags: Must be 0. + * @flags: Supported values: + * + * - %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF + * - %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON * * This system call enables to enforce a Landlock ruleset on the current * thread. Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its @@ -453,7 +463,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, * Possible returned errors are: * * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; - * - %EINVAL: @flags is not 0. + * - %EINVAL: @flags contains an unknown bit. * - %EBADF: @ruleset_fd is not a file descriptor for the current thread; * - %EBADFD: @ruleset_fd is not a ruleset file descriptor; * - %EPERM: @ruleset_fd has no read access to the underlying ruleset, or the @@ -469,6 +479,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, *ruleset __free(landlock_put_ruleset) = NULL; struct cred *new_cred; struct landlock_cred_security *new_llcred; + bool __maybe_unused log_same_exec, log_new_exec; if (!is_initialized()) return -EOPNOTSUPP; @@ -481,10 +492,15 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; - /* No flag for now. */ - if (flags) + if ((flags | LANDLOCK_MASK_RESTRICT_SELF) != + LANDLOCK_MASK_RESTRICT_SELF) return -EINVAL; + /* Translates "off" flag to boolean. */ + log_same_exec = !(flags & LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF); + /* Translates "on" flag to boolean. */ + log_new_exec = !!(flags & LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON); + /* Gets and checks the ruleset. */ ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); if (IS_ERR(ruleset)) @@ -507,6 +523,13 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, return PTR_ERR(new_dom); } +#ifdef CONFIG_AUDIT + new_dom->hierarchy->log_same_exec = log_same_exec; + new_dom->hierarchy->log_new_exec = log_new_exec; + if (!log_same_exec && !log_new_exec) + new_dom->hierarchy->log_status = LANDLOCK_LOG_DISABLED; +#endif /* CONFIG_AUDIT */ + /* Replaces the old (prepared) domain. */ landlock_put_ruleset(new_llcred->domain); new_llcred->domain = new_dom; diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 4766f8fec9f6..932cf0635a5b 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -76,7 +76,7 @@ TEST(abi_version) const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, }; - ASSERT_EQ(6, landlock_create_ruleset(NULL, 0, + ASSERT_EQ(7, landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, |