| 1 |
|
| 2 | |
| 3 | * Landlock - Domain management
|
| 4 | *
|
| 5 | * Copyright © 2016-2020 Mickaël Salaün <[email protected]>
|
| 6 | * Copyright © 2018-2020 ANSSI
|
| 7 | * Copyright © 2024-2025 Microsoft Corporation
|
| 8 | * Copyright © 2026 Cloudflare, Inc.
|
| 9 |
|
| 10 |
|
| 11 | #ifndef _SECURITY_LANDLOCK_DOMAIN_H
|
| 12 | #define _SECURITY_LANDLOCK_DOMAIN_H
|
| 13 |
|
| 14 | #include <linux/cleanup.h>
|
| 15 | #include <linux/limits.h>
|
| 16 | #include <linux/mm.h>
|
| 17 | #include <linux/path.h>
|
| 18 | #include <linux/pid.h>
|
| 19 | #include <linux/refcount.h>
|
| 20 | #include <linux/sched.h>
|
| 21 | #include <linux/slab.h>
|
| 22 | #include <linux/workqueue.h>
|
| 23 |
|
| 24 | #include "access.h"
|
| 25 | #include "log.h"
|
| 26 | #include "ruleset.h"
|
| 27 |
|
| 28 | enum landlock_log_status {
|
| 29 | |
| 30 | * Hierarchy whose creation event has not been emitted, so it is not yet
|
| 31 | * observable from user space. A hierarchy is born in this state (the
|
| 32 | * zero value, so a partially initialized hierarchy defaults to "not
|
| 33 | * observable") and leaves it when landlock_restrict_self() emits its
|
| 34 | * creation event, right after the merge and before the thread-sync
|
| 35 | * wait. No trace free_domain event (and no audit deallocation record)
|
| 36 | * fires while a hierarchy is in this state, so a hierarchy that never
|
| 37 | * became observable (e.g. its initialization failed) is freed silently.
|
| 38 | * A domain aborted by a thread-sync failure already emitted its
|
| 39 | * creation event, so it is no longer UNCOMMITTED and does fire
|
| 40 | * free_domain.
|
| 41 |
|
| 42 | LANDLOCK_LOG_UNCOMMITTED = 0,
|
| 43 | LANDLOCK_LOG_PENDING,
|
| 44 | LANDLOCK_LOG_RECORDED,
|
| 45 | LANDLOCK_LOG_DISABLED,
|
| 46 | };
|
| 47 |
|
| 48 | |
| 49 | * struct landlock_details - Domain's creation information
|
| 50 | *
|
| 51 | * Rarely accessed, mainly when logging the first domain's denial.
|
| 52 | *
|
| 53 | * The contained pointers are initialized at the domain creation time and never
|
| 54 | * changed again.
|
| 55 |
|
| 56 | struct landlock_details {
|
| 57 | |
| 58 | * @pid: PID of the task that initially restricted itself. It still
|
| 59 | * identifies the same task. Keeping a reference to this PID ensures that
|
| 60 | * it will not be recycled.
|
| 61 |
|
| 62 | struct pid *pid;
|
| 63 | |
| 64 | * @uid: UID of the task that initially restricted itself, at creation time.
|
| 65 |
|
| 66 | uid_t uid;
|
| 67 | |
| 68 | * @comm: Command line of the task that initially restricted itself, at
|
| 69 | * creation time. Always NULL terminated.
|
| 70 |
|
| 71 | char comm[TASK_COMM_LEN];
|
| 72 | |
| 73 | * @exe_path: Executable path of the task that initially restricted
|
| 74 | * itself, at creation time. Always NULL terminated, and never greater
|
| 75 | * than LANDLOCK_PATH_MAX_SIZE.
|
| 76 |
|
| 77 | char exe_path[];
|
| 78 | };
|
| 79 |
|
| 80 |
|
| 81 | #define LANDLOCK_PATH_MAX_SIZE (PATH_MAX + 11)
|
| 82 |
|
| 83 |
|
| 84 | static_assert(struct_size_t(struct landlock_details, exe_path,
|
| 85 | LANDLOCK_PATH_MAX_SIZE) <= KMALLOC_MAX_SIZE);
|
| 86 |
|
| 87 | |
| 88 | * struct landlock_hierarchy - Node in a domain hierarchy
|
| 89 |
|
| 90 | struct landlock_hierarchy {
|
| 91 | |
| 92 | * @parent: Pointer to the parent node, or NULL if it is a root
|
| 93 | * Landlock domain.
|
| 94 |
|
| 95 | struct landlock_hierarchy *parent;
|
| 96 | |
| 97 | * @usage: Number of potential children domains plus their parent
|
| 98 | * domain.
|
| 99 |
|
| 100 | refcount_t usage;
|
| 101 |
|
| 102 | #ifdef CONFIG_SECURITY_LANDLOCK_LOG
|
| 103 | |
| 104 | * @log_status: Whether this domain should be logged or not. Because
|
| 105 | * concurrent log entries may be created at the same time, it is still
|
| 106 | * possible to have several domain records of the same domain.
|
| 107 |
|
| 108 | enum landlock_log_status log_status;
|
| 109 | |
| 110 | * @num_denials: Number of access requests denied by this domain.
|
| 111 | * Masked (i.e. never logged) denials are still counted.
|
| 112 |
|
| 113 | atomic64_t num_denials;
|
| 114 | |
| 115 | * @id: Landlock domain ID, set once at domain creation time.
|
| 116 |
|
| 117 | u64 id;
|
| 118 | |
| 119 | * @details: Information about the related domain.
|
| 120 |
|
| 121 | const struct landlock_details *details;
|
| 122 | |
| 123 | * @log_same_exec: Set if the domain is *not* configured with
|
| 124 | * %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF. Set to true by default.
|
| 125 |
|
| 126 | u32 log_same_exec : 1,
|
| 127 | |
| 128 | * @log_new_exec: Set if the domain is configured with
|
| 129 | * %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default.
|
| 130 |
|
| 131 | log_new_exec : 1;
|
| 132 | |
| 133 | * @quiet_masks: Bitmasks of access that should be quieted (i.e. not
|
| 134 | * logged) if the related object is marked as quiet.
|
| 135 |
|
| 136 | struct access_masks quiet_masks;
|
| 137 | #endif
|
| 138 | };
|
| 139 |
|
| 140 | #ifdef CONFIG_SECURITY_LANDLOCK_LOG
|
| 141 |
|
| 142 | deny_masks_t
|
| 143 | landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
|
| 144 | const access_mask_t optional_access,
|
| 145 | const struct layer_masks *const masks);
|
| 146 |
|
| 147 | optional_access_t landlock_get_quiet_optional_accesses(
|
| 148 | const access_mask_t all_existing_optional_access,
|
| 149 | const deny_masks_t deny_masks, const struct layer_masks *const masks);
|
| 150 |
|
| 151 | int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy);
|
| 152 |
|
| 153 | static inline void
|
| 154 | landlock_free_hierarchy_details(struct landlock_hierarchy *const hierarchy)
|
| 155 | {
|
| 156 | if (!hierarchy || !hierarchy->details)
|
| 157 | return;
|
| 158 |
|
| 159 | put_pid(hierarchy->details->pid);
|
| 160 | kfree(hierarchy->details);
|
| 161 | }
|
| 162 |
|
| 163 | #else
|
| 164 |
|
| 165 | static inline int
|
| 166 | landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy)
|
| 167 | {
|
| 168 | return 0;
|
| 169 | }
|
| 170 |
|
| 171 | static inline void
|
| 172 | landlock_free_hierarchy_details(struct landlock_hierarchy *const hierarchy)
|
| 173 | {
|
| 174 | }
|
| 175 |
|
| 176 | #endif
|
| 177 |
|
| 178 | static inline void
|
| 179 | landlock_get_hierarchy(struct landlock_hierarchy *const hierarchy)
|
| 180 | {
|
| 181 | if (hierarchy)
|
| 182 | refcount_inc(&hierarchy->usage);
|
| 183 | }
|
| 184 |
|
| 185 | static inline void landlock_put_hierarchy(struct landlock_hierarchy *hierarchy)
|
| 186 | {
|
| 187 | while (hierarchy && refcount_dec_and_test(&hierarchy->usage)) {
|
| 188 | const struct landlock_hierarchy *const freeme = hierarchy;
|
| 189 |
|
| 190 | landlock_log_free_domain(hierarchy);
|
| 191 | landlock_free_hierarchy_details(hierarchy);
|
| 192 | hierarchy = hierarchy->parent;
|
| 193 | kfree(freeme);
|
| 194 | }
|
| 195 | }
|
| 196 |
|
| 197 | |
| 198 | * struct landlock_domain - Immutable Landlock domain
|
| 199 | *
|
| 200 | * A domain is created from a ruleset by landlock_merge_ruleset() and enforced
|
| 201 | * on a task. Once created, its rules and access masks are immutable. Unlike
|
| 202 | * &struct landlock_ruleset, a domain has no lock field.
|
| 203 |
|
| 204 | struct landlock_domain {
|
| 205 | |
| 206 | * @rules: Red-black tree storage for rules.
|
| 207 |
|
| 208 | struct landlock_rules rules;
|
| 209 | |
| 210 | * @hierarchy: Enables hierarchy identification even when a parent
|
| 211 | * domain vanishes. This is needed for the ptrace and scope
|
| 212 | * restrictions.
|
| 213 |
|
| 214 | struct landlock_hierarchy *hierarchy;
|
| 215 | union {
|
| 216 | |
| 217 | * @work_free: Enables to free a domain within a lockless
|
| 218 | * section. This is only used by landlock_put_domain_deferred()
|
| 219 | * when @usage reaches zero. The fields @usage, @num_layers and
|
| 220 | * @handled_masks are then unused.
|
| 221 |
|
| 222 | struct work_struct work_free;
|
| 223 | struct {
|
| 224 | |
| 225 | * @usage: Number of credentials referencing this
|
| 226 | * domain.
|
| 227 |
|
| 228 | refcount_t usage;
|
| 229 | |
| 230 | * @num_layers: Number of layers that are used in this
|
| 231 | * domain. This enables to check that all the layers
|
| 232 | * allow an access request.
|
| 233 |
|
| 234 | u32 num_layers;
|
| 235 | |
| 236 | * @handled_masks: Contains the subset of filesystem and
|
| 237 | * network actions that are restricted by a domain. A
|
| 238 | * domain saves all layers of merged rulesets in a stack
|
| 239 | * (FAM), starting from the first layer to the last one.
|
| 240 | * These layers are used when merging rulesets, for user
|
| 241 | * space backward compatibility (i.e. future-proof), and
|
| 242 | * to properly handle merged rulesets without
|
| 243 | * overlapping access rights. These layers are set once
|
| 244 | * and never changed for the lifetime of the domain.
|
| 245 | *
|
| 246 | * Not annotated with __counted_by(num_layers): clang
|
| 247 | * miscomputes the counter address for a FAM nested in
|
| 248 | * an anonymous struct within a union, which trips
|
| 249 | * FORTIFY:
|
| 250 | * https://github.com/llvm/llvm-project/pull/228309
|
| 251 |
|
| 252 | struct access_masks handled_masks[];
|
| 253 | };
|
| 254 | };
|
| 255 | };
|
| 256 |
|
| 257 | static inline access_mask_t
|
| 258 | landlock_get_fs_access_mask(const struct landlock_domain *const domain,
|
| 259 | const u16 layer_level)
|
| 260 | {
|
| 261 |
|
| 262 | return domain->handled_masks[layer_level].fs |
|
| 263 | _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
|
| 264 | }
|
| 265 |
|
| 266 | static inline access_mask_t
|
| 267 | landlock_get_net_access_mask(const struct landlock_domain *const domain,
|
| 268 | const u16 layer_level)
|
| 269 | {
|
| 270 | return domain->handled_masks[layer_level].net;
|
| 271 | }
|
| 272 |
|
| 273 | static inline access_mask_t
|
| 274 | landlock_get_scope_mask(const struct landlock_domain *const domain,
|
| 275 | const u16 layer_level)
|
| 276 | {
|
| 277 | return domain->handled_masks[layer_level].scope;
|
| 278 | }
|
| 279 |
|
| 280 | |
| 281 | * landlock_union_access_masks - Return all access rights handled in the
|
| 282 | * domain
|
| 283 | *
|
| 284 | * @domain: Landlock domain
|
| 285 | *
|
| 286 | * Return: An access_masks result of the OR of all the domain's access masks.
|
| 287 |
|
| 288 | static inline struct access_masks
|
| 289 | landlock_union_access_masks(const struct landlock_domain *const domain)
|
| 290 | {
|
| 291 | union access_masks_all matches = {};
|
| 292 | size_t layer_level;
|
| 293 |
|
| 294 | for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
|
| 295 | union access_masks_all layer = {
|
| 296 | .masks = domain->handled_masks[layer_level],
|
| 297 | };
|
| 298 |
|
| 299 | matches.all |= layer.all;
|
| 300 | }
|
| 301 |
|
| 302 | return matches.masks;
|
| 303 | }
|
| 304 |
|
| 305 | void landlock_put_domain(struct landlock_domain *const domain);
|
| 306 | void landlock_put_domain_deferred(struct landlock_domain *const domain);
|
| 307 |
|
| 308 | DEFINE_FREE(landlock_put_domain, struct landlock_domain *,
|
| 309 | if (!IS_ERR_OR_NULL(_T)) landlock_put_domain(_T))
|
| 310 |
|
| 311 | struct landlock_domain *
|
| 312 | landlock_merge_ruleset(struct landlock_domain *const parent,
|
| 313 | struct landlock_ruleset *const ruleset);
|
| 314 |
|
| 315 | bool landlock_unmask_layers(const struct landlock_domain *const domain,
|
| 316 | const struct landlock_id id,
|
| 317 | struct layer_masks *masks,
|
| 318 | const struct landlock_rule **matched_rule);
|
| 319 |
|
| 320 | access_mask_t
|
| 321 | landlock_init_layer_masks(const struct landlock_domain *const domain,
|
| 322 | const access_mask_t access_request,
|
| 323 | struct layer_masks *masks,
|
| 324 | const enum landlock_key_type key_type);
|
| 325 |
|
| 326 | static inline void landlock_get_domain(struct landlock_domain *const domain)
|
| 327 | {
|
| 328 | if (domain)
|
| 329 | refcount_inc(&domain->usage);
|
| 330 | }
|
| 331 |
|
| 332 | #endif
|