// SPDX-License-Identifier: GPL-2.0-only /* * Landlock - Domain management * * Copyright © 2016-2020 Mickaël Salaün * Copyright © 2018-2020 ANSSI * Copyright © 2024-2025 Microsoft Corporation * Copyright © 2026 Cloudflare, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "access.h" #include "common.h" #include "domain.h" #include "id.h" #include "limits.h" #include "ruleset.h" static void build_check_domain(void) { const struct landlock_domain domain = { .num_layers = ~0, }; BUILD_BUG_ON(domain.num_layers < LANDLOCK_MAX_NUM_LAYERS); } static struct landlock_domain *create_domain(const u32 num_layers) { struct landlock_domain *new_domain; build_check_domain(); new_domain = kzalloc_flex(*new_domain, handled_masks, num_layers, GFP_KERNEL_ACCOUNT); if (!new_domain) return ERR_PTR(-ENOMEM); refcount_set(&new_domain->usage, 1); new_domain->rules.root_inode = RB_ROOT; #if IS_ENABLED(CONFIG_INET) new_domain->rules.root_net_port = RB_ROOT; #endif /* IS_ENABLED(CONFIG_INET) */ new_domain->num_layers = num_layers; return new_domain; } static void free_domain(struct landlock_domain *const domain) { might_sleep(); landlock_free_rules(&domain->rules); landlock_put_hierarchy(domain->hierarchy); kfree(domain); } void landlock_put_domain(struct landlock_domain *const domain) { might_sleep(); if (domain && refcount_dec_and_test(&domain->usage)) free_domain(domain); } static void free_domain_work(struct work_struct *const work) { struct landlock_domain *domain; domain = container_of(work, struct landlock_domain, work_free); free_domain(domain); } void landlock_put_domain_deferred(struct landlock_domain *const domain) { if (domain && refcount_dec_and_test(&domain->usage)) { INIT_WORK(&domain->work_free, free_domain_work); schedule_work(&domain->work_free); } } /* The returned access has the same lifetime as the domain. */ static const struct landlock_rule * find_rule(const struct landlock_domain *const domain, const struct landlock_id id) { const struct rb_root *root; const struct rb_node *node; root = landlock_get_rule_root((struct landlock_rules *)&domain->rules, id.type); if (IS_ERR(root)) return NULL; node = root->rb_node; while (node) { struct landlock_rule *this = rb_entry(node, struct landlock_rule, node); if (this->key.data == id.key.data) return this; if (this->key.data < id.key.data) node = node->rb_right; else node = node->rb_left; } return NULL; } /** * landlock_unmask_layers - Remove the access rights in @masks which are * granted by a matching rule * * Looks up the rule matching @id in @domain, then updates the set of * (per-layer) unfulfilled access rights @masks so that all the access rights * granted by that rule are removed (because they are now fulfilled). * * @domain: The Landlock domain to search for a matching rule. * @id: Identifier for the rule target (e.g. inode, port). * @masks: A matrix of unfulfilled access rights for each layer. * @matched_rule: Optional output for the matched rule (for tracing); set to * the matching rule when non-NULL, unchanged otherwise. * * Return: True if the request is allowed (i.e. the access rights granted all * remaining unfulfilled access rights and masks has no leftover set bits). */ bool landlock_unmask_layers(const struct landlock_domain *const domain, const struct landlock_id id, struct layer_masks *masks, const struct landlock_rule **matched_rule) { const struct landlock_rule *rule; if (!masks) return true; rule = find_rule(domain, id); if (!rule) return false; if (matched_rule) *matched_rule = rule; /* * An access is granted if, for each policy layer, at least one rule * encountered on the pathwalk grants the requested access, regardless * of its position in the layer stack. We must then check the remaining * layers for each inode, from the first added layer to the last one. * When there are multiple requested accesses, for each policy layer, * the full set of requested accesses may not be granted by only one * rule, but by the union (binary OR) of multiple rules. For example, * /a/b + /a grants /a/b . * * This function is called once per matching rule during the pathwalk, * progressively clearing bits in @masks. The overall access decision * is per-layer: access is granted iff masks->layers[l].access == 0 for * all layers l. When two independent mechanisms can each grant access * within a layer (e.g. a path rule OR a scope exception), the * composition must evaluate per-layer: FOR-ALL l (A(l) OR B(l)), not * (FOR-ALL l A(l)) OR (FOR-ALL l B(l)), to prevent bypass when * different layers grant via different mechanisms. */ for (size_t i = 0; i < rule->num_layers; i++) { const struct landlock_layer *const layer = &rule->layers[i]; /* Clear the bits where the layer in the rule grants access. */ masks->layers[layer->level - 1].access &= ~layer->access; #ifdef CONFIG_SECURITY_LANDLOCK_LOG /* Collect rule flags for each layer. */ if (layer->flags.quiet) masks->layers[layer->level - 1].quiet = true; #endif /* CONFIG_SECURITY_LANDLOCK_LOG */ } for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) { if (masks->layers[i].access) return false; } return true; } typedef access_mask_t get_access_mask_t(const struct landlock_domain *const domain, const u16 layer_level); /** * landlock_init_layer_masks - Initialize layer masks from an access request * * Populates @masks such that for each access right in @access_request, the bits * for all the layers are set where this access right is handled. Rule flags * are also zeroed. * * @domain: The domain that defines the current restrictions. * @access_request: The requested access rights to check. * @masks: Layer access masks to populate. * @key_type: The key type to switch between access masks of different types. * * Return: An access mask where each access right bit is set which is handled in * any of the active layers in @domain. */ access_mask_t landlock_init_layer_masks(const struct landlock_domain *const domain, const access_mask_t access_request, struct layer_masks *const masks, const enum landlock_key_type key_type) { access_mask_t handled_accesses = 0; get_access_mask_t *get_access_mask; switch (key_type) { case LANDLOCK_KEY_INODE: get_access_mask = landlock_get_fs_access_mask; break; #if IS_ENABLED(CONFIG_INET) case LANDLOCK_KEY_NET_PORT: get_access_mask = landlock_get_net_access_mask; break; #endif /* IS_ENABLED(CONFIG_INET) */ default: WARN_ON_ONCE(1); return 0; } /* An empty access request can happen because of O_WRONLY | O_RDWR. */ if (!access_request) return 0; for (size_t i = 0; i < domain->num_layers; i++) { const access_mask_t handled = get_access_mask(domain, i); masks->layers[i].access = access_request & handled; handled_accesses |= masks->layers[i].access; #ifdef CONFIG_SECURITY_LANDLOCK_LOG masks->layers[i].quiet = false; #endif /* CONFIG_SECURITY_LANDLOCK_LOG */ } for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->layers); i++) { masks->layers[i].access = 0; #ifdef CONFIG_SECURITY_LANDLOCK_LOG masks->layers[i].quiet = false; #endif /* CONFIG_SECURITY_LANDLOCK_LOG */ } return handled_accesses; } static int merge_tree(struct landlock_domain *const dst, struct landlock_ruleset *const src, const enum landlock_key_type key_type) { struct landlock_rule *walker_rule, *next_rule; struct rb_root *src_root; int err = 0; might_sleep(); lockdep_assert_held(&src->lock); src_root = landlock_get_rule_root(&src->rules, key_type); if (IS_ERR(src_root)) return PTR_ERR(src_root); /* Merges the @src tree. */ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, src_root, node) { struct landlock_layer layers[] = { { .level = dst->num_layers, } }; const struct landlock_id id = { .key = walker_rule->key, .type = key_type, }; if (WARN_ON_ONCE(walker_rule->num_layers != 1)) return -EINVAL; if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) return -EINVAL; layers[0].access = walker_rule->layers[0].access; layers[0].flags = walker_rule->layers[0].flags; err = landlock_store_rule(&dst->rules, id, &layers, ARRAY_SIZE(layers)); if (err) return err; } return err; } static int merge_ruleset(struct landlock_domain *const dst, struct landlock_ruleset *const src) { int err = 0; might_sleep(); /* Should already be checked by landlock_merge_ruleset() */ if (WARN_ON_ONCE(!src)) return 0; /* Only merge into a domain. */ if (WARN_ON_ONCE(!dst || !dst->hierarchy)) return -EINVAL; lockdep_assert_held(&src->lock); /* Stacks the new layer. */ if (WARN_ON_ONCE(dst->num_layers < 1)) return -EINVAL; dst->handled_masks[dst->num_layers - 1] = landlock_upgrade_handled_access_masks(src->handled_masks); /* Merges the @src inode tree. */ err = merge_tree(dst, src, LANDLOCK_KEY_INODE); if (err) return err; #if IS_ENABLED(CONFIG_INET) /* Merges the @src network port tree. */ err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT); if (err) return err; #endif /* IS_ENABLED(CONFIG_INET) */ return 0; } static int inherit_tree(struct landlock_domain *const parent, struct landlock_domain *const child, const enum landlock_key_type key_type) { struct landlock_rule *walker_rule, *next_rule; struct rb_root *parent_root; int err = 0; might_sleep(); parent_root = landlock_get_rule_root(&parent->rules, key_type); if (IS_ERR(parent_root)) return PTR_ERR(parent_root); /* Copies the @parent inode or network tree. */ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, parent_root, node) { const struct landlock_id id = { .key = walker_rule->key, .type = key_type, }; err = landlock_store_rule(&child->rules, id, &walker_rule->layers, walker_rule->num_layers); if (err) return err; } return err; } static int inherit_ruleset(struct landlock_domain *const parent, struct landlock_domain *const child) { int err = 0; might_sleep(); if (!parent) return 0; /* Copies the @parent inode tree. */ err = inherit_tree(parent, child, LANDLOCK_KEY_INODE); if (err) return err; #if IS_ENABLED(CONFIG_INET) /* Copies the @parent network port tree. */ err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT); if (err) return err; #endif /* IS_ENABLED(CONFIG_INET) */ if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) return -EINVAL; /* * Copies the parent layer stack and leaves a space for the new layer. */ memcpy(child->handled_masks, parent->handled_masks, flex_array_size(parent, handled_masks, parent->num_layers)); if (WARN_ON_ONCE(!parent->hierarchy)) return -EINVAL; landlock_get_hierarchy(parent->hierarchy); child->hierarchy->parent = parent->hierarchy; return 0; } /** * landlock_merge_ruleset - Merge a ruleset with a domain * * @parent: Parent domain. * @ruleset: New ruleset to be merged. * * The current task is requesting to be restricted. The subjective credentials * must not be in an overridden state. cf. landlock_init_hierarchy_log(). * * The caller must hold @ruleset->lock. * * Return: A new domain merging @parent and @ruleset on success, or ERR_PTR() on * failure. If @parent is NULL, the new domain duplicates @ruleset. */ struct landlock_domain * landlock_merge_ruleset(struct landlock_domain *const parent, struct landlock_ruleset *const ruleset) { struct landlock_domain *new_dom __free(landlock_put_domain) = NULL; u32 num_layers; int err; might_sleep(); lockdep_assert_held(&ruleset->lock); if (WARN_ON_ONCE(!ruleset)) return ERR_PTR(-EINVAL); if (parent) { if (parent->num_layers >= LANDLOCK_MAX_NUM_LAYERS) return ERR_PTR(-E2BIG); num_layers = parent->num_layers + 1; } else { num_layers = 1; } /* Creates a new domain... */ new_dom = create_domain(num_layers); if (IS_ERR(new_dom)) return new_dom; new_dom->hierarchy = kzalloc_obj(*new_dom->hierarchy, GFP_KERNEL_ACCOUNT); if (!new_dom->hierarchy) return ERR_PTR(-ENOMEM); refcount_set(&new_dom->hierarchy->usage, 1); /* ...as a child of @parent... */ err = inherit_ruleset(parent, new_dom); if (err) return ERR_PTR(err); /* ...and including @ruleset. */ err = merge_ruleset(new_dom, ruleset); if (err) return ERR_PTR(err); err = landlock_init_hierarchy_log(new_dom->hierarchy); if (err) return ERR_PTR(err); #ifdef CONFIG_SECURITY_LANDLOCK_LOG new_dom->hierarchy->quiet_masks = ruleset->quiet_masks; #endif /* CONFIG_SECURITY_LANDLOCK_LOG */ return no_free_ptr(new_dom); } #ifdef CONFIG_SECURITY_LANDLOCK_LOG /** * get_current_exe - Get the current's executable path, if any * * @exe_str: Returned pointer to a path string with a lifetime tied to the * returned buffer, if any. * @exe_size: Returned size of @exe_str (including the trailing null * character), if any. * * Return: A pointer to an allocated buffer where @exe_str point to, %NULL if * there is no executable path, or an error otherwise. */ static const void *get_current_exe(const char **const exe_str, size_t *const exe_size) { const size_t buffer_size = LANDLOCK_PATH_MAX_SIZE; struct mm_struct *mm = current->mm; struct file *file __free(fput) = NULL; char *buffer __free(kfree) = NULL; const char *exe; ssize_t size; if (!mm) return NULL; file = get_mm_exe_file(mm); if (!file) return NULL; buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) return ERR_PTR(-ENOMEM); exe = d_path(&file->f_path, buffer, buffer_size); if (WARN_ON_ONCE(IS_ERR(exe))) /* Should never happen according to LANDLOCK_PATH_MAX_SIZE. */ return ERR_CAST(exe); size = buffer + buffer_size - exe; if (WARN_ON_ONCE(size <= 0)) return ERR_PTR(-ENAMETOOLONG); *exe_size = size; *exe_str = exe; return no_free_ptr(buffer); } /* * Return: A newly allocated object describing a domain, or an error * otherwise. */ static struct landlock_details *get_current_details(void) { /* Cf. audit_log_d_path_exe() */ static const char null_path[] = "(null)"; const char *path_str = null_path; size_t path_size = sizeof(null_path); const void *buffer __free(kfree) = NULL; struct landlock_details *details; buffer = get_current_exe(&path_str, &path_size); if (IS_ERR(buffer)) return ERR_CAST(buffer); /* * Create the new details according to the path's length. Account to * the calling task's memcg, like the other Landlock per-domain * allocations, even if it may not control the related size. */ details = kzalloc_flex(*details, exe_path, path_size, GFP_KERNEL_ACCOUNT); if (!details) return ERR_PTR(-ENOMEM); memcpy(details->exe_path, path_str, path_size); details->pid = get_pid(task_tgid(current)); details->uid = from_kuid(&init_user_ns, current_uid()); get_task_comm(details->comm, current); return details; } /** * landlock_init_hierarchy_log - Partially initialize landlock_hierarchy * * @hierarchy: The hierarchy to initialize. * * The current task is referenced as the domain that is enforcing the * restriction. The subjective credentials must not be in an overridden state. * * @hierarchy->parent and @hierarchy->usage should already be set. * * Return: 0 on success, -errno on failure. */ int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy) { struct landlock_details *details; details = get_current_details(); if (IS_ERR(details)) return PTR_ERR(details); hierarchy->details = details; hierarchy->id = landlock_get_id_range(1); /* * The hierarchy is born unobservable: landlock_restrict_self() moves it * out of LANDLOCK_LOG_UNCOMMITTED once it has emitted the creation * event, so the matching free_domain event fires for it and not for a * hierarchy whose creation was never observed. */ hierarchy->log_status = LANDLOCK_LOG_UNCOMMITTED; hierarchy->log_same_exec = true; hierarchy->log_new_exec = false; atomic64_set(&hierarchy->num_denials, 0); return 0; } static deny_masks_t get_layer_deny_mask(const access_mask_t all_existing_optional_access, const unsigned long access_bit, const size_t layer) { unsigned long access_weight; /* This may require change with new object types. */ WARN_ON_ONCE(all_existing_optional_access != _LANDLOCK_ACCESS_FS_OPTIONAL); if (WARN_ON_ONCE(layer >= LANDLOCK_MAX_NUM_LAYERS)) return 0; access_weight = hweight_long(all_existing_optional_access & GENMASK(access_bit, 0)); if (WARN_ON_ONCE(access_weight < 1)) return 0; return layer << ((access_weight - 1) * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1)); } /** * landlock_get_quiet_optional_accesses - Get optional accesses which are * covered by quiet rule flags. * * @all_existing_optional_access: Bitmask of valid optional accesses. * @deny_masks: Domain layer levels that denied each optional access (the * deny_masks field on struct landlock_file_security). * @masks: The struct layer_masks collected during the path walk. * * Return: a bitmask of which optional accesses are denied by layers for which * the quiet flag was collected during the path walk. */ optional_access_t landlock_get_quiet_optional_accesses( const access_mask_t all_existing_optional_access, const deny_masks_t deny_masks, const struct layer_masks *const masks) { const unsigned long access_opt = all_existing_optional_access; size_t access_index = 0; unsigned long access_bit; optional_access_t quiet_optional_accesses = 0; /* This will require change with new object types. */ WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); for_each_set_bit(access_bit, &access_opt, BITS_PER_TYPE(access_mask_t)) { const u8 layer = (deny_masks >> (access_index * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) & (LANDLOCK_MAX_NUM_LAYERS - 1); if (masks->layers[layer].quiet) quiet_optional_accesses |= BIT(access_index); access_index++; } return quiet_optional_accesses; } #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_get_layer_deny_mask(struct kunit *const test) { const unsigned long truncate = BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE); const unsigned long ioctl_dev = BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV); KUNIT_EXPECT_EQ(test, 0, get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, truncate, 0)); KUNIT_EXPECT_EQ(test, 0x3, get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, truncate, 3)); KUNIT_EXPECT_EQ(test, 0, get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, ioctl_dev, 0)); KUNIT_EXPECT_EQ(test, 0xf0, get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, ioctl_dev, 15)); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ deny_masks_t landlock_get_deny_masks(const access_mask_t all_existing_optional_access, const access_mask_t optional_access, const struct layer_masks *const masks) { const unsigned long access_opt = optional_access; unsigned long access_bit; deny_masks_t deny_masks = 0; access_mask_t all_denied = 0; /* This may require change with new object types. */ WARN_ON_ONCE(!access_mask_subset(optional_access, all_existing_optional_access)); if (WARN_ON_ONCE(!masks)) return 0; if (WARN_ON_ONCE(!access_opt)) return 0; for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) { const access_mask_t denied = masks->layers[i].access & optional_access; const unsigned long newly_denied = denied & ~all_denied; if (!newly_denied) continue; for_each_set_bit(access_bit, &newly_denied, 8 * sizeof(access_mask_t)) { deny_masks |= get_layer_deny_mask( all_existing_optional_access, access_bit, i); } all_denied |= denied; } return deny_masks; } #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_landlock_get_deny_masks(struct kunit *const test) { const struct layer_masks layers1 = { .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_IOCTL_DEV, .layers[1].access = LANDLOCK_ACCESS_FS_TRUNCATE, .layers[2].access = LANDLOCK_ACCESS_FS_IOCTL_DEV, .layers[9].access = LANDLOCK_ACCESS_FS_EXECUTE, }; KUNIT_EXPECT_EQ(test, 0x1, landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, LANDLOCK_ACCESS_FS_TRUNCATE, &layers1)); KUNIT_EXPECT_EQ(test, 0x20, landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, LANDLOCK_ACCESS_FS_IOCTL_DEV, &layers1)); KUNIT_EXPECT_EQ( test, 0x21, landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV, &layers1)); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static struct kunit_case test_cases[] = { /* clang-format off */ KUNIT_CASE(test_get_layer_deny_mask), KUNIT_CASE(test_landlock_get_deny_masks), {} /* clang-format on */ }; static struct kunit_suite test_suite = { .name = "landlock_domain", .test_cases = test_cases, }; kunit_test_suite(test_suite); #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ #endif /* CONFIG_SECURITY_LANDLOCK_LOG */