mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 11:03:07 -04:00
landlock: Consolidate access-right and scope names in a shared header
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 <gnoack@google.com>
Cc: Tingmao Wang <m@maowtm.org>
Link: https://patch.msgid.link/20260811094338.288094-7-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
@@ -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/
|
||||
|
||||
55
include/linux/landlock.h
Normal file
55
include/linux/landlock.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Landlock - Public types and definitions
|
||||
*
|
||||
* Copyright © 2016-2026 Mickaël Salaün <mic@digikod.net>
|
||||
* 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 */
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user