1e761a88cSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only 2e761a88cSMickaël Salaün /* 3e761a88cSMickaël Salaün * Landlock - Log helpers 4e761a88cSMickaël Salaün * 5e761a88cSMickaël Salaün * Copyright © 2023-2025 Microsoft Corporation 669ca5782SMickaël Salaün * Copyright © 2026 Cloudflare, Inc. 7e761a88cSMickaël Salaün */ 8e761a88cSMickaël Salaün 9e761a88cSMickaël Salaün #include <kunit/test.h> 10e761a88cSMickaël Salaün #include <linux/bitops.h> 11e761a88cSMickaël Salaün #include <uapi/linux/landlock.h> 12e761a88cSMickaël Salaün 13e761a88cSMickaël Salaün #include "access.h" 14e761a88cSMickaël Salaün #include "audit.h" 15e761a88cSMickaël Salaün #include "common.h" 16e761a88cSMickaël Salaün #include "cred.h" 17e761a88cSMickaël Salaün #include "domain.h" 18e761a88cSMickaël Salaün #include "limits.h" 19e761a88cSMickaël Salaün #include "log.h" 20e761a88cSMickaël Salaün #include "ruleset.h" 2167567f03SMickaël Salaün #include "trace.h" 22e761a88cSMickaël Salaün 23e761a88cSMickaël Salaün static struct landlock_hierarchy * 24e761a88cSMickaël Salaün get_hierarchy(const struct landlock_domain *const domain, const size_t layer) 25e761a88cSMickaël Salaün { 26e761a88cSMickaël Salaün struct landlock_hierarchy *hierarchy = domain->hierarchy; 27e761a88cSMickaël Salaün ssize_t i; 28e761a88cSMickaël Salaün 29e761a88cSMickaël Salaün if (WARN_ON_ONCE(layer >= domain->num_layers)) 30e761a88cSMickaël Salaün return hierarchy; 31e761a88cSMickaël Salaün 32e761a88cSMickaël Salaün for (i = domain->num_layers - 1; i > layer; i--) { 33e761a88cSMickaël Salaün if (WARN_ON_ONCE(!hierarchy->parent)) 34e761a88cSMickaël Salaün break; 35e761a88cSMickaël Salaün 36e761a88cSMickaël Salaün hierarchy = hierarchy->parent; 37e761a88cSMickaël Salaün } 38e761a88cSMickaël Salaün 39e761a88cSMickaël Salaün return hierarchy; 40e761a88cSMickaël Salaün } 41e761a88cSMickaël Salaün 42e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 43e761a88cSMickaël Salaün 44e761a88cSMickaël Salaün static void test_get_hierarchy(struct kunit *const test) 45e761a88cSMickaël Salaün { 46e761a88cSMickaël Salaün struct landlock_hierarchy dom0_hierarchy = { 47e761a88cSMickaël Salaün .id = 10, 48e761a88cSMickaël Salaün }; 49e761a88cSMickaël Salaün struct landlock_hierarchy dom1_hierarchy = { 50e761a88cSMickaël Salaün .parent = &dom0_hierarchy, 51e761a88cSMickaël Salaün .id = 20, 52e761a88cSMickaël Salaün }; 53e761a88cSMickaël Salaün struct landlock_hierarchy dom2_hierarchy = { 54e761a88cSMickaël Salaün .parent = &dom1_hierarchy, 55e761a88cSMickaël Salaün .id = 30, 56e761a88cSMickaël Salaün }; 57e761a88cSMickaël Salaün struct landlock_domain dom2 = { 58e761a88cSMickaël Salaün .hierarchy = &dom2_hierarchy, 59e761a88cSMickaël Salaün .num_layers = 3, 60e761a88cSMickaël Salaün }; 61e761a88cSMickaël Salaün 62e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id); 63e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id); 64e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id); 65e761a88cSMickaël Salaün /* KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); */ 66e761a88cSMickaël Salaün } 67e761a88cSMickaël Salaün 68e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 69e761a88cSMickaël Salaün 70e761a88cSMickaël Salaün /* Get the youngest layer that denied the access_request. */ 71e761a88cSMickaël Salaün static size_t get_denied_layer(const struct landlock_domain *const domain, 72e761a88cSMickaël Salaün access_mask_t *const access_request, 73e761a88cSMickaël Salaün const struct layer_masks *masks) 74e761a88cSMickaël Salaün { 75e761a88cSMickaël Salaün for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) { 76e761a88cSMickaël Salaün if (masks->layers[i].access & *access_request) { 77e761a88cSMickaël Salaün *access_request &= masks->layers[i].access; 78e761a88cSMickaël Salaün return i; 79e761a88cSMickaël Salaün } 80e761a88cSMickaël Salaün } 81e761a88cSMickaël Salaün 82e761a88cSMickaël Salaün /* Not found - fall back to default values */ 83e761a88cSMickaël Salaün *access_request = 0; 84e761a88cSMickaël Salaün return domain->num_layers - 1; 85e761a88cSMickaël Salaün } 86e761a88cSMickaël Salaün 87e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 88e761a88cSMickaël Salaün 89e761a88cSMickaël Salaün static void test_get_denied_layer(struct kunit *const test) 90e761a88cSMickaël Salaün { 91e761a88cSMickaël Salaün const struct landlock_domain dom = { 92e761a88cSMickaël Salaün .num_layers = 5, 93e761a88cSMickaël Salaün }; 94e761a88cSMickaël Salaün const struct layer_masks masks = { 95e761a88cSMickaël Salaün .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | 96e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 97e761a88cSMickaël Salaün .layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE | 98e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 99e761a88cSMickaël Salaün .layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 100e761a88cSMickaël Salaün }; 101e761a88cSMickaël Salaün access_mask_t access; 102e761a88cSMickaël Salaün 103e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_EXECUTE; 104e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks)); 105e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE); 106e761a88cSMickaël Salaün 107e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE; 108e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); 109e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE); 110e761a88cSMickaël Salaün 111e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_DIR; 112e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); 113e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 114e761a88cSMickaël Salaün 115e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR; 116e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); 117e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, 118e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 119e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR); 120e761a88cSMickaël Salaün 121e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR; 122e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks)); 123e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 124e761a88cSMickaël Salaün 125e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_WRITE_FILE; 126e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks)); 127e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, 0); 128e761a88cSMickaël Salaün } 129e761a88cSMickaël Salaün 130e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 131e761a88cSMickaël Salaün 132e761a88cSMickaël Salaün static size_t 133e761a88cSMickaël Salaün get_layer_from_deny_masks(access_mask_t *const access_request, 134e761a88cSMickaël Salaün const access_mask_t all_existing_optional_access, 135e761a88cSMickaël Salaün const deny_masks_t deny_masks, 136e761a88cSMickaël Salaün optional_access_t quiet_optional_accesses, 137e761a88cSMickaël Salaün bool *quiet) 138e761a88cSMickaël Salaün { 139e761a88cSMickaël Salaün const unsigned long access_opt = all_existing_optional_access; 140e761a88cSMickaël Salaün const unsigned long access_req = *access_request; 141e761a88cSMickaël Salaün access_mask_t missing = 0; 142e761a88cSMickaël Salaün size_t youngest_layer = 0; 143e761a88cSMickaël Salaün size_t access_index = 0; 144e761a88cSMickaël Salaün unsigned long access_bit; 145e761a88cSMickaël Salaün bool should_quiet = false; 146e761a88cSMickaël Salaün 147e761a88cSMickaël Salaün /* This will require change with new object types. */ 148e761a88cSMickaël Salaün WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); 149e761a88cSMickaël Salaün 150e761a88cSMickaël Salaün for_each_set_bit(access_bit, &access_opt, 151e761a88cSMickaël Salaün BITS_PER_TYPE(access_mask_t)) { 152e761a88cSMickaël Salaün if (access_req & BIT(access_bit)) { 153e761a88cSMickaël Salaün const size_t layer = 154e761a88cSMickaël Salaün (deny_masks >> 155e761a88cSMickaël Salaün (access_index * 156e761a88cSMickaël Salaün HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) & 157e761a88cSMickaël Salaün (LANDLOCK_MAX_NUM_LAYERS - 1); 158e761a88cSMickaël Salaün const bool layer_has_quiet = 159e761a88cSMickaël Salaün !!(quiet_optional_accesses & BIT(access_index)); 160e761a88cSMickaël Salaün 161e761a88cSMickaël Salaün if (layer > youngest_layer) { 162e761a88cSMickaël Salaün youngest_layer = layer; 163e761a88cSMickaël Salaün missing = BIT(access_bit); 164e761a88cSMickaël Salaün should_quiet = layer_has_quiet; 165e761a88cSMickaël Salaün } else if (layer == youngest_layer) { 166e761a88cSMickaël Salaün missing |= BIT(access_bit); 167e761a88cSMickaël Salaün /* 168e761a88cSMickaël Salaün * Whether the layer has rules with quiet flag 169e761a88cSMickaël Salaün * covering the file accessed does not depend on 170e761a88cSMickaël Salaün * the access, and so the following 171e761a88cSMickaël Salaün * WARN_ON_ONCE() should not fail. 172e761a88cSMickaël Salaün */ 173e761a88cSMickaël Salaün WARN_ON_ONCE(should_quiet && !layer_has_quiet); 174e761a88cSMickaël Salaün should_quiet = layer_has_quiet; 175e761a88cSMickaël Salaün } 176e761a88cSMickaël Salaün } 177e761a88cSMickaël Salaün access_index++; 178e761a88cSMickaël Salaün } 179e761a88cSMickaël Salaün 180e761a88cSMickaël Salaün *access_request = missing; 181e761a88cSMickaël Salaün *quiet = should_quiet; 182e761a88cSMickaël Salaün return youngest_layer; 183e761a88cSMickaël Salaün } 184e761a88cSMickaël Salaün 185e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 186e761a88cSMickaël Salaün 187e761a88cSMickaël Salaün static void test_get_layer_from_deny_masks(struct kunit *const test) 188e761a88cSMickaël Salaün { 189e761a88cSMickaël Salaün deny_masks_t deny_mask; 190e761a88cSMickaël Salaün access_mask_t access; 191e761a88cSMickaël Salaün optional_access_t quiet_optional_accesses; 192e761a88cSMickaël Salaün bool quiet; 193e761a88cSMickaël Salaün 194e761a88cSMickaël Salaün /* truncate:0 ioctl_dev:2 */ 195e761a88cSMickaël Salaün deny_mask = 0x20; 196e761a88cSMickaël Salaün quiet_optional_accesses = 0; 197e761a88cSMickaël Salaün 198e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 199e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 200e761a88cSMickaël Salaün get_layer_from_deny_masks( 201e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 202e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 203e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 204e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 205e761a88cSMickaël Salaün 206e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_IOCTL_DEV; 207e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 208e761a88cSMickaël Salaün get_layer_from_deny_masks( 209e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 210e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 211e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 212e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 213e761a88cSMickaël Salaün 214e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 215e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 216e761a88cSMickaël Salaün get_layer_from_deny_masks( 217e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 218e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 219e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 220e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 221e761a88cSMickaël Salaün 222e761a88cSMickaël Salaün /* layer denying truncate: quiet, ioctl: not quiet */ 223e761a88cSMickaël Salaün quiet_optional_accesses = 0b01; 224e761a88cSMickaël Salaün 225e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 226e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 227e761a88cSMickaël Salaün get_layer_from_deny_masks( 228e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 229e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 230e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 231e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 232e761a88cSMickaël Salaün 233e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_IOCTL_DEV; 234e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 235e761a88cSMickaël Salaün get_layer_from_deny_masks( 236e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 237e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 238e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 239e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 240e761a88cSMickaël Salaün 241e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 242e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 243e761a88cSMickaël Salaün get_layer_from_deny_masks( 244e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 245e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 246e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 247e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 248e761a88cSMickaël Salaün 249e761a88cSMickaël Salaün /* Reverse order - truncate:2 ioctl_dev:0 */ 250e761a88cSMickaël Salaün deny_mask = 0x02; 251e761a88cSMickaël Salaün quiet_optional_accesses = 0; 252e761a88cSMickaël Salaün 253e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 254e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 255e761a88cSMickaël Salaün get_layer_from_deny_masks( 256e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 257e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 258e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 259e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 260e761a88cSMickaël Salaün 261e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_IOCTL_DEV; 262e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 263e761a88cSMickaël Salaün get_layer_from_deny_masks( 264e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 265e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 266e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 267e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 268e761a88cSMickaël Salaün 269e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 270e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 271e761a88cSMickaël Salaün get_layer_from_deny_masks( 272e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 273e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 274e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 275e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 276e761a88cSMickaël Salaün 277e761a88cSMickaël Salaün /* layer denying truncate: quiet, ioctl: not quiet */ 278e761a88cSMickaël Salaün quiet_optional_accesses = 0b01; 279e761a88cSMickaël Salaün 280e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 281e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 282e761a88cSMickaël Salaün get_layer_from_deny_masks( 283e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 284e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 285e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 286e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 287e761a88cSMickaël Salaün 288e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_IOCTL_DEV; 289e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 290e761a88cSMickaël Salaün get_layer_from_deny_masks( 291e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 292e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 293e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 294e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 295e761a88cSMickaël Salaün 296e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 297e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 298e761a88cSMickaël Salaün get_layer_from_deny_masks( 299e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 300e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 301e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 302e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 303e761a88cSMickaël Salaün 304e761a88cSMickaël Salaün /* layer denying truncate: not quiet, ioctl: quiet */ 305e761a88cSMickaël Salaün quiet_optional_accesses = 0b10; 306e761a88cSMickaël Salaün 307e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 308e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 309e761a88cSMickaël Salaün get_layer_from_deny_masks( 310e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 311e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 312e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 313e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 314e761a88cSMickaël Salaün 315e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_IOCTL_DEV; 316e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 0, 317e761a88cSMickaël Salaün get_layer_from_deny_masks( 318e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 319e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 320e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 321e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 322e761a88cSMickaël Salaün 323e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 324e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 2, 325e761a88cSMickaël Salaün get_layer_from_deny_masks( 326e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 327e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 328e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 329e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 330e761a88cSMickaël Salaün 331e761a88cSMickaël Salaün /* truncate:15 ioctl_dev:15 */ 332e761a88cSMickaël Salaün deny_mask = 0xff; 333e761a88cSMickaël Salaün quiet_optional_accesses = 0; 334e761a88cSMickaël Salaün 335e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 336e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 15, 337e761a88cSMickaël Salaün get_layer_from_deny_masks( 338e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 339e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 340e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 341e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 342e761a88cSMickaël Salaün 343e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 344e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 15, 345e761a88cSMickaël Salaün get_layer_from_deny_masks( 346e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 347e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 348e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, 349e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_TRUNCATE | 350e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_IOCTL_DEV); 351e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, false); 352e761a88cSMickaël Salaün 353e761a88cSMickaël Salaün /* Both quiet (same layer so quietness must be the same) */ 354e761a88cSMickaël Salaün quiet_optional_accesses = 0b11; 355e761a88cSMickaël Salaün 356e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE; 357e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 15, 358e761a88cSMickaël Salaün get_layer_from_deny_masks( 359e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 360e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 361e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 362e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 363e761a88cSMickaël Salaün 364e761a88cSMickaël Salaün access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 365e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, 15, 366e761a88cSMickaël Salaün get_layer_from_deny_masks( 367e761a88cSMickaël Salaün &access, _LANDLOCK_ACCESS_FS_OPTIONAL, 368e761a88cSMickaël Salaün deny_mask, quiet_optional_accesses, &quiet)); 369e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, access, 370e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_TRUNCATE | 371e761a88cSMickaël Salaün LANDLOCK_ACCESS_FS_IOCTL_DEV); 372e761a88cSMickaël Salaün KUNIT_EXPECT_EQ(test, quiet, true); 373e761a88cSMickaël Salaün } 374e761a88cSMickaël Salaün 375e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 376e761a88cSMickaël Salaün 377e761a88cSMickaël Salaün static bool is_valid_request(const struct landlock_request *const request) 378e761a88cSMickaël Salaün { 379e761a88cSMickaël Salaün if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS)) 380e761a88cSMickaël Salaün return false; 381e761a88cSMickaël Salaün 382e761a88cSMickaël Salaün if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access))) 383e761a88cSMickaël Salaün return false; 384e761a88cSMickaël Salaün 385e761a88cSMickaël Salaün if (request->access) { 386e761a88cSMickaël Salaün if (WARN_ON_ONCE(!(!!request->layer_masks ^ 387e761a88cSMickaël Salaün !!request->all_existing_optional_access))) 388e761a88cSMickaël Salaün return false; 389e761a88cSMickaël Salaün } else { 390e761a88cSMickaël Salaün if (WARN_ON_ONCE(request->layer_masks || 391e761a88cSMickaël Salaün request->all_existing_optional_access)) 392e761a88cSMickaël Salaün return false; 393e761a88cSMickaël Salaün } 394e761a88cSMickaël Salaün 395e761a88cSMickaël Salaün if (request->deny_masks) { 396e761a88cSMickaël Salaün if (WARN_ON_ONCE(!request->all_existing_optional_access)) 397e761a88cSMickaël Salaün return false; 398e761a88cSMickaël Salaün static_assert(sizeof(request->all_existing_optional_access) == 399e761a88cSMickaël Salaün sizeof(u32)); 400e761a88cSMickaël Salaün if (WARN_ON_ONCE( 401e761a88cSMickaël Salaün request->quiet_optional_accesses >= 402e761a88cSMickaël Salaün BIT(hweight32( 403e761a88cSMickaël Salaün request->all_existing_optional_access)))) 404e761a88cSMickaël Salaün return false; 405e761a88cSMickaël Salaün } 406e761a88cSMickaël Salaün 407e761a88cSMickaël Salaün return true; 408e761a88cSMickaël Salaün } 409e761a88cSMickaël Salaün 41069ca5782SMickaël Salaün static access_mask_t 41169ca5782SMickaël Salaün pick_access_mask_for_request_type(const enum landlock_request_type type, 41269ca5782SMickaël Salaün const struct access_masks access_masks) 41369ca5782SMickaël Salaün { 41469ca5782SMickaël Salaün switch (type) { 41569ca5782SMickaël Salaün case LANDLOCK_REQUEST_FS_ACCESS: 41669ca5782SMickaël Salaün return access_masks.fs; 41769ca5782SMickaël Salaün case LANDLOCK_REQUEST_NET_ACCESS: 41869ca5782SMickaël Salaün return access_masks.net; 41969ca5782SMickaël Salaün default: 42069ca5782SMickaël Salaün WARN_ONCE(1, "Invalid request type %d passed to %s", type, 42169ca5782SMickaël Salaün __func__); 42269ca5782SMickaël Salaün return 0; 42369ca5782SMickaël Salaün } 42469ca5782SMickaël Salaün } 42569ca5782SMickaël Salaün 42669ca5782SMickaël Salaün /* 42769ca5782SMickaël Salaün * Whether a quiet rule silences the denial: the rule must cover the whole 42869ca5782SMickaël Salaün * denied access in the layer that denied it (a quiet rule in a non-denying 42969ca5782SMickaël Salaün * layer does not suppress the denial). 43069ca5782SMickaël Salaün */ 43169ca5782SMickaël Salaün static bool 43269ca5782SMickaël Salaün is_denial_quieted(const struct landlock_request *const request, 43369ca5782SMickaël Salaün const struct landlock_hierarchy *const youngest_denied, 43469ca5782SMickaël Salaün const access_mask_t missing, const bool object_quiet_flag) 43569ca5782SMickaël Salaün { 43669ca5782SMickaël Salaün if (object_quiet_flag) { 43769ca5782SMickaël Salaün const access_mask_t quiet_mask = 43869ca5782SMickaël Salaün pick_access_mask_for_request_type( 43969ca5782SMickaël Salaün request->type, youngest_denied->quiet_masks); 44069ca5782SMickaël Salaün 44169ca5782SMickaël Salaün return (quiet_mask & missing) == missing; 44269ca5782SMickaël Salaün } 44369ca5782SMickaël Salaün 44469ca5782SMickaël Salaün /* 44569ca5782SMickaël Salaün * Either the object is not quiet, or this is a scope request. We check 44669ca5782SMickaël Salaün * request->type to distinguish between the two cases. 44769ca5782SMickaël Salaün */ 44869ca5782SMickaël Salaün switch (request->type) { 44969ca5782SMickaël Salaün case LANDLOCK_REQUEST_SCOPE_SIGNAL: 45069ca5782SMickaël Salaün return !!(youngest_denied->quiet_masks.scope & 45169ca5782SMickaël Salaün LANDLOCK_SCOPE_SIGNAL); 45269ca5782SMickaël Salaün case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: 45369ca5782SMickaël Salaün return !!(youngest_denied->quiet_masks.scope & 45469ca5782SMickaël Salaün LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 45569ca5782SMickaël Salaün /* 45669ca5782SMickaël Salaün * Leave LANDLOCK_REQUEST_PTRACE and LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY 45769ca5782SMickaël Salaün * unhandled for now - they are never quiet. 45869ca5782SMickaël Salaün */ 45969ca5782SMickaël Salaün default: 46069ca5782SMickaël Salaün return false; 46169ca5782SMickaël Salaün } 46269ca5782SMickaël Salaün } 46369ca5782SMickaël Salaün 46469ca5782SMickaël Salaün /* 46569ca5782SMickaël Salaün * Computes whether a denial from youngest_denied is selected for logging by the 46669ca5782SMickaël Salaün * domain's policy: its logging must not be disabled (by both per-execution 46769ca5782SMickaël Salaün * flags being off, or by an ancestor's 46869ca5782SMickaël Salaün * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF), the per-execution flag matching 46969ca5782SMickaël Salaün * same_exec must be set, and no quiet rule may cover the denied access. 47069ca5782SMickaël Salaün * landlock_log_denial() computes this once and passes it to 47169ca5782SMickaël Salaün * landlock_audit_denial(), which additionally requires audit_enabled. 47269ca5782SMickaël Salaün */ 47369ca5782SMickaël Salaün static bool 47469ca5782SMickaël Salaün is_denial_logged(const struct landlock_request *const request, 47569ca5782SMickaël Salaün const struct landlock_hierarchy *const youngest_denied, 47669ca5782SMickaël Salaün const access_mask_t missing, const bool same_exec, 47769ca5782SMickaël Salaün const bool object_quiet_flag) 47869ca5782SMickaël Salaün { 47969ca5782SMickaël Salaün if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED) 48069ca5782SMickaël Salaün return false; 48169ca5782SMickaël Salaün 48269ca5782SMickaël Salaün if (!(same_exec ? youngest_denied->log_same_exec : 48369ca5782SMickaël Salaün youngest_denied->log_new_exec)) 48469ca5782SMickaël Salaün return false; 48569ca5782SMickaël Salaün 48669ca5782SMickaël Salaün return !is_denial_quieted(request, youngest_denied, missing, 48769ca5782SMickaël Salaün object_quiet_flag); 48869ca5782SMickaël Salaün } 48969ca5782SMickaël Salaün 490e761a88cSMickaël Salaün /** 491e761a88cSMickaël Salaün * landlock_log_denial - Log a denied access 492e761a88cSMickaël Salaün * 493e761a88cSMickaël Salaün * @subject: The Landlock subject's credential denying an action. 494e761a88cSMickaël Salaün * @request: Detail of the user space request. 495e761a88cSMickaël Salaün */ 496e761a88cSMickaël Salaün void landlock_log_denial(const struct landlock_cred_security *const subject, 497e761a88cSMickaël Salaün const struct landlock_request *const request) 498e761a88cSMickaël Salaün { 499e761a88cSMickaël Salaün struct landlock_hierarchy *youngest_denied; 500e761a88cSMickaël Salaün size_t youngest_layer; 501e761a88cSMickaël Salaün access_mask_t missing; 502e761a88cSMickaël Salaün bool object_quiet_flag = false; 503e761a88cSMickaël Salaün 504e761a88cSMickaël Salaün if (WARN_ON_ONCE(!subject || !subject->domain || 505e761a88cSMickaël Salaün !subject->domain->hierarchy || !request)) 506e761a88cSMickaël Salaün return; 507e761a88cSMickaël Salaün 508e761a88cSMickaël Salaün if (!is_valid_request(request)) 509e761a88cSMickaël Salaün return; 510e761a88cSMickaël Salaün 511e761a88cSMickaël Salaün missing = request->access; 512e761a88cSMickaël Salaün if (missing) { 513e761a88cSMickaël Salaün /* Gets the nearest domain that denies the request. */ 514e761a88cSMickaël Salaün if (request->layer_masks) { 515e761a88cSMickaël Salaün youngest_layer = get_denied_layer(subject->domain, 516e761a88cSMickaël Salaün &missing, 517e761a88cSMickaël Salaün request->layer_masks); 518e761a88cSMickaël Salaün object_quiet_flag = 519e761a88cSMickaël Salaün request->layer_masks->layers[youngest_layer] 520e761a88cSMickaël Salaün .quiet; 521e761a88cSMickaël Salaün } else { 522e761a88cSMickaël Salaün youngest_layer = get_layer_from_deny_masks( 523e761a88cSMickaël Salaün &missing, _LANDLOCK_ACCESS_FS_OPTIONAL, 524e761a88cSMickaël Salaün request->deny_masks, 525e761a88cSMickaël Salaün request->quiet_optional_accesses, 526e761a88cSMickaël Salaün &object_quiet_flag); 527e761a88cSMickaël Salaün } 528e761a88cSMickaël Salaün youngest_denied = 529e761a88cSMickaël Salaün get_hierarchy(subject->domain, youngest_layer); 530e761a88cSMickaël Salaün } else { 531e761a88cSMickaël Salaün youngest_layer = request->layer_plus_one - 1; 532e761a88cSMickaël Salaün youngest_denied = 533e761a88cSMickaël Salaün get_hierarchy(subject->domain, youngest_layer); 534e761a88cSMickaël Salaün } 535e761a88cSMickaël Salaün 53669ca5782SMickaël Salaün const bool same_exec = !!(subject->domain_exec & BIT(youngest_layer)); 53769ca5782SMickaël Salaün const bool logged = is_denial_logged(request, youngest_denied, missing, 53869ca5782SMickaël Salaün same_exec, object_quiet_flag); 539e761a88cSMickaël Salaün 540e761a88cSMickaël Salaün /* 541e761a88cSMickaël Salaün * Consistently keeps track of the number of denied access requests even 542e761a88cSMickaël Salaün * if audit is currently disabled, or if audit rules currently exclude 543e761a88cSMickaël Salaün * this record type, or if landlock_restrict_self(2)'s flags quiet logs. 544e761a88cSMickaël Salaün */ 545e761a88cSMickaël Salaün atomic64_inc(&youngest_denied->num_denials); 546e761a88cSMickaël Salaün 547*01ce260fSMickaël Salaün landlock_trace_denial(request, youngest_denied, missing, same_exec, 548*01ce260fSMickaël Salaün logged); 54969ca5782SMickaël Salaün landlock_audit_denial(request, youngest_denied, missing, logged); 550e761a88cSMickaël Salaün } 551e761a88cSMickaël Salaün 552e761a88cSMickaël Salaün /** 553e761a88cSMickaël Salaün * landlock_log_free_domain - Log domain deallocation 554e761a88cSMickaël Salaün * 555e761a88cSMickaël Salaün * @hierarchy: The domain's hierarchy being deallocated. 556e761a88cSMickaël Salaün * 55767567f03SMickaël Salaün * Called from landlock_put_domain_deferred() (via a work queue scheduled by 55867567f03SMickaël Salaün * hook_cred_free()) or directly from landlock_put_domain(). 559e761a88cSMickaël Salaün */ 560e761a88cSMickaël Salaün void landlock_log_free_domain(const struct landlock_hierarchy *const hierarchy) 561e761a88cSMickaël Salaün { 562e761a88cSMickaël Salaün if (WARN_ON_ONCE(!hierarchy)) 563e761a88cSMickaël Salaün return; 564e761a88cSMickaël Salaün 56567567f03SMickaël Salaün landlock_trace_free_domain(hierarchy); 566e761a88cSMickaël Salaün landlock_audit_free_domain(hierarchy); 567e761a88cSMickaël Salaün } 568e761a88cSMickaël Salaün 569e761a88cSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 570e761a88cSMickaël Salaün 571e761a88cSMickaël Salaün static struct kunit_case test_cases[] = { 572e761a88cSMickaël Salaün /* clang-format off */ 573e761a88cSMickaël Salaün KUNIT_CASE(test_get_hierarchy), 574e761a88cSMickaël Salaün KUNIT_CASE(test_get_denied_layer), 575e761a88cSMickaël Salaün KUNIT_CASE(test_get_layer_from_deny_masks), 576e761a88cSMickaël Salaün {} 577e761a88cSMickaël Salaün /* clang-format on */ 578e761a88cSMickaël Salaün }; 579e761a88cSMickaël Salaün 580e761a88cSMickaël Salaün static struct kunit_suite test_suite = { 581e761a88cSMickaël Salaün .name = "landlock_log", 582e761a88cSMickaël Salaün .test_cases = test_cases, 583e761a88cSMickaël Salaün }; 584e761a88cSMickaël Salaün 585e761a88cSMickaël Salaün kunit_test_suite(test_suite); 586e761a88cSMickaël Salaün 587e761a88cSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 588