Log denied access for processes resulting from an execve(2), which is
not the case by default.

The rationale is that a program should know its own behavior, but not
necessarily the behavior of other programs.

Signed-off-by: Mickaël Salaün <m...@digikod.net>
Link: https://lore.kernel.org/r/20250131163059.1139617-19-...@digikod.net
---

Changes since v4:
- New patch to replace the now-removed Landlock-specific audit rule
  types.
---
 include/uapi/linux/landlock.h |  6 ++++++
 security/landlock/audit.c     |  3 ++-
 security/landlock/domain.c    |  1 +
 security/landlock/domain.h    |  8 +++++++-
 security/landlock/limits.h    |  2 +-
 security/landlock/syscalls.c  | 10 +++++++++-
 6 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index d810fd9e17c6..65a9340b9c0e 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -81,10 +81,16 @@ struct landlock_ruleset_attr {
  *   init systems.  Unlike %LANDLOCK_RESTRICT_SELF_QUIET,
  *   %LANDLOCK_RESTRICT_SELF_QUIET_SUBDOMAINS does not impact the requested
  *   restriction but only the potential descendant domains.
+ * - %LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC: 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).
+ *   This flag is incompatible with %LANDLOCK_RESTRICT_SELF_QUIET.
  */
 /* clang-format off */
 #define LANDLOCK_RESTRICT_SELF_QUIET                   (1U << 0)
 #define LANDLOCK_RESTRICT_SELF_QUIET_SUBDOMAINS                (1U << 1)
+#define LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC          (1U << 2)
 /* clang-format on */
 
 /**
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index beebe45a47e6..6e1de850a5b4 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -434,7 +434,8 @@ void landlock_log_denial(const struct 
landlock_cred_security *const subject,
        atomic64_inc(&youngest_denied->num_denials);
 
        /* Ignores denials after an execution. */
-       if (!(subject->domain_exec & (1 << youngest_layer)))
+       if (!(subject->domain_exec & (1 << youngest_layer)) &&
+           !youngest_denied->log_cross_exec)
                return;
 
        if (!unlikely(audit_context() && audit_enabled))
diff --git a/security/landlock/domain.c b/security/landlock/domain.c
index eff7c774bf06..49ccb0f72e53 100644
--- a/security/landlock/domain.c
+++ b/security/landlock/domain.c
@@ -128,6 +128,7 @@ int landlock_init_hierarchy_log(struct landlock_hierarchy 
*const hierarchy)
        hierarchy->id = landlock_get_id_range(1);
        hierarchy->log_status = LANDLOCK_LOG_PENDING;
        hierarchy->quiet_subdomains = false;
+       hierarchy->log_cross_exec = false;
        atomic64_set(&hierarchy->num_denials, 0);
        return 0;
 }
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index 8979cf00f8be..06b213aa7579 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -108,7 +108,13 @@ struct landlock_hierarchy {
         * @quiet_subdomains: Set if the domain descendants's log_status
         * should be set to %LANDLOCK_LOG_DISABLED.
         */
-       u32 quiet_subdomains : 1;
+       u32 quiet_subdomains : 1,
+               /**
+                * @log_cross_exec: Set if the domain is configured with
+                * %LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC to log denials across
+                * child executions.
+                */
+               log_cross_exec : 1;
 #endif /* CONFIG_AUDIT */
 };
 
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 48aa75c98665..d9b70d9259c0 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -30,7 +30,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_QUIET_SUBDOMAINS
+#define LANDLOCK_LAST_RESTRICT_SELF    LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC
 #define LANDLOCK_MASK_RESTRICT_SELF    ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1)
 
 /* clang-format on */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index f44f4f884499..5709a53c4a09 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -441,6 +441,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
  *
  * - %LANDLOCK_RESTRICT_SELF_QUIET
  * - %LANDLOCK_RESTRICT_SELF_QUIET_SUBDOMAINS
+ * - %LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC
  *
  * 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
@@ -451,6 +452,8 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
  *
  * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot 
time;
  * - %EINVAL: @flags contains an unknown bit.
+ * - %EINVAL: @flags contains %LANDLOCK_RESTRICT_SELF_QUIET and
+ *   %LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC, which are incompatible.
  * - %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
@@ -467,7 +470,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, 
ruleset_fd, const __u32,
        struct cred *new_cred;
        struct landlock_cred_security *new_llcred;
        bool is_quiet, is_quiet_subdomains,
-               __maybe_unused inherits_quiet_subdomains;
+               __maybe_unused inherits_quiet_subdomains, is_log_cross_exec;
 
        if (!is_initialized())
                return -EOPNOTSUPP;
@@ -487,6 +490,9 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, 
ruleset_fd, const __u32,
        is_quiet = !!(flags & LANDLOCK_RESTRICT_SELF_QUIET);
        is_quiet_subdomains =
                !!(flags & LANDLOCK_RESTRICT_SELF_QUIET_SUBDOMAINS);
+       is_log_cross_exec = !!(flags & LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC);
+       if (is_quiet && is_log_cross_exec)
+               return -EINVAL;
 
        /* Gets and checks the ruleset. */
        ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
@@ -518,6 +524,8 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, 
ruleset_fd, const __u32,
                                               inherits_quiet_subdomains;
        if (is_quiet || inherits_quiet_subdomains)
                new_dom->hierarchy->log_status = LANDLOCK_LOG_DISABLED;
+
+       new_dom->hierarchy->log_cross_exec = is_log_cross_exec;
 #endif /* CONFIG_AUDIT */
 
        /* Replaces the old (prepared) domain. */
-- 
2.48.1


Reply via email to