| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Landlock - Log helpers |
| * |
| * Copyright © 2023-2025 Microsoft Corporation |
| * Copyright © 2026 Cloudflare, Inc. |
| */ |
| |
| #include <kunit/test.h> |
| #include <linux/bitops.h> |
| #include <uapi/linux/landlock.h> |
| |
| #include "access.h" |
| #include "audit.h" |
| #include "common.h" |
| #include "cred.h" |
| #include "domain.h" |
| #include "limits.h" |
| #include "log.h" |
| #include "ruleset.h" |
| #include "trace.h" |
| |
| static struct landlock_hierarchy * |
| get_hierarchy(const struct landlock_domain *const domain, const size_t layer) |
| { |
| struct landlock_hierarchy *hierarchy = domain->hierarchy; |
| ssize_t i; |
| |
| if (WARN_ON_ONCE(layer >= domain->num_layers)) |
| return hierarchy; |
| |
| for (i = domain->num_layers - 1; i > layer; i--) { |
| if (WARN_ON_ONCE(!hierarchy->parent)) |
| break; |
| |
| hierarchy = hierarchy->parent; |
| } |
| |
| return hierarchy; |
| } |
| |
| #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST |
| |
| static void test_get_hierarchy(struct kunit *const test) |
| { |
| struct landlock_hierarchy dom0_hierarchy = { |
| .id = 10, |
| }; |
| struct landlock_hierarchy dom1_hierarchy = { |
| .parent = &dom0_hierarchy, |
| .id = 20, |
| }; |
| struct landlock_hierarchy dom2_hierarchy = { |
| .parent = &dom1_hierarchy, |
| .id = 30, |
| }; |
| struct landlock_domain dom2 = { |
| .hierarchy = &dom2_hierarchy, |
| .num_layers = 3, |
| }; |
| |
| KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id); |
| KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id); |
| KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id); |
| /* KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); */ |
| } |
| |
| #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ |
| |
| /* Get the youngest layer that denied the access_request. */ |
| static size_t get_denied_layer(const struct landlock_domain *const domain, |
| access_mask_t *const access_request, |
| const struct layer_masks *masks) |
| { |
| for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) { |
| if (masks->layers[i].access & *access_request) { |
| *access_request &= masks->layers[i].access; |
| return i; |
| } |
| } |
| |
| /* Not found - fall back to default values */ |
| *access_request = 0; |
| return domain->num_layers - 1; |
| } |
| |
| #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST |
| |
| static void test_get_denied_layer(struct kunit *const test) |
| { |
| const struct landlock_domain dom = { |
| .num_layers = 5, |
| }; |
| const struct layer_masks masks = { |
| .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | |
| LANDLOCK_ACCESS_FS_READ_DIR, |
| .layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE | |
| LANDLOCK_ACCESS_FS_READ_DIR, |
| .layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR, |
| }; |
| access_mask_t access; |
| |
| access = LANDLOCK_ACCESS_FS_EXECUTE; |
| KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE); |
| |
| access = LANDLOCK_ACCESS_FS_READ_FILE; |
| KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE); |
| |
| access = LANDLOCK_ACCESS_FS_READ_DIR; |
| KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); |
| |
| access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR; |
| KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, |
| LANDLOCK_ACCESS_FS_READ_FILE | |
| LANDLOCK_ACCESS_FS_READ_DIR); |
| |
| access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR; |
| KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); |
| |
| access = LANDLOCK_ACCESS_FS_WRITE_FILE; |
| KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks)); |
| KUNIT_EXPECT_EQ(test, access, 0); |
| } |
| |
| #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ |
| |
| static size_t |
| get_layer_from_deny_masks(access_mask_t *const access_request, |
| const access_mask_t all_existing_optional_access, |
| const deny_masks_t deny_masks, |
| optional_access_t quiet_optional_accesses, |
| bool *quiet) |
| { |
| const unsigned long access_opt = all_existing_optional_access; |
| const unsigned long access_req = *access_request; |
| access_mask_t missing = 0; |
| size_t youngest_layer = 0; |
| size_t access_index = 0; |
| unsigned long access_bit; |
| bool should_quiet = false; |
| |
| /* 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)) { |
| if (access_req & BIT(access_bit)) { |
| const size_t layer = |
| (deny_masks >> |
| (access_index * |
| HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) & |
| (LANDLOCK_MAX_NUM_LAYERS - 1); |
| const bool layer_has_quiet = |
| !!(quiet_optional_accesses & BIT(access_index)); |
| |
| if (layer > youngest_layer) { |
| youngest_layer = layer; |
| missing = BIT(access_bit); |
| should_quiet = layer_has_quiet; |
| } else if (layer == youngest_layer) { |
| missing |= BIT(access_bit); |
| /* |
| * Whether the layer has rules with quiet flag |
| * covering the file accessed does not depend on |
| * the access, and so the following |
| * WARN_ON_ONCE() should not fail. |
| */ |
| WARN_ON_ONCE(should_quiet && !layer_has_quiet); |
| should_quiet = layer_has_quiet; |
| } |
| } |
| access_index++; |
| } |
| |
| *access_request = missing; |
| *quiet = should_quiet; |
| return youngest_layer; |
| } |
| |
| #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST |
| |
| static void test_get_layer_from_deny_masks(struct kunit *const test) |
| { |
| deny_masks_t deny_mask; |
| access_mask_t access; |
| optional_access_t quiet_optional_accesses; |
| bool quiet; |
| |
| /* truncate:0 ioctl_dev:2 */ |
| deny_mask = 0x20; |
| quiet_optional_accesses = 0; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 0, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| /* layer denying truncate: quiet, ioctl: not quiet */ |
| quiet_optional_accesses = 0b01; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 0, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| |
| access = LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| /* Reverse order - truncate:2 ioctl_dev:0 */ |
| deny_mask = 0x02; |
| quiet_optional_accesses = 0; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 0, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| /* layer denying truncate: quiet, ioctl: not quiet */ |
| quiet_optional_accesses = 0b01; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| |
| access = LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 0, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| |
| /* layer denying truncate: not quiet, ioctl: quiet */ |
| quiet_optional_accesses = 0b10; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 0, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 2, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| /* truncate:15 ioctl_dev:15 */ |
| deny_mask = 0xff; |
| quiet_optional_accesses = 0; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 15, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 15, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, |
| LANDLOCK_ACCESS_FS_TRUNCATE | |
| LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, false); |
| |
| /* Both quiet (same layer so quietness must be the same) */ |
| quiet_optional_accesses = 0b11; |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE; |
| KUNIT_EXPECT_EQ(test, 15, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| |
| access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; |
| KUNIT_EXPECT_EQ(test, 15, |
| get_layer_from_deny_masks( |
| &access, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| deny_mask, quiet_optional_accesses, &quiet)); |
| KUNIT_EXPECT_EQ(test, access, |
| LANDLOCK_ACCESS_FS_TRUNCATE | |
| LANDLOCK_ACCESS_FS_IOCTL_DEV); |
| KUNIT_EXPECT_EQ(test, quiet, true); |
| } |
| |
| #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ |
| |
| static bool is_valid_request(const struct landlock_request *const request) |
| { |
| if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS)) |
| return false; |
| |
| if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access))) |
| return false; |
| |
| if (request->access) { |
| if (WARN_ON_ONCE(!(!!request->layer_masks ^ |
| !!request->all_existing_optional_access))) |
| return false; |
| } else { |
| if (WARN_ON_ONCE(request->layer_masks || |
| request->all_existing_optional_access)) |
| return false; |
| } |
| |
| if (request->deny_masks) { |
| if (WARN_ON_ONCE(!request->all_existing_optional_access)) |
| return false; |
| static_assert(sizeof(request->all_existing_optional_access) == |
| sizeof(u32)); |
| if (WARN_ON_ONCE( |
| request->quiet_optional_accesses >= |
| BIT(hweight32( |
| request->all_existing_optional_access)))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static access_mask_t |
| pick_access_mask_for_request_type(const enum landlock_request_type type, |
| const struct access_masks access_masks) |
| { |
| switch (type) { |
| case LANDLOCK_REQUEST_FS_ACCESS: |
| return access_masks.fs; |
| case LANDLOCK_REQUEST_NET_ACCESS: |
| return access_masks.net; |
| default: |
| WARN_ONCE(1, "Invalid request type %d passed to %s", type, |
| __func__); |
| return 0; |
| } |
| } |
| |
| /* |
| * Whether a quiet rule silences the denial: the rule must cover the whole |
| * denied access in the layer that denied it (a quiet rule in a non-denying |
| * layer does not suppress the denial). |
| */ |
| static bool |
| is_denial_quieted(const struct landlock_request *const request, |
| const struct landlock_hierarchy *const youngest_denied, |
| const access_mask_t missing, const bool object_quiet_flag) |
| { |
| if (object_quiet_flag) { |
| const access_mask_t quiet_mask = |
| pick_access_mask_for_request_type( |
| request->type, youngest_denied->quiet_masks); |
| |
| return (quiet_mask & missing) == missing; |
| } |
| |
| /* |
| * Either the object is not quiet, or this is a scope request. We check |
| * request->type to distinguish between the two cases. |
| */ |
| switch (request->type) { |
| case LANDLOCK_REQUEST_SCOPE_SIGNAL: |
| return !!(youngest_denied->quiet_masks.scope & |
| LANDLOCK_SCOPE_SIGNAL); |
| case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: |
| return !!(youngest_denied->quiet_masks.scope & |
| LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); |
| /* |
| * Leave LANDLOCK_REQUEST_PTRACE and LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY |
| * unhandled for now - they are never quiet. |
| */ |
| default: |
| return false; |
| } |
| } |
| |
| /* |
| * Computes whether a denial from youngest_denied is selected for logging by the |
| * domain's policy: its logging must not be disabled (by both per-execution |
| * flags being off, or by an ancestor's |
| * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF), the per-execution flag matching |
| * same_exec must be set, and no quiet rule may cover the denied access. |
| * landlock_log_denial() computes this once and passes it to |
| * landlock_audit_denial(), which additionally requires audit_enabled. |
| */ |
| static bool |
| is_denial_logged(const struct landlock_request *const request, |
| const struct landlock_hierarchy *const youngest_denied, |
| const access_mask_t missing, const bool same_exec, |
| const bool object_quiet_flag) |
| { |
| if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED) |
| return false; |
| |
| if (!(same_exec ? youngest_denied->log_same_exec : |
| youngest_denied->log_new_exec)) |
| return false; |
| |
| return !is_denial_quieted(request, youngest_denied, missing, |
| object_quiet_flag); |
| } |
| |
| /** |
| * landlock_log_denial - Log a denied access |
| * |
| * @subject: The Landlock subject's credential denying an action. |
| * @request: Detail of the user space request. |
| */ |
| void landlock_log_denial(const struct landlock_cred_security *const subject, |
| const struct landlock_request *const request) |
| { |
| struct landlock_hierarchy *youngest_denied; |
| size_t youngest_layer; |
| access_mask_t missing; |
| bool object_quiet_flag = false; |
| |
| if (WARN_ON_ONCE(!subject || !subject->domain || |
| !subject->domain->hierarchy || !request)) |
| return; |
| |
| if (!is_valid_request(request)) |
| return; |
| |
| missing = request->access; |
| if (missing) { |
| /* Gets the nearest domain that denies the request. */ |
| if (request->layer_masks) { |
| youngest_layer = get_denied_layer(subject->domain, |
| &missing, |
| request->layer_masks); |
| object_quiet_flag = |
| request->layer_masks->layers[youngest_layer] |
| .quiet; |
| } else { |
| youngest_layer = get_layer_from_deny_masks( |
| &missing, _LANDLOCK_ACCESS_FS_OPTIONAL, |
| request->deny_masks, |
| request->quiet_optional_accesses, |
| &object_quiet_flag); |
| } |
| youngest_denied = |
| get_hierarchy(subject->domain, youngest_layer); |
| } else { |
| youngest_layer = request->layer_plus_one - 1; |
| youngest_denied = |
| get_hierarchy(subject->domain, youngest_layer); |
| } |
| |
| const bool same_exec = !!(subject->domain_exec & BIT(youngest_layer)); |
| const bool logged = is_denial_logged(request, youngest_denied, missing, |
| same_exec, object_quiet_flag); |
| |
| /* |
| * Consistently keeps track of the number of denied access requests even |
| * if audit is currently disabled, or if audit rules currently exclude |
| * this record type, or if landlock_restrict_self(2)'s flags quiet logs. |
| */ |
| atomic64_inc(&youngest_denied->num_denials); |
| |
| landlock_trace_denial(request, youngest_denied, missing, same_exec, |
| logged); |
| landlock_audit_denial(request, youngest_denied, missing, logged); |
| } |
| |
| /** |
| * landlock_log_free_domain - Log domain deallocation |
| * |
| * @hierarchy: The domain's hierarchy being deallocated. |
| * |
| * Called from landlock_put_domain_deferred() (via a work queue scheduled by |
| * hook_cred_free()) or directly from landlock_put_domain(). |
| */ |
| void landlock_log_free_domain(const struct landlock_hierarchy *const hierarchy) |
| { |
| if (WARN_ON_ONCE(!hierarchy)) |
| return; |
| |
| landlock_trace_free_domain(hierarchy); |
| landlock_audit_free_domain(hierarchy); |
| } |
| |
| #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST |
| |
| static struct kunit_case test_cases[] = { |
| /* clang-format off */ |
| KUNIT_CASE(test_get_hierarchy), |
| KUNIT_CASE(test_get_denied_layer), |
| KUNIT_CASE(test_get_layer_from_deny_masks), |
| {} |
| /* clang-format on */ |
| }; |
| |
| static struct kunit_suite test_suite = { |
| .name = "landlock_log", |
| .test_cases = test_cases, |
| }; |
| |
| kunit_test_suite(test_suite); |
| |
| #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ |