Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

landlock: Log truncate and IOCTL denials

Add audit support to the file_truncate and file_ioctl hooks.

Add a deny_masks_t type and related helpers to store the domain's layer
level per optional access rights (i.e. LANDLOCK_ACCESS_FS_TRUNCATE and
LANDLOCK_ACCESS_FS_IOCTL_DEV) when opening a file, which cannot be
inferred later. In practice, the landlock_file_security aligned blob size is
still 16 bytes because this new one-byte deny_masks field follows the
existing two-bytes allowed_access field and precede the packed
fown_subject.

Implementing deny_masks_t with a bitfield instead of a struct enables a
generic implementation to store and extract layer levels.

Add KUnit tests to check the identification of a layer level from a
deny_masks_t, and the computation of a deny_masks_t from an access right
with its layer level or a layer_mask_t array.

Audit event sample:

type=LANDLOCK_DENY msg=audit(1729738800.349:44): domain=195ba459b blockers=fs.ioctl_dev path="/dev/tty" dev="devtmpfs" ino=9 ioctlcmd=0x5401

Cc: Günther Noack <gnoack@google.com>
Link: https://lore.kernel.org/r/20250320190717.2287696-15-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>

+307 -6
+24 -1
security/landlock/access.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Landlock LSM - Access types and helpers 3 + * Landlock - Access types and helpers 4 4 * 5 5 * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 6 * Copyright © 2018-2020 ANSSI ··· 26 26 /* clang-format off */ 27 27 #define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \ 28 28 LANDLOCK_ACCESS_FS_REFER) 29 + /* clang-format on */ 30 + 31 + /* clang-format off */ 32 + #define _LANDLOCK_ACCESS_FS_OPTIONAL ( \ 33 + LANDLOCK_ACCESS_FS_TRUNCATE | \ 34 + LANDLOCK_ACCESS_FS_IOCTL_DEV) 29 35 /* clang-format on */ 30 36 31 37 typedef u16 access_mask_t; ··· 65 59 66 60 /* Makes sure all layers can be checked. */ 67 61 static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS); 62 + 63 + /* 64 + * Tracks domains responsible of a denied access. This is required to avoid 65 + * storing in each object the full layer_masks[] required by update_request(). 66 + */ 67 + typedef u8 deny_masks_t; 68 + 69 + /* 70 + * Makes sure all optional access rights can be tied to a layer index (cf. 71 + * get_deny_mask). 72 + */ 73 + static_assert(BITS_PER_TYPE(deny_masks_t) >= 74 + (HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1) * 75 + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL))); 76 + 77 + /* LANDLOCK_MAX_NUM_LAYERS must be a power of two (cf. deny_masks_t assert). */ 78 + static_assert(HWEIGHT(LANDLOCK_MAX_NUM_LAYERS) == 1); 68 79 69 80 /* Upgrades with all initially denied by default access rights. */ 70 81 static inline struct access_masks
+96 -5
security/landlock/audit.c
··· 12 12 #include <linux/pid.h> 13 13 #include <uapi/linux/landlock.h> 14 14 15 + #include "access.h" 15 16 #include "audit.h" 16 17 #include "common.h" 17 18 #include "cred.h" ··· 250 249 251 250 #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 252 251 252 + static size_t 253 + get_layer_from_deny_masks(access_mask_t *const access_request, 254 + const access_mask_t all_existing_optional_access, 255 + const deny_masks_t deny_masks) 256 + { 257 + const unsigned long access_opt = all_existing_optional_access; 258 + const unsigned long access_req = *access_request; 259 + access_mask_t missing = 0; 260 + size_t youngest_layer = 0; 261 + size_t access_index = 0; 262 + unsigned long access_bit; 263 + 264 + /* This will require change with new object types. */ 265 + WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); 266 + 267 + for_each_set_bit(access_bit, &access_opt, 268 + BITS_PER_TYPE(access_mask_t)) { 269 + if (access_req & BIT(access_bit)) { 270 + const size_t layer = 271 + (deny_masks >> (access_index * 4)) & 272 + (LANDLOCK_MAX_NUM_LAYERS - 1); 273 + 274 + if (layer > youngest_layer) { 275 + youngest_layer = layer; 276 + missing = BIT(access_bit); 277 + } else if (layer == youngest_layer) { 278 + missing |= BIT(access_bit); 279 + } 280 + } 281 + access_index++; 282 + } 283 + 284 + *access_request = missing; 285 + return youngest_layer; 286 + } 287 + 288 + #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 289 + 290 + static void test_get_layer_from_deny_masks(struct kunit *const test) 291 + { 292 + deny_masks_t deny_mask; 293 + access_mask_t access; 294 + 295 + /* truncate:0 ioctl_dev:2 */ 296 + deny_mask = 0x20; 297 + 298 + access = LANDLOCK_ACCESS_FS_TRUNCATE; 299 + KUNIT_EXPECT_EQ(test, 0, 300 + get_layer_from_deny_masks(&access, 301 + _LANDLOCK_ACCESS_FS_OPTIONAL, 302 + deny_mask)); 303 + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 304 + 305 + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 306 + KUNIT_EXPECT_EQ(test, 2, 307 + get_layer_from_deny_masks(&access, 308 + _LANDLOCK_ACCESS_FS_OPTIONAL, 309 + deny_mask)); 310 + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); 311 + 312 + /* truncate:15 ioctl_dev:15 */ 313 + deny_mask = 0xff; 314 + 315 + access = LANDLOCK_ACCESS_FS_TRUNCATE; 316 + KUNIT_EXPECT_EQ(test, 15, 317 + get_layer_from_deny_masks(&access, 318 + _LANDLOCK_ACCESS_FS_OPTIONAL, 319 + deny_mask)); 320 + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); 321 + 322 + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; 323 + KUNIT_EXPECT_EQ(test, 15, 324 + get_layer_from_deny_masks(&access, 325 + _LANDLOCK_ACCESS_FS_OPTIONAL, 326 + deny_mask)); 327 + KUNIT_EXPECT_EQ(test, access, 328 + LANDLOCK_ACCESS_FS_TRUNCATE | 329 + LANDLOCK_ACCESS_FS_IOCTL_DEV); 330 + } 331 + 332 + #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 333 + 253 334 static bool is_valid_request(const struct landlock_request *const request) 254 335 { 255 336 if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS)) ··· 341 258 return false; 342 259 343 260 if (request->access) { 344 - if (WARN_ON_ONCE(!request->layer_masks)) 261 + if (WARN_ON_ONCE(!(!!request->layer_masks ^ 262 + !!request->all_existing_optional_access))) 345 263 return false; 346 264 } else { 347 - if (WARN_ON_ONCE(request->layer_masks)) 265 + if (WARN_ON_ONCE(request->layer_masks || 266 + request->all_existing_optional_access)) 348 267 return false; 349 268 } 350 269 351 270 if (WARN_ON_ONCE(!!request->layer_masks ^ !!request->layer_masks_size)) 352 271 return false; 272 + 273 + if (request->deny_masks) { 274 + if (WARN_ON_ONCE(!request->all_existing_optional_access)) 275 + return false; 276 + } 353 277 354 278 return true; 355 279 } ··· 390 300 subject->domain, &missing, request->layer_masks, 391 301 request->layer_masks_size); 392 302 } else { 393 - /* This will change with the next commit. */ 394 - WARN_ON_ONCE(1); 395 - youngest_layer = subject->domain->num_layers; 303 + youngest_layer = get_layer_from_deny_masks( 304 + &missing, request->all_existing_optional_access, 305 + request->deny_masks); 396 306 } 397 307 youngest_denied = 398 308 get_hierarchy(subject->domain, youngest_layer); ··· 477 387 /* clang-format off */ 478 388 KUNIT_CASE(test_get_hierarchy), 479 389 KUNIT_CASE(test_get_denied_layer), 390 + KUNIT_CASE(test_get_layer_from_deny_masks), 480 391 {} 481 392 /* clang-format on */ 482 393 };
+4
security/landlock/audit.h
··· 42 42 /* Required fields for requests with layer masks. */ 43 43 const layer_mask_t (*layer_masks)[]; 44 44 size_t layer_masks_size; 45 + 46 + /* Required fields for requests with deny masks. */ 47 + const access_mask_t all_existing_optional_access; 48 + deny_masks_t deny_masks; 45 49 }; 46 50 47 51 #ifdef CONFIG_AUDIT
+133
security/landlock/domain.c
··· 7 7 * Copyright © 2024-2025 Microsoft Corporation 8 8 */ 9 9 10 + #include <kunit/test.h> 11 + #include <linux/bitops.h> 12 + #include <linux/bits.h> 10 13 #include <linux/cred.h> 11 14 #include <linux/file.h> 12 15 #include <linux/mm.h> ··· 18 15 #include <linux/sched.h> 19 16 #include <linux/uidgid.h> 20 17 18 + #include "access.h" 19 + #include "common.h" 21 20 #include "domain.h" 22 21 #include "id.h" 23 22 ··· 130 125 atomic64_set(&hierarchy->num_denials, 0); 131 126 return 0; 132 127 } 128 + 129 + static deny_masks_t 130 + get_layer_deny_mask(const access_mask_t all_existing_optional_access, 131 + const unsigned long access_bit, const size_t layer) 132 + { 133 + unsigned long access_weight; 134 + 135 + /* This may require change with new object types. */ 136 + WARN_ON_ONCE(all_existing_optional_access != 137 + _LANDLOCK_ACCESS_FS_OPTIONAL); 138 + 139 + if (WARN_ON_ONCE(layer >= LANDLOCK_MAX_NUM_LAYERS)) 140 + return 0; 141 + 142 + access_weight = hweight_long(all_existing_optional_access & 143 + GENMASK(access_bit, 0)); 144 + if (WARN_ON_ONCE(access_weight < 1)) 145 + return 0; 146 + 147 + return layer 148 + << ((access_weight - 1) * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1)); 149 + } 150 + 151 + #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 152 + 153 + static void test_get_layer_deny_mask(struct kunit *const test) 154 + { 155 + const unsigned long truncate = BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE); 156 + const unsigned long ioctl_dev = BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV); 157 + 158 + KUNIT_EXPECT_EQ(test, 0, 159 + get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, 160 + truncate, 0)); 161 + KUNIT_EXPECT_EQ(test, 0x3, 162 + get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, 163 + truncate, 3)); 164 + 165 + KUNIT_EXPECT_EQ(test, 0, 166 + get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, 167 + ioctl_dev, 0)); 168 + KUNIT_EXPECT_EQ(test, 0xf0, 169 + get_layer_deny_mask(_LANDLOCK_ACCESS_FS_OPTIONAL, 170 + ioctl_dev, 15)); 171 + } 172 + 173 + #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 174 + 175 + deny_masks_t 176 + landlock_get_deny_masks(const access_mask_t all_existing_optional_access, 177 + const access_mask_t optional_access, 178 + const layer_mask_t (*const layer_masks)[], 179 + const size_t layer_masks_size) 180 + { 181 + const unsigned long access_opt = optional_access; 182 + unsigned long access_bit; 183 + deny_masks_t deny_masks = 0; 184 + 185 + /* This may require change with new object types. */ 186 + WARN_ON_ONCE(access_opt != 187 + (optional_access & all_existing_optional_access)); 188 + 189 + if (WARN_ON_ONCE(!layer_masks)) 190 + return 0; 191 + 192 + if (WARN_ON_ONCE(!access_opt)) 193 + return 0; 194 + 195 + for_each_set_bit(access_bit, &access_opt, layer_masks_size) { 196 + const layer_mask_t mask = (*layer_masks)[access_bit]; 197 + 198 + if (!mask) 199 + continue; 200 + 201 + /* __fls(1) == 0 */ 202 + deny_masks |= get_layer_deny_mask(all_existing_optional_access, 203 + access_bit, __fls(mask)); 204 + } 205 + return deny_masks; 206 + } 207 + 208 + #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 209 + 210 + static void test_landlock_get_deny_masks(struct kunit *const test) 211 + { 212 + const layer_mask_t layers1[BITS_PER_TYPE(access_mask_t)] = { 213 + [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) | 214 + BIT_ULL(9), 215 + [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = BIT_ULL(1), 216 + [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = BIT_ULL(2) | 217 + BIT_ULL(0), 218 + }; 219 + 220 + KUNIT_EXPECT_EQ(test, 0x1, 221 + landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, 222 + LANDLOCK_ACCESS_FS_TRUNCATE, 223 + &layers1, ARRAY_SIZE(layers1))); 224 + KUNIT_EXPECT_EQ(test, 0x20, 225 + landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, 226 + LANDLOCK_ACCESS_FS_IOCTL_DEV, 227 + &layers1, ARRAY_SIZE(layers1))); 228 + KUNIT_EXPECT_EQ( 229 + test, 0x21, 230 + landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL, 231 + LANDLOCK_ACCESS_FS_TRUNCATE | 232 + LANDLOCK_ACCESS_FS_IOCTL_DEV, 233 + &layers1, ARRAY_SIZE(layers1))); 234 + } 235 + 236 + #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 237 + 238 + #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 239 + 240 + static struct kunit_case test_cases[] = { 241 + /* clang-format off */ 242 + KUNIT_CASE(test_get_layer_deny_mask), 243 + KUNIT_CASE(test_landlock_get_deny_masks), 244 + {} 245 + /* clang-format on */ 246 + }; 247 + 248 + static struct kunit_suite test_suite = { 249 + .name = "landlock_domain", 250 + .test_cases = test_cases, 251 + }; 252 + 253 + kunit_test_suite(test_suite); 254 + 255 + #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 133 256 134 257 #endif /* CONFIG_AUDIT */
+7
security/landlock/domain.h
··· 18 18 #include <linux/sched.h> 19 19 #include <linux/slab.h> 20 20 21 + #include "access.h" 21 22 #include "audit.h" 22 23 23 24 enum landlock_log_status { ··· 107 106 }; 108 107 109 108 #ifdef CONFIG_AUDIT 109 + 110 + deny_masks_t 111 + landlock_get_deny_masks(const access_mask_t all_existing_optional_access, 112 + const access_mask_t optional_access, 113 + const layer_mask_t (*const layer_masks)[], 114 + size_t layer_masks_size); 110 115 111 116 int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy); 112 117
+34
security/landlock/fs.c
··· 43 43 #include "audit.h" 44 44 #include "common.h" 45 45 #include "cred.h" 46 + #include "domain.h" 46 47 #include "fs.h" 47 48 #include "limits.h" 48 49 #include "object.h" ··· 1672 1671 * file access rights in the opened struct file. 1673 1672 */ 1674 1673 landlock_file(file)->allowed_access = allowed_access; 1674 + #ifdef CONFIG_AUDIT 1675 + landlock_file(file)->deny_masks = landlock_get_deny_masks( 1676 + _LANDLOCK_ACCESS_FS_OPTIONAL, optional_access, &layer_masks, 1677 + ARRAY_SIZE(layer_masks)); 1678 + #endif /* CONFIG_AUDIT */ 1675 1679 1676 1680 if ((open_access_request & allowed_access) == open_access_request) 1677 1681 return 0; ··· 1701 1695 */ 1702 1696 if (landlock_file(file)->allowed_access & LANDLOCK_ACCESS_FS_TRUNCATE) 1703 1697 return 0; 1698 + 1699 + landlock_log_denial(landlock_cred(file->f_cred), &(struct landlock_request) { 1700 + .type = LANDLOCK_REQUEST_FS_ACCESS, 1701 + .audit = { 1702 + .type = LSM_AUDIT_DATA_FILE, 1703 + .u.file = file, 1704 + }, 1705 + .all_existing_optional_access = _LANDLOCK_ACCESS_FS_OPTIONAL, 1706 + .access = LANDLOCK_ACCESS_FS_TRUNCATE, 1707 + #ifdef CONFIG_AUDIT 1708 + .deny_masks = landlock_file(file)->deny_masks, 1709 + #endif /* CONFIG_AUDIT */ 1710 + }); 1704 1711 return -EACCES; 1705 1712 } 1706 1713 ··· 1738 1719 is_masked_device_ioctl(cmd)) 1739 1720 return 0; 1740 1721 1722 + landlock_log_denial(landlock_cred(file->f_cred), &(struct landlock_request) { 1723 + .type = LANDLOCK_REQUEST_FS_ACCESS, 1724 + .audit = { 1725 + .type = LSM_AUDIT_DATA_IOCTL_OP, 1726 + .u.op = &(struct lsm_ioctlop_audit) { 1727 + .path = file->f_path, 1728 + .cmd = cmd, 1729 + }, 1730 + }, 1731 + .all_existing_optional_access = _LANDLOCK_ACCESS_FS_OPTIONAL, 1732 + .access = LANDLOCK_ACCESS_FS_IOCTL_DEV, 1733 + #ifdef CONFIG_AUDIT 1734 + .deny_masks = landlock_file(file)->deny_masks, 1735 + #endif /* CONFIG_AUDIT */ 1736 + }); 1741 1737 return -EACCES; 1742 1738 } 1743 1739
+9
security/landlock/fs.h
··· 55 55 * needed to authorize later operations on the open file. 56 56 */ 57 57 access_mask_t allowed_access; 58 + 59 + #ifdef CONFIG_AUDIT 60 + /** 61 + * @deny_masks: Domain layer levels that deny an optional access (see 62 + * _LANDLOCK_ACCESS_FS_OPTIONAL). 63 + */ 64 + deny_masks_t deny_masks; 65 + #endif /* CONFIG_AUDIT */ 66 + 58 67 /** 59 68 * @fown_subject: Landlock credential of the task that set the PID that 60 69 * may receive a signal e.g., SIGURG when writing MSG_OOB to the