mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
The layer masks data structure tracks the requested but unfulfilled access rights during an operation's security check. It stores one bit for each combination of access right and layer index. If the bit is set, that access right is not granted (yet) in the given layer and we have to traverse the path further upwards to grant it. Previously, the layer masks were stored as arrays mapping from access right indices to layer_mask_t. The layer_mask_t value then indicates all layers in which the given access right is still (tentatively) denied. This patch introduces struct layer_access_masks instead: This struct contains an array with the access_mask_t of each (tentatively) denied access right in that layer. The hypothesis of this patch is that this simplifies the code enough so that the resulting code will run faster: * We can use bitwise operations in multiple places where we previously looped over bits individually with macros. (Should require less branch speculation and lends itself to better loop unrolling.) * Code is ~75 lines smaller. Other noteworthy changes: * In no_more_access(), call a new helper function may_refer(), which only solves the asymmetric case. Previously, the code interleaved the checks for the two symmetric cases in RENAME_EXCHANGE. It feels that the code is clearer when renames without RENAME_EXCHANGE are more obviously the normal case. Tradeoffs: This change improves performance, at a slight size increase to the layer masks data structure. This fixes the size of the data structure at 32 bytes for all types of access rights. (64, once we introduce a 17th filesystem access right). For filesystem access rights, at the moment, the data structure has the same size as before, but once we introduce the 17th filesystem access right, it will double in size (from 32 to 64 bytes), as access_mask_t grows from 16 to 32 bit [1]. Link: https://lore.kernel.org/all/20260120.haeCh4li9Vae@digikod.net/ [1] Signed-off-by: Günther Noack <gnoack3000@gmail.com> Link: https://lore.kernel.org/r/20260206151154.97915-5-gnoack3000@gmail.com [mic: Cosmetic fixes, moved struct layer_access_masks definition] Signed-off-by: Mickaël Salaün <mic@digikod.net>
173 lines
4.6 KiB
C
173 lines
4.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Landlock - Domain management
|
|
*
|
|
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
|
|
* Copyright © 2018-2020 ANSSI
|
|
* Copyright © 2024-2025 Microsoft Corporation
|
|
*/
|
|
|
|
#ifndef _SECURITY_LANDLOCK_DOMAIN_H
|
|
#define _SECURITY_LANDLOCK_DOMAIN_H
|
|
|
|
#include <linux/limits.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/path.h>
|
|
#include <linux/pid.h>
|
|
#include <linux/refcount.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "access.h"
|
|
#include "audit.h"
|
|
|
|
enum landlock_log_status {
|
|
LANDLOCK_LOG_PENDING = 0,
|
|
LANDLOCK_LOG_RECORDED,
|
|
LANDLOCK_LOG_DISABLED,
|
|
};
|
|
|
|
/**
|
|
* struct landlock_details - Domain's creation information
|
|
*
|
|
* Rarely accessed, mainly when logging the first domain's denial.
|
|
*
|
|
* The contained pointers are initialized at the domain creation time and never
|
|
* changed again. Contrary to most other Landlock object types, this one is
|
|
* not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the
|
|
* caller's control (e.g. unknown exe_path) and the data is not explicitly
|
|
* requested nor used by tasks.
|
|
*/
|
|
struct landlock_details {
|
|
/**
|
|
* @pid: PID of the task that initially restricted itself. It still
|
|
* identifies the same task. Keeping a reference to this PID ensures that
|
|
* it will not be recycled.
|
|
*/
|
|
struct pid *pid;
|
|
/**
|
|
* @uid: UID of the task that initially restricted itself, at creation time.
|
|
*/
|
|
uid_t uid;
|
|
/**
|
|
* @comm: Command line of the task that initially restricted itself, at
|
|
* creation time. Always NULL terminated.
|
|
*/
|
|
char comm[TASK_COMM_LEN];
|
|
/**
|
|
* @exe_path: Executable path of the task that initially restricted
|
|
* itself, at creation time. Always NULL terminated, and never greater
|
|
* than LANDLOCK_PATH_MAX_SIZE.
|
|
*/
|
|
char exe_path[];
|
|
};
|
|
|
|
/* Adds 11 extra characters for the potential " (deleted)" suffix. */
|
|
#define LANDLOCK_PATH_MAX_SIZE (PATH_MAX + 11)
|
|
|
|
/* Makes sure the greatest landlock_details can be allocated. */
|
|
static_assert(struct_size_t(struct landlock_details, exe_path,
|
|
LANDLOCK_PATH_MAX_SIZE) <= KMALLOC_MAX_SIZE);
|
|
|
|
/**
|
|
* struct landlock_hierarchy - Node in a domain hierarchy
|
|
*/
|
|
struct landlock_hierarchy {
|
|
/**
|
|
* @parent: Pointer to the parent node, or NULL if it is a root
|
|
* Landlock domain.
|
|
*/
|
|
struct landlock_hierarchy *parent;
|
|
/**
|
|
* @usage: Number of potential children domains plus their parent
|
|
* domain.
|
|
*/
|
|
refcount_t usage;
|
|
|
|
#ifdef CONFIG_AUDIT
|
|
/**
|
|
* @log_status: Whether this domain should be logged or not. Because
|
|
* concurrent log entries may be created at the same time, it is still
|
|
* possible to have several domain records of the same domain.
|
|
*/
|
|
enum landlock_log_status log_status;
|
|
/**
|
|
* @num_denials: Number of access requests denied by this domain.
|
|
* Masked (i.e. never logged) denials are still counted.
|
|
*/
|
|
atomic64_t num_denials;
|
|
/**
|
|
* @id: Landlock domain ID, set once at domain creation time.
|
|
*/
|
|
u64 id;
|
|
/**
|
|
* @details: Information about the related domain.
|
|
*/
|
|
const struct landlock_details *details;
|
|
/**
|
|
* @log_same_exec: Set if the domain is *not* configured with
|
|
* %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF. Set to true by default.
|
|
*/
|
|
u32 log_same_exec : 1,
|
|
/**
|
|
* @log_new_exec: Set if the domain is configured with
|
|
* %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default.
|
|
*/
|
|
log_new_exec : 1;
|
|
#endif /* CONFIG_AUDIT */
|
|
};
|
|
|
|
#ifdef CONFIG_AUDIT
|
|
|
|
deny_masks_t
|
|
landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
|
|
const access_mask_t optional_access,
|
|
const struct layer_access_masks *const masks);
|
|
|
|
int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy);
|
|
|
|
static inline void
|
|
landlock_free_hierarchy_details(struct landlock_hierarchy *const hierarchy)
|
|
{
|
|
if (!hierarchy || !hierarchy->details)
|
|
return;
|
|
|
|
put_pid(hierarchy->details->pid);
|
|
kfree(hierarchy->details);
|
|
}
|
|
|
|
#else /* CONFIG_AUDIT */
|
|
|
|
static inline int
|
|
landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
landlock_free_hierarchy_details(struct landlock_hierarchy *const hierarchy)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_AUDIT */
|
|
|
|
static inline void
|
|
landlock_get_hierarchy(struct landlock_hierarchy *const hierarchy)
|
|
{
|
|
if (hierarchy)
|
|
refcount_inc(&hierarchy->usage);
|
|
}
|
|
|
|
static inline void landlock_put_hierarchy(struct landlock_hierarchy *hierarchy)
|
|
{
|
|
while (hierarchy && refcount_dec_and_test(&hierarchy->usage)) {
|
|
const struct landlock_hierarchy *const freeme = hierarchy;
|
|
|
|
landlock_log_drop_domain(hierarchy);
|
|
landlock_free_hierarchy_details(hierarchy);
|
|
hierarchy = hierarchy->parent;
|
|
kfree(freeme);
|
|
}
|
|
}
|
|
|
|
#endif /* _SECURITY_LANDLOCK_DOMAIN_H */
|