@@ -8,7 +8,7 @@ Landlock: unprivileged access control
=====================================
:Author: Mickaël Salaün
-:Date: October 2024
+:Date: November 2024
The goal of Landlock is to enable restriction of ambient rights (e.g. global
filesystem or network access) for a set of processes. Because Landlock
@@ -62,6 +62,20 @@ struct landlock_ruleset_attr {
#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
/* clang-format on */
+/*
+ * sys_landlock_restrict_self() flags:
+ *
+ * - %LANDLOCK_RESTRICT_SELF_LOGLESS: 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).
+ */
+/* clang-format off */
+#define LANDLOCK_RESTRICT_SELF_LOGLESS (1U << 0)
+/* clang-format on */
+
/**
* enum landlock_rule_type - Landlock rule type
*
@@ -404,11 +404,6 @@ void landlock_log_denial(const struct landlock_ruleset *const domain,
if (!audit_enabled)
return;
- ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
- AUDIT_LANDLOCK_DENY);
- if (!ab)
- return;
-
missing = request->access;
if (missing) {
size_t youngest_layer;
@@ -429,6 +424,14 @@ void landlock_log_denial(const struct landlock_ruleset *const domain,
get_hierarchy(domain, request->layer_plus_one - 1);
}
+ if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED)
+ return;
+
+ ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
+ AUDIT_LANDLOCK_DENY);
+ if (!ab)
+ return;
+
audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id);
log_blockers(ab, request->type, missing);
audit_log_lsm_data(ab, &request->audit);
@@ -23,6 +23,7 @@
enum landlock_log_status {
LANDLOCK_LOG_PENDING = 0,
LANDLOCK_LOG_RECORDED,
+ LANDLOCK_LOG_DISABLED,
};
/**
@@ -151,7 +151,12 @@ 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). Only increment this
+ * version once per Linux release.
+ */
+#define LANDLOCK_ABI_VERSION 7
/**
* sys_landlock_create_ruleset - Create a new ruleset
@@ -452,7 +457,7 @@ 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 value: %LANDLOCK_RESTRICT_SELF_LOGLESS.
*
* 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
@@ -478,6 +483,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
struct cred *new_cred;
struct landlock_cred_security *new_llcred;
int err;
+ bool is_logless = false;
if (!is_initialized())
return -EOPNOTSUPP;
@@ -490,9 +496,12 @@ 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)
- return -EINVAL;
+ if (flags) {
+ if (flags == LANDLOCK_RESTRICT_SELF_LOGLESS)
+ is_logless = true;
+ else
+ return -EINVAL;
+ }
/* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
@@ -517,6 +526,12 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
goto out_put_creds;
}
+ if (is_logless) {
+#ifdef CONFIG_AUDIT
+ 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;
@@ -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,
Most of the time we want to log denied access because they should not happen and such information helps diagnose issues. However, when sandboxing processes that we know will try to access denied resources (e.g. unknown, bogus, or malicious binary), we might want to not log related access requests that might fill up logs. To disable any log for a specific Landlock domain, add a LANDLOCK_RESTRICT_SELF_LOGLESS optional flag to the landlock_restrict_self() system call. Because this flag is set for a specific Landlock domain, it makes it possible to selectively mask some access requests that would be logged by a parent domain, which might be handy for unprivileged processes to limit logs. However, system administrators should still use the audit filtering mechanism. There is intentionally no audit nor sysctl configuration to re-enable these logless domains. This is delegated to the user space program. Increment the Landlock ABI version to reflect this interface change. Cc: Günther Noack <gnoack@google.com> Cc: Paul Moore <paul@paul-moore.com> Closes: https://github.com/landlock-lsm/linux/issues/3 Signed-off-by: Mickaël Salaün <mic@digikod.net> Link: https://lore.kernel.org/r/20241122143353.59367-20-mic@digikod.net --- Changes since v2: - Update ABI version test. --- Documentation/userspace-api/landlock.rst | 2 +- include/uapi/linux/landlock.h | 14 +++++++++++ security/landlock/audit.c | 13 ++++++---- security/landlock/domain.h | 1 + security/landlock/syscalls.c | 25 ++++++++++++++++---- tools/testing/selftests/landlock/base_test.c | 2 +- 6 files changed, 45 insertions(+), 12 deletions(-)