[PATCH v4 06/19] landlock: Consolidate access-right and scope names in a shared header

Mickaël Salaün <[email protected]>
Newsgroups org.kernel.vger.linux-security-module,org.kernel.vger.linux-trace-kernel
Message-ID <[email protected]>
Audit formats denial records with per-right name strings.  A following
commit adds trace events that print the same access and scope masks with
__print_flags() and need the same names, but a trace event header cannot
include Landlock-internal headers, so the names cannot be shared from
the logging unit.

Define the filesystem, network, and scope names once, as the
_LANDLOCK_ACCESS_FS_NAMES, _LANDLOCK_ACCESS_NET_NAMES, and
_LANDLOCK_SCOPE_NAMES lists in the public Landlock header.  Each entry
is a _LANDLOCK_NAME_ENTRY() the consumer expands: audit maps it to a
"[bit] = name" array slot for an O(1) lookup, the trace events map it to
a __print_flags() { mask, name } pair.  The bit value comes only from
the LANDLOCK_* UAPI constant each entry references, so every bit-to-name
mapping has a single source and does not depend on entry order.

The shared names are unprefixed; blocker_prefix() prepends the
fs./net./scope. category for audit records, so the scope names move from
inline literals to the shared table too.  Audit records are unchanged.

No functional change.

Cc: Günther Noack <[email protected]>
Cc: Tingmao Wang <[email protected]>
Signed-off-by: Mickaël Salaün <[email protected]>
---

Changes since v2:
- New patch.
---
 MAINTAINERS               |  1 +
 include/linux/landlock.h  | 55 +++++++++++++++++++++++++
 security/landlock/audit.c | 84 ++++++++++++++++++++++++---------------
 3 files changed, 109 insertions(+), 31 deletions(-)
 create mode 100644 include/linux/landlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5114e6db7307..61677067199b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14599,6 +14599,7 @@ F:	Documentation/admin-guide/LSM/landlock.rst
 F:	Documentation/security/landlock.rst
 F:	Documentation/userspace-api/landlock.rst
 F:	fs/ioctl.c
+F:	include/linux/landlock.h
 F:	include/uapi/linux/landlock.h
 F:	samples/landlock/
 F:	security/landlock/
diff --git a/include/linux/landlock.h b/include/linux/landlock.h
new file mode 100644
index 000000000000..cabe6c784833
--- /dev/null
+++ b/include/linux/landlock.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock - Public types and definitions
+ *
+ * Copyright © 2016-2026 Mickaël Salaün <[email protected]>
+ * Copyright © 2026 Cloudflare, Inc.
+ */
+
+#ifndef _LINUX_LANDLOCK_H
+#define _LINUX_LANDLOCK_H
+
+#include <uapi/linux/landlock.h>
+
+/*
+ * Access-right and scope names, shared between the audit records (get_blocker()
+ * in security/landlock/audit.c) and the trace events
+ * (include/trace/events/landlock.h).  A consumer defines
+ * _LANDLOCK_NAME_ENTRY(mask, name) before expanding a list and undefines it
+ * afterwards: audit maps each entry to a "[bit] = name" slot for O(1) lookup,
+ * the trace events map it to a __print_flags() { mask, name } pair.  The bit
+ * value lives only in the LANDLOCK_* UAPI constant each entry references.
+ * Names are unprefixed; audit prepends the "fs."/"net."/"scope." category.
+ */
+#define _LANDLOCK_ACCESS_FS_NAMES \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_EXECUTE, "execute"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_WRITE_FILE, "write_file"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_READ_FILE, "read_file"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_READ_DIR, "read_dir"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_REMOVE_DIR, "remove_dir"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_REMOVE_FILE, "remove_file"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_CHAR, "make_char"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_DIR, "make_dir"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_REG, "make_reg"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_SOCK, "make_sock"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_FIFO, "make_fifo"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_BLOCK, "make_block"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_MAKE_SYM, "make_sym"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_REFER, "refer"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_TRUNCATE, "truncate"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_IOCTL_DEV, "ioctl_dev"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_RESOLVE_UNIX, "resolve_unix")
+
+#define _LANDLOCK_ACCESS_NET_NAMES \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_TCP, "bind_tcp"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_TCP, "connect_tcp"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_UDP, "bind_udp"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, \
+			     "connect_send_udp")
+
+#define _LANDLOCK_SCOPE_NAMES \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, \
+			     "abstract_unix_socket"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_SCOPE_SIGNAL, "signal")
+
+#endif /* _LINUX_LANDLOCK_H */
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index 32260e7cbfe9..e02963834e48 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -7,6 +7,7 @@
 
 #include <linux/audit.h>
 #include <linux/bitops.h>
+#include <linux/landlock.h>
 #include <linux/lsm_audit.h>
 #include <linux/pid.h>
 #include <uapi/linux/landlock.h>
@@ -19,38 +20,28 @@
 #include "limits.h"
 #include "log.h"
 
-static const char *const fs_access_strings[] = {
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = "fs.execute",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = "fs.write_file",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = "fs.read_file",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = "fs.read_dir",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = "fs.remove_dir",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_FILE)] = "fs.remove_file",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_CHAR)] = "fs.make_char",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_DIR)] = "fs.make_dir",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = "fs.make_reg",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SOCK)] = "fs.make_sock",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_FIFO)] = "fs.make_fifo",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_BLOCK)] = "fs.make_block",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SYM)] = "fs.make_sym",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
-	[BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
-};
+/*
+ * Access-right and scope names are built from the lists shared with the trace
+ * events (see <linux/landlock.h>).  The designated initializer places each name
+ * at its bit index, so the lookup stays O(1) and does not depend on the entry
+ * order.  log_blockers() adds the "fs."/"net."/"scope." category prefix.
+ */
+#define _LANDLOCK_NAME_ENTRY(mask, name) [BIT_INDEX(mask)] = name
+
+static const char *const fs_access_strings[] = { _LANDLOCK_ACCESS_FS_NAMES };
 
 static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
 
-static const char *const net_access_strings[] = {
-	[BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_TCP)] = "net.bind_tcp",
-	[BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_TCP)] = "net.connect_tcp",
-	[BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_UDP)] = "net.bind_udp",
-	[BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)] =
-		"net.connect_send_udp",
-};
+static const char *const net_access_strings[] = { _LANDLOCK_ACCESS_NET_NAMES };
 
 static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);
 
+static const char *const scope_strings[] = { _LANDLOCK_SCOPE_NAMES };
+
+static_assert(ARRAY_SIZE(scope_strings) == LANDLOCK_NUM_SCOPE);
+
+#undef _LANDLOCK_NAME_ENTRY
+
 static __attribute_const__ const char *
 get_blocker(const enum landlock_request_type type,
 	    const unsigned long access_bit)
@@ -62,7 +53,7 @@ get_blocker(const enum landlock_request_type type,
 
 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
 		WARN_ON_ONCE(access_bit != -1);
-		return "fs.change_topology";
+		return "change_topology";
 
 	case LANDLOCK_REQUEST_FS_ACCESS:
 		if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(fs_access_strings)))
@@ -76,32 +67,63 @@ get_blocker(const enum landlock_request_type type,
 
 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
 		WARN_ON_ONCE(access_bit != -1);
-		return "scope.abstract_unix_socket";
+		return scope_strings[BIT_INDEX(
+			LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET)];
 
 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
 		WARN_ON_ONCE(access_bit != -1);
-		return "scope.signal";
+		return scope_strings[BIT_INDEX(LANDLOCK_SCOPE_SIGNAL)];
 	}
 
 	WARN_ON_ONCE(1);
 	return "unknown";
 }
 
+/*
+ * Returns the audit category prefix prepended to the unprefixed blocker name
+ * returned by get_blocker() (filesystem and network access rights,
+ * change_topology, and scopes).  The ptrace blocker is standalone and carries
+ * its full name in get_blocker(), so it uses no prefix.
+ */
+static __attribute_const__ const char *
+blocker_prefix(const enum landlock_request_type type)
+{
+	switch (type) {
+	case LANDLOCK_REQUEST_PTRACE:
+		return "";
+
+	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
+	case LANDLOCK_REQUEST_FS_ACCESS:
+		return "fs.";
+
+	case LANDLOCK_REQUEST_NET_ACCESS:
+		return "net.";
+
+	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
+	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
+		return "scope.";
+	}
+
+	WARN_ON_ONCE(1);
+	return "";
+}
+
 static void log_blockers(struct audit_buffer *const ab,
 			 const enum landlock_request_type type,
 			 const access_mask_t access)
 {
 	const unsigned long access_mask = access;
+	const char *const prefix = blocker_prefix(type);
 	unsigned long access_bit;
 	bool is_first = true;
 
 	for_each_set_bit(access_bit, &access_mask, BITS_PER_TYPE(access)) {
-		audit_log_format(ab, "%s%s", is_first ? "" : ",",
+		audit_log_format(ab, "%s%s%s", is_first ? "" : ",", prefix,
 				 get_blocker(type, access_bit));
 		is_first = false;
 	}
 	if (is_first)
-		audit_log_format(ab, "%s", get_blocker(type, -1));
+		audit_log_format(ab, "%s%s", prefix, get_blocker(type, -1));
 }
 
 static void log_domain(struct landlock_hierarchy *const hierarchy)
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.