]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
landlock: Add LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
authorMickaël Salaün <mic@digikod.net>
Thu, 20 Mar 2025 19:07:07 +0000 (20:07 +0100)
committerMickaël Salaün <mic@digikod.net>
Wed, 26 Mar 2025 12:59:43 +0000 (13:59 +0100)
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>
include/uapi/linux/landlock.h
security/landlock/cred.h
security/landlock/limits.h
security/landlock/syscalls.c

index 56b0094ef79243981e16b3c87f85039a6875bc7c..d9d0cb827117db39e02ab5eab2f526621dc9048e 100644 (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 */
 
 /**
index 3bf18551d7b8619ccab1f2f02b1aeef6e6c5f673..c82fe63ec598a45addd0f23619482f725843286d 100644 (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;
 
index 404e880cccf951f989006c638fa0fa078af0979d..65b5ff0516747fe4bff544920dd7238c137b5d0c 100644 (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 */
index 75bc9fcd0a8fdd09a8adbbb68871cb6e09b6e7ef..54a9f29e6ebb11616d45efd88fa36233f89e9d58 100644 (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 */