[PATCH v5 18/24] landlock: Add LANDLOCK_RESTRICT_SELF_LOG_CROSS_EXEC

Mickaël Salaün mic at digikod.net
Fri Jan 31 16:30:53 UTC 2025


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 <mic at digikod.net>
Link: https://lore.kernel.org/r/20250131163059.1139617-19-mic@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




More information about the Linux-security-module-archive mailing list