landlock: Add LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF

Add LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF for the case of sandboxer
tools, init systems, or runtime containers launching programs sandboxing
themselves in an inconsistent way.  Setting this flag should only
depends on runtime configuration (i.e. not hardcoded).

We don't create a new ruleset's option because this should not be part
of the security policy: only the task that enforces the policy (not the
one that create it) knows if itself or its children may request denied
actions.

This is the first and only flag that can be set without actually
restricting the caller (i.e. without providing a ruleset).

Extend struct landlock_cred_security with a u8 log_subdomains_off.
struct landlock_file_security is still 16 bytes.

Cc: Günther Noack <gnoack@google.com>
Cc: Paul Moore <paul@paul-moore.com>
Closes: https://github.com/landlock-lsm/linux/issues/3
Link: https://lore.kernel.org/r/20250320190717.2287696-19-mic@digikod.net
[mic: Fix comment]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Mickaël Salaün 2025-03-20 20:07:07 +01:00
parent 12bfcda73a
commit ead9079f75
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2
4 changed files with 55 additions and 7 deletions

View File

@ -79,10 +79,22 @@ struct landlock_ruleset_attr {
* 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).
* - %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF: Do not create any log related
* to the enforced restrictions coming from future nested domains created by
* the caller or its descendants. This should only be set according to a
* runtime configuration (i.e. not hardcoded) by programs launching other
* unknown or untrusted programs that may create their own Landlock domains
* and spam logs. The main use case is for container runtimes to enable users
* to mute buggy sandboxed programs for a specific container image. Other use
* cases include sandboxer tools and init systems. Unlike
* %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
* %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF does not impact the requested
* restriction (if any) but only the future nested domains.
*/
/* clang-format off */
#define LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF (1U << 0)
#define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1)
#define LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF (1U << 2)
/* clang-format on */
/**

View File

@ -40,6 +40,13 @@ struct landlock_cred_security {
* landlock_restrict_self(2)).
*/
u16 domain_exec;
/**
* @log_subdomains_off: Set if the domain descendants's log_status should be
* set to %LANDLOCK_LOG_DISABLED. This is not a landlock_hierarchy
* configuration because it applies to future descendant domains and it does
* not require a current domain.
*/
u8 log_subdomains_off : 1;
#endif /* CONFIG_AUDIT */
} __packed;

View File

@ -31,7 +31,7 @@
#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_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
#define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1)
/* clang-format on */

View File

@ -454,12 +454,16 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
*
* - %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF
* - %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON
* - %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
*
* 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
* namespace or is running with no_new_privs. This avoids scenarios where
* unprivileged tasks can affect the behavior of privileged children.
*
* It is allowed to only pass the %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
* flag with a @ruleset_fd value of -1.
*
* Possible returned errors are:
*
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
@ -479,7 +483,8 @@ 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;
bool __maybe_unused log_same_exec, log_new_exec, log_subdomains,
prev_log_subdomains;
if (!is_initialized())
return -EOPNOTSUPP;
@ -500,11 +505,20 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
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);
/* Translates "off" flag to boolean. */
log_subdomains = !(flags & LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
/* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
/*
* It is allowed to set LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with
* -1 as ruleset_fd, but no other flag must be set.
*/
if (!(ruleset_fd == -1 &&
flags == LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) {
/* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
}
/* Prepares new credentials. */
new_cred = prepare_creds();
@ -513,6 +527,21 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
new_llcred = landlock_cred(new_cred);
#ifdef CONFIG_AUDIT
prev_log_subdomains = !new_llcred->log_subdomains_off;
new_llcred->log_subdomains_off = !prev_log_subdomains ||
!log_subdomains;
#endif /* CONFIG_AUDIT */
/*
* The only case when a ruleset may not be set is if
* LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set and ruleset_fd is -1.
* We could optimize this case by not calling commit_creds() if this flag
* was already set, but it is not worth the complexity.
*/
if (!ruleset)
return commit_creds(new_cred);
/*
* There is no possible race condition while copying and manipulating
* the current credentials because they are dedicated per thread.
@ -526,7 +555,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
#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)
if ((!log_same_exec && !log_new_exec) || !prev_log_subdomains)
new_dom->hierarchy->log_status = LANDLOCK_LOG_DISABLED;
#endif /* CONFIG_AUDIT */